- Notifications
You must be signed in to change notification settings - Fork 15.3k
[clang-format] Add SpaceInComments option to control spacing around /* */ #162105
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
[clang-format] Add SpaceInComments option to control spacing around /* */ #162105
Conversation
| 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 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. |
| @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:
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" |
| @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:
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" |
*/ 9f59655 to ed55a3b Compare 9b0b3cf to 975bc0b Compare fe5908b to 3cfb708 Compare clang/include/clang/Format/Format.h Outdated
| /// block comments that contain content. | ||
| /// \code | ||
| /// true: false: | ||
| /// /* comment */ vs. /* comment*/ |
There was a problem hiding this comment.
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);There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’ve wired up SpaceInComments per your struct.
At the moment, AfterOpeningParamComment defaults to Leave, which means parameter comments fall back to whatever AfterOpeningComment specifies—e.g., if the general option is Always, we still insert the leading space in /*param=*/, and if it’s Never, we remove it. Setting AfterOpeningParamComment to Always or Never overrides that fallback.
Is that the interaction you were expecting, or should Leave on the parameter knob preserve the existing spacing even when the general control is driving changes?
af00c19 to d947839 Compare ecebae0 to 1cf571b Compare 1cf571b to 7de5905 Compare 7de5905 to 7a29a63 Compare
HazardyKnusperkeks left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For further review it would be nice if you neither amend or rebase. Otherwise there is no method of viewing the diff of your new changes.
clang/lib/Format/BreakableToken.cpp Outdated
| FormatStyle::CommentSpaceMode ParamOverrideMode, | ||
| const bool ForceDocstringLeave) { | ||
| if (Tok.getBlockCommentKind() == CommentKind::Parameter) { | ||
| if (ParamOverrideMode != FormatStyle::CommentSpaceMode::Leave) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't make the parameter option to use the other one, if set to Leave.
HazardyKnusperkeks left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@owenca can you also take a look?
| @Men-cotton please describe in the commit message what this new option is intended to solve. Not just an issue number.
What I would do is to add a single
Comments ending in |
*/SpaceInComments option to control spacing around /* */ …ify whitespace adjustment logic
We should name the option
When I suggested "to add a single |
| @owenca To provide a bit of context on how this evolved into a struct: I initially split out inline parameter comments from regular block comments based on earlier feedback from @mydeveloperday. I’m planning to rework the patch to follow your proposal and expose a single For the actual behavior, my current thought is to apply this option only to “ordinary” block comments and to treat |
Yes, that's what I had in mind, as mentioned at the end of my comment above. In fact, this option should only affect real block comments. For example, embedded |
Summary
SpaceInCommentsoption to control spaces right after/*and right before*/in block comments.Always(force a single space)Never(remove the space)Leave(default; keep existing formatting).SpaceInComments. Each setting acceptsAlways,Never, orLeave.AfterOpeningCommentandBeforeClosingCommentfor regular block comments.AfterOpeningParamCommentandBeforeClosingParamCommentfor inline parameter comments such as/*param=*/./*!or/**are intentionally left unchanged.Behavior
AlwaysNeverLeave(default)Keeps whatever spacing is already present in the comment.
Closes #160682