diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -4258,6 +4258,33 @@ void operator++ (int a); vs. void operator++(int a); object.operator++ (10); object.operator++(10); + * ``AfterPlacementOperatorStyle AfterPlacementOperator`` :versionbadge:`clang-format 14` + + 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. diff --git a/clang/docs/tools/dump_format_style.py b/clang/docs/tools/dump_format_style.py --- a/clang/docs/tools/dump_format_style.py +++ b/clang/docs/tools/dump_format_style.py @@ -121,11 +121,17 @@ return self.comment + '\n' + '\n'.join(map(str, self.values)) 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))) @@ -140,14 +146,20 @@ return '\n'.join(map(str, self.values)) 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, + 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) @@ -227,7 +239,7 @@ def read_options(self): class State: BeforeStruct, Finished, InStruct, InNestedStruct, InNestedFieldComment, \ - InFieldComment, InEnum, InEnumMemberComment = range(8) + InFieldComment, InEnum, InEnumMemberComment, InNestedEnum, InNestedEnumMemberComment = range(10) state = State.BeforeStruct options = [] @@ -286,19 +298,30 @@ 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[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)) - + nested_struct.values.append(NestedField(field_type + " " + field_name, comment, version)) + version = None elif state == State.InEnum: if line.startswith('///'): state = State.InEnumMemberComment @@ -310,6 +333,17 @@ # 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) @@ -323,6 +357,19 @@ 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') diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -3498,6 +3498,28 @@ /// 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 14 + AfterPlacementOperatorStyle AfterPlacementOperator; /// If ``true``, put space between requires keyword in a requires clause and /// opening parentheses, if there is one. /// \code @@ -3530,8 +3552,9 @@ : 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 && @@ -3541,6 +3564,7 @@ AfterFunctionDefinitionName == Other.AfterFunctionDefinitionName && AfterIfMacros == Other.AfterIfMacros && AfterOverloadedOperator == Other.AfterOverloadedOperator && + AfterPlacementOperator == Other.AfterPlacementOperator && AfterRequiresInClause == Other.AfterRequiresInClause && AfterRequiresInExpression == Other.AfterRequiresInExpression && BeforeNonEmptyParentheses == Other.BeforeNonEmptyParentheses; diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -936,6 +936,7 @@ 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); @@ -944,6 +945,22 @@ } }; +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 { static void mapping(IO &IO, FormatStyle::RawStringFormat &Format) { IO.mapOptional("Language", Format.Language); @@ -1149,6 +1166,8 @@ switch (Expanded.SpaceBeforeParens) { case FormatStyle::SBPO_Never: + Expanded.SpaceBeforeParensOptions.AfterPlacementOperator = + FormatStyle::SpaceBeforeParensCustom::APO_Never; break; case FormatStyle::SBPO_ControlStatements: Expanded.SpaceBeforeParensOptions.AfterControlStatements = true; diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -3393,6 +3393,18 @@ if (Left.is(TT_IfMacro)) 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)) diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -10130,6 +10130,42 @@ "void delete(link p);\n", format("void new (link p);\n" "void delete (link p);\n")); + + FormatStyle AfterPlacementOperator = getLLVMStyle(); + AfterPlacementOperator.SpaceBeforeParens = FormatStyle::SBPO_Custom; + EXPECT_EQ( + AfterPlacementOperator.SpaceBeforeParensOptions.AfterPlacementOperator, + FormatStyle::SpaceBeforeParensCustom::APO_Leave); + EXPECT_EQ("new (buf) int;", format("new (buf) int;", AfterPlacementOperator)); + EXPECT_EQ("new(buf) int;", format("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) { @@ -20308,6 +20344,24 @@ 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); + Style.ColumnLimit = 123; FormatStyle BaseStyle = getLLVMStyle(); CHECK_PARSE("BasedOnStyle: LLVM", ColumnLimit, BaseStyle.ColumnLimit);