Skip to content

Commit 40671bb

Browse files
authored
[clang-format] Handle control statements in BreakAfterAttributes (#71995)
This patch doesn't work for do-while loops. Fixed #64474.
1 parent 1ffcac1 commit 40671bb

File tree

4 files changed

+185
-16
lines changed

4 files changed

+185
-16
lines changed

clang/docs/ClangFormatStyleOptions.rst

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2049,8 +2049,10 @@ the configuration (without a prefix: ``Auto``).
20492049
.. _BreakAfterAttributes:
20502050

20512051
**BreakAfterAttributes** (``AttributeBreakingStyle``) :versionbadge:`clang-format 16` :ref:`<BreakAfterAttributes>`
2052-
Break after a group of C++11 attributes before a variable/function
2053-
(including constructor/destructor) declaration/definition name.
2052+
Break after a group of C++11 attributes before variable or function
2053+
(including constructor/destructor) declaration/definition names or before
2054+
control statements, i.e. ``if``, ``switch`` (including ``case`` and
2055+
``default`` labels), ``for``, and ``while`` statements.
20542056

20552057
Possible values:
20562058

@@ -2063,11 +2065,28 @@ the configuration (without a prefix: ``Auto``).
20632065
const int i;
20642066
[[gnu::const]] [[maybe_unused]]
20652067
int j;
2068+
20662069
[[nodiscard]]
20672070
inline int f();
20682071
[[gnu::const]] [[nodiscard]]
20692072
int g();
20702073

2074+
[[likely]]
2075+
if (a)
2076+
f();
2077+
else
2078+
g();
2079+
2080+
switch (b) {
2081+
[[unlikely]]
2082+
case 1:
2083+
++b;
2084+
break;
2085+
[[likely]]
2086+
default:
2087+
return;
2088+
}
2089+
20712090
* ``ABS_Leave`` (in configuration: ``Leave``)
20722091
Leave the line breaking after attributes as is.
20732092

@@ -2076,20 +2095,49 @@ the configuration (without a prefix: ``Auto``).
20762095
[[maybe_unused]] const int i;
20772096
[[gnu::const]] [[maybe_unused]]
20782097
int j;
2098+
20792099
[[nodiscard]] inline int f();
20802100
[[gnu::const]] [[nodiscard]]
20812101
int g();
20822102

2103+
[[likely]] if (a)
2104+
f();
2105+
else
2106+
g();
2107+
2108+
switch (b) {
2109+
[[unlikely]] case 1:
2110+
++b;
2111+
break;
2112+
[[likely]]
2113+
default:
2114+
return;
2115+
}
2116+
20832117
* ``ABS_Never`` (in configuration: ``Never``)
20842118
Never break after attributes.
20852119

20862120
.. code-block:: c++
20872121

20882122
[[maybe_unused]] const int i;
20892123
[[gnu::const]] [[maybe_unused]] int j;
2124+
20902125
[[nodiscard]] inline int f();
20912126
[[gnu::const]] [[nodiscard]] int g();
20922127

2128+
[[likely]] if (a)
2129+
f();
2130+
else
2131+
g();
2132+
2133+
switch (b) {
2134+
[[unlikely]] case 1:
2135+
++b;
2136+
break;
2137+
[[likely]] default:
2138+
return;
2139+
}
2140+
20932141

20942142

20952143
.. _BreakAfterJavaFieldAnnotations:

clang/include/clang/Format/Format.h

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1432,34 +1432,82 @@ struct FormatStyle {
14321432
/// const int i;
14331433
/// [[gnu::const]] [[maybe_unused]]
14341434
/// int j;
1435+
///
14351436
/// [[nodiscard]]
14361437
/// inline int f();
14371438
/// [[gnu::const]] [[nodiscard]]
14381439
/// int g();
1440+
///
1441+
/// [[likely]]
1442+
/// if (a)
1443+
/// f();
1444+
/// else
1445+
/// g();
1446+
///
1447+
/// switch (b) {
1448+
/// [[unlikely]]
1449+
/// case 1:
1450+
/// ++b;
1451+
/// break;
1452+
/// [[likely]]
1453+
/// default:
1454+
/// return;
1455+
/// }
14391456
/// \endcode
14401457
ABS_Always,
14411458
/// Leave the line breaking after attributes as is.
14421459
/// \code
14431460
/// [[maybe_unused]] const int i;
14441461
/// [[gnu::const]] [[maybe_unused]]
14451462
/// int j;
1463+
///
14461464
/// [[nodiscard]] inline int f();
14471465
/// [[gnu::const]] [[nodiscard]]
14481466
/// int g();
1467+
///
1468+
/// [[likely]] if (a)
1469+
/// f();
1470+
/// else
1471+
/// g();
1472+
///
1473+
/// switch (b) {
1474+
/// [[unlikely]] case 1:
1475+
/// ++b;
1476+
/// break;
1477+
/// [[likely]]
1478+
/// default:
1479+
/// return;
1480+
/// }
14491481
/// \endcode
14501482
ABS_Leave,
14511483
/// Never break after attributes.
14521484
/// \code
14531485
/// [[maybe_unused]] const int i;
14541486
/// [[gnu::const]] [[maybe_unused]] int j;
1487+
///
14551488
/// [[nodiscard]] inline int f();
14561489
/// [[gnu::const]] [[nodiscard]] int g();
1490+
///
1491+
/// [[likely]] if (a)
1492+
/// f();
1493+
/// else
1494+
/// g();
1495+
///
1496+
/// switch (b) {
1497+
/// [[unlikely]] case 1:
1498+
/// ++b;
1499+
/// break;
1500+
/// [[likely]] default:
1501+
/// return;
1502+
/// }
14571503
/// \endcode
14581504
ABS_Never,
14591505
};
14601506

1461-
/// Break after a group of C++11 attributes before a variable/function
1462-
/// (including constructor/destructor) declaration/definition name.
1507+
/// Break after a group of C++11 attributes before variable or function
1508+
/// (including constructor/destructor) declaration/definition names or before
1509+
/// control statements, i.e. ``if``, ``switch`` (including ``case`` and
1510+
/// ``default`` labels), ``for``, and ``while`` statements.
14631511
/// \version 16
14641512
AttributeBreakingStyle BreakAfterAttributes;
14651513

clang/lib/Format/TokenAnnotator.cpp

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,18 @@
2424
namespace clang {
2525
namespace format {
2626

27+
static bool mustBreakAfterAttributes(const FormatToken &Tok,
28+
const FormatStyle &Style) {
29+
switch (Style.BreakAfterAttributes) {
30+
case FormatStyle::ABS_Always:
31+
return true;
32+
case FormatStyle::ABS_Leave:
33+
return Tok.NewlinesBefore > 0;
34+
default:
35+
return false;
36+
}
37+
}
38+
2739
namespace {
2840

2941
/// Returns \c true if the line starts with a token that can start a statement
@@ -961,6 +973,15 @@ class AnnotatingParser {
961973
}
962974

963975
bool consumeToken() {
976+
if (Style.isCpp()) {
977+
const auto *Prev = CurrentToken->getPreviousNonComment();
978+
if (Prev && Prev->is(tok::r_square) && Prev->is(TT_AttributeSquare) &&
979+
CurrentToken->isOneOf(tok::kw_if, tok::kw_switch, tok::kw_case,
980+
tok::kw_default, tok::kw_for, tok::kw_while) &&
981+
mustBreakAfterAttributes(*CurrentToken, Style)) {
982+
CurrentToken->MustBreakBefore = true;
983+
}
984+
}
964985
FormatToken *Tok = CurrentToken;
965986
next();
966987
// In Verilog primitives' state tables, `:`, `?`, and `-` aren't normal
@@ -3420,18 +3441,6 @@ bool TokenAnnotator::mustBreakForReturnType(const AnnotatedLine &Line) const {
34203441
return false;
34213442
}
34223443

3423-
static bool mustBreakAfterAttributes(const FormatToken &Tok,
3424-
const FormatStyle &Style) {
3425-
switch (Style.BreakAfterAttributes) {
3426-
case FormatStyle::ABS_Always:
3427-
return true;
3428-
case FormatStyle::ABS_Leave:
3429-
return Tok.NewlinesBefore > 0;
3430-
default:
3431-
return false;
3432-
}
3433-
}
3434-
34353444
void TokenAnnotator::calculateFormattingInformation(AnnotatedLine &Line) const {
34363445
for (AnnotatedLine *ChildLine : Line.Children)
34373446
calculateFormattingInformation(*ChildLine);

clang/unittests/Format/FormatTest.cpp

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26255,6 +26255,70 @@ TEST_F(FormatTest, BreakAfterAttributes) {
2625526255
"}",
2625626256
Code, Style);
2625726257

26258+
constexpr StringRef CtrlStmtCode("[[likely]] if (a)\n"
26259+
" f();\n"
26260+
"else\n"
26261+
" g();\n"
26262+
"[[foo([[]])]]\n"
26263+
"switch (b) {\n"
26264+
"[[unlikely]] case 1:\n"
26265+
" ++b;\n"
26266+
" break;\n"
26267+
"[[likely]]\n"
26268+
"default:\n"
26269+
" return;\n"
26270+
"}\n"
26271+
"[[unlikely]] for (; c > 0; --c)\n"
26272+
" h();\n"
26273+
"[[likely]]\n"
26274+
"while (d > 0)\n"
26275+
" --d;");
26276+
26277+
Style.BreakAfterAttributes = FormatStyle::ABS_Leave;
26278+
verifyNoChange(CtrlStmtCode, Style);
26279+
26280+
Style.BreakAfterAttributes = FormatStyle::ABS_Never;
26281+
verifyFormat("[[likely]] if (a)\n"
26282+
" f();\n"
26283+
"else\n"
26284+
" g();\n"
26285+
"[[foo([[]])]] switch (b) {\n"
26286+
"[[unlikely]] case 1:\n"
26287+
" ++b;\n"
26288+
" break;\n"
26289+
"[[likely]] default:\n"
26290+
" return;\n"
26291+
"}\n"
26292+
"[[unlikely]] for (; c > 0; --c)\n"
26293+
" h();\n"
26294+
"[[likely]] while (d > 0)\n"
26295+
" --d;",
26296+
CtrlStmtCode, Style);
26297+
26298+
Style.BreakAfterAttributes = FormatStyle::ABS_Always;
26299+
verifyFormat("[[likely]]\n"
26300+
"if (a)\n"
26301+
" f();\n"
26302+
"else\n"
26303+
" g();\n"
26304+
"[[foo([[]])]]\n"
26305+
"switch (b) {\n"
26306+
"[[unlikely]]\n"
26307+
"case 1:\n"
26308+
" ++b;\n"
26309+
" break;\n"
26310+
"[[likely]]\n"
26311+
"default:\n"
26312+
" return;\n"
26313+
"}\n"
26314+
"[[unlikely]]\n"
26315+
"for (; c > 0; --c)\n"
26316+
" h();\n"
26317+
"[[likely]]\n"
26318+
"while (d > 0)\n"
26319+
" --d;",
26320+
CtrlStmtCode, Style);
26321+
2625826322
constexpr StringRef CtorDtorCode("struct Foo {\n"
2625926323
" [[deprecated]] Foo();\n"
2626026324
" [[deprecated]] Foo() {}\n"

0 commit comments

Comments
 (0)