MyDeveloperDay created this revision.
MyDeveloperDay added reviewers: HazardyKnusperkeks, owenpan, curdeius.
MyDeveloperDay added a project: clang-format.
Herald added a project: All.
MyDeveloperDay requested review of this revision.
Herald added a project: clang.

As highlighted by https://github.com/llvm/llvm-project/issues/57609

Give more control when using the AllowShortCaseLabelsOnASingleLine to not put 
fallthrough code onto the same line

  switch (i) {
  case 0: g(); [[fallthrough]];
  case 1: f(); break;
  }

whether that fallthrough is marked with the attribute or implicit

  switch (i) {
  case 0:
  case 1: return i;
  case 2:
  case 3: i *= 2; break;
  }

in these cases don't make them short

  switch (i) {
  case 0:
    g();
    [[fallthrough]];
  case 1:
    f();
    break;
  }



  switch (i) {
  case 0:
  case 1:
    return i;
  case 2:
  case 3:
    i *= 2;
    break;
  }

Fixes: 57609


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D133571

Files:
  clang/docs/ClangFormatStyleOptions.rst
  clang/include/clang/Format/Format.h
  clang/lib/Format/Format.cpp
  clang/lib/Format/UnwrappedLineFormatter.cpp
  clang/unittests/Format/FormatTest.cpp

Index: clang/unittests/Format/FormatTest.cpp
===================================================================
--- clang/unittests/Format/FormatTest.cpp
+++ clang/unittests/Format/FormatTest.cpp
@@ -2882,7 +2882,7 @@
 
 TEST_F(FormatTest, ShortCaseLabels) {
   FormatStyle Style = getLLVMStyle();
-  Style.AllowShortCaseLabelsOnASingleLine = true;
+  Style.AllowShortCaseLabelsOnASingleLine = FormatStyle::SCLS_Always;
   verifyFormat("switch (a) {\n"
                "case 1: x = 1; break;\n"
                "case 2: return;\n"
@@ -3011,7 +3011,7 @@
                "}",
                Style);
   Style.ColumnLimit = 80;
-  Style.AllowShortCaseLabelsOnASingleLine = false;
+  Style.AllowShortCaseLabelsOnASingleLine = FormatStyle::SCLS_Never;
   Style.IndentCaseLabels = true;
   EXPECT_EQ("switch (n) {\n"
             "  default /*comments*/:\n"
@@ -3026,7 +3026,7 @@
                    "  return false;\n"
                    "}",
                    Style));
-  Style.AllowShortCaseLabelsOnASingleLine = true;
+  Style.AllowShortCaseLabelsOnASingleLine = FormatStyle::SCLS_Always;
   Style.BreakBeforeBraces = FormatStyle::BS_Custom;
   Style.BraceWrapping.AfterCaseLabel = true;
   Style.BraceWrapping.AfterControlStatement = FormatStyle::BWACS_Always;
@@ -3051,6 +3051,55 @@
                    "  }\n"
                    "}",
                    Style));
