[clang] cc0f33b - [clang-format] Add SpacesInComments option for block comments (#204727)

via cfe-commits cfe-commits at lists.llvm.org
Thu Jul 30 13:49:25 PDT 2026


Author: Frank
Date: 2026-07-30T22:49:21+02:00
New Revision: cc0f33be52a5a04fd809d6e6e6e41c0af794283d

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

LOG: [clang-format] Add SpacesInComments option for block comments (#204727)

Adds a new `SpacesInBlockComments` clang-format option to control
spacing after `/*` and before `*/` in ordinary block comments.

Supported values:

* Always: formats `/*comment*/` as `/* comment */`
* Never: formats `/* comment */` as `/*comment*/`
* Leave: preserves existing spacing

Documentation comments such as `/** ... */` and `/*! ... */`, and
parameter comments such as `/*Arg=*/`, are left unchanged.

Tests added for all option values, multiline block comments, and
excluded parameter/doc comments.

Addresses #160682

Added: 
    

Modified: 
    clang/docs/ClangFormatStyleOptions.rst
    clang/docs/ReleaseNotes.md
    clang/include/clang/Format/Format.h
    clang/lib/Format/BreakableToken.cpp
    clang/lib/Format/Format.cpp
    clang/unittests/Format/ConfigParseTest.cpp
    clang/unittests/Format/FormatTestComments.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst
index 7c5f42b974fb7..e8cf2409e6c70 100644
--- a/clang/docs/ClangFormatStyleOptions.rst
+++ b/clang/docs/ClangFormatStyleOptions.rst
@@ -7403,6 +7403,35 @@ the configuration (without a prefix: ``Auto``).
 
 
 
+.. _SpacesInBlockComments:
+
+**SpacesInBlockComments** (``SpacesInBlockCommentsStyle``) :versionbadge:`clang-format 24` :ref:`¶ <SpacesInBlockComments>`
+  The SpacesInBlockCommentsStyle to use for ordinary block comments.
+  Documentation comments such as ``/** ... */`` and ``/*! ... */``
+  and parameter comments ending with ``=`` before the closing ``*/`` are
+  left unchanged.
+
+  Possible values:
+
+  * ``SIBCS_Never`` (in configuration: ``Never``)
+    Remove spaces after ``/*`` and before ``*/``.
+
+    .. code-block:: c++
+
+       /*comment*/
+
+  * ``SIBCS_Always`` (in configuration: ``Always``)
+    Add spaces after ``/*`` and before ``*/``.
+
+    .. code-block:: c++
+
+       /* comment */
+
+  * ``SIBCS_Leave`` (in configuration: ``Leave``)
+    Leave existing spaces unchanged.
+
+
+
 .. _SpacesInCStyleCastParentheses:
 
 **SpacesInCStyleCastParentheses** (``Boolean``) :versionbadge:`clang-format 3.7` :ref:`¶ <SpacesInCStyleCastParentheses>`

diff  --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index f0d34731e0e50..804f93cc37e68 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -495,6 +495,9 @@ features cannot lower the translation-unit ABI level;
 
 ### clang-format
 
+- Add `SpacesInBlockComments` option to control spacing after `/*` and
+  before `*/` in ordinary block comments.
+
 ### libclang
 
 - visit identifier initializers in lambda capture as VarDecl instead of VariableRef. Warning: this changes behaviour.

diff  --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h
index 0c8acd6c4dbbb..540a50047696a 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -5599,6 +5599,30 @@ struct FormatStyle {
   /// \version 3.4
   SpacesInAnglesStyle SpacesInAngles;
 
+  /// Styles for controlling spacing after ``/*`` and before ``*/`` in block
+  /// comments.
+  enum SpacesInBlockCommentsStyle : int8_t {
+    /// Remove spaces after ``/*`` and before ``*/``.
+    /// \code
+    ///    /*comment*/
+    /// \endcode
+    SIBCS_Never,
+    /// Add spaces after ``/*`` and before ``*/``.
+    /// \code
+    ///    /* comment */
+    /// \endcode
+    SIBCS_Always,
+    /// Leave existing spaces unchanged.
+    SIBCS_Leave
+  };
+
+  /// The SpacesInBlockCommentsStyle to use for ordinary block comments.
+  /// Documentation comments such as ``/** ... */`` and ``/*! ... */``
+  /// and parameter comments ending with ``=`` before the closing ``*/`` are
+  /// left unchanged.
+  /// \version 24
+  SpacesInBlockCommentsStyle SpacesInBlockComments;
+
   /// If ``true``, spaces will be inserted around if/for/switch/while
   /// conditions.
   /// This option is **deprecated**. See ``InConditionalStatements`` of
@@ -6250,6 +6274,7 @@ struct FormatStyle {
            SpaceInEmptyBraces == R.SpaceInEmptyBraces &&
            SpacesBeforeTrailingComments == R.SpacesBeforeTrailingComments &&
            SpacesInAngles == R.SpacesInAngles &&
+           SpacesInBlockComments == R.SpacesInBlockComments &&
            SpacesInContainerLiterals == R.SpacesInContainerLiterals &&
            SpacesInLineCommentPrefix.Minimum ==
                R.SpacesInLineCommentPrefix.Minimum &&

diff  --git a/clang/lib/Format/BreakableToken.cpp b/clang/lib/Format/BreakableToken.cpp
index 9571a64797a2d..ffee5987bad5d 100644
--- a/clang/lib/Format/BreakableToken.cpp
+++ b/clang/lib/Format/BreakableToken.cpp
@@ -778,6 +778,50 @@ void BreakableBlockComment::reflow(unsigned LineIndex,
 void BreakableBlockComment::adaptStartOfLine(
     unsigned LineIndex, WhitespaceManager &Whitespaces) const {
   if (LineIndex == 0) {
+    StringRef Text = tokenAt(LineIndex).TokenText;
+    if (Style.SpacesInBlockComments != FormatStyle::SIBCS_Leave &&
+        Text.size() >= 4) {
+      const bool IsDocComment =
+          Text.starts_with("/**") || Text.starts_with("/*!");
+      const bool IsParamComment = Text.drop_back(2).trim(Blanks).ends_with("=");
+      if (!IsDocComment && !IsParamComment) {
+        if (StringRef AfterOpening = Text.drop_front(2);
+            !AfterOpening.empty()) {
+          const bool HasSpace = isWhitespace(AfterOpening.front());
+          if (Style.SpacesInBlockComments == FormatStyle::SIBCS_Always &&
+              !HasSpace) {
+            Whitespaces.replaceWhitespaceInToken(
+                tokenAt(LineIndex), /*Offset=*/2, /*ReplaceChars=*/0,
+                /*PreviousPostfix=*/"", /*CurrentPrefix=*/"", InPPDirective,
+                /*Newlines=*/0, /*Spaces=*/1);
+          } else if (Style.SpacesInBlockComments == FormatStyle::SIBCS_Never &&
+                     HasSpace) {
+            Whitespaces.replaceWhitespaceInToken(
+                tokenAt(LineIndex), /*Offset=*/2, /*ReplaceChars=*/1,
+                /*PreviousPostfix=*/"", /*CurrentPrefix=*/"", InPPDirective,
+                /*Newlines=*/0, /*Spaces=*/0);
+          }
+        }
+
+        if (StringRef BeforeClosing = Text.drop_back(2);
+            !BeforeClosing.empty()) {
+          const bool HasSpace = isWhitespace(BeforeClosing.back());
+          if (Style.SpacesInBlockComments == FormatStyle::SIBCS_Always &&
+              !HasSpace) {
+            Whitespaces.replaceWhitespaceInToken(
+                tokenAt(LineIndex), Text.size() - 2, /*ReplaceChars=*/0,
+                /*PreviousPostfix=*/"", /*CurrentPrefix=*/"", InPPDirective,
+                /*Newlines=*/0, /*Spaces=*/1);
+          } else if (Style.SpacesInBlockComments == FormatStyle::SIBCS_Never &&
+                     HasSpace) {
+            Whitespaces.replaceWhitespaceInToken(
+                tokenAt(LineIndex), Text.size() - 3, /*ReplaceChars=*/1,
+                /*PreviousPostfix=*/"", /*CurrentPrefix=*/"", InPPDirective,
+                /*Newlines=*/0, /*Spaces=*/0);
+          }
+        }
+      }
+    }
     if (DelimitersOnNewline) {
       // Since we're breaking at index 1 below, the break position and the
       // break length are the same.

diff  --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index 7d55c738a9ca3..2b6e65efbf026 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -972,6 +972,16 @@ template <> struct ScalarEnumerationTraits<FormatStyle::SpacesInAnglesStyle> {
   }
 };
 
+template <>
+struct ScalarEnumerationTraits<FormatStyle::SpacesInBlockCommentsStyle> {
+  static void enumeration(IO &IO,
+                          FormatStyle::SpacesInBlockCommentsStyle &Value) {
+    IO.enumCase(Value, "Never", FormatStyle::SIBCS_Never);
+    IO.enumCase(Value, "Always", FormatStyle::SIBCS_Always);
+    IO.enumCase(Value, "Leave", FormatStyle::SIBCS_Leave);
+  }
+};
+
 template <> struct MappingTraits<FormatStyle::SpacesInLineComment> {
   static void mapping(IO &IO, FormatStyle::SpacesInLineComment &Space) {
     // Transform the maximum to signed, to parse "-1" correctly
@@ -1505,6 +1515,7 @@ template <> struct MappingTraits<FormatStyle> {
     IO.mapOptional("SpacesBeforeTrailingComments",
                    Style.SpacesBeforeTrailingComments);
     IO.mapOptional("SpacesInAngles", Style.SpacesInAngles);
+    IO.mapOptional("SpacesInBlockComments", Style.SpacesInBlockComments);
     IO.mapOptional("SpacesInContainerLiterals",
                    Style.SpacesInContainerLiterals);
     IO.mapOptional("SpacesInLineCommentPrefix",
@@ -2029,6 +2040,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
   LLVMStyle.SpaceInEmptyBraces = FormatStyle::SIEB_Never;
   LLVMStyle.SpacesBeforeTrailingComments = 1;
   LLVMStyle.SpacesInAngles = FormatStyle::SIAS_Never;
+  LLVMStyle.SpacesInBlockComments = FormatStyle::SIBCS_Leave;
   LLVMStyle.SpacesInContainerLiterals = true;
   LLVMStyle.SpacesInLineCommentPrefix = {
       /*Minimum=*/1, /*Maximum=*/std::numeric_limits<unsigned>::max()};

diff  --git a/clang/unittests/Format/ConfigParseTest.cpp b/clang/unittests/Format/ConfigParseTest.cpp
index 51e59345325a9..9350ba7eb3de4 100644
--- a/clang/unittests/Format/ConfigParseTest.cpp
+++ b/clang/unittests/Format/ConfigParseTest.cpp
@@ -1253,6 +1253,14 @@ TEST(ConfigParseTest, ParsesConfiguration) {
   CHECK_PARSE("SpacesInAngles: false", SpacesInAngles, FormatStyle::SIAS_Never);
   CHECK_PARSE("SpacesInAngles: true", SpacesInAngles, FormatStyle::SIAS_Always);
 
+  Style.SpacesInBlockComments = FormatStyle::SIBCS_Always;
+  CHECK_PARSE("SpacesInBlockComments: Never", SpacesInBlockComments,
+              FormatStyle::SIBCS_Never);
+  CHECK_PARSE("SpacesInBlockComments: Always", SpacesInBlockComments,
+              FormatStyle::SIBCS_Always);
+  CHECK_PARSE("SpacesInBlockComments: Leave", SpacesInBlockComments,
+              FormatStyle::SIBCS_Leave);
+
   CHECK_PARSE("RequiresClausePosition: WithPreceding", RequiresClausePosition,
               FormatStyle::RCPS_WithPreceding);
   CHECK_PARSE("RequiresClausePosition: WithFollowing", RequiresClausePosition,

diff  --git a/clang/unittests/Format/FormatTestComments.cpp b/clang/unittests/Format/FormatTestComments.cpp
index d5a43318626f4..3b281dc29fc39 100644
--- a/clang/unittests/Format/FormatTestComments.cpp
+++ b/clang/unittests/Format/FormatTestComments.cpp
@@ -370,6 +370,59 @@ TEST_F(FormatTestComments, RemovesTrailingWhitespaceOfComments) {
   verifyFormat("// comment    \\\n", "// comment    \\\n  \t \v   \f   ");
 }
 
+TEST_F(FormatTestComments, SpacesInBlockComments) {
+  FormatStyle Style = getLLVMStyle();
+
+  Style.SpacesInBlockComments = FormatStyle::SIBCS_Always;
+  verifyFormat("/* comment */", "/* comment*/", Style);
+  verifyFormat("/* comment */", "/*comment */", Style);
+  verifyFormat("/* comment */", "/*comment*/", Style);
+
+  Style.SpacesInBlockComments = FormatStyle::SIBCS_Leave;
+  verifyFormat("/*comment*/", Style);
+  verifyFormat("/* comment*/", Style);
+  verifyFormat("/*comment */", Style);
+  verifyFormat("/* comment */", Style);
+
+  Style.SpacesInBlockComments = FormatStyle::SIBCS_Never;
+  verifyFormat("/*comment*/", "/* comment */", Style);
+  verifyFormat("/*comment*/", "/* comment*/", Style);
+  verifyFormat("/*comment*/", "/*comment */", Style);
+}
+
+TEST_F(FormatTestComments, SpacesInMultilineBlockComments) {
+  FormatStyle Style = getLLVMStyleWithColumns(20);
+  Style.ReflowComments = FormatStyle::RCS_IndentOnly;
+
+  Style.SpacesInBlockComments = FormatStyle::SIBCS_Always;
+  verifyFormat("/* aaaaaaaaa aaaaaaaaaa aaaaaaaaaa\n"
+               " * aaaaaaaaa */",
+               "/*aaaaaaaaa aaaaaaaaaa aaaaaaaaaa\n"
+               " * aaaaaaaaa*/",
+               Style);
+
+  Style.SpacesInBlockComments = FormatStyle::SIBCS_Never;
+  verifyFormat("/*aaaaaaaaa aaaaaaaaaa aaaaaaaaaa\n"
+               " * aaaaaaaaa*/",
+               "/* aaaaaaaaa aaaaaaaaaa aaaaaaaaaa\n"
+               " * aaaaaaaaa */",
+               Style);
+}
+
+TEST_F(FormatTestComments, SpacesInBlockCommentsIgnoreParamAndDocComments) {
+  FormatStyle Style = getLLVMStyle();
+
+  Style.SpacesInBlockComments = FormatStyle::SIBCS_Always;
+  verifyFormat("foo(/*Arg=*/value);", Style);
+  verifyFormat("/**doc*/", Style);
+  verifyFormat("/*!doc*/", Style);
+
+  Style.SpacesInBlockComments = FormatStyle::SIBCS_Never;
+  verifyFormat("foo(/*Arg=*/value);", Style);
+  verifyFormat("/** doc */", Style);
+  verifyFormat("/*! doc */", Style);
+}
+
 TEST_F(FormatTestComments, UnderstandsBlockComments) {
   verifyFormat("f(/*noSpaceAfterParameterNamingComment=*/true);");
   verifyFormat("void f() { g(/*aaa=*/x, /*bbb=*/!y, /*c=*/::c); }");


        


More information about the cfe-commits mailing list