[clang] [clang-format] Add KwBreakBeforeCaseLabel (PR #227668)

Anan Yablonko via cfe-commits cfe-commits at lists.llvm.org
Sat Oct 3 03:04:53 PDT 2026


https://github.com/ananski-the-3rd updated https://github.com/llvm/llvm-project/pull/227668

>From 3b5b393a7b53dcfd8a27381196cd2e99bd25201e Mon Sep 17 00:00:00 2001
From: "anan.yablonko" <anan.yablonko at skillz-edu.org>
Date: Wed, 30 Sep 2026 13:52:16 +0300
Subject: [PATCH 1/2] [clang-format] Add KwBreakBeforeCaseLabel

---
 clang/docs/ClangFormatStyleOptions.md       | 17 +++++++
 clang/include/clang/Format/Format.h         | 15 +++++++
 clang/lib/Format/Format.cpp                 |  2 +
 clang/lib/Format/UnwrappedLineFormatter.cpp |  8 ++++
 clang/lib/Format/UnwrappedLineParser.cpp    | 19 +++++++-
 clang/lib/Format/UnwrappedLineParser.h      |  1 +
 clang/unittests/Format/ConfigParseTest.cpp  |  1 +
 clang/unittests/Format/FormatTest.cpp       | 50 +++++++++++++++++++++
 8 files changed, 112 insertions(+), 1 deletion(-)

diff --git a/clang/docs/ClangFormatStyleOptions.md b/clang/docs/ClangFormatStyleOptions.md
index cd3c7b60391a0d..1ceffd6c492215 100644
--- a/clang/docs/ClangFormatStyleOptions.md
+++ b/clang/docs/ClangFormatStyleOptions.md
@@ -5442,6 +5442,23 @@ the configuration (without a prefix: `Auto`).
   newlines. (See
   www.gnu.org/prep/standards/html_node/Formatting.html#:~:text=formfeed.)
 
+(kwbreakbeforecaselabel)=
+
+**KwBreakBeforeCaseLabel** (`Boolean`) {ref}`¶ <KwBreakBeforeCaseLabel>`
+
+: Keep the break keyword and the following case label on the same line.
+
+  ```c++
+  false:                         true:
+  switch (foo) {                 switch (foo) {
+    case 1:                        case 1:
+      bar();                        bar();
+      break;                        break; case 2:
+    case 2:                         bar();
+      bar();                       }
+  }
+  ```
+
 (lambdabodyindentation)=
 
 **LambdaBodyIndentation** (`LambdaBodyIndentationKind`) {versionbadge}`clang-format 13` {ref}`¶ <LambdaBodyIndentation>`
diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h
index 3f3f02469fccd4..65ea77281e2525 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -3801,6 +3801,20 @@ struct FormatStyle {
   /// \version 20
   bool KeepFormFeed;
 
+  /// Keep the break keyword and the following case label on the same line.
+  ///
+  /// \code
+  ///    false:                         true:
+  ///    switch (foo) {                 switch (foo) {
+  ///      case 1:                        case 1:
+  ///        bar();                        bar();
+  ///        break;                        break; case 2:
+  ///      case 2:                         bar();
+  ///        bar();                       }
+  ///    }
+  /// \endcode
+  bool KwBreakBeforeCaseLabel;
+
   /// Indentation logic for lambda bodies.
   enum LambdaBodyIndentationKind : int8_t {
     /// Align lambda body relative to the lambda signature. This is the default.
@@ -6269,6 +6283,7 @@ struct FormatStyle {
            JavaScriptWrapImports == R.JavaScriptWrapImports &&
            KeepEmptyLines == R.KeepEmptyLines &&
            KeepFormFeed == R.KeepFormFeed && Language == R.Language &&
+           KwBreakBeforeCaseLabel == R.KwBreakBeforeCaseLabel &&
            LambdaBodyIndentation == R.LambdaBodyIndentation &&
            LineEnding == R.LineEnding && MacroBlockBegin == R.MacroBlockBegin &&
            MacroBlockEnd == R.MacroBlockEnd && Macros == R.Macros &&
diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index c49144f3291de1..2b92d644240fcd 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -1419,6 +1419,7 @@ template <> struct MappingTraits<FormatStyle> {
     IO.mapOptional("JavaScriptWrapImports", Style.JavaScriptWrapImports);
     IO.mapOptional("KeepEmptyLines", Style.KeepEmptyLines);
     IO.mapOptional("KeepFormFeed", Style.KeepFormFeed);
+    IO.mapOptional("KwBreakBeforeCaseLabel", Style.KwBreakBeforeCaseLabel);
     IO.mapOptional("LambdaBodyIndentation", Style.LambdaBodyIndentation);
     IO.mapOptional("LineEnding", Style.LineEnding);
     IO.mapOptional("MacroBlockBegin", Style.MacroBlockBegin);
@@ -2000,6 +2001,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
       /*AtStartOfFile=*/true,
   };
   LLVMStyle.KeepFormFeed = false;
+  LLVMStyle.KwBreakBeforeCaseLabel = false;
   LLVMStyle.LambdaBodyIndentation = FormatStyle::LBI_Signature;
   LLVMStyle.Language = Language;
   LLVMStyle.LineEnding = FormatStyle::LE_DeriveLF;
diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp
index 11a6d359527ae4..bdc1f5869e520d 100644
--- a/clang/lib/Format/UnwrappedLineFormatter.cpp
+++ b/clang/lib/Format/UnwrappedLineFormatter.cpp
@@ -281,6 +281,14 @@ class LineJoiner {
         return MergedLines;
     }
 
+    // Handle case labels preceded by a break statement
+    if (Style.KwBreakBeforeCaseLabel &&
+        NextLine.First->isOneOf(tok::kw_case, tok::kw_default) &&
+        TheLine->endsWith(tok::semi, tok::kw_break) &&
+        TheLine->Level <= NextLine.Level) {
+      return 1;
+    }
+
     const auto *PreviousLine = I != AnnotatedLines.begin() ? I[-1] : nullptr;
 
     // Handle blocks where the brace has already been wrapped.
diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp
index 4825e825af1fa6..b8b025d9d7b1e6 100644
--- a/clang/lib/Format/UnwrappedLineParser.cpp
+++ b/clang/lib/Format/UnwrappedLineParser.cpp
@@ -344,6 +344,17 @@ bool UnwrappedLineParser::precededByCommentOrPPDirective() const {
          (Previous->IsMultiline || Previous->NewlinesBefore > 0);
 }
 
+bool UnwrappedLineParser::linePrecededByKwBreak() const {
+  if (Lines.empty() || Lines.back().Tokens.empty())
+    return false;
+  const UnwrappedLine &PreviousLine = Lines.back();
+
+  if (PreviousLine.Level > Line->Level)
+    return false;
+
+  return PreviousLine.Tokens.front().Tok->is(tok::kw_break);
+}
+
 /// Parses a level, that is ???.
 /// \param OpeningBrace Opening brace (\p nullptr if absent) of that level.
 /// \param IfKind The \p if statement kind in the level.
@@ -3472,6 +3483,11 @@ void UnwrappedLineParser::parseLabel(bool IsGotoLabel) {
   const auto OldLineLevel = Line->Level;
   auto &Level = Line->Level;
 
+  if (!IsGotoLabel && Style.KwBreakBeforeCaseLabel && linePrecededByKwBreak()) {
+    assert(OldLineLevel > 0);
+    Lines.back().Level = OldLineLevel - 1;
+  }
+
   if (IsGotoLabel && IndentGotoLabel == FormatStyle::IGLS_NoIndent)
     Level = 0;
 
@@ -3488,7 +3504,8 @@ void UnwrappedLineParser::parseLabel(bool IsGotoLabel) {
     parseBlock();
     if (FormatTok->is(tok::kw_break)) {
       if (Style.BraceWrapping.AfterControlStatement ==
-          FormatStyle::BWACS_Always) {
+              FormatStyle::BWACS_Always ||
+          Style.KwBreakBeforeCaseLabel) {
         addUnwrappedLine();
         if (!Style.IndentCaseBlocks &&
             Style.BreakBeforeBraces == FormatStyle::BS_Whitesmiths) {
diff --git a/clang/lib/Format/UnwrappedLineParser.h b/clang/lib/Format/UnwrappedLineParser.h
index 5b93c8f346d75a..3225e44a064c56 100644
--- a/clang/lib/Format/UnwrappedLineParser.h
+++ b/clang/lib/Format/UnwrappedLineParser.h
@@ -124,6 +124,7 @@ class UnwrappedLineParser {
   void reset();
   void parseFile();
   bool precededByCommentOrPPDirective() const;
+  bool linePrecededByKwBreak() const;
   bool parseLevel(const FormatToken *OpeningBrace = nullptr,
                   IfStmtKind *IfKind = nullptr,
                   FormatToken **IfLeftBrace = nullptr);
diff --git a/clang/unittests/Format/ConfigParseTest.cpp b/clang/unittests/Format/ConfigParseTest.cpp
index 32839712e297af..0957c70a5529d0 100644
--- a/clang/unittests/Format/ConfigParseTest.cpp
+++ b/clang/unittests/Format/ConfigParseTest.cpp
@@ -202,6 +202,7 @@ TEST(ConfigParseTest, ParsesConfigurationBools) {
   CHECK_PARSE_BOOL_FIELD(KeepEmptyLines.AtStartOfBlock,
                          "KeepEmptyLinesAtTheStartOfBlocks");
   CHECK_PARSE_BOOL(KeepFormFeed);
+  CHECK_PARSE_BOOL(KwBreakBeforeCaseLabel);
   CHECK_PARSE_BOOL(ObjCSpaceAfterMethodDeclarationPrefix);
   CHECK_PARSE_BOOL(ObjCSpaceAfterProperty);
   CHECK_PARSE_BOOL(ObjCSpaceBeforeProtocolList);
diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index 0a5950c25c60ef..1fec26ac8ed740 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -2833,6 +2833,56 @@ TEST_F(FormatTest, FormatsSwitchStatement) {
                Style);
 }
 
+TEST_F(FormatTest, FormatsSwitchStatementKwBreakBeforeCase) {
+  FormatStyle Style = getLLVMStyle();
+  Style.IndentCaseLabels = true;
+  Style.KwBreakBeforeCaseLabel = true;
+
+  verifyFormat("switch (n) {\n"
+               "  break; case 0:;\n"
+               "}",
+               Style);
+
+  verifyFormat("switch (n) {\n"
+               "  break; case 0: {\n"
+               "    return false;\n"
+               "  }\n"
+               "  break; case 1:\n"
+               "  break; default: {\n"
+               "    return true;\n"
+               "  }\n"
+               "}",
+               Style);
+
+  verifyFormat("switch (n) {\n"
+               "  case 0: {\n"
+               "    return false;\n"
+               "  }\n"
+               "  break; case 1:\n"
+               "    while (x)\n"
+               "      break;\n"
+               "  case 2:\n"
+               "  break; default: {\n"
+               "    return true;\n"
+               "  }\n"
+               "}",
+               "switch (n) {\n"
+               "case 0: {\n"
+               "  return false;\n"
+               "}\n"
+               "  break;\n"
+               "case 1:\n"
+               "  while (x)\n"
+               "    break;\n"
+               "case 2:\n"
+               "  break;\n"
+               "default: {\n"
+               "  return true;\n"
+               "}\n"
+               "}",
+               Style);
+}
+
 TEST_F(FormatTest, CaseRanges) {
   verifyFormat("switch (x) {\n"
                "case 'A' ... 'Z':\n"

>From 0f6bfa27febe46a35eb4f10e2b08342cfecc94af Mon Sep 17 00:00:00 2001
From: "anan.yablonko" <anan.yablonko at skillz-edu.org>
Date: Fri, 2 Oct 2026 00:47:31 +0300
Subject: [PATCH 2/2] option name changed added test case with IndentCaseLabels
 = false

---
 clang/include/clang/Format/Format.h         | 30 ++++++++--------
 clang/lib/Format/Format.cpp                 |  4 +--
 clang/lib/Format/UnwrappedLineFormatter.cpp |  4 +--
 clang/lib/Format/UnwrappedLineParser.cpp    |  4 +--
 clang/unittests/Format/ConfigParseTest.cpp  |  2 +-
 clang/unittests/Format/FormatTest.cpp       | 38 +++++++--------------
 6 files changed, 34 insertions(+), 48 deletions(-)

diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h
index 65ea77281e2525..99ed32bb64e71a 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -2838,6 +2838,20 @@ struct FormatStyle {
   /// \version 19
   BreakTemplateDeclarationsStyle BreakTemplateDeclarations;
 
+  /// Keep the break keyword and the following case label on the same line.
+  ///
+  /// \code
+  ///    false:                         true:
+  ///    switch (n) {                 switch (n) {
+  ///      case 1:                      case 1:
+  ///        foo();                       foo();
+  ///        break;                     break; case 2:
+  ///      case 2:                        bar();
+  ///        bar();                   }
+  ///    }
+  /// \endcode
+  bool CompactCaseLabels;
+
   /// If `true`, consecutive namespace declarations will be on the same
   /// line. If `false`, each namespace is declared on a new line.
   /// \code
@@ -3801,20 +3815,6 @@ struct FormatStyle {
   /// \version 20
   bool KeepFormFeed;
 
-  /// Keep the break keyword and the following case label on the same line.
-  ///
-  /// \code
-  ///    false:                         true:
-  ///    switch (foo) {                 switch (foo) {
-  ///      case 1:                        case 1:
-  ///        bar();                        bar();
-  ///        break;                        break; case 2:
-  ///      case 2:                         bar();
-  ///        bar();                       }
-  ///    }
-  /// \endcode
-  bool KwBreakBeforeCaseLabel;
-
   /// Indentation logic for lambda bodies.
   enum LambdaBodyIndentationKind : int8_t {
     /// Align lambda body relative to the lambda signature. This is the default.
@@ -6244,6 +6244,7 @@ struct FormatStyle {
            BreakStringLiterals == R.BreakStringLiterals &&
            BreakTemplateDeclarations == R.BreakTemplateDeclarations &&
            ColumnLimit == R.ColumnLimit && CommentPragmas == R.CommentPragmas &&
+           CompactCaseLabels == R.CompactCaseLabels &&
            CompactNamespaces == R.CompactNamespaces &&
            ConstructorInitializerIndentWidth ==
                R.ConstructorInitializerIndentWidth &&
@@ -6283,7 +6284,6 @@ struct FormatStyle {
            JavaScriptWrapImports == R.JavaScriptWrapImports &&
            KeepEmptyLines == R.KeepEmptyLines &&
            KeepFormFeed == R.KeepFormFeed && Language == R.Language &&
-           KwBreakBeforeCaseLabel == R.KwBreakBeforeCaseLabel &&
            LambdaBodyIndentation == R.LambdaBodyIndentation &&
            LineEnding == R.LineEnding && MacroBlockBegin == R.MacroBlockBegin &&
            MacroBlockEnd == R.MacroBlockEnd && Macros == R.Macros &&
diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index 2b92d644240fcd..1ebf7d49ec7afa 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -1377,6 +1377,7 @@ template <> struct MappingTraits<FormatStyle> {
                    Style.BreakTemplateDeclarations);
     IO.mapOptional("ColumnLimit", Style.ColumnLimit);
     IO.mapOptional("CommentPragmas", Style.CommentPragmas);
+    IO.mapOptional("CompactCaseLabels ", Style.CompactCaseLabels);
     IO.mapOptional("CompactNamespaces", Style.CompactNamespaces);
     IO.mapOptional("ConstructorInitializerIndentWidth",
                    Style.ConstructorInitializerIndentWidth);
@@ -1419,7 +1420,6 @@ template <> struct MappingTraits<FormatStyle> {
     IO.mapOptional("JavaScriptWrapImports", Style.JavaScriptWrapImports);
     IO.mapOptional("KeepEmptyLines", Style.KeepEmptyLines);
     IO.mapOptional("KeepFormFeed", Style.KeepFormFeed);
-    IO.mapOptional("KwBreakBeforeCaseLabel", Style.KwBreakBeforeCaseLabel);
     IO.mapOptional("LambdaBodyIndentation", Style.LambdaBodyIndentation);
     IO.mapOptional("LineEnding", Style.LineEnding);
     IO.mapOptional("MacroBlockBegin", Style.MacroBlockBegin);
@@ -1955,6 +1955,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
   LLVMStyle.BreakStringLiterals = true;
   LLVMStyle.BreakTemplateDeclarations = FormatStyle::BTDS_MultiLine;
   LLVMStyle.ColumnLimit = 80;
+  LLVMStyle.CompactCaseLabels = false;
   LLVMStyle.CommentPragmas = "^ IWYU pragma:";
   LLVMStyle.CompactNamespaces = false;
   LLVMStyle.ConstructorInitializerIndentWidth = 4;
@@ -2001,7 +2002,6 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
       /*AtStartOfFile=*/true,
   };
   LLVMStyle.KeepFormFeed = false;
-  LLVMStyle.KwBreakBeforeCaseLabel = false;
   LLVMStyle.LambdaBodyIndentation = FormatStyle::LBI_Signature;
   LLVMStyle.Language = Language;
   LLVMStyle.LineEnding = FormatStyle::LE_DeriveLF;
diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp
index bdc1f5869e520d..12c24019e1ad0e 100644
--- a/clang/lib/Format/UnwrappedLineFormatter.cpp
+++ b/clang/lib/Format/UnwrappedLineFormatter.cpp
@@ -281,8 +281,8 @@ class LineJoiner {
         return MergedLines;
     }
 
-    // Handle case labels preceded by a break statement
-    if (Style.KwBreakBeforeCaseLabel &&
+    // Handle case labels preceded by a break statement.
+    if (Style.CompactCaseLabels &&
         NextLine.First->isOneOf(tok::kw_case, tok::kw_default) &&
         TheLine->endsWith(tok::semi, tok::kw_break) &&
         TheLine->Level <= NextLine.Level) {
diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp
index b8b025d9d7b1e6..db1ac2a3e4c59f 100644
--- a/clang/lib/Format/UnwrappedLineParser.cpp
+++ b/clang/lib/Format/UnwrappedLineParser.cpp
@@ -3483,7 +3483,7 @@ void UnwrappedLineParser::parseLabel(bool IsGotoLabel) {
   const auto OldLineLevel = Line->Level;
   auto &Level = Line->Level;
 
-  if (!IsGotoLabel && Style.KwBreakBeforeCaseLabel && linePrecededByKwBreak()) {
+  if (!IsGotoLabel && Style.CompactCaseLabels && linePrecededByKwBreak()) {
     assert(OldLineLevel > 0);
     Lines.back().Level = OldLineLevel - 1;
   }
@@ -3505,7 +3505,7 @@ void UnwrappedLineParser::parseLabel(bool IsGotoLabel) {
     if (FormatTok->is(tok::kw_break)) {
       if (Style.BraceWrapping.AfterControlStatement ==
               FormatStyle::BWACS_Always ||
-          Style.KwBreakBeforeCaseLabel) {
+          Style.CompactCaseLabels) {
         addUnwrappedLine();
         if (!Style.IndentCaseBlocks &&
             Style.BreakBeforeBraces == FormatStyle::BS_Whitesmiths) {
diff --git a/clang/unittests/Format/ConfigParseTest.cpp b/clang/unittests/Format/ConfigParseTest.cpp
index 0957c70a5529d0..681ffd946c174d 100644
--- a/clang/unittests/Format/ConfigParseTest.cpp
+++ b/clang/unittests/Format/ConfigParseTest.cpp
@@ -185,6 +185,7 @@ TEST(ConfigParseTest, ParsesConfigurationBools) {
   CHECK_PARSE_BOOL(BreakBeforeTernaryOperators);
   CHECK_PARSE_BOOL(BreakFunctionDeclarationParameters);
   CHECK_PARSE_BOOL(BreakStringLiterals);
+  CHECK_PARSE_BOOL(CompactCaseLabels);
   CHECK_PARSE_BOOL(CompactNamespaces);
   CHECK_PARSE_BOOL(DerivePointerAlignment);
   CHECK_PARSE_BOOL_FIELD(DerivePointerAlignment, "DerivePointerBinding");
@@ -202,7 +203,6 @@ TEST(ConfigParseTest, ParsesConfigurationBools) {
   CHECK_PARSE_BOOL_FIELD(KeepEmptyLines.AtStartOfBlock,
                          "KeepEmptyLinesAtTheStartOfBlocks");
   CHECK_PARSE_BOOL(KeepFormFeed);
-  CHECK_PARSE_BOOL(KwBreakBeforeCaseLabel);
   CHECK_PARSE_BOOL(ObjCSpaceAfterMethodDeclarationPrefix);
   CHECK_PARSE_BOOL(ObjCSpaceAfterProperty);
   CHECK_PARSE_BOOL(ObjCSpaceBeforeProtocolList);
diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index 1fec26ac8ed740..f7df970789d323 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -2833,29 +2833,29 @@ TEST_F(FormatTest, FormatsSwitchStatement) {
                Style);
 }
 
-TEST_F(FormatTest, FormatsSwitchStatementKwBreakBeforeCase) {
+TEST_F(FormatTest, FormatsSwitchStatementCompactCaseLabels) {
   FormatStyle Style = getLLVMStyle();
-  Style.IndentCaseLabels = true;
-  Style.KwBreakBeforeCaseLabel = true;
+  Style.CompactCaseLabels = true;
 
+  Style.IndentCaseLabels = false;
   verifyFormat("switch (n) {\n"
-               "  break; case 0:;\n"
+               "break; case 0: {\n"
+               "  return false;\n"
+               "}\n"
+               "break; default: {\n"
+               "  return true;\n"
+               "}\n"
                "}",
                Style);
 
+  Style.IndentCaseLabels = true;
   verifyFormat("switch (n) {\n"
-               "  break; case 0: {\n"
-               "    return false;\n"
-               "  }\n"
-               "  break; case 1:\n"
-               "  break; default: {\n"
-               "    return true;\n"
-               "  }\n"
+               "  break; case 0:;\n"
                "}",
                Style);
 
   verifyFormat("switch (n) {\n"
-               "  case 0: {\n"
+               "  break; case 0: {\n"
                "    return false;\n"
                "  }\n"
                "  break; case 1:\n"
@@ -2866,20 +2866,6 @@ TEST_F(FormatTest, FormatsSwitchStatementKwBreakBeforeCase) {
                "    return true;\n"
                "  }\n"
                "}",
-               "switch (n) {\n"
-               "case 0: {\n"
-               "  return false;\n"
-               "}\n"
-               "  break;\n"
-               "case 1:\n"
-               "  while (x)\n"
-               "    break;\n"
-               "case 2:\n"
-               "  break;\n"
-               "default: {\n"
-               "  return true;\n"
-               "}\n"
-               "}",
                Style);
 }
 



More information about the cfe-commits mailing list