+
+  Style.AllowShortCaseLabelsOnASingleLine = FormatStyle::SCLS_NoFallThrough;
+  /*
+  verifyFormat("switch (a)\n"
+               "{\n"
+               "case 1: x = 1; break;\n"
+               "case 2: return;\n"
+               "case 3:\n"
+               "case 4:\n"
+               "case 5: return;\n"
+               "case 6: // comment\n"
+               "  return;\n"
+               "case 7:\n"
+               "  // comment\n"
+               "  return;\n"
+               "case 8:\n"
+               "  x = 8; // comment\n"
+               "  break;\n"
+               "default: y = 1; break;\n"
+               "}",
+               Style);
+  */
+  verifyFormat("switch (a)\n"
+               "{\n"
+               "  case 0:\n"
+               "  case 1:\n"
+               "    x = 1;\n"
+               "    break;\n"
+               "  case 2: x = 2; break;\n"
+               "  case 3:\n"
+               "    x = 3;\n"
+               "    [[fallthrough]];\n"
+               "  case 4:\n"
+               "    x = 4;\n"
+               "    break;\n"
+               "  default: y = 1; break;\n"
+               "}",
+               Style);
+  Style.AllowShortCaseLabelsOnASingleLine = FormatStyle::SCLS_Always;
+  verifyFormat("switch (a)\n"
+               "{\n"
+               "  case 0:\n"
+               "  case 1: x = 1; break;\n"
+               "  case 2: x = 2; break;\n"
+               "  case 3: x = 3; [[fallthrough]];\n"
+               "  case 4: x = 4; break;\n"
+               "  default: y = 1; break;\n"
+               "}",
+               Style);
 }
 
 TEST_F(FormatTest, FormatsLabels) {
@@ -20148,7 +20197,6 @@
   CHECK_PARSE_BOOL(AlignTrailingComments);
   CHECK_PARSE_BOOL(AllowAllArgumentsOnNextLine);
   CHECK_PARSE_BOOL(AllowAllParametersOfDeclarationOnNextLine);
-  CHECK_PARSE_BOOL(AllowShortCaseLabelsOnASingleLine);
   CHECK_PARSE_BOOL(AllowShortEnumsOnASingleLine);
   CHECK_PARSE_BOOL(AllowShortLoopsOnASingleLine);
   CHECK_PARSE_BOOL(BinPackArguments);
@@ -20509,6 +20557,20 @@
   CHECK_PARSE("AllowShortFunctionsOnASingleLine: true",
               AllowShortFunctionsOnASingleLine, FormatStyle::SFS_All);
 
+  Style.AllowShortCaseLabelsOnASingleLine = FormatStyle::SCLS_Always;
+  CHECK_PARSE("AllowShortCaseLabelsOnASingleLine: Never",
+              AllowShortCaseLabelsOnASingleLine, FormatStyle::SCLS_Never);
+  CHECK_PARSE("AllowShortCaseLabelsOnASingleLine: Always",
+              AllowShortCaseLabelsOnASingleLine, FormatStyle::SCLS_Always);
+  CHECK_PARSE("AllowShortCaseLabelsOnASingleLine: NoFallThrough",
+              AllowShortCaseLabelsOnASingleLine,
+              FormatStyle::SCLS_NoFallThrough);
+  // For backward compatibility:
+  CHECK_PARSE("AllowShortCaseLabelsOnASingleLine: false",
+              AllowShortCaseLabelsOnASingleLine, FormatStyle::SCLS_Never);
+  CHECK_PARSE("AllowShortCaseLabelsOnASingleLine: true",
+              AllowShortCaseLabelsOnASingleLine, FormatStyle::SCLS_Always);
+
   Style.SpaceAroundPointerQualifiers = FormatStyle::SAPQ_Both;
   CHECK_PARSE("SpaceAroundPointerQualifiers: Default",
               SpaceAroundPointerQualifiers, FormatStyle::SAPQ_Default);
Index: clang/lib/Format/UnwrappedLineFormatter.cpp
===================================================================
--- clang/lib/Format/UnwrappedLineFormatter.cpp
+++ clang/lib/Format/UnwrappedLineFormatter.cpp
@@ -564,7 +564,7 @@
     }
     if (TheLine->First->isOneOf(tok::kw_case, tok::kw_default)) {
       return Style.AllowShortCaseLabelsOnASingleLine
-                 ? tryMergeShortCaseLabels(I, E, Limit)
+                 ? tryMergeShortCaseLabels(I, E, Limit, PreviousLine, Style)
                  : 0;
     }
     if (TheLine->InPPDirective &&
@@ -632,13 +632,29 @@
   unsigned
   tryMergeShortCaseLabels(SmallVectorImpl<AnnotatedLine *>::const_iterator I,
                           SmallVectorImpl<AnnotatedLine *>::const_iterator E,
-                          unsigned Limit) {
+                          unsigned Limit, const AnnotatedLine *PreviousLine,
+                          const FormatStyle &Style) {
     if (Limit == 0 || I + 1 == E ||
         I[1]->First->isOneOf(tok::kw_case, tok::kw_default)) {
       return 0;
     }
     if (I[0]->Last->is(tok::l_brace) || I[1]->First->is(tok::l_brace))
       return 0;
+    bool NoFallThrough = Style.AllowShortCaseLabelsOnASingleLine ==
+                         FormatStyle::SCLS_NoFallThrough;
+    // If the last thing on the line before was just case X:  then don't merge.
+    if (NoFallThrough && PreviousLine && PreviousLine->Last &&
+        PreviousLine->Last->is(tok::colon))
+      return 0;
+    // Check if the last thing on the line before was an attribute and if so
+    // don't merge.
+    if (NoFallThrough && PreviousLine && PreviousLine->Last &&
+        PreviousLine->Last->Previous && PreviousLine->Last->Previous) {
+      auto *PrevPrev = PreviousLine->Last->Previous->Previous;
+      if (PrevPrev && PrevPrev->startsSequence(TT_AttributeSquare,
+                                               TT_AttributeSquare, tok::semi))
+        return 0;
+    }
     unsigned NumStmts = 0;
     unsigned Length = 0;
     bool EndsWithComment = false;
@@ -652,6 +668,10 @@
         break;
       if (Line->First->isOneOf(tok::kw_case, tok::kw_default, tok::r_brace))
         break;
+      // When the contents of the case contains an [[attribute]] don't merge it
+      // could be a fallthough.
+      if (NoFallThrough && Line->First->is(TT_AttributeSquare))
+        return 0;
       if (Line->First->isOneOf(tok::kw_if, tok::kw_for, tok::kw_switch,
                                tok::kw_while) ||
           EndsWithComment) {
Index: clang/lib/Format/Format.cpp
===================================================================
--- clang/lib/Format/Format.cpp
+++ clang/lib/Format/Format.cpp
@@ -591,6 +591,18 @@
   }
 };
 
+template <> struct ScalarEnumerationTraits<FormatStyle::ShortCaseLabelStyle> {
+  static void enumeration(IO &IO, FormatStyle::ShortCaseLabelStyle &Value) {
+    IO.enumCase(Value, "Never", FormatStyle::SCLS_Never);
+    IO.enumCase(Value, "Always", FormatStyle::SCLS_Always);
+    IO.enumCase(Value, "NoFallThrough", FormatStyle::SCLS_NoFallThrough);
+
+    // For backward compatibility.
+    IO.enumCase(Value, "false", FormatStyle::SCLS_Never);
+    IO.enumCase(Value, "true", FormatStyle::SCLS_Always);
+  }
+};
+
 template <> struct MappingTraits<FormatStyle> {
   static void mapping(IO &IO, FormatStyle &Style) {
     // When reading, read the language first, we need it for getPredefinedStyle.
@@ -1196,7 +1208,7 @@
   LLVMStyle.AllowShortEnumsOnASingleLine = true;
   LLVMStyle.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_All;
   LLVMStyle.AllowShortBlocksOnASingleLine = FormatStyle::SBS_Never;
-  LLVMStyle.AllowShortCaseLabelsOnASingleLine = false;
+  LLVMStyle.AllowShortCaseLabelsOnASingleLine = FormatStyle::SCLS_Never;
   LLVMStyle.AllowShortIfStatementsOnASingleLine = FormatStyle::SIS_Never;
   LLVMStyle.AllowShortLambdasOnASingleLine = FormatStyle::SLS_All;
   LLVMStyle.AllowShortLoopsOnASingleLine = false;
@@ -1650,7 +1662,7 @@
   Style.PenaltyReturnTypeOnItsOwnLine = 1000;
   Style.AllowShortEnumsOnASingleLine = false;
   Style.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_None;
-  Style.AllowShortCaseLabelsOnASingleLine = false;
+  Style.AllowShortCaseLabelsOnASingleLine = FormatStyle::SCLS_Never;
   Style.AllowShortIfStatementsOnASingleLine = FormatStyle::SIS_Never;
   Style.AllowShortLoopsOnASingleLine = false;
   Style.AlwaysBreakAfterDefinitionReturnType = FormatStyle::DRTBS_None;
Index: clang/include/clang/Format/Format.h
===================================================================
--- clang/include/clang/Format/Format.h
+++ clang/include/clang/Format/Format.h
@@ -465,19 +465,40 @@
   /// \version 3.5
   ShortBlockStyle AllowShortBlocksOnASingleLine;
 
-  /// If ``true``, short case labels will be contracted to a single line.
-  /// \code
-  ///   true:                                   false:
-  ///   switch (a) {                    vs.     switch (a) {
-  ///   case 1: x = 1; break;                   case 1:
-  ///   case 2: return;                           x = 1;
-  ///   }                                         break;
-  ///                                           case 2:
-  ///                                             return;
-  ///                                           }
-  /// \endcode
+  /// Different styles for merging short case labels.
+  enum ShortCaseLabelStyle : int8_t {
+    /// Never merge case code
+    SCLS_Never,
+    /// Always contract 'simple' small case label code but
+    /// ignore cases where fallthrough occurs
+    /// (implicit or marked fallthrough)
+    /// \code
+    ///   case 0:
+    ///   case 1:
+    ///     x = 1;
+    ///     break;
+    ///   case 2: x = 2; break;
+    ///   case 3: x = 3;
+    ///           [[fallthrough]];
+    ///   case 4:
+    ///     x = 4;
+    ///     break;
+    /// \endcode
+    SCLS_NoFallThrough,
+    /// Always contract short case labels to a single line.
+    /// \code
+    ///   case 0:
+    ///   case 1: x = 1; break;
+    ///   case 2: x = 2; break;
+    ///   case 3: x = 3; [[fallthrough]];
+    ///   case 4: x = 4; break;
+    /// \endcode
+    SCLS_Always
+  };
+
+  /// Determine if Short case labels will be contracted to a single line.
   /// \version 3.6
-  bool AllowShortCaseLabelsOnASingleLine;
+  ShortCaseLabelStyle AllowShortCaseLabelsOnASingleLine;
 
   /// Different styles for merging short functions containing at most one
   /// statement.
Index: clang/docs/ClangFormatStyleOptions.rst
===================================================================
--- clang/docs/ClangFormatStyleOptions.rst
+++ clang/docs/ClangFormatStyleOptions.rst
@@ -931,19 +931,44 @@
 
 
 
-**AllowShortCaseLabelsOnASingleLine** (``Boolean``) :versionbadge:`clang-format 3.6`
-  If ``true``, short case labels will be contracted to a single line.
+**AllowShortCaseLabelsOnASingleLine** (``ShortCaseLabelStyle``) :versionbadge:`clang-format 3.6`
+  Determine if Short case labels will be contracted to a single line.
+
+  Possible values:
+
+  * ``SCLS_Never`` (in configuration: ``Never``)
+    Never merge case code
+
+  * ``SCLS_NoFallThrough`` (in configuration: ``NoFallThrough``)
+    Always contract 'simple' small case label code but
+    ignore cases where fallthrough occurs
+    (implicit or marked fallthrough)
+
+    .. code-block:: c++
+
+      case 0:
+      case 1:
+        x = 1;
+        break;
+      case 2: x = 2; break;
+      case 3: x = 3;
+              [[fallthrough]];
+      case 4:
+        x = 4;
+        break;
+
+  * ``SCLS_Always`` (in configuration: ``Always``)
+    Always contract short case labels to a single line.
+
+    .. code-block:: c++
+
+      case 0:
+      case 1: x = 1; break;
+      case 2: x = 2; break;
+      case 3: x = 3; [[fallthrough]];
+      case 4: x = 4; break;
 
-  .. code-block:: c++
 
-    true:                                   false:
-    switch (a) {                    vs.     switch (a) {
-    case 1: x = 1; break;                   case 1:
-    case 2: return;                           x = 1;
-    }                                         break;
-                                            case 2:
-                                              return;
-                                            }
 
 **AllowShortEnumsOnASingleLine** (``Boolean``) :versionbadge:`clang-format 11`
   Allow short enums on a single line.
@@ -3567,7 +3592,7 @@
 
     .. code-block:: yaml
 
-      QualifierOrder: ['inline', 'static', 'type', 'const']
+      QualifierOrder: ['inline', 'static' , 'type', 'const']
 
 
     .. code-block:: c++
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to