[clang] [clang-format] Handle control statements in BreakAfterAttributes (PR #71995)
Owen Pan via cfe-commits
cfe-commits at lists.llvm.org
Fri Nov 10 14:44:20 PST 2023
https://github.com/owenca created https://github.com/llvm/llvm-project/pull/71995
This patch doesn't work for do-while loops.
Fixed #64474.
>From b67ea36d180e700cc6b0661e8f2390eda515c566 Mon Sep 17 00:00:00 2001
From: Owen Pan <owenpiano at gmail.com>
Date: Fri, 10 Nov 2023 00:21:58 -0800
Subject: [PATCH] [clang-format] Handle control statements in
BreakAfterAttributes
This patch doesn't work for do-while loops.
Fixed #64474.
---
clang/docs/ClangFormatStyleOptions.rst | 52 ++++++++++++++++++++-
clang/include/clang/Format/Format.h | 52 ++++++++++++++++++++-
clang/lib/Format/TokenAnnotator.cpp | 33 ++++++++-----
clang/unittests/Format/FormatTest.cpp | 64 ++++++++++++++++++++++++++
4 files changed, 185 insertions(+), 16 deletions(-)
diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst
index d496fc85f7ae71a..2e9297879684aa5 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:`ΒΆ <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:
@@ -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 9442344000e142b..55ba46d45c142ab 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 725f02a37581d24..03f3c3583f2ec44 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 bb01f57dce31a7a..21df86bf2eace55 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"
More information about the cfe-commits
mailing list