[clang] [clang-tools-extra] [llvm] [clang-format] Handle variable declarations in BreakAfterAttributes (PR #71935)
Owen Pan via cfe-commits
cfe-commits at lists.llvm.org
Fri Nov 10 05:14:58 PST 2023
https://github.com/owenca created https://github.com/llvm/llvm-project/pull/71935
Also fixed a bug in `isStartOfName()` and cleaned up some old test cases.
Fixed #71563.
This is a rework of #71755.
>From 17921d5a1d74017128691428815ede53ac1a4d67 Mon Sep 17 00:00:00 2001
From: Owen Pan <owenpiano at gmail.com>
Date: Wed, 8 Nov 2023 17:55:56 -0800
Subject: [PATCH 1/3] [clang-format] Handle variable declarations in
BreakAfterAttributes
Also cleaned up some old test cases.
Fixes #71563.
---
clang/docs/ClangFormatStyleOptions.rst | 13 +++++++--
clang/include/clang/Format/Format.h | 13 +++++++--
clang/lib/Format/TokenAnnotator.cpp | 22 +++++++++------
clang/unittests/Format/FormatTest.cpp | 38 ++++++++++++++++++--------
4 files changed, 62 insertions(+), 24 deletions(-)
diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst
index 21342e1b89ea866..d496fc85f7ae71a 100644
--- a/clang/docs/ClangFormatStyleOptions.rst
+++ b/clang/docs/ClangFormatStyleOptions.rst
@@ -2049,8 +2049,8 @@ the configuration (without a prefix: ``Auto``).
.. _BreakAfterAttributes:
**BreakAfterAttributes** (``AttributeBreakingStyle``) :versionbadge:`clang-format 16` :ref:`ΒΆ <BreakAfterAttributes>`
- Break after a group of C++11 attributes before a function
- declaration/definition name.
+ Break after a group of C++11 attributes before a variable/function
+ (including constructor/destructor) declaration/definition name.
Possible values:
@@ -2059,6 +2059,10 @@ the configuration (without a prefix: ``Auto``).
.. code-block:: c++
+ [[maybe_unused]]
+ const int i;
+ [[gnu::const]] [[maybe_unused]]
+ int j;
[[nodiscard]]
inline int f();
[[gnu::const]] [[nodiscard]]
@@ -2069,6 +2073,9 @@ the configuration (without a prefix: ``Auto``).
.. code-block:: c++
+ [[maybe_unused]] const int i;
+ [[gnu::const]] [[maybe_unused]]
+ int j;
[[nodiscard]] inline int f();
[[gnu::const]] [[nodiscard]]
int g();
@@ -2078,6 +2085,8 @@ the configuration (without a prefix: ``Auto``).
.. code-block:: c++
+ [[maybe_unused]] const int i;
+ [[gnu::const]] [[maybe_unused]] int j;
[[nodiscard]] inline int f();
[[gnu::const]] [[nodiscard]] int g();
diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h
index 3e9d1915badd87f..9442344000e142b 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -1428,6 +1428,10 @@ struct FormatStyle {
enum AttributeBreakingStyle : int8_t {
/// Always break after attributes.
/// \code
+ /// [[maybe_unused]]
+ /// const int i;
+ /// [[gnu::const]] [[maybe_unused]]
+ /// int j;
/// [[nodiscard]]
/// inline int f();
/// [[gnu::const]] [[nodiscard]]
@@ -1436,6 +1440,9 @@ struct FormatStyle {
ABS_Always,
/// Leave the line breaking after attributes as is.
/// \code
+ /// [[maybe_unused]] const int i;
+ /// [[gnu::const]] [[maybe_unused]]
+ /// int j;
/// [[nodiscard]] inline int f();
/// [[gnu::const]] [[nodiscard]]
/// int g();
@@ -1443,14 +1450,16 @@ struct FormatStyle {
ABS_Leave,
/// Never break after attributes.
/// \code
+ /// [[maybe_unused]] const int i;
+ /// [[gnu::const]] [[maybe_unused]] int j;
/// [[nodiscard]] inline int f();
/// [[gnu::const]] [[nodiscard]] int g();
/// \endcode
ABS_Never,
};
- /// Break after a group of C++11 attributes before a function
- /// declaration/definition name.
+ /// Break after a group of C++11 attributes before a variable/function
+ /// (including constructor/destructor) declaration/definition name.
/// \version 16
AttributeBreakingStyle BreakAfterAttributes;
diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp
index 729e7e370bf62ea..12f2e2ded19a01c 100644
--- a/clang/lib/Format/TokenAnnotator.cpp
+++ b/clang/lib/Format/TokenAnnotator.cpp
@@ -2000,6 +2000,10 @@ class AnnotatingParser {
(!Line.MightBeFunctionDecl || Current.NestingLevel != 0)) {
Contexts.back().FirstStartOfName = &Current;
Current.setType(TT_StartOfName);
+ if (auto *PrevNonComment = Current.getPreviousNonComment();
+ PrevNonComment && PrevNonComment->is(TT_StartOfName)) {
+ PrevNonComment->setType(TT_Unknown);
+ }
} else if (Current.is(tok::semi)) {
// Reset FirstStartOfName after finding a semicolon so that a for loop
// with multiple increment statements is not confused with a for loop
@@ -3258,7 +3262,7 @@ static bool isFunctionDeclarationName(bool IsCpp, const FormatToken &Current,
if (Current.is(TT_FunctionDeclarationName))
return true;
- if (!Current.Tok.getIdentifierInfo())
+ if (!Current.Tok.getIdentifierInfo() || Current.is(TT_CtorDtorDeclName))
return false;
auto skipOperatorName = [](const FormatToken *Next) -> const FormatToken * {
@@ -3441,19 +3445,19 @@ void TokenAnnotator::calculateFormattingInformation(AnnotatedLine &Line) const {
if (AlignArrayOfStructures)
calculateArrayInitializerColumnList(Line);
+ const bool IsCpp = Style.isCpp();
bool LineIsFunctionDeclaration = false;
FormatToken *ClosingParen = nullptr;
for (FormatToken *Tok = Current, *AfterLastAttribute = nullptr; Tok;
Tok = Tok->Next) {
if (Tok->Previous->EndsCppAttributeGroup)
AfterLastAttribute = Tok;
- if (const bool IsCtorOrDtor = Tok->is(TT_CtorDtorDeclName);
- IsCtorOrDtor ||
- isFunctionDeclarationName(Style.isCpp(), *Tok, Line, ClosingParen)) {
- if (!IsCtorOrDtor) {
- LineIsFunctionDeclaration = true;
- Tok->setFinalizedType(TT_FunctionDeclarationName);
- }
+ if (isFunctionDeclarationName(IsCpp, *Tok, Line, ClosingParen)) {
+ LineIsFunctionDeclaration = true;
+ Tok->setFinalizedType(TT_FunctionDeclarationName);
+ }
+ if (Tok->isOneOf(TT_FunctionDeclarationName, TT_CtorDtorDeclName,
+ TT_StartOfName)) {
if (AfterLastAttribute &&
mustBreakAfterAttributes(*AfterLastAttribute, Style)) {
AfterLastAttribute->MustBreakBefore = true;
@@ -3463,7 +3467,7 @@ void TokenAnnotator::calculateFormattingInformation(AnnotatedLine &Line) const {
}
}
- if (Style.isCpp()) {
+ if (IsCpp) {
if (!LineIsFunctionDeclaration) {
// Annotate */&/&& in `operator` function calls as binary operators.
for (const auto *Tok = Line.First; Tok; Tok = Tok->Next) {
diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index 80903e7630c8073..dcd05b4175e0b94 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -8479,18 +8479,25 @@ TEST_F(FormatTest, BreaksFunctionDeclarationsWithTrailingTokens) {
" aaaaaaaaaaaaaaaaaaaaaaaaa));");
verifyFormat("bool aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n"
" __attribute__((unused));");
- verifyGoogleFormat(
+
+ Style = getGoogleStyle();
+ Style.AttributeMacros.push_back("GUARDED_BY");
+ verifyFormat(
"bool aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n"
- " GUARDED_BY(aaaaaaaaaaaa);");
- verifyGoogleFormat(
+ " GUARDED_BY(aaaaaaaaaaaa);",
+ Style);
+ verifyFormat(
"bool aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n"
- " GUARDED_BY(aaaaaaaaaaaa);");
- verifyGoogleFormat(
+ " GUARDED_BY(aaaaaaaaaaaa);",
+ Style);
+ verifyFormat(
"bool aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa GUARDED_BY(aaaaaaaaaaaa) =\n"
- " aaaaaaaa::aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;");
- verifyGoogleFormat(
+ " aaaaaaaa::aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;",
+ Style);
+ verifyFormat(
"bool aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa GUARDED_BY(aaaaaaaaaaaa) =\n"
- " aaaaaaaaaaaaaaaaaaaaaaaaa;");
+ " aaaaaaaaaaaaaaaaaaaaaaaaa;",
+ Style);
}
TEST_F(FormatTest, FunctionAnnotations) {
@@ -26194,7 +26201,10 @@ TEST_F(FormatTest, RemoveSemicolon) {
TEST_F(FormatTest, BreakAfterAttributes) {
FormatStyle Style = getLLVMStyle();
- constexpr StringRef Code("[[nodiscard]] inline int f(int &i);\n"
+ constexpr StringRef Code("[[maybe_unused]] const int i;\n"
+ "[[foo([[]])]] [[maybe_unused]]\n"
+ "int j;\n"
+ "[[nodiscard]] inline int f(int &i);\n"
"[[foo([[]])]] [[nodiscard]]\n"
"int g(int &i);\n"
"[[nodiscard]]\n"
@@ -26211,7 +26221,9 @@ TEST_F(FormatTest, BreakAfterAttributes) {
verifyNoChange(Code, Style);
Style.BreakAfterAttributes = FormatStyle::ABS_Never;
- verifyFormat("[[nodiscard]] inline int f(int &i);\n"
+ verifyFormat("[[maybe_unused]] const int i;\n"
+ "[[foo([[]])]] [[maybe_unused]] int j;\n"
+ "[[nodiscard]] inline int f(int &i);\n"
"[[foo([[]])]] [[nodiscard]] int g(int &i);\n"
"[[nodiscard]] inline int f(int &i) {\n"
" i = 1;\n"
@@ -26224,7 +26236,11 @@ TEST_F(FormatTest, BreakAfterAttributes) {
Code, Style);
Style.BreakAfterAttributes = FormatStyle::ABS_Always;
- verifyFormat("[[nodiscard]]\n"
+ verifyFormat("[[maybe_unused]]\n"
+ "const int i;\n"
+ "[[foo([[]])]] [[maybe_unused]]\n"
+ "int j;\n"
+ "[[nodiscard]]\n"
"inline int f(int &i);\n"
"[[foo([[]])]] [[nodiscard]]\n"
"int g(int &i);\n"
>From 9e7b0cf5aadf2cb56d92fdc8435050e7526206a6 Mon Sep 17 00:00:00 2001
From: Owen Pan <owenpiano at gmail.com>
Date: Thu, 9 Nov 2023 20:21:20 -0800
Subject: [PATCH 2/3] minor cleanup
---
clang/lib/Format/TokenAnnotator.cpp | 9 +++++----
clang/unittests/Format/FormatTest.cpp | 3 +--
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp
index 12f2e2ded19a01c..e06b4d5a3b05eef 100644
--- a/clang/lib/Format/TokenAnnotator.cpp
+++ b/clang/lib/Format/TokenAnnotator.cpp
@@ -3456,12 +3456,13 @@ void TokenAnnotator::calculateFormattingInformation(AnnotatedLine &Line) const {
LineIsFunctionDeclaration = true;
Tok->setFinalizedType(TT_FunctionDeclarationName);
}
- if (Tok->isOneOf(TT_FunctionDeclarationName, TT_CtorDtorDeclName,
- TT_StartOfName)) {
- if (AfterLastAttribute &&
+ if (LineIsFunctionDeclaration ||
+ Tok->isOneOf(TT_CtorDtorDeclName, TT_StartOfName)) {
+ if (IsCpp && AfterLastAttribute &&
mustBreakAfterAttributes(*AfterLastAttribute, Style)) {
AfterLastAttribute->MustBreakBefore = true;
- Line.ReturnTypeWrapped = true;
+ if (LineIsFunctionDeclaration)
+ Line.ReturnTypeWrapped = true;
}
break;
}
diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index dcd05b4175e0b94..bb01f57dce31a7a 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -26199,8 +26199,6 @@ TEST_F(FormatTest, RemoveSemicolon) {
}
TEST_F(FormatTest, BreakAfterAttributes) {
- FormatStyle Style = getLLVMStyle();
-
constexpr StringRef Code("[[maybe_unused]] const int i;\n"
"[[foo([[]])]] [[maybe_unused]]\n"
"int j;\n"
@@ -26217,6 +26215,7 @@ TEST_F(FormatTest, BreakAfterAttributes) {
" return 1;\n"
"}");
+ FormatStyle Style = getLLVMStyle();
EXPECT_EQ(Style.BreakAfterAttributes, FormatStyle::ABS_Leave);
verifyNoChange(Code, Style);
>From c3ceb93a129d1a2faca9fbddaf94e197974118fa Mon Sep 17 00:00:00 2001
From: Owen Pan <owenpiano at gmail.com>
Date: Fri, 10 Nov 2023 04:58:50 -0800
Subject: [PATCH 3/3] Work around bugs in isStartOfName()
---
clang/lib/Format/TokenAnnotator.cpp | 40 +++++++++++++++++++----------
1 file changed, 26 insertions(+), 14 deletions(-)
diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp
index d2eebf6abe145af..725f02a37581d24 100644
--- a/clang/lib/Format/TokenAnnotator.cpp
+++ b/clang/lib/Format/TokenAnnotator.cpp
@@ -2188,6 +2188,11 @@ class AnnotatingParser {
if (Tok.isNot(tok::identifier) || !Tok.Previous)
return false;
+ if (const auto *NextNonComment = Tok.getNextNonComment();
+ !NextNonComment || NextNonComment->isPointerOrReference()) {
+ return false;
+ }
+
if (Tok.Previous->isOneOf(TT_LeadingJavaAnnotation, Keywords.kw_instanceof,
Keywords.kw_as)) {
return false;
@@ -3259,7 +3264,7 @@ static bool isFunctionDeclarationName(bool IsCpp, const FormatToken &Current,
if (Current.is(TT_FunctionDeclarationName))
return true;
- if (!Current.Tok.getIdentifierInfo() || Current.is(TT_CtorDtorDeclName))
+ if (!Current.Tok.getIdentifierInfo())
return false;
auto skipOperatorName = [](const FormatToken *Next) -> const FormatToken * {
@@ -3443,28 +3448,35 @@ void TokenAnnotator::calculateFormattingInformation(AnnotatedLine &Line) const {
calculateArrayInitializerColumnList(Line);
const bool IsCpp = Style.isCpp();
+ bool SeenName = false;
bool LineIsFunctionDeclaration = false;
FormatToken *ClosingParen = nullptr;
- for (FormatToken *Tok = Current, *AfterLastAttribute = nullptr; Tok;
- Tok = Tok->Next) {
+ FormatToken *AfterLastAttribute = nullptr;
+
+ for (auto *Tok = Current; Tok; Tok = Tok->Next) {
+ if (Tok->is(TT_StartOfName))
+ SeenName = true;
if (Tok->Previous->EndsCppAttributeGroup)
AfterLastAttribute = Tok;
- if (isFunctionDeclarationName(IsCpp, *Tok, Line, ClosingParen)) {
- LineIsFunctionDeclaration = true;
- Tok->setFinalizedType(TT_FunctionDeclarationName);
- }
- if (LineIsFunctionDeclaration ||
- Tok->isOneOf(TT_CtorDtorDeclName, TT_StartOfName)) {
- if (IsCpp && AfterLastAttribute &&
- mustBreakAfterAttributes(*AfterLastAttribute, Style)) {
- AfterLastAttribute->MustBreakBefore = true;
- if (LineIsFunctionDeclaration)
- Line.ReturnTypeWrapped = true;
+ if (const bool IsCtorOrDtor = Tok->is(TT_CtorDtorDeclName);
+ IsCtorOrDtor ||
+ isFunctionDeclarationName(Style.isCpp(), *Tok, Line, ClosingParen)) {
+ if (!IsCtorOrDtor) {
+ LineIsFunctionDeclaration = true;
+ Tok->setFinalizedType(TT_FunctionDeclarationName);
}
+ SeenName = true;
break;
}
}
+ if (IsCpp && SeenName && AfterLastAttribute &&
+ mustBreakAfterAttributes(*AfterLastAttribute, Style)) {
+ AfterLastAttribute->MustBreakBefore = true;
+ if (LineIsFunctionDeclaration)
+ Line.ReturnTypeWrapped = true;
+ }
+
if (IsCpp) {
if (!LineIsFunctionDeclaration) {
// Annotate */&/&& in `operator` function calls as binary operators.
More information about the cfe-commits
mailing list