-
Couldn't load subscription status.
- Fork 15k
[clang-format] Option to insert spaces before the closing */
#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] Option to insert spaces before the closing */
#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?
| Style.SpaceBeforeClosingBlockComment = true; | ||
|
|
||
| verifyFormat("foo(/* comment */);", "foo(/* comment*/);", Style); | ||
| verifyFormat("foo(/*Logger= */nullptr);", "foo(/*Logger=*/nullptr);", Style); |
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 I want to handle this different from comments
| verifyFormat("switch (n) {\n" | ||
| "case 1:\n" | ||
| " foo();\n" | ||
| "/*FALLTHROUGH */ case 2:\n" |
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.
Do we want to handle "/*" while we are at it..
af00c19 to
d947839
Compare
| verifyFormat("/* \ncomment line\n*/", "/*\ncomment line\n*/", Style); | ||
| verifyFormat("/* \n * comment star\n*/", "/*\n * comment star\n*/", Style); | ||
| verifyFormat("/* comment line\nnext */", "/*comment line\nnext */", Style); | ||
| EXPECT_EQ("/*\n*/", format("/*\n*/", Style)); |
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.
| EXPECT_EQ("/*\n*/", format("/*\n*/", Style)); | |
| EXPECT_EQ("/*\n*/", format("/*\n*/", Style)); |
Why not verifyFormat?
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.
Thanks for the suggestion!
I agree that using verifyFormat is the better approach here.
I looked into it, and found that the single-argument version, verifyFormat("/*\n*/", Style), fails. This is because the internal messUp() function removes the newline from the empty comment (turning it into /**/), which then doesn't match the expected output. This also happens for the other two test cases where I was using EXPECT_EQ. This behavior of messUp() seems to be a known characteristic, as it's mentioned in the comments of the AlignTrailingCommentsAcrossEmptyLines test.
By switching to the two-argument version, like verifyFormat("/*\n*/", "/*\n*/", Style), the tests pass. This version avoids the messUp() call and is more robust than a plain EXPECT_EQ because it also performs a stability check.
For these reasons, I believe changing it to the two-argument verifyFormat is the best path forward. Let me know what you think.
| // A comment that looks like a parameter, e.g. /*in*/. | ||
| Parameter, | ||
| // A comment that is a sentinel, e.g. /*FALLTHROUGH*/. | ||
| Sentinel, |
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 don't know what to think about this. Especially since it is undocumented.
d947839 to
ecebae0
Compare
#160682