[clang] 40671bb - [clang-format] Handle control statements in BreakAfterAttributes (#71995)

via cfe-commits cfe-commits at lists.llvm.org
Sun Nov 12 01:08:31 PST 2023


Author: Owen Pan
Date: 2023-11-12T01:08:27-08:00
New Revision: 40671bbdefb6ff83e2685576a3cb041b62f25bbe

URL: https://github.com/llvm/llvm-project/commit/40671bbdefb6ff83e2685576a3cb041b62f25bbe
DIFF: https://github.com/llvm/llvm-project/commit/40671bbdefb6ff83e2685576a3cb041b62f25bbe.diff

LOG: [clang-format] Handle control statements in BreakAfterAttributes (#71995)

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

Fixed #64474.

Added: 
    

Modified: 
    clang/docs/ClangFormatStyleOptions.rst
    clang/include/clang/Format/Format.h
    clang/lib/Format/TokenAnnotator.cpp
    clang/unittests/Format/FormatTest.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst
index d496fc85f7ae71a..ff424828ff63cdb 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..bc412611ef62493 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