https://github.com/frank-suwen created 
https://github.com/llvm/llvm-project/pull/204727

Adds a new `SpacesInComments` clang-format option to control spacing after `/*` 
and before `*/` in ordinary single-line 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 and for excluded parameter/doc comments.

Fixes #160682.

>From d2039b6757185d86f47e8185776eb332141fd887 Mon Sep 17 00:00:00 2001
From: frank-suwen <[email protected]>
Date: Thu, 18 Jun 2026 21:49:51 -0700
Subject: [PATCH] [clang-format] Add SpacesInComments option for block comments

---
 clang/include/clang/Format/Format.h           | 25 +++++++++++
 clang/lib/Format/BreakableToken.cpp           | 42 +++++++++++++++++++
 clang/lib/Format/Format.cpp                   | 10 +++++
 clang/unittests/Format/FormatTestComments.cpp | 34 +++++++++++++++
 4 files changed, 111 insertions(+)

diff --git a/clang/include/clang/Format/Format.h 
b/clang/include/clang/Format/Format.h
index 27b2d8f4a405b..10b546506684d 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -5590,6 +5590,30 @@ struct FormatStyle {
   /// \version 3.4
   SpacesInAnglesStyle SpacesInAngles;
 
+  /// Styles for controlling spacing after ``/*`` and before ``*/`` in block
+  /// comments.
+  enum SpacesInCommentsStyle : int8_t {
+    /// Remove spaces after ``/*`` and before ``*/``.
+    /// \code
+    ///    /*comment*/
+    /// \endcode
+    SICS_Never,
+    /// Add spaces after ``/*`` and before ``*/``.
+    /// \code
+    ///    /* comment */
+    /// \endcode
+    SICS_Always,
+    /// Leave existing spaces unchanged.
+    SICS_Leave
+  };
+
+  /// The SpacesInCommentsStyle to use for single-line ordinary block comments.
+  /// Documentation comments such as ``/** ... */`` and ``/*! ... */`` and
+  /// parameter comments ending with ``=`` before the closing ``*/`` are left
+  /// unchanged.
+  /// \version 23
+  SpacesInCommentsStyle SpacesInComments;
+
   /// If ``true``, spaces will be inserted around if/for/switch/while
   /// conditions.
   /// This option is **deprecated**. See ``InConditionalStatements`` of
@@ -6241,6 +6265,7 @@ struct FormatStyle {
            SpaceInEmptyBraces == R.SpaceInEmptyBraces &&
            SpacesBeforeTrailingComments == R.SpacesBeforeTrailingComments &&
            SpacesInAngles == R.SpacesInAngles &&
+           SpacesInComments == R.SpacesInComments &&
            SpacesInContainerLiterals == R.SpacesInContainerLiterals &&
            SpacesInLineCommentPrefix.Minimum ==
                R.SpacesInLineCommentPrefix.Minimum &&
diff --git a/clang/lib/Format/BreakableToken.cpp 
b/clang/lib/Format/BreakableToken.cpp
index 9571a64797a2d..aaca54a010f4a 100644
--- a/clang/lib/Format/BreakableToken.cpp
+++ b/clang/lib/Format/BreakableToken.cpp
@@ -778,6 +778,48 @@ void BreakableBlockComment::reflow(unsigned LineIndex,
 void BreakableBlockComment::adaptStartOfLine(
     unsigned LineIndex, WhitespaceManager &Whitespaces) const {
   if (LineIndex == 0) {
+    StringRef Text = tokenAt(LineIndex).TokenText;
+    if (Style.SpacesInComments != FormatStyle::SICS_Leave &&
+        Lines.size() == 1 && 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) {
+        StringRef AfterOpening = Text.drop_front(2);
+        if (!AfterOpening.empty()) {
+          const bool HasSpace = isWhitespace(AfterOpening.front());
+          if (Style.SpacesInComments == FormatStyle::SICS_Always && !HasSpace) 
{
+            Whitespaces.replaceWhitespaceInToken(
+                tokenAt(LineIndex), /*Offset=*/2, /*ReplaceChars=*/0,
+                /*PreviousPostfix=*/"", /*CurrentPrefix=*/"", InPPDirective,
+                /*Newlines=*/0, /*Spaces=*/1);
+          } else if (Style.SpacesInComments == FormatStyle::SICS_Never &&
+                     HasSpace) {
+            Whitespaces.replaceWhitespaceInToken(
+                tokenAt(LineIndex), /*Offset=*/2, /*ReplaceChars=*/1,
+                /*PreviousPostfix=*/"", /*CurrentPrefix=*/"", InPPDirective,
+                /*Newlines=*/0, /*Spaces=*/0);
+          }
+        }
+
+        StringRef BeforeClosing = Text.drop_back(2);
+        if (!BeforeClosing.empty()) {
+          const bool HasSpace = isWhitespace(BeforeClosing.back());
+          if (Style.SpacesInComments == FormatStyle::SICS_Always && !HasSpace) 
{
+            Whitespaces.replaceWhitespaceInToken(
+                tokenAt(LineIndex), Text.size() - 2, /*ReplaceChars=*/0,
+                /*PreviousPostfix=*/"", /*CurrentPrefix=*/"", InPPDirective,
+                /*Newlines=*/0, /*Spaces=*/1);
+          } else if (Style.SpacesInComments == FormatStyle::SICS_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 a29d62c99bb95..a8801f5b5de8e 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -962,6 +962,14 @@ template <> struct 
ScalarEnumerationTraits<FormatStyle::SpacesInAnglesStyle> {
   }
 };
 
+template <> struct ScalarEnumerationTraits<FormatStyle::SpacesInCommentsStyle> 
{
+  static void enumeration(IO &IO, FormatStyle::SpacesInCommentsStyle &Value) {
+    IO.enumCase(Value, "Never", FormatStyle::SICS_Never);
+    IO.enumCase(Value, "Always", FormatStyle::SICS_Always);
+    IO.enumCase(Value, "Leave", FormatStyle::SICS_Leave);
+  }
+};
+
 template <> struct MappingTraits<FormatStyle::SpacesInLineComment> {
   static void mapping(IO &IO, FormatStyle::SpacesInLineComment &Space) {
     // Transform the maximum to signed, to parse "-1" correctly
@@ -1495,6 +1503,7 @@ template <> struct MappingTraits<FormatStyle> {
     IO.mapOptional("SpacesBeforeTrailingComments",
                    Style.SpacesBeforeTrailingComments);
     IO.mapOptional("SpacesInAngles", Style.SpacesInAngles);
+    IO.mapOptional("SpacesInComments", Style.SpacesInComments);
     IO.mapOptional("SpacesInContainerLiterals",
                    Style.SpacesInContainerLiterals);
     IO.mapOptional("SpacesInLineCommentPrefix",
@@ -2019,6 +2028,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind 
Language) {
   LLVMStyle.SpaceInEmptyBraces = FormatStyle::SIEB_Never;
   LLVMStyle.SpacesBeforeTrailingComments = 1;
   LLVMStyle.SpacesInAngles = FormatStyle::SIAS_Never;
+  LLVMStyle.SpacesInComments = FormatStyle::SICS_Leave;
   LLVMStyle.SpacesInContainerLiterals = true;
   LLVMStyle.SpacesInLineCommentPrefix = {
       /*Minimum=*/1, /*Maximum=*/std::numeric_limits<unsigned>::max()};
diff --git a/clang/unittests/Format/FormatTestComments.cpp 
b/clang/unittests/Format/FormatTestComments.cpp
index 707016096f7d2..6d10831124079 100644
--- a/clang/unittests/Format/FormatTestComments.cpp
+++ b/clang/unittests/Format/FormatTestComments.cpp
@@ -370,6 +370,40 @@ TEST_F(FormatTestComments, 
RemovesTrailingWhitespaceOfComments) {
   verifyFormat("// comment    \\\n", "// comment    \\\n  \t \v   \f   ");
 }
 
+TEST_F(FormatTestComments, SpacesInBlockComments) {
+  FormatStyle Style = getLLVMStyle();
+
+  Style.SpacesInComments = FormatStyle::SICS_Always;
+  verifyFormat("/* comment */", "/* comment*/", Style);
+  verifyFormat("/* comment */", "/*comment */", Style);
+  verifyFormat("/* comment */", "/*comment*/", Style);
+
+  Style.SpacesInComments = FormatStyle::SICS_Leave;
+  verifyFormat("/*comment*/", Style);
+  verifyFormat("/* comment*/", Style);
+  verifyFormat("/*comment */", Style);
+  verifyFormat("/* comment */", Style);
+
+  Style.SpacesInComments = FormatStyle::SICS_Never;
+  verifyFormat("/*comment*/", "/* comment */", Style);
+  verifyFormat("/*comment*/", "/* comment*/", Style);
+  verifyFormat("/*comment*/", "/*comment */", Style);
+}
+
+TEST_F(FormatTestComments, SpacesInBlockCommentsIgnoreParamAndDocComments) {
+  FormatStyle Style = getLLVMStyle();
+
+  Style.SpacesInComments = FormatStyle::SICS_Always;
+  verifyFormat("foo(/*Arg=*/value);", Style);
+  verifyFormat("/**doc*/", Style);
+  verifyFormat("/*!doc*/", Style);
+
+  Style.SpacesInComments = FormatStyle::SICS_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); }");

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to