[clang] [clang-format] Add SpacesInComments option for block comments (PR #204727)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Jun 18 22:01:51 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang-format
Author: Frank (frank-suwen)
<details>
<summary>Changes</summary>
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.
---
Full diff: https://github.com/llvm/llvm-project/pull/204727.diff
4 Files Affected:
- (modified) clang/include/clang/Format/Format.h (+25)
- (modified) clang/lib/Format/BreakableToken.cpp (+42)
- (modified) clang/lib/Format/Format.cpp (+10)
- (modified) clang/unittests/Format/FormatTestComments.cpp (+34)
``````````diff
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); }");
``````````
</details>
https://github.com/llvm/llvm-project/pull/204727
More information about the cfe-commits
mailing list