diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index d496fc85f7ae7..ff424828ff63c 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -2049,8 +2049,10 @@ the configuration (without a prefix: ``Auto``). .. _BreakAfterAttributes: **BreakAfterAttributes** (``AttributeBreakingStyle``) :versionbadge:`clang-format 16` :ref:`ΒΆ ` - 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: @@ -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. @@ -2076,10 +2095,25 @@ 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. @@ -2087,9 +2121,23 @@ 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; + } + .. _BreakAfterJavaFieldAnnotations: diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index 9442344000e14..bc412611ef624 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -1432,10 +1432,27 @@ 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. @@ -1443,23 +1460,54 @@ struct FormatStyle { /// [[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; diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index 725f02a37581d..03f3c3583f2ec 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -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 @@ -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 @@ -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); diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index bb01f57dce31a..21df86bf2eace 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -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"