[clang] 7c15dd6 - [clang-format] Add space in placement new expression

Owen Pan via cfe-commits cfe-commits at lists.llvm.org
Fri Oct 20 03:16:39 PDT 2023


Author: Omar Ahmed
Date: 2023-10-20T03:16:28-07:00
New Revision: 7c15dd60ec4549f53f1a51c5302c61f8a025a4a5

URL: https://github.com/llvm/llvm-project/commit/7c15dd60ec4549f53f1a51c5302c61f8a025a4a5
DIFF: https://github.com/llvm/llvm-project/commit/7c15dd60ec4549f53f1a51c5302c61f8a025a4a5.diff

LOG: [clang-format] Add space in placement new expression

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

Added: 
    

Modified: 
    clang/docs/ClangFormatStyleOptions.rst
    clang/docs/tools/dump_format_style.py
    clang/include/clang/Format/Format.h
    clang/lib/Format/Format.cpp
    clang/lib/Format/TokenAnnotator.cpp
    clang/unittests/Format/ConfigParseTest.cpp
    clang/unittests/Format/FormatTest.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst
index c5c72c68ee9b800..cfd57f5fa8153f4 100644
--- a/clang/docs/ClangFormatStyleOptions.rst
+++ b/clang/docs/ClangFormatStyleOptions.rst
@@ -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.
 

diff  --git a/clang/docs/tools/dump_format_style.py b/clang/docs/tools/dump_format_style.py
index 270ac34c03b39ca..75d4a044ef19f68 100755
--- a/clang/docs/tools/dump_format_style.py
+++ b/clang/docs/tools/dump_format_style.py
@@ -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)),
@@ -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
@@ -278,7 +295,9 @@ class State:
                 InFieldComment,
                 InEnum,
                 InEnumMemberComment,
-            ) = range(8)
+                InNestedEnum,
+                InNestedEnumMemberComment,
+            ) = range(10)
 
         state = State.BeforeStruct
 
@@ -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
@@ -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)
@@ -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")
 

diff  --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h
index 2707ea608c74efd..ed92ef6fc655522 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -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
@@ -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 &&
@@ -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;

diff  --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index 7b0ebe2cf621b17..acbed56a86e141f 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -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);
@@ -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);
@@ -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;

diff  --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp
index 293f7286abe4202..7f85f48de2ed2ed 100644
--- a/clang/lib/Format/TokenAnnotator.cpp
+++ b/clang/lib/Format/TokenAnnotator.cpp
@@ -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))

diff  --git a/clang/unittests/Format/ConfigParseTest.cpp b/clang/unittests/Format/ConfigParseTest.cpp
index dedaf546ea5ff4f..c35c82955f6a558 100644
--- a/clang/unittests/Format/ConfigParseTest.cpp
+++ b/clang/unittests/Format/ConfigParseTest.cpp
@@ -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 = {};

diff  --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index 963fb8f4d441618..0a87cfc4f1d6af9 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -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) {


        


More information about the cfe-commits mailing list