Skip to content

Commit

Permalink
[clang-format] Add space in placement new expression
Browse files Browse the repository at this point in the history
Add AfterPlacementNew option to SpaceBeforeParensOptions to have more
control on placement new expressions.

Fixes #41501
Relates to #54703

Differential Revision: https://reviews.llvm.org/D127270
  • Loading branch information
omarahmed1111 authored and owenca committed Oct 20, 2023
1 parent b99f7e6 commit 7c15dd6
Show file tree
Hide file tree
Showing 7 changed files with 204 additions and 13 deletions.
27 changes: 27 additions & 0 deletions clang/docs/ClangFormatStyleOptions.rst
Expand Up @@ -5224,6 +5224,33 @@ the configuration (without a prefix: ``Auto``).
void operator++ (int a); vs. void operator++(int a);
object.operator++ (10); object.operator++(10);

* ``AfterPlacementOperatorStyle AfterPlacementOperator`` :versionbadge:`clang-format 18`

Defines in which cases to put a space between ``new/delete`` operators
and opening parentheses.

Possible values:

* ``APO_Never`` (in configuration: ``Never``)
Remove space after ``new/delete`` operators and before ``(``.

.. code-block:: c++

new(buf) T;
delete(buf) T;

* ``APO_Always`` (in configuration: ``Always``)
Always add space after ``new/delete`` operators and before ``(``.

.. code-block:: c++

new (buf) T;
delete (buf) T;

* ``APO_Leave`` (in configuration: ``Leave``)
Leave placement ``new/delete`` expressions as they are.


* ``bool AfterRequiresInClause`` If ``true``, put space between requires keyword in a requires clause and
opening parentheses, if there is one.

Expand Down
76 changes: 65 additions & 11 deletions clang/docs/tools/dump_format_style.py
Expand Up @@ -143,11 +143,18 @@ def __str__(self):


class NestedField(object):
def __init__(self, name, comment):
def __init__(self, name, comment, version):
self.name = name
self.comment = comment.strip()
self.version = version

def __str__(self):
if self.version:
return "\n* ``%s`` :versionbadge:`clang-format %s`\n%s" % (
self.name,
self.version,
doxygen2rst(indent(self.comment, 2, indent_first_line=False)),
)
return "\n* ``%s`` %s" % (
self.name,
doxygen2rst(indent(self.comment, 2, indent_first_line=False)),
Expand All @@ -165,18 +172,28 @@ def __str__(self):


class NestedEnum(object):
def __init__(self, name, enumtype, comment, values):
def __init__(self, name, enumtype, comment, version, values):
self.name = name
self.comment = comment
self.values = values
self.type = enumtype
self.version = version

def __str__(self):
s = "\n* ``%s %s``\n%s" % (
to_yaml_type(self.type),
self.name,
doxygen2rst(indent(self.comment, 2)),
)
s = ""
if self.version:
s = "\n* ``%s %s`` :versionbadge:`clang-format %s`\n\n%s" % (
to_yaml_type(self.type),
self.name,
self.version,
doxygen2rst(indent(self.comment, 2)),
)
else:
s = "\n* ``%s %s``\n%s" % (
to_yaml_type(self.type),
self.name,
doxygen2rst(indent(self.comment, 2)),
)
s += indent("\nPossible values:\n\n", 2)
s += indent("\n".join(map(str, self.values)), 2)
return s
Expand Down Expand Up @@ -278,7 +295,9 @@ class State:
InFieldComment,
InEnum,
InEnumMemberComment,
) = range(8)
InNestedEnum,
InNestedEnumMemberComment,
) = range(10)

state = State.BeforeStruct

Expand Down Expand Up @@ -344,27 +363,38 @@ class State:
state = State.InStruct
nested_structs[nested_struct.name] = nested_struct
elif state == State.InNestedFieldComment:
if line.startswith("///"):
if line.startswith(r"/// \version"):
match = re.match(r"/// \\version\s*(?P<version>[0-9.]+)*", line)
if match:
version = match.group("version")
elif line.startswith("///"):
comment += self.__clean_comment_line(line)
elif line.startswith("enum"):
state = State.InNestedEnum
name = re.sub(r"enum\s+(\w+)\s*(:((\s*\w+)+)\s*)?\{", "\\1", line)
enum = Enum(name, comment)
else:
state = State.InNestedStruct
field_type, field_name = re.match(
r"([<>:\w(,\s)]+)\s+(\w+);", line
).groups()
# if not version:
# self.__warning(f"missing version for {field_name}", line)
if field_type in enums:
nested_struct.values.append(
NestedEnum(
field_name,
field_type,
comment,
version,
enums[field_type].values,
)
)
else:
nested_struct.values.append(
NestedField(field_type + " " + field_name, comment)
NestedField(field_type + " " + field_name, comment, version)
)

version = None
elif state == State.InEnum:
if line.startswith("///"):
state = State.InEnumMemberComment
Expand All @@ -376,6 +406,17 @@ class State:
# Enum member without documentation. Must be documented where the enum
# is used.
pass
elif state == State.InNestedEnum:
if line.startswith("///"):
state = State.InNestedEnumMemberComment
comment = self.__clean_comment_line(line)
elif line == "};":
state = State.InNestedStruct
enums[enum.name] = enum
else:
# Enum member without documentation. Must be
# documented where the enum is used.
pass
elif state == State.InEnumMemberComment:
if line.startswith("///"):
comment += self.__clean_comment_line(line)
Expand All @@ -389,6 +430,19 @@ class State:
else:
config = val
enum.values.append(EnumValue(val, comment, config))
elif state == State.InNestedEnumMemberComment:
if line.startswith("///"):
comment += self.__clean_comment_line(line)
else:
state = State.InNestedEnum
val = line.replace(",", "")
pos = val.find(" // ")
if pos != -1:
config = val[pos + 4 :]
val = val[:pos]
else:
config = val
enum.values.append(EnumValue(val, comment, config))
if state != State.Finished:
raise Exception("Not finished by the end of file")

Expand Down
28 changes: 26 additions & 2 deletions clang/include/clang/Format/Format.h
Expand Up @@ -4128,6 +4128,28 @@ struct FormatStyle {
/// object.operator++ (10); object.operator++(10);
/// \endcode
bool AfterOverloadedOperator;
/// Styles for adding spacing between ``new/delete`` operators and opening
/// parentheses.
enum AfterPlacementOperatorStyle : int8_t {
/// Remove space after ``new/delete`` operators and before ``(``.
/// \code
/// new(buf) T;
/// delete(buf) T;
/// \endcode
APO_Never,
/// Always add space after ``new/delete`` operators and before ``(``.
/// \code
/// new (buf) T;
/// delete (buf) T;
/// \endcode
APO_Always,
/// Leave placement ``new/delete`` expressions as they are.
APO_Leave,
};
/// Defines in which cases to put a space between ``new/delete`` operators
/// and opening parentheses.
/// \version 18
AfterPlacementOperatorStyle AfterPlacementOperator;
/// If ``true``, put space between requires keyword in a requires clause and
/// opening parentheses, if there is one.
/// \code
Expand Down Expand Up @@ -4160,8 +4182,9 @@ struct FormatStyle {
: AfterControlStatements(false), AfterForeachMacros(false),
AfterFunctionDeclarationName(false),
AfterFunctionDefinitionName(false), AfterIfMacros(false),
AfterOverloadedOperator(false), AfterRequiresInClause(false),
AfterRequiresInExpression(false), BeforeNonEmptyParentheses(false) {}
AfterOverloadedOperator(false), AfterPlacementOperator(APO_Leave),
AfterRequiresInClause(false), AfterRequiresInExpression(false),
BeforeNonEmptyParentheses(false) {}

bool operator==(const SpaceBeforeParensCustom &Other) const {
return AfterControlStatements == Other.AfterControlStatements &&
Expand All @@ -4171,6 +4194,7 @@ struct FormatStyle {
AfterFunctionDefinitionName == Other.AfterFunctionDefinitionName &&
AfterIfMacros == Other.AfterIfMacros &&
AfterOverloadedOperator == Other.AfterOverloadedOperator &&
AfterPlacementOperator == Other.AfterPlacementOperator &&
AfterRequiresInClause == Other.AfterRequiresInClause &&
AfterRequiresInExpression == Other.AfterRequiresInExpression &&
BeforeNonEmptyParentheses == Other.BeforeNonEmptyParentheses;
Expand Down
19 changes: 19 additions & 0 deletions clang/lib/Format/Format.cpp
Expand Up @@ -504,6 +504,22 @@ struct ScalarEnumerationTraits<FormatStyle::QualifierAlignmentStyle> {
}
};

template <>
struct MappingTraits<
FormatStyle::SpaceBeforeParensCustom::AfterPlacementOperatorStyle> {
static void
mapping(IO &IO,
FormatStyle::SpaceBeforeParensCustom::AfterPlacementOperatorStyle
&Value) {
IO.enumCase(Value, "Always",
FormatStyle::SpaceBeforeParensCustom::APO_Always);
IO.enumCase(Value, "Never",
FormatStyle::SpaceBeforeParensCustom::APO_Never);
IO.enumCase(Value, "Leave",
FormatStyle::SpaceBeforeParensCustom::APO_Leave);
}
};

template <> struct MappingTraits<FormatStyle::RawStringFormat> {
static void mapping(IO &IO, FormatStyle::RawStringFormat &Format) {
IO.mapOptional("Language", Format.Language);
Expand Down Expand Up @@ -679,6 +695,7 @@ template <> struct MappingTraits<FormatStyle::SpaceBeforeParensCustom> {
Spacing.AfterFunctionDeclarationName);
IO.mapOptional("AfterIfMacros", Spacing.AfterIfMacros);
IO.mapOptional("AfterOverloadedOperator", Spacing.AfterOverloadedOperator);
IO.mapOptional("AfterPlacementOperator", Spacing.AfterPlacementOperator);
IO.mapOptional("AfterRequiresInClause", Spacing.AfterRequiresInClause);
IO.mapOptional("AfterRequiresInExpression",
Spacing.AfterRequiresInExpression);
Expand Down Expand Up @@ -1369,6 +1386,8 @@ static void expandPresetsSpaceBeforeParens(FormatStyle &Expanded) {

switch (Expanded.SpaceBeforeParens) {
case FormatStyle::SBPO_Never:
Expanded.SpaceBeforeParensOptions.AfterPlacementOperator =
FormatStyle::SpaceBeforeParensCustom::APO_Never;
break;
case FormatStyle::SBPO_ControlStatements:
Expanded.SpaceBeforeParensOptions.AfterControlStatements = true;
Expand Down
13 changes: 13 additions & 0 deletions clang/lib/Format/TokenAnnotator.cpp
Expand Up @@ -4234,6 +4234,19 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line,
return Style.SpaceBeforeParensOptions.AfterIfMacros ||
spaceRequiredBeforeParens(Right);
}
if (Style.SpaceBeforeParens == FormatStyle::SBPO_Custom &&
Left.isOneOf(tok::kw_new, tok::kw_delete) &&
Right.isNot(TT_OverloadedOperatorLParen) &&
!(Line.MightBeFunctionDecl && Left.is(TT_FunctionDeclarationName))) {
if (Style.SpaceBeforeParensOptions.AfterPlacementOperator ==
FormatStyle::SpaceBeforeParensCustom::APO_Always ||
(Style.SpaceBeforeParensOptions.AfterPlacementOperator ==
FormatStyle::SpaceBeforeParensCustom::APO_Leave &&
Right.hasWhitespaceBefore())) {
return true;
}
return false;
}
if (Line.Type == LT_ObjCDecl)
return true;
if (Left.is(tok::semi))
Expand Down
18 changes: 18 additions & 0 deletions clang/unittests/Format/ConfigParseTest.cpp
Expand Up @@ -591,6 +591,24 @@ TEST(ConfigParseTest, ParsesConfiguration) {
SpaceBeforeParens,
FormatStyle::SBPO_ControlStatementsExceptControlMacros);

Style.SpaceBeforeParens = FormatStyle::SBPO_Custom;
Style.SpaceBeforeParensOptions.AfterPlacementOperator =
FormatStyle::SpaceBeforeParensCustom::APO_Always;
CHECK_PARSE("SpaceBeforeParensOptions:\n"
" AfterPlacementOperator: Never",
SpaceBeforeParensOptions.AfterPlacementOperator,
FormatStyle::SpaceBeforeParensCustom::APO_Never);

CHECK_PARSE("SpaceBeforeParensOptions:\n"
" AfterPlacementOperator: Always",
SpaceBeforeParensOptions.AfterPlacementOperator,
FormatStyle::SpaceBeforeParensCustom::APO_Always);

CHECK_PARSE("SpaceBeforeParensOptions:\n"
" AfterPlacementOperator: Leave",
SpaceBeforeParensOptions.AfterPlacementOperator,
FormatStyle::SpaceBeforeParensCustom::APO_Leave);

// For backward compatibility:
Style.SpacesInParens = FormatStyle::SIPO_Never;
Style.SpacesInParensOptions = {};
Expand Down
36 changes: 36 additions & 0 deletions clang/unittests/Format/FormatTest.cpp
Expand Up @@ -11189,6 +11189,42 @@ TEST_F(FormatTest, UnderstandsNewAndDelete) {
"void delete(link p);",
"void new (link p);\n"
"void delete (link p);");

FormatStyle AfterPlacementOperator = getLLVMStyle();
AfterPlacementOperator.SpaceBeforeParens = FormatStyle::SBPO_Custom;
EXPECT_EQ(
AfterPlacementOperator.SpaceBeforeParensOptions.AfterPlacementOperator,
FormatStyle::SpaceBeforeParensCustom::APO_Leave);
verifyFormat("new (buf) int;", AfterPlacementOperator);
verifyFormat("new(buf) int;", AfterPlacementOperator);

AfterPlacementOperator.SpaceBeforeParensOptions.AfterPlacementOperator =
FormatStyle::SpaceBeforeParensCustom::APO_Never;
verifyFormat("struct A {\n"
" int *a;\n"
" A(int *p) : a(new(p) int) {\n"
" new(p) int;\n"
" int *b = new(p) int;\n"
" int *c = new(p) int(3);\n"
" delete(b);\n"
" }\n"
"};",
AfterPlacementOperator);
verifyFormat("void operator new(void *foo) ATTRIB;", AfterPlacementOperator);

AfterPlacementOperator.SpaceBeforeParensOptions.AfterPlacementOperator =
FormatStyle::SpaceBeforeParensCustom::APO_Always;
verifyFormat("struct A {\n"
" int *a;\n"
" A(int *p) : a(new (p) int) {\n"
" new (p) int;\n"
" int *b = new (p) int;\n"
" int *c = new (p) int(3);\n"
" delete (b);\n"
" }\n"
"};",
AfterPlacementOperator);
verifyFormat("void operator new(void *foo) ATTRIB;", AfterPlacementOperator);
}

TEST_F(FormatTest, UnderstandsUsesOfStarAndAmp) {
Expand Down

0 comments on commit 7c15dd6

Please sign in to comment.