[clang] [clang-format] add an option to insert a space only for non-code block empty braces, not for empty parentheses (PR #93634)

Kohei Asano via cfe-commits cfe-commits at lists.llvm.org
Sun Jun 2 19:02:19 PDT 2024


https://github.com/khei4 updated https://github.com/llvm/llvm-project/pull/93634

>From 5d409bcd0966a7581effdf1488c2f4cfa32aebc6 Mon Sep 17 00:00:00 2001
From: Kohei Asano <Kohei.Asano at sony.com>
Date: Mon, 3 Jun 2024 09:15:44 +0900
Subject: [PATCH] [clang-format] add an option to insert a space only for empty
 braces

---
 clang/docs/ClangFormatStyleOptions.rst      | 78 +++++++++++++++++++-
 clang/include/clang/Format/Format.h         | 82 +++++++++++++++++++--
 clang/lib/Format/Format.cpp                 | 36 ++++++++-
 clang/lib/Format/TokenAnnotator.cpp         | 12 ++-
 clang/lib/Format/UnwrappedLineFormatter.cpp |  6 +-
 clang/unittests/Format/ConfigParseTest.cpp  |  9 ++-
 clang/unittests/Format/FormatTest.cpp       | 42 ++++++++++-
 7 files changed, 246 insertions(+), 19 deletions(-)

diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst
index 6d092219877f9..bf0088203dd54 100644
--- a/clang/docs/ClangFormatStyleOptions.rst
+++ b/clang/docs/ClangFormatStyleOptions.rst
@@ -6028,12 +6028,84 @@ the configuration (without a prefix: ``Auto``).
 
 **SpaceInEmptyBlock** (``Boolean``) :versionbadge:`clang-format 10` :ref:`¶ <SpaceInEmptyBlock>`
   If ``true``, spaces will be inserted into ``{}``.
+  This option is **deprecated**. The previous behavior is preserved by using
+  ``SpaceInEmptyBraces`` with ``Custom`` and by setting Record
+  ``SpaceInEmptyBracesOptions`` to ``true``.
+
+.. _SpaceInEmptyBlock:
+
+**SpaceInEmptyBlock** (``Boolean``) :versionbadge:`clang-format 10` :ref:`¶ <SpaceInEmptyBlock>`
+  If ``true``, spaces will be inserted into ``{}`` for record declarations
+  and blocks. This option is **deprecated**. The previous behavior is
+  preserved by using ``SpaceInEmptyBraces`` with ``Custom`` and by setting
+  ``Record`` on ``SpaceInEmptyBracesOptions`` to ``true``.
+
+.. _SpaceInEmptyBraces:
+
+**SpaceInEmptyBraces** (``SpaceInEmptyBracesStyle``) :versionbadge:`clang-format 19` :ref:`¶ <SpaceInEmptyBraces>`
+  Defines in which cases spaces will be inserted in empty braces.
+
+  Possible values:
+
+  * ``SIEBO_Never`` (in configuration: ``Never``)
+    Never put a space in empty braces.
+
+    .. code-block:: c++
+
+       T x{};
+       while (true) {}
+       struct Unit {};
+
+  * ``SIEBO_Custom`` (in configuration: ``Custom``)
+    Configure each individual space in empty braces in
+    `SpacesInEmptyBracesOptions`.
+
+
+
+.. _SpaceInEmptyBracesOptions:
+
+**SpaceInEmptyBracesOptions** (``SpaceInEmptyBracesCustom``) :versionbadge:`clang-format 19` :ref:`¶ <SpaceInEmptyBracesOptions>`
+  Control of individual spaces in empty braces.
+
+  If ``SpaceInEmptyBraces`` is set to ``Custom``, use this to specify
+  how each individual space in empty braces case should be handled.
+  Otherwise, this is ignored.
+
+  .. code-block:: yaml
+
+    # Example of usage:
+    SpaceInEmptyBraces: Custom
+    SpaceInEmptyBracesOptions:
+      Block: true
+      Record: true
+
+  Nested configuration flags:
+
+  Precise control over the spacing in empty braces.
 
   .. code-block:: c++
 
-     true:                                false:
-     void f() { }                   vs.   void f() {}
-     while (true) { }                     while (true) {}
+    # Should be declared this way:
+    SpaceInEmptyBraces: Custom
+    SpaceInEmptyBracesOptions:
+      Block: true
+      Record: true
+
+  * ``bool Block`` Put a space in empty braces of code blocks and record declarations.
+
+    .. code-block:: c++
+
+       true:                                  false:
+       int f() { }                    vs.     int f() {}
+       struct Unit {};                        struct Unit {};
+
+  * ``bool InitList`` Put a space in empty braces of initializer list.
+
+    .. code-block:: c++
+
+       true:                                  false:
+       T x{ };                        vs.     T x{};
+
 
 .. _SpaceInEmptyParentheses:
 
diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h
index 274b45d1bc586..b63ddd7e2b92b 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -4487,13 +4487,11 @@ struct FormatStyle {
   bool SpaceBeforeRangeBasedForLoopColon;
 
   /// If ``true``, spaces will be inserted into ``{}``.
-  /// \code
-  ///    true:                                false:
-  ///    void f() { }                   vs.   void f() {}
-  ///    while (true) { }                     while (true) {}
-  /// \endcode
+  /// This option is **deprecated**. The previous behavior is preserved by using
+  /// ``SpaceInEmptyBraces`` with ``Custom`` and by setting Record
+  /// ``SpaceInEmptyBracesOptions`` to ``true``.
   /// \version 10
-  bool SpaceInEmptyBlock;
+  // bool SpaceInEmptyBlock;
 
   /// If ``true``, spaces may be inserted into ``()``.
   /// This option is **deprecated**. See ``InEmptyParentheses`` of
@@ -4715,6 +4713,75 @@ struct FormatStyle {
   /// \version 17
   SpacesInParensCustom SpacesInParensOptions;
 
+  /// Different ways to put a space in empty braces.
+  enum SpaceInEmptyBracesStyle : int8_t {
+    /// Never put a space in empty braces.
+    /// \code
+    ///    T x{};
+    ///    while (true) {}
+    ///    struct Unit {};
+    /// \endcode
+    SIEBO_Never,
+    /// Configure each individual space in empty braces in
+    /// `SpacesInEmptyBracesOptions`.
+    SIEBO_Custom,
+  };
+
+  /// Defines in which cases spaces will be inserted in empty braces.
+  /// \version 19
+  SpaceInEmptyBracesStyle SpaceInEmptyBraces;
+
+  /// Precise control over the spacing in empty braces.
+  /// \code
+  ///   # Should be declared this way:
+  ///   SpaceInEmptyBraces: Custom
+  ///   SpaceInEmptyBracesOptions:
+  ///     Block: true
+  ///     Record: true
+  /// \endcode
+  struct SpaceInEmptyBracesCustom {
+    /// Put a space in empty braces of code blocks and record declarations.
+    /// \code
+    ///    true:                                  false:
+    ///    int f() { }                    vs.     int f() {}
+    ///    struct Unit {};                        struct Unit {};
+    /// \endcode
+    bool Block;
+    /// Put a space in empty braces of initializer list.
+    /// \code
+    ///    true:                                  false:
+    ///    T x{ };                        vs.     T x{};
+    /// \endcode
+    bool InitList;
+
+    SpaceInEmptyBracesCustom() : Block(false), InitList(false) {}
+
+    SpaceInEmptyBracesCustom(bool Block, bool InitList)
+        : Block(Block), InitList(InitList) {}
+
+    bool operator==(const SpaceInEmptyBracesCustom &R) const {
+      return Block == R.Block && InitList == R.InitList;
+    }
+    bool operator!=(const SpaceInEmptyBracesCustom &R) const {
+      return !(*this == R);
+    }
+  };
+
+  /// Control of individual spaces in empty braces.
+  ///
+  /// If ``SpaceInEmptyBraces`` is set to ``Custom``, use this to specify
+  /// how each individual space in empty braces case should be handled.
+  /// Otherwise, this is ignored.
+  /// \code{.yaml}
+  ///   # Example of usage:
+  ///   SpaceInEmptyBraces: Custom
+  ///   SpaceInEmptyBracesOptions:
+  ///     Block: true
+  ///     Record: true
+  /// \endcode
+  /// \version 19
+  SpaceInEmptyBracesCustom SpaceInEmptyBracesOptions;
+
   /// If ``true``, spaces will be inserted after ``[`` and before ``]``.
   /// Lambdas without arguments or unspecified size array declarations will not
   /// be affected.
@@ -5088,7 +5155,8 @@ struct FormatStyle {
            SpaceBeforeRangeBasedForLoopColon ==
                R.SpaceBeforeRangeBasedForLoopColon &&
            SpaceBeforeSquareBrackets == R.SpaceBeforeSquareBrackets &&
-           SpaceInEmptyBlock == R.SpaceInEmptyBlock &&
+           SpaceInEmptyBraces == R.SpaceInEmptyBraces &&
+           SpaceInEmptyBracesOptions == R.SpaceInEmptyBracesOptions &&
            SpacesBeforeTrailingComments == R.SpacesBeforeTrailingComments &&
            SpacesInAngles == R.SpacesInAngles &&
            SpacesInContainerLiterals == R.SpacesInContainerLiterals &&
diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index 9cba0c2614eef..8c6f8c440d73d 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -718,6 +718,21 @@ template <> struct MappingTraits<FormatStyle::SpacesInLineComment> {
   }
 };
 
+template <> struct MappingTraits<FormatStyle::SpaceInEmptyBracesCustom> {
+  static void mapping(IO &IO, FormatStyle::SpaceInEmptyBracesCustom &Space) {
+    IO.mapOptional("Block", Space.Block);
+    IO.mapOptional("InitList", Space.InitList);
+  }
+};
+
+template <>
+struct ScalarEnumerationTraits<FormatStyle::SpaceInEmptyBracesStyle> {
+  static void enumeration(IO &IO, FormatStyle::SpaceInEmptyBracesStyle &Value) {
+    IO.enumCase(Value, "Never", FormatStyle::SIEBO_Never);
+    IO.enumCase(Value, "Custom", FormatStyle::SIEBO_Custom);
+  }
+};
+
 template <> struct MappingTraits<FormatStyle::SpacesInParensCustom> {
   static void mapping(IO &IO, FormatStyle::SpacesInParensCustom &Spaces) {
     IO.mapOptional("InCStyleCasts", Spaces.InCStyleCasts);
@@ -849,6 +864,7 @@ template <> struct MappingTraits<FormatStyle> {
     bool UseCRLF = false;
 
     bool SpaceInEmptyParentheses = false;
+    bool SpaceInEmptyBlock = false;
     bool SpacesInConditionalStatement = false;
     bool SpacesInCStyleCastParentheses = false;
     bool SpacesInParentheses = false;
@@ -875,6 +891,7 @@ template <> struct MappingTraits<FormatStyle> {
       IO.mapOptional("SpaceAfterControlStatementKeyword",
                      Style.SpaceBeforeParens);
       IO.mapOptional("SpaceInEmptyParentheses", SpaceInEmptyParentheses);
+      IO.mapOptional("SpaceInEmptyBlock", SpaceInEmptyBlock);
       IO.mapOptional("SpacesInConditionalStatement",
                      SpacesInConditionalStatement);
       IO.mapOptional("SpacesInCStyleCastParentheses",
@@ -1091,7 +1108,6 @@ template <> struct MappingTraits<FormatStyle> {
                    Style.SpaceBeforeRangeBasedForLoopColon);
     IO.mapOptional("SpaceBeforeSquareBrackets",
                    Style.SpaceBeforeSquareBrackets);
-    IO.mapOptional("SpaceInEmptyBlock", Style.SpaceInEmptyBlock);
     IO.mapOptional("SpacesBeforeTrailingComments",
                    Style.SpacesBeforeTrailingComments);
     IO.mapOptional("SpacesInAngles", Style.SpacesInAngles);
@@ -1099,6 +1115,9 @@ template <> struct MappingTraits<FormatStyle> {
                    Style.SpacesInContainerLiterals);
     IO.mapOptional("SpacesInLineCommentPrefix",
                    Style.SpacesInLineCommentPrefix);
+    IO.mapOptional("SpaceInEmptyBraces", Style.SpaceInEmptyBraces);
+    IO.mapOptional("SpaceInEmptyBracesOptions",
+                   Style.SpaceInEmptyBracesOptions);
     IO.mapOptional("SpacesInParens", Style.SpacesInParens);
     IO.mapOptional("SpacesInParensOptions", Style.SpacesInParensOptions);
     IO.mapOptional("SpacesInSquareBrackets", Style.SpacesInSquareBrackets);
@@ -1193,6 +1212,13 @@ template <> struct MappingTraits<FormatStyle> {
       }
       Style.SpacesInParens = FormatStyle::SIPO_Custom;
     }
+
+    if (Style.SpaceInEmptyBraces != FormatStyle::SIEBO_Custom &&
+        SpaceInEmptyBlock) {
+      Style.SpaceInEmptyBraces = FormatStyle::SIEBO_Custom;
+      Style.SpaceInEmptyBracesOptions.Block = true;
+      Style.SpaceInEmptyBracesOptions.InitList = false;
+    }
   }
 };
 
@@ -1562,7 +1588,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
   LLVMStyle.SpaceBeforeParensOptions.AfterIfMacros = true;
   LLVMStyle.SpaceBeforeRangeBasedForLoopColon = true;
   LLVMStyle.SpaceBeforeSquareBrackets = false;
-  LLVMStyle.SpaceInEmptyBlock = false;
+  LLVMStyle.SpaceInEmptyBraces = FormatStyle::SIEBO_Never;
   LLVMStyle.SpacesBeforeTrailingComments = 1;
   LLVMStyle.SpacesInAngles = FormatStyle::SIAS_Never;
   LLVMStyle.SpacesInContainerLiterals = true;
@@ -1863,7 +1889,11 @@ FormatStyle getWebKitStyle() {
   Style.ObjCSpaceAfterProperty = true;
   Style.PointerAlignment = FormatStyle::PAS_Left;
   Style.SpaceBeforeCpp11BracedList = true;
-  Style.SpaceInEmptyBlock = true;
+  Style.SpaceInEmptyBraces = FormatStyle::SIEBO_Custom;
+  Style.SpaceInEmptyBracesOptions.Block = true;
+  Style.SpaceInEmptyBracesOptions.InitList = true;
+  Style.SpacesInParensOptions.InEmptyParentheses = false;
+  Style.SpacesInParensOptions.Other = false;
   return Style;
 }
 
diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp
index 7c4c76a91f2c5..c94a8ad2bb571 100644
--- a/clang/lib/Format/TokenAnnotator.cpp
+++ b/clang/lib/Format/TokenAnnotator.cpp
@@ -4337,13 +4337,21 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line,
     return Left.is(tok::hash);
   if (Left.isOneOf(tok::hashhash, tok::hash))
     return Right.is(tok::hash);
+  // FIXME: separate function and other block?
   if (Left.is(BK_Block) && Right.is(tok::r_brace) &&
       Right.MatchingParen == &Left && Line.Children.empty()) {
-    return Style.SpaceInEmptyBlock;
+    return Style.SpaceInEmptyBracesOptions.Block;
   }
-  if ((Left.is(tok::l_paren) && Right.is(tok::r_paren)) ||
+  if (Style.SpaceInEmptyBracesOptions.InitList &&
       (Left.is(tok::l_brace) && Left.isNot(BK_Block) &&
        Right.is(tok::r_brace) && Right.isNot(BK_Block))) {
+    return Style.SpaceInEmptyBracesOptions.InitList;
+  }
+  if ((Left.is(tok::l_paren) && Right.is(tok::r_paren)) ||
+      ((Style.SpaceInEmptyBraces == FormatStyle::SIEBO_Never ||
+        Style.SpaceInEmptyBracesOptions.InitList) &&
+       Left.is(tok::l_brace) && Left.isNot(BK_Block) &&
+       Right.is(tok::r_brace) && Right.isNot(BK_Block))) {
     return Style.SpacesInParensOptions.InEmptyParentheses;
   }
   if (Style.SpacesInParensOptions.InConditionalStatements) {
diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp
index 4d53361aaf333..e1a17f29b627d 100644
--- a/clang/lib/Format/UnwrappedLineFormatter.cpp
+++ b/clang/lib/Format/UnwrappedLineFormatter.cpp
@@ -824,7 +824,11 @@ class LineJoiner {
       if (ShouldMerge()) {
         // We merge empty blocks even if the line exceeds the column limit.
         Tok->SpacesRequiredBefore =
-            (Style.SpaceInEmptyBlock || Line.Last->is(tok::comment)) ? 1 : 0;
+            ((Style.SpaceInEmptyBraces == FormatStyle::SIEBO_Custom &&
+              Style.SpaceInEmptyBracesOptions.Block) ||
+             Line.Last->is(tok::comment))
+                ? 1
+                : 0;
         Tok->CanBreakBefore = true;
         return 1;
       } else if (Limit != 0 && !Line.startsWithNamespace() &&
diff --git a/clang/unittests/Format/ConfigParseTest.cpp b/clang/unittests/Format/ConfigParseTest.cpp
index 82e72f08ffb5e..44d418b7ed575 100644
--- a/clang/unittests/Format/ConfigParseTest.cpp
+++ b/clang/unittests/Format/ConfigParseTest.cpp
@@ -187,7 +187,6 @@ TEST(ConfigParseTest, ParsesConfigurationBools) {
   CHECK_PARSE_BOOL(RemoveSemicolon);
   CHECK_PARSE_BOOL(SkipMacroDefinitionBody);
   CHECK_PARSE_BOOL(SpacesInSquareBrackets);
-  CHECK_PARSE_BOOL(SpaceInEmptyBlock);
   CHECK_PARSE_BOOL(SpacesInContainerLiterals);
   CHECK_PARSE_BOOL(SpaceAfterCStyleCast);
   CHECK_PARSE_BOOL(SpaceAfterTemplateKeyword);
@@ -235,6 +234,8 @@ TEST(ConfigParseTest, ParsesConfigurationBools) {
   CHECK_PARSE_NESTED_BOOL(SpaceBeforeParensOptions, AfterOverloadedOperator);
   CHECK_PARSE_NESTED_BOOL(SpaceBeforeParensOptions, AfterPlacementOperator);
   CHECK_PARSE_NESTED_BOOL(SpaceBeforeParensOptions, BeforeNonEmptyParentheses);
+  CHECK_PARSE_NESTED_BOOL(SpaceInEmptyBracesOptions, Block);
+  CHECK_PARSE_NESTED_BOOL(SpaceInEmptyBracesOptions, InitList);
   CHECK_PARSE_NESTED_BOOL(SpacesInParensOptions, InCStyleCasts);
   CHECK_PARSE_NESTED_BOOL(SpacesInParensOptions, InConditionalStatements);
   CHECK_PARSE_NESTED_BOOL(SpacesInParensOptions, InEmptyParentheses);
@@ -636,6 +637,12 @@ TEST(ConfigParseTest, ParsesConfiguration) {
   Style.SpacesInParens = FormatStyle::SIPO_Never;
   Style.SpacesInParensOptions = {};
 
+  // For backward compatibility
+  Style.SpaceInEmptyBraces = FormatStyle::SIEBO_Never;
+  Style.SpaceInEmptyBracesOptions = {};
+  CHECK_PARSE("SpaceInEmptyBlock: true", SpaceInEmptyBracesOptions,
+              FormatStyle::SpaceInEmptyBracesCustom(true, false));
+
   Style.ColumnLimit = 123;
   FormatStyle BaseStyle = getLLVMStyle();
   CHECK_PARSE("BasedOnStyle: LLVM", ColumnLimit, BaseStyle.ColumnLimit);
diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index 2f0c0f0266774..6961d863a3007 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -6961,7 +6961,8 @@ TEST_F(FormatTest, PutEmptyBlocksIntoOneLine) {
   verifyFormat("enum E {};");
   verifyFormat("enum E {}");
   FormatStyle Style = getLLVMStyle();
-  Style.SpaceInEmptyBlock = true;
+  Style.SpaceInEmptyBraces = FormatStyle::SIEBO_Custom;
+  Style.SpaceInEmptyBracesOptions.Block = true;
   verifyFormat("void f() { }", "void f() {}", Style);
   Style.AllowShortBlocksOnASingleLine = FormatStyle::SBS_Empty;
   verifyFormat("{ }", Style);
@@ -6989,7 +6990,8 @@ TEST_F(FormatTest, PutEmptyBlocksIntoOneLine) {
                Style);
 
   Style = getLLVMStyle(FormatStyle::LK_CSharp);
-  Style.SpaceInEmptyBlock = true;
+  Style.SpaceInEmptyBraces = FormatStyle::SIEBO_Custom;
+  Style.SpaceInEmptyBracesOptions.Block = true;
   verifyFormat("Event += () => { };", Style);
 }
 
@@ -14029,6 +14031,42 @@ TEST_F(FormatTest, LayoutCxx11BraceInitializers) {
   verifyFormat("vector< int > x{ };", SpaceBetweenBraces);
 }
 
+TEST_F(FormatTest, EmptyBracesTest) {
+  FormatStyle SpaceInEmptyBraces = getLLVMStyle();
+  SpaceInEmptyBraces.SpaceInEmptyBraces = FormatStyle::SIEBO_Never;
+  verifyFormat("void f() {}\n"
+               "struct Unit {};\n"
+               "int x{};\n",
+               SpaceInEmptyBraces);
+
+  SpaceInEmptyBraces.SpaceInEmptyBraces = FormatStyle::SIEBO_Custom;
+  SpaceInEmptyBraces.SpaceInEmptyBracesOptions.Block = true;
+  SpaceInEmptyBraces.SpaceInEmptyBracesOptions.InitList = false;
+  verifyFormat("void f() { }\n"
+               "struct Unit { };\n"
+               "int x{};\n",
+               SpaceInEmptyBraces);
+
+  SpaceInEmptyBraces.SpaceInEmptyBraces = FormatStyle::SIEBO_Custom;
+  SpaceInEmptyBraces.SpaceInEmptyBracesOptions.Block = false;
+  SpaceInEmptyBraces.SpaceInEmptyBracesOptions.InitList = true;
+  verifyFormat("void f() {}\n"
+               "struct Unit {};\n"
+               "int x{ };\n",
+               SpaceInEmptyBraces);
+
+  // SpacesInParensOptions.InEmptyParentheses can be overwritten.
+  SpaceInEmptyBraces.SpaceInEmptyBraces = FormatStyle::SIEBO_Custom;
+  SpaceInEmptyBraces.SpaceInEmptyBracesOptions.Block = false;
+  SpaceInEmptyBraces.SpaceInEmptyBracesOptions.InitList = false;
+  SpaceInEmptyBraces.SpacesInParens = FormatStyle::SIPO_Custom;
+  SpaceInEmptyBraces.SpacesInParensOptions.InEmptyParentheses = true;
+  verifyFormat("void f( ) {}\n"
+               "struct Unit {};\n"
+               "int x{};\n",
+               SpaceInEmptyBraces);
+}
+
 TEST_F(FormatTest, FormatsBracedListsInColumnLayout) {
   verifyFormat("vector<int> x = {1, 22, 333, 4444, 55555, 666666, 7777777,\n"
                "                 1, 22, 333, 4444, 55555, 666666, 7777777,\n"



More information about the cfe-commits mailing list