diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -5224,6 +5224,33 @@ 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. 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 @@ -143,11 +143,18 @@ 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)), @@ -165,18 +172,28 @@ 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 @@ -278,7 +295,9 @@ InFieldComment, InEnum, InEnumMemberComment, - ) = range(8) + InNestedEnum, + InNestedEnumMemberComment, + ) = range(10) state = State.BeforeStruct @@ -344,27 +363,38 @@ 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) + NestedField(field_type + " " + field_name, comment, version) ) - + version = None elif state == State.InEnum: if line.startswith("///"): state = State.InEnumMemberComment @@ -376,6 +406,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) @@ -389,6 +430,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 @@ -4128,6 +4128,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 18 + AfterPlacementOperatorStyle AfterPlacementOperator; /// If ``true``, put space between requires keyword in a requires clause and /// opening parentheses, if there is one. /// \code @@ -4160,8 +4182,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 && @@ -4171,6 +4194,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 @@ -504,6 +504,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); @@ -679,6 +695,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); @@ -1369,6 +1386,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 @@ -4234,6 +4234,19 @@ 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/ConfigParseTest.cpp b/clang/unittests/Format/ConfigParseTest.cpp --- a/clang/unittests/Format/ConfigParseTest.cpp +++ b/clang/unittests/Format/ConfigParseTest.cpp @@ -591,6 +591,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); + // For backward compatibility: Style.SpacesInParens = FormatStyle::SIPO_Never; Style.SpacesInParensOptions = {}; 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 @@ -11189,6 +11189,42 @@ "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) {