Skip to content

Conversation

Men-cotton
Copy link

Copy link

github-actions bot commented Oct 6, 2025

Thank you for submitting a Pull Request (PR) to the LLVM Project!

This PR will be automatically labeled and the relevant teams will be notified.

If you wish to, you can add reviewers by using the "Reviewers" section on this page.

If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using @ followed by their GitHub username.

If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers.

If you have further questions, they may be answered by the LLVM GitHub User Guide.

You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums.

@llvmbot llvmbot added clang Clang issues not falling into any other category clang-format labels Oct 6, 2025
@llvmbot
Copy link
Member

llvmbot commented Oct 6, 2025

@llvm/pr-subscribers-clang

Author: Men-cotton (Men-cotton)

Changes

#160682


Full diff: https://github.com/llvm/llvm-project/pull/162105.diff

6 Files Affected:

  • (modified) clang/docs/ClangFormatStyleOptions.rst (+11)
  • (modified) clang/include/clang/Format/Format.h (+10)
  • (modified) clang/lib/Format/Format.cpp (+3)
  • (modified) clang/lib/Format/FormatTokenLexer.cpp (+19)
  • (modified) clang/lib/Format/FormatTokenLexer.h (+3)
  • (modified) clang/unittests/Format/FormatTestComments.cpp (+11)
diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst
index b746df5dab264..70582b6c40980 100644
--- a/clang/docs/ClangFormatStyleOptions.rst
+++ b/clang/docs/ClangFormatStyleOptions.rst
@@ -6343,6 +6343,17 @@ the configuration (without a prefix: ``Auto``).
       case 1 : break;                         case 1: break;
     }                                       }
 
+.. _SpaceBeforeClosingBlockComment:
+
+**SpaceBeforeClosingBlockComment** (``Boolean``) :versionbadge:`clang-format 21` :ref:`¶ <SpaceBeforeClosingBlockComment>`
+  If ``true``, a space is inserted immediately before the closing ``*/`` in
+  block comments that contain content.
+
+  .. code-block:: c++
+
+     true:                                  false:
+     /* comment */                  vs.     /* comment*/
+
 .. _SpaceBeforeCpp11BracedList:
 
 **SpaceBeforeCpp11BracedList** (``Boolean``) :versionbadge:`clang-format 7` :ref:`¶ <SpaceBeforeCpp11BracedList>`
diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h
index 3df5b92654094..7136fd2c5a4f8 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -4684,6 +4684,15 @@ struct FormatStyle {
   /// \version 17
   bool SpaceBeforeJsonColon;
 
+  /// If ``true``, a space is inserted immediately before the closing ``*/`` in
+  /// block comments that contain content.
+  /// \code
+  ///    true:                                  false:
+  ///    /* comment */                  vs.     /* comment*/
+  /// \endcode
+  /// \version 21
+  bool SpaceBeforeClosingBlockComment;
+
   /// Different ways to put a space before opening parentheses.
   enum SpaceBeforeParensStyle : int8_t {
     /// This is **deprecated** and replaced by ``Custom`` below, with all
@@ -5611,6 +5620,7 @@ struct FormatStyle {
            SpaceAroundPointerQualifiers == R.SpaceAroundPointerQualifiers &&
            SpaceBeforeRangeBasedForLoopColon ==
                R.SpaceBeforeRangeBasedForLoopColon &&
+           SpaceBeforeClosingBlockComment == R.SpaceBeforeClosingBlockComment &&
            SpaceBeforeSquareBrackets == R.SpaceBeforeSquareBrackets &&
            SpaceInEmptyBraces == R.SpaceInEmptyBraces &&
            SpacesBeforeTrailingComments == R.SpacesBeforeTrailingComments &&
diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index 686e54128d372..06292c75f27e0 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -1222,6 +1222,8 @@ template <> struct MappingTraits<FormatStyle> {
     IO.mapOptional("SpaceBeforeInheritanceColon",
                    Style.SpaceBeforeInheritanceColon);
     IO.mapOptional("SpaceBeforeJsonColon", Style.SpaceBeforeJsonColon);
+    IO.mapOptional("SpaceBeforeClosingBlockComment",
+                   Style.SpaceBeforeClosingBlockComment);
     IO.mapOptional("SpaceBeforeParens", Style.SpaceBeforeParens);
     IO.mapOptional("SpaceBeforeParensOptions", Style.SpaceBeforeParensOptions);
     IO.mapOptional("SpaceBeforeRangeBasedForLoopColon",
@@ -1717,6 +1719,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
   LLVMStyle.SpaceBeforeCtorInitializerColon = true;
   LLVMStyle.SpaceBeforeInheritanceColon = true;
   LLVMStyle.SpaceBeforeJsonColon = false;
+  LLVMStyle.SpaceBeforeClosingBlockComment = false;
   LLVMStyle.SpaceBeforeParens = FormatStyle::SBPO_ControlStatements;
   LLVMStyle.SpaceBeforeParensOptions = {};
   LLVMStyle.SpaceBeforeParensOptions.AfterControlStatements = true;
diff --git a/clang/lib/Format/FormatTokenLexer.cpp b/clang/lib/Format/FormatTokenLexer.cpp
index 86a5185a92a52..b48d1b7a82026 100644
--- a/clang/lib/Format/FormatTokenLexer.cpp
+++ b/clang/lib/Format/FormatTokenLexer.cpp
@@ -18,8 +18,11 @@
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/SourceManager.h"
 #include "clang/Format/Format.h"
+#include "llvm/ADT/SmallString.h"
 #include "llvm/Support/Regex.h"
 
+#include <algorithm>
+
 namespace clang {
 namespace format {
 
@@ -1386,6 +1389,22 @@ FormatToken *FormatTokenLexer::getNextToken() {
     StringRef UntrimmedText = FormatTok->TokenText;
     FormatTok->TokenText = FormatTok->TokenText.rtrim(" \t\v\f");
     TrailingWhitespace = UntrimmedText.size() - FormatTok->TokenText.size();
+    if (Style.SpaceBeforeClosingBlockComment &&
+        FormatTok->TokenText.starts_with("/*") &&
+        FormatTok->TokenText.ends_with("*/")) {
+      StringRef Body = FormatTok->TokenText.drop_front(2).drop_back(2);
+      if (!Body.empty()) {
+        const char BeforeClosing = Body.back();
+        if (!isWhitespace(static_cast<unsigned char>(BeforeClosing))) {
+          llvm::SmallString<64> Adjusted(FormatTok->TokenText);
+          Adjusted.insert(Adjusted.end() - 2, ' ');
+          char *Storage = CommentTextAllocator.Allocate<char>(Adjusted.size());
+          std::copy(Adjusted.begin(), Adjusted.end(), Storage);
+          FormatTok->TokenText = StringRef(Storage, Adjusted.size());
+          FormatTok->Tok.setLength(FormatTok->TokenText.size());
+        }
+      }
+    }
   } else if (FormatTok->is(tok::raw_identifier)) {
     IdentifierInfo &Info = IdentTable.get(FormatTok->TokenText);
     FormatTok->Tok.setIdentifierInfo(&Info);
diff --git a/clang/lib/Format/FormatTokenLexer.h b/clang/lib/Format/FormatTokenLexer.h
index 57c572af3defd..65b1199c1501c 100644
--- a/clang/lib/Format/FormatTokenLexer.h
+++ b/clang/lib/Format/FormatTokenLexer.h
@@ -20,6 +20,7 @@
 #include "llvm/ADT/MapVector.h"
 #include "llvm/ADT/SmallPtrSet.h"
 #include "llvm/ADT/StringSet.h"
+#include "llvm/Support/Allocator.h"
 
 #include <stack>
 
@@ -130,6 +131,8 @@ class FormatTokenLexer {
   unsigned FirstInLineIndex;
   SmallVector<FormatToken *, 16> Tokens;
 
+  llvm::BumpPtrAllocator CommentTextAllocator;
+
   llvm::SmallMapVector<IdentifierInfo *, TokenType, 8> Macros;
 
   llvm::SmallPtrSet<IdentifierInfo *, 8> MacrosSkippedByRemoveParentheses,
diff --git a/clang/unittests/Format/FormatTestComments.cpp b/clang/unittests/Format/FormatTestComments.cpp
index 69026bce98705..e12e17c0e12a8 100644
--- a/clang/unittests/Format/FormatTestComments.cpp
+++ b/clang/unittests/Format/FormatTestComments.cpp
@@ -332,6 +332,17 @@ TEST_F(FormatTestComments, UnderstandsSingleLineComments) {
   verifyNoCrash(StringRef("/*\\\0\n/", 6));
 }
 
+TEST_F(FormatTestComments, InsertsSpaceBeforeClosingBlockComment) {
+  FormatStyle Style = getLLVMStyle();
+  Style.SpaceBeforeClosingBlockComment = true;
+
+  verifyFormat("foo(/* comment */);", "foo(/* comment*/);", Style);
+  verifyFormat("foo(/*Logger= */nullptr);", "foo(/*Logger=*/nullptr);", Style);
+  verifyFormat("/* comment */", Style);
+  verifyFormat("/* leading */\nint x;", Style);
+  verifyFormat("/* multiline\n */", Style);
+}
+
 TEST_F(FormatTestComments, KeepsParameterWithTrailingCommentsOnTheirOwnLine) {
   EXPECT_EQ("SomeFunction(a,\n"
             "             b, // comment\n"

@llvmbot
Copy link
Member

llvmbot commented Oct 6, 2025

@llvm/pr-subscribers-clang-format

Author: Men-cotton (Men-cotton)

Changes

#160682


Full diff: https://github.com/llvm/llvm-project/pull/162105.diff

6 Files Affected:

  • (modified) clang/docs/ClangFormatStyleOptions.rst (+11)
  • (modified) clang/include/clang/Format/Format.h (+10)
  • (modified) clang/lib/Format/Format.cpp (+3)
  • (modified) clang/lib/Format/FormatTokenLexer.cpp (+19)
  • (modified) clang/lib/Format/FormatTokenLexer.h (+3)
  • (modified) clang/unittests/Format/FormatTestComments.cpp (+11)
diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst
index b746df5dab264..70582b6c40980 100644
--- a/clang/docs/ClangFormatStyleOptions.rst
+++ b/clang/docs/ClangFormatStyleOptions.rst
@@ -6343,6 +6343,17 @@ the configuration (without a prefix: ``Auto``).
       case 1 : break;                         case 1: break;
     }                                       }
 
+.. _SpaceBeforeClosingBlockComment:
+
+**SpaceBeforeClosingBlockComment** (``Boolean``) :versionbadge:`clang-format 21` :ref:`¶ <SpaceBeforeClosingBlockComment>`
+  If ``true``, a space is inserted immediately before the closing ``*/`` in
+  block comments that contain content.
+
+  .. code-block:: c++
+
+     true:                                  false:
+     /* comment */                  vs.     /* comment*/
+
 .. _SpaceBeforeCpp11BracedList:
 
 **SpaceBeforeCpp11BracedList** (``Boolean``) :versionbadge:`clang-format 7` :ref:`¶ <SpaceBeforeCpp11BracedList>`
diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h
index 3df5b92654094..7136fd2c5a4f8 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -4684,6 +4684,15 @@ struct FormatStyle {
   /// \version 17
   bool SpaceBeforeJsonColon;
 
+  /// If ``true``, a space is inserted immediately before the closing ``*/`` in
+  /// block comments that contain content.
+  /// \code
+  ///    true:                                  false:
+  ///    /* comment */                  vs.     /* comment*/
+  /// \endcode
+  /// \version 21
+  bool SpaceBeforeClosingBlockComment;
+
   /// Different ways to put a space before opening parentheses.
   enum SpaceBeforeParensStyle : int8_t {
     /// This is **deprecated** and replaced by ``Custom`` below, with all
@@ -5611,6 +5620,7 @@ struct FormatStyle {
            SpaceAroundPointerQualifiers == R.SpaceAroundPointerQualifiers &&
            SpaceBeforeRangeBasedForLoopColon ==
                R.SpaceBeforeRangeBasedForLoopColon &&
+           SpaceBeforeClosingBlockComment == R.SpaceBeforeClosingBlockComment &&
            SpaceBeforeSquareBrackets == R.SpaceBeforeSquareBrackets &&
            SpaceInEmptyBraces == R.SpaceInEmptyBraces &&
            SpacesBeforeTrailingComments == R.SpacesBeforeTrailingComments &&
diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index 686e54128d372..06292c75f27e0 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -1222,6 +1222,8 @@ template <> struct MappingTraits<FormatStyle> {
     IO.mapOptional("SpaceBeforeInheritanceColon",
                    Style.SpaceBeforeInheritanceColon);
     IO.mapOptional("SpaceBeforeJsonColon", Style.SpaceBeforeJsonColon);
+    IO.mapOptional("SpaceBeforeClosingBlockComment",
+                   Style.SpaceBeforeClosingBlockComment);
     IO.mapOptional("SpaceBeforeParens", Style.SpaceBeforeParens);
     IO.mapOptional("SpaceBeforeParensOptions", Style.SpaceBeforeParensOptions);
     IO.mapOptional("SpaceBeforeRangeBasedForLoopColon",
@@ -1717,6 +1719,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
   LLVMStyle.SpaceBeforeCtorInitializerColon = true;
   LLVMStyle.SpaceBeforeInheritanceColon = true;
   LLVMStyle.SpaceBeforeJsonColon = false;
+  LLVMStyle.SpaceBeforeClosingBlockComment = false;
   LLVMStyle.SpaceBeforeParens = FormatStyle::SBPO_ControlStatements;
   LLVMStyle.SpaceBeforeParensOptions = {};
   LLVMStyle.SpaceBeforeParensOptions.AfterControlStatements = true;
diff --git a/clang/lib/Format/FormatTokenLexer.cpp b/clang/lib/Format/FormatTokenLexer.cpp
index 86a5185a92a52..b48d1b7a82026 100644
--- a/clang/lib/Format/FormatTokenLexer.cpp
+++ b/clang/lib/Format/FormatTokenLexer.cpp
@@ -18,8 +18,11 @@
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/SourceManager.h"
 #include "clang/Format/Format.h"
+#include "llvm/ADT/SmallString.h"
 #include "llvm/Support/Regex.h"
 
+#include <algorithm>
+
 namespace clang {
 namespace format {
 
@@ -1386,6 +1389,22 @@ FormatToken *FormatTokenLexer::getNextToken() {
     StringRef UntrimmedText = FormatTok->TokenText;
     FormatTok->TokenText = FormatTok->TokenText.rtrim(" \t\v\f");
     TrailingWhitespace = UntrimmedText.size() - FormatTok->TokenText.size();
+    if (Style.SpaceBeforeClosingBlockComment &&
+        FormatTok->TokenText.starts_with("/*") &&
+        FormatTok->TokenText.ends_with("*/")) {
+      StringRef Body = FormatTok->TokenText.drop_front(2).drop_back(2);
+      if (!Body.empty()) {
+        const char BeforeClosing = Body.back();
+        if (!isWhitespace(static_cast<unsigned char>(BeforeClosing))) {
+          llvm::SmallString<64> Adjusted(FormatTok->TokenText);
+          Adjusted.insert(Adjusted.end() - 2, ' ');
+          char *Storage = CommentTextAllocator.Allocate<char>(Adjusted.size());
+          std::copy(Adjusted.begin(), Adjusted.end(), Storage);
+          FormatTok->TokenText = StringRef(Storage, Adjusted.size());
+          FormatTok->Tok.setLength(FormatTok->TokenText.size());
+        }
+      }
+    }
   } else if (FormatTok->is(tok::raw_identifier)) {
     IdentifierInfo &Info = IdentTable.get(FormatTok->TokenText);
     FormatTok->Tok.setIdentifierInfo(&Info);
diff --git a/clang/lib/Format/FormatTokenLexer.h b/clang/lib/Format/FormatTokenLexer.h
index 57c572af3defd..65b1199c1501c 100644
--- a/clang/lib/Format/FormatTokenLexer.h
+++ b/clang/lib/Format/FormatTokenLexer.h
@@ -20,6 +20,7 @@
 #include "llvm/ADT/MapVector.h"
 #include "llvm/ADT/SmallPtrSet.h"
 #include "llvm/ADT/StringSet.h"
+#include "llvm/Support/Allocator.h"
 
 #include <stack>
 
@@ -130,6 +131,8 @@ class FormatTokenLexer {
   unsigned FirstInLineIndex;
   SmallVector<FormatToken *, 16> Tokens;
 
+  llvm::BumpPtrAllocator CommentTextAllocator;
+
   llvm::SmallMapVector<IdentifierInfo *, TokenType, 8> Macros;
 
   llvm::SmallPtrSet<IdentifierInfo *, 8> MacrosSkippedByRemoveParentheses,
diff --git a/clang/unittests/Format/FormatTestComments.cpp b/clang/unittests/Format/FormatTestComments.cpp
index 69026bce98705..e12e17c0e12a8 100644
--- a/clang/unittests/Format/FormatTestComments.cpp
+++ b/clang/unittests/Format/FormatTestComments.cpp
@@ -332,6 +332,17 @@ TEST_F(FormatTestComments, UnderstandsSingleLineComments) {
   verifyNoCrash(StringRef("/*\\\0\n/", 6));
 }
 
+TEST_F(FormatTestComments, InsertsSpaceBeforeClosingBlockComment) {
+  FormatStyle Style = getLLVMStyle();
+  Style.SpaceBeforeClosingBlockComment = true;
+
+  verifyFormat("foo(/* comment */);", "foo(/* comment*/);", Style);
+  verifyFormat("foo(/*Logger= */nullptr);", "foo(/*Logger=*/nullptr);", Style);
+  verifyFormat("/* comment */", Style);
+  verifyFormat("/* leading */\nint x;", Style);
+  verifyFormat("/* multiline\n */", Style);
+}
+
 TEST_F(FormatTestComments, KeepsParameterWithTrailingCommentsOnTheirOwnLine) {
   EXPECT_EQ("SomeFunction(a,\n"
             "             b, // comment\n"

@Men-cotton Men-cotton changed the title [clang-format] Option to insert spaces before the closing */ [clang-format] Option to insert spaces before the closing */ Oct 6, 2025
@Men-cotton Men-cotton marked this pull request as draft October 6, 2025 16:24
@Men-cotton Men-cotton force-pushed the users/Men-cotton/clang-format/space-before-closing-comment branch 2 times, most recently from 9f59655 to ed55a3b Compare October 8, 2025 02:31
@Men-cotton Men-cotton marked this pull request as ready for review October 8, 2025 02:32
@Men-cotton Men-cotton marked this pull request as draft October 8, 2025 03:22
@Men-cotton Men-cotton force-pushed the users/Men-cotton/clang-format/space-before-closing-comment branch from 9b0b3cf to 975bc0b Compare October 8, 2025 06:43
@Men-cotton Men-cotton marked this pull request as ready for review October 8, 2025 06:45
@Men-cotton Men-cotton force-pushed the users/Men-cotton/clang-format/space-before-closing-comment branch from fe5908b to 3cfb708 Compare October 8, 2025 15:50
/// block comments that contain content.
/// \code
/// true: false:
/// /* comment */ vs. /* comment*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we want options to handle this differently.

foo(/*bar=*/true);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs to have at least Space, NoSpace, Leave because the default of false is going to now remove spaces everywhere

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All options normally go through a phase boolean -> enum -> struct I think in this case we should short circuit straight to struct

struct SpaceInComments
{
AfterOpeningComment = Leave
BeforeClosingComment = Leave
AfterOpeningParamComment = Leave
BeforeCloseingParamComment = No // I think this is the current behavior
}

SpaceAroundPointerQualifiers == R.SpaceAroundPointerQualifiers &&
SpaceBeforeRangeBasedForLoopColon ==
R.SpaceBeforeRangeBasedForLoopColon &&
SpaceBeforeClosingBlockComment == R.SpaceBeforeClosingBlockComment &&
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alphabetic ordering please

Style.SpaceBeforeClosingBlockComment = true;

verifyFormat("foo(/* comment */);", "foo(/* comment*/);", Style);
verifyFormat("foo(/*Logger= */nullptr);", "foo(/*Logger=*/nullptr);", Style);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I want to handle this different from comments

verifyFormat("switch (n) {\n"
"case 1:\n"
" foo();\n"
"/*FALLTHROUGH */ case 2:\n"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to handle "/*" while we are at it..

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang Clang issues not falling into any other category clang-format
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants