Skip to content

Commit

Permalink
[clang-format] Handle control statements in BreakAfterAttributes (#71995
Browse files Browse the repository at this point in the history
)

This patch doesn't work for do-while loops.

Fixed #64474.
  • Loading branch information
owenca committed Nov 12, 2023
1 parent 1ffcac1 commit 40671bb
Show file tree
Hide file tree
Showing 4 changed files with 185 additions and 16 deletions.
52 changes: 50 additions & 2 deletions clang/docs/ClangFormatStyleOptions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2049,8 +2049,10 @@ 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 variable/function
(including constructor/destructor) declaration/definition name.
Break after a group of C++11 attributes before variable or function
(including constructor/destructor) declaration/definition names or before
control statements, i.e. ``if``, ``switch`` (including ``case`` and
``default`` labels), ``for``, and ``while`` statements.

Possible values:

Expand All @@ -2063,11 +2065,28 @@ the configuration (without a prefix: ``Auto``).
const int i;
[[gnu::const]] [[maybe_unused]]
int j;

[[nodiscard]]
inline int f();
[[gnu::const]] [[nodiscard]]
int g();

[[likely]]
if (a)
f();
else
g();

switch (b) {
[[unlikely]]
case 1:
++b;
break;
[[likely]]
default:
return;
}

* ``ABS_Leave`` (in configuration: ``Leave``)
Leave the line breaking after attributes as is.

Expand All @@ -2076,20 +2095,49 @@ the configuration (without a prefix: ``Auto``).
[[maybe_unused]] const int i;
[[gnu::const]] [[maybe_unused]]
int j;

[[nodiscard]] inline int f();
[[gnu::const]] [[nodiscard]]
int g();

[[likely]] if (a)
f();
else
g();

switch (b) {
[[unlikely]] case 1:
++b;
break;
[[likely]]
default:
return;
}

* ``ABS_Never`` (in configuration: ``Never``)
Never break after attributes.

.. code-block:: c++

[[maybe_unused]] const int i;
[[gnu::const]] [[maybe_unused]] int j;

[[nodiscard]] inline int f();
[[gnu::const]] [[nodiscard]] int g();

[[likely]] if (a)
f();
else
g();

switch (b) {
[[unlikely]] case 1:
++b;
break;
[[likely]] default:
return;
}



.. _BreakAfterJavaFieldAnnotations:
Expand Down
52 changes: 50 additions & 2 deletions clang/include/clang/Format/Format.h
Original file line number Diff line number Diff line change
Expand Up @@ -1432,34 +1432,82 @@ struct FormatStyle {
/// const int i;
/// [[gnu::const]] [[maybe_unused]]
/// int j;
///
/// [[nodiscard]]
/// inline int f();
/// [[gnu::const]] [[nodiscard]]
/// int g();
///
/// [[likely]]
/// if (a)
/// f();
/// else
/// g();
///
/// switch (b) {
/// [[unlikely]]
/// case 1:
/// ++b;
/// break;
/// [[likely]]
/// default:
/// return;
/// }
/// \endcode
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();
///
/// [[likely]] if (a)
/// f();
/// else
/// g();
///
/// switch (b) {
/// [[unlikely]] case 1:
/// ++b;
/// break;
/// [[likely]]
/// default:
/// return;
/// }
/// \endcode
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();
///
/// [[likely]] if (a)
/// f();
/// else
/// g();
///
/// switch (b) {
/// [[unlikely]] case 1:
/// ++b;
/// break;
/// [[likely]] default:
/// return;
/// }
/// \endcode
ABS_Never,
};

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

Expand Down
33 changes: 21 additions & 12 deletions clang/lib/Format/TokenAnnotator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,18 @@
namespace clang {
namespace format {

static bool mustBreakAfterAttributes(const FormatToken &Tok,
const FormatStyle &Style) {
switch (Style.BreakAfterAttributes) {
case FormatStyle::ABS_Always:
return true;
case FormatStyle::ABS_Leave:
return Tok.NewlinesBefore > 0;
default:
return false;
}
}

namespace {

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

bool consumeToken() {
if (Style.isCpp()) {
const auto *Prev = CurrentToken->getPreviousNonComment();
if (Prev && Prev->is(tok::r_square) && Prev->is(TT_AttributeSquare) &&
CurrentToken->isOneOf(tok::kw_if, tok::kw_switch, tok::kw_case,
tok::kw_default, tok::kw_for, tok::kw_while) &&
mustBreakAfterAttributes(*CurrentToken, Style)) {
CurrentToken->MustBreakBefore = true;
}
}
FormatToken *Tok = CurrentToken;
next();
// In Verilog primitives' state tables, `:`, `?`, and `-` aren't normal
Expand Down Expand Up @@ -3420,18 +3441,6 @@ bool TokenAnnotator::mustBreakForReturnType(const AnnotatedLine &Line) const {
return false;
}

static bool mustBreakAfterAttributes(const FormatToken &Tok,
const FormatStyle &Style) {
switch (Style.BreakAfterAttributes) {
case FormatStyle::ABS_Always:
return true;
case FormatStyle::ABS_Leave:
return Tok.NewlinesBefore > 0;
default:
return false;
}
}

void TokenAnnotator::calculateFormattingInformation(AnnotatedLine &Line) const {
for (AnnotatedLine *ChildLine : Line.Children)
calculateFormattingInformation(*ChildLine);
Expand Down
64 changes: 64 additions & 0 deletions clang/unittests/Format/FormatTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26255,6 +26255,70 @@ TEST_F(FormatTest, BreakAfterAttributes) {
"}",
Code, Style);

constexpr StringRef CtrlStmtCode("[[likely]] if (a)\n"
" f();\n"
"else\n"
" g();\n"
"[[foo([[]])]]\n"
"switch (b) {\n"
"[[unlikely]] case 1:\n"
" ++b;\n"
" break;\n"
"[[likely]]\n"
"default:\n"
" return;\n"
"}\n"
"[[unlikely]] for (; c > 0; --c)\n"
" h();\n"
"[[likely]]\n"
"while (d > 0)\n"
" --d;");

Style.BreakAfterAttributes = FormatStyle::ABS_Leave;
verifyNoChange(CtrlStmtCode, Style);

Style.BreakAfterAttributes = FormatStyle::ABS_Never;
verifyFormat("[[likely]] if (a)\n"
" f();\n"
"else\n"
" g();\n"
"[[foo([[]])]] switch (b) {\n"
"[[unlikely]] case 1:\n"
" ++b;\n"
" break;\n"
"[[likely]] default:\n"
" return;\n"
"}\n"
"[[unlikely]] for (; c > 0; --c)\n"
" h();\n"
"[[likely]] while (d > 0)\n"
" --d;",
CtrlStmtCode, Style);

Style.BreakAfterAttributes = FormatStyle::ABS_Always;
verifyFormat("[[likely]]\n"
"if (a)\n"
" f();\n"
"else\n"
" g();\n"
"[[foo([[]])]]\n"
"switch (b) {\n"
"[[unlikely]]\n"
"case 1:\n"
" ++b;\n"
" break;\n"
"[[likely]]\n"
"default:\n"
" return;\n"
"}\n"
"[[unlikely]]\n"
"for (; c > 0; --c)\n"
" h();\n"
"[[likely]]\n"
"while (d > 0)\n"
" --d;",
CtrlStmtCode, Style);

constexpr StringRef CtorDtorCode("struct Foo {\n"
" [[deprecated]] Foo();\n"
" [[deprecated]] Foo() {}\n"
Expand Down

0 comments on commit 40671bb

Please sign in to comment.