[clang] [clang-format] Fix BreakAfterOpenBracket options being ignored with larger continuation indents (PR #228085)

Hadong Lee via cfe-commits cfe-commits at lists.llvm.org
Sat Oct 3 05:27:48 PDT 2026


https://github.com/ChrisLee02 updated https://github.com/llvm/llvm-project/pull/228085

>From d84b258b67e6c65c8b23761665b93b4c6c0af924 Mon Sep 17 00:00:00 2001
From: Chrislee02 <hdchris765 at snu.ac.kr>
Date: Thu, 1 Oct 2026 21:46:08 +0900
Subject: [PATCH 1/3] [clang-format] Add baseline tests for
 BreakAfterOpenBracket options

---
 clang/unittests/Format/AlignBracketsTest.cpp | 92 ++++++++++++++++++++
 1 file changed, 92 insertions(+)

diff --git a/clang/unittests/Format/AlignBracketsTest.cpp b/clang/unittests/Format/AlignBracketsTest.cpp
index e7e57bf839c0e1..171872d6740774 100644
--- a/clang/unittests/Format/AlignBracketsTest.cpp
+++ b/clang/unittests/Format/AlignBracketsTest.cpp
@@ -861,6 +861,98 @@ TEST_F(AlignBracketsTest, BlockIndentAndNamespace) {
       Style);
 }
 
+TEST_F(AlignBracketsTest, BreakAfterOpenBracketIfStatement) {
+  auto Style = getLLVMStyleWithColumns(40);
+  Style.PackArguments.BinPack = FormatStyle::BPAS_OnePerLine;
+  Style.BreakAfterOpenBracketIf = true;
+
+  verifyFormat("void f() {\n"
+               "  if (call(aaaaaaaaaaaaaaaaaaaa,\n"
+               "           bbbbbbbbbbbbbbbbbbbb,\n"
+               "           cccccccccccccccccccc) < 0)\n"
+               "    return;\n"
+               "}",
+               "void f() {\n"
+               "  if (call(aaaaaaaaaaaaaaaaaaaa, bbbbbbbbbbbbbbbbbbbb, "
+               "cccccccccccccccccccc) < 0) return;\n"
+               "}",
+               Style);
+}
+
+TEST_F(AlignBracketsTest, BreakAfterOpenBracketFunctionCall) {
+  auto Style = getLLVMStyleWithColumns(40);
+  Style.PackArguments.BinPack = FormatStyle::BPAS_OnePerLine;
+  Style.BreakAfterOpenBracketFunction = true;
+
+  verifyFormat("void f() {\n"
+               "  g(aaaaaaaaaaaaaaaaaaaa,\n"
+               "    bbbbbbbbbbbbbbbbbbbb,\n"
+               "    cccccccccccccccccccc);\n"
+               "}",
+               "void f() {\n"
+               "  g(aaaaaaaaaaaaaaaaaaaa, bbbbbbbbbbbbbbbbbbbb, "
+               "cccccccccccccccccccc);\n"
+               "}",
+               Style);
+}
+
+TEST_F(AlignBracketsTest, BreakAfterOpenBracketLoopStatement) {
+  auto Style = getLLVMStyleWithColumns(40);
+  Style.ContinuationIndentWidth = 7;
+  Style.PackArguments.BinPack = FormatStyle::BPAS_OnePerLine;
+  Style.BreakAfterOpenBracketLoop = true;
+
+  verifyFormat("void f() {\n"
+               "  while (call(aaaaaaaaaaaaaaaaaaaa,\n"
+               "              bbbbbbbbbbbbbbbbbbbb,\n"
+               "              cccccccccccccccccccc) < 0)\n"
+               "    return;\n"
+               "}",
+               "void f() {\n"
+               "  while (call(aaaaaaaaaaaaaaaaaaaa, bbbbbbbbbbbbbbbbbbbb, "
+               "cccccccccccccccccccc) < 0) return;\n"
+               "}",
+               Style);
+}
+
+TEST_F(AlignBracketsTest, BreakAfterOpenBracketSwitchStatement) {
+  auto Style = getLLVMStyleWithColumns(40);
+  Style.ContinuationIndentWidth = 8;
+  Style.PackArguments.BinPack = FormatStyle::BPAS_OnePerLine;
+  Style.BreakAfterOpenBracketSwitch = true;
+
+  verifyFormat("void f() {\n"
+               "  switch (call(aaaaaaaaaaaaaaaaaaaa,\n"
+               "               bbbbbbbbbbbbbbbbbbbb,\n"
+               "               cccccccccccccccccccc)) {\n"
+               "  default:\n"
+               "    return;\n"
+               "  }\n"
+               "}",
+               "void f() {\n"
+               "  switch (call(aaaaaaaaaaaaaaaaaaaa, bbbbbbbbbbbbbbbbbbbb, "
+               "cccccccccccccccccccc)) { default: return; }\n"
+               "}",
+               Style);
+}
+
+TEST_F(AlignBracketsTest, BreakAfterOpenBracketBracedList) {
+  auto Style = getLLVMStyle();
+  Style.ContinuationIndentWidth = 11;
+  Style.BreakAfterOpenBracketBracedList = true;
+
+  verifyFormat("void f() {\n"
+               "  int a[] = {111111111111111111, 222222222222222222, "
+               "333333333333333333,\n"
+               "             444444444444444444};\n"
+               "}",
+               "void f() {\n"
+               "  int a[] = {111111111111111111, 222222222222222222, "
+               "333333333333333333, 444444444444444444};\n"
+               "}",
+               Style);
+}
+
 } // namespace
 } // namespace test
 } // namespace format

>From dbe30504212d86a1ed61755081727181f3b5df55 Mon Sep 17 00:00:00 2001
From: Chrislee02 <hdchris765 at snu.ac.kr>
Date: Thu, 1 Oct 2026 23:00:40 +0900
Subject: [PATCH 2/3] [clang-format] Fix BreakAfterOpenBracket options being
 ignored with larger continuation indents

---
 clang/lib/Format/ContinuationIndenter.cpp    | 10 ++++++++--
 clang/unittests/Format/AlignBracketsTest.cpp | 19 ++++++++++++-------
 clang/unittests/Format/FormatTest.cpp        |  3 ++-
 clang/unittests/Format/FormatTestJS.cpp      | 14 ++++++++------
 4 files changed, 30 insertions(+), 16 deletions(-)

diff --git a/clang/lib/Format/ContinuationIndenter.cpp b/clang/lib/Format/ContinuationIndenter.cpp
index cb3dee9d75c23c..1791d0fbfe8b85 100644
--- a/clang/lib/Format/ContinuationIndenter.cpp
+++ b/clang/lib/Format/ContinuationIndenter.cpp
@@ -918,7 +918,7 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun,
   // In "AlwaysBreak" or "BlockIndent" mode, enforce wrapping directly after the
   // parenthesis by disallowing any further line breaks if there is no line
   // break after the opening parenthesis. Don't break if it doesn't conserve
-  // columns.
+  // columns, unless the style option applies and a break is allowed.
   auto IsOpeningBracket = [&](const FormatToken &Tok) {
     auto IsStartOfBracedList = [&]() {
       return Tok.is(tok::l_brace) && Tok.isNot(BK_Block) &&
@@ -996,7 +996,13 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun,
            Next->is(TT_FunctionDeclarationLParen) || IsFunctionCallParen(*Next);
   };
   if (IsOpeningBracket(Previous) &&
-      State.Column > getNewLineColumn(State).Total &&
+      (State.Column > getNewLineColumn(State).Total ||
+       // IsOpeningBracket checks BreakAfterOpenBracketBracedList before its
+       // (!Tok.Previous) shortcut. All other true returns with a preceding
+       // token require a BreakAfterOpenBracket* option.
+       // Only forbid later breaks if a break here is possible to prevent
+       // alternatives from being blocked.
+       ((Previous.Previous || Previous.is(tok::l_brace)) && canBreak(State))) &&
       // Don't do this for simple (no expressions) one-argument function calls
       // as that feels like needlessly wasting whitespace, e.g.:
       //
diff --git a/clang/unittests/Format/AlignBracketsTest.cpp b/clang/unittests/Format/AlignBracketsTest.cpp
index 171872d6740774..fe47ccae86c9f1 100644
--- a/clang/unittests/Format/AlignBracketsTest.cpp
+++ b/clang/unittests/Format/AlignBracketsTest.cpp
@@ -867,7 +867,8 @@ TEST_F(AlignBracketsTest, BreakAfterOpenBracketIfStatement) {
   Style.BreakAfterOpenBracketIf = true;
 
   verifyFormat("void f() {\n"
-               "  if (call(aaaaaaaaaaaaaaaaaaaa,\n"
+               "  if (\n"
+               "      call(aaaaaaaaaaaaaaaaaaaa,\n"
                "           bbbbbbbbbbbbbbbbbbbb,\n"
                "           cccccccccccccccccccc) < 0)\n"
                "    return;\n"
@@ -885,9 +886,10 @@ TEST_F(AlignBracketsTest, BreakAfterOpenBracketFunctionCall) {
   Style.BreakAfterOpenBracketFunction = true;
 
   verifyFormat("void f() {\n"
-               "  g(aaaaaaaaaaaaaaaaaaaa,\n"
-               "    bbbbbbbbbbbbbbbbbbbb,\n"
-               "    cccccccccccccccccccc);\n"
+               "  g(\n"
+               "      aaaaaaaaaaaaaaaaaaaa,\n"
+               "      bbbbbbbbbbbbbbbbbbbb,\n"
+               "      cccccccccccccccccccc);\n"
                "}",
                "void f() {\n"
                "  g(aaaaaaaaaaaaaaaaaaaa, bbbbbbbbbbbbbbbbbbbb, "
@@ -903,7 +905,8 @@ TEST_F(AlignBracketsTest, BreakAfterOpenBracketLoopStatement) {
   Style.BreakAfterOpenBracketLoop = true;
 
   verifyFormat("void f() {\n"
-               "  while (call(aaaaaaaaaaaaaaaaaaaa,\n"
+               "  while (\n"
+               "         call(aaaaaaaaaaaaaaaaaaaa,\n"
                "              bbbbbbbbbbbbbbbbbbbb,\n"
                "              cccccccccccccccccccc) < 0)\n"
                "    return;\n"
@@ -922,7 +925,8 @@ TEST_F(AlignBracketsTest, BreakAfterOpenBracketSwitchStatement) {
   Style.BreakAfterOpenBracketSwitch = true;
 
   verifyFormat("void f() {\n"
-               "  switch (call(aaaaaaaaaaaaaaaaaaaa,\n"
+               "  switch (\n"
+               "          call(aaaaaaaaaaaaaaaaaaaa,\n"
                "               bbbbbbbbbbbbbbbbbbbb,\n"
                "               cccccccccccccccccccc)) {\n"
                "  default:\n"
@@ -942,7 +946,8 @@ TEST_F(AlignBracketsTest, BreakAfterOpenBracketBracedList) {
   Style.BreakAfterOpenBracketBracedList = true;
 
   verifyFormat("void f() {\n"
-               "  int a[] = {111111111111111111, 222222222222222222, "
+               "  int a[] = {\n"
+               "             111111111111111111, 222222222222222222, "
                "333333333333333333,\n"
                "             444444444444444444};\n"
                "}",
diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index 0a5950c25c60ef..09bebd446da0c8 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -21724,7 +21724,8 @@ TEST_F(FormatTest, FormatsLambdas) {
   Style.BraceWrapping.BeforeLambdaBody = true;
   verifyFormat("void foo() {\n"
                "  aFunction(\n"
-               "      1, b(c(foo, Bar{}, baz, [](d) -> Foo\n"
+               "      1, b(c(\n"
+               "             foo, Bar{}, baz, [](d) -> Foo\n"
                "  {\n"
                "    auto f = e(\n"
                "        [&]\n"
diff --git a/clang/unittests/Format/FormatTestJS.cpp b/clang/unittests/Format/FormatTestJS.cpp
index b405a73bce0860..710f8d3682ad8a 100644
--- a/clang/unittests/Format/FormatTestJS.cpp
+++ b/clang/unittests/Format/FormatTestJS.cpp
@@ -238,7 +238,8 @@ TEST_F(FormatTestJS, UnderstandsJavaScriptOperators) {
   verifyFormat("aaaaaaa ===\n    b;", getGoogleJSStyleWithColumns(10));
   verifyFormat("a !== b;");
   verifyFormat("aaaaaaa !==\n    b;", getGoogleJSStyleWithColumns(10));
-  verifyFormat("if (a + b + c +\n"
+  verifyFormat("if (\n"
+               "    a + b + c +\n"
                "        d !==\n"
                "    e + f + g)\n"
                "  q();",
@@ -250,7 +251,8 @@ TEST_F(FormatTestJS, UnderstandsJavaScriptOperators) {
   verifyFormat("aaaaaaa >>>\n    b;", getGoogleJSStyleWithColumns(10));
   verifyFormat("a >>>= b;");
   verifyFormat("aaaaaaa >>>=\n    b;", getGoogleJSStyleWithColumns(10));
-  verifyFormat("if (a + b + c +\n"
+  verifyFormat("if (\n"
+               "    a + b + c +\n"
                "        d >>>\n"
                "    e + f + g)\n"
                "  q();",
@@ -1273,10 +1275,10 @@ TEST_F(FormatTestJS, ArrowFunctions) {
   verifyFormat("var x = (a) => a;");
   verifyFormat("return () => [];");
   verifyFormat("var aaaaaaaaaaaaaaaaaaaa = {\n"
-               "  aaaaaaaaaaaaaaaaaaaaaaaaaaaa:\n"
-               "      (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,\n"
-               "       aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) =>\n"
-               "          aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,\n"
+               "  aaaaaaaaaaaaaaaaaaaaaaaaaaaa: (\n"
+               "      aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,\n"
+               "      aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) =>\n"
+               "      aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,\n"
                "};");
   verifyFormat("var a = a.aaaaaaa(\n"
                "    (a: a) => aaaaaaaaaaaaaaaaaaaaaaaaa(bbbbbbbbb) &&\n"

>From f3e28468fbb4ee296f3ea31a16726a6908c76f2b Mon Sep 17 00:00:00 2001
From: Chrislee02 <hdchris765 at snu.ac.kr>
Date: Sat, 3 Oct 2026 21:27:28 +0900
Subject: [PATCH 3/3] [clang-format] Clarify BreakAfterOpenBracket option
 matching

---
 clang/lib/Format/ContinuationIndenter.cpp | 25 +++++++++++++++++++----
 1 file changed, 21 insertions(+), 4 deletions(-)

diff --git a/clang/lib/Format/ContinuationIndenter.cpp b/clang/lib/Format/ContinuationIndenter.cpp
index 1791d0fbfe8b85..f64c3b3907b71e 100644
--- a/clang/lib/Format/ContinuationIndenter.cpp
+++ b/clang/lib/Format/ContinuationIndenter.cpp
@@ -942,6 +942,26 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun,
     }
     return false;
   };
+
+  auto ShouldBreakAfterOpeningBracket = [&](const FormatToken &Tok) {
+    // Suppose that IsOpeningBracket returned true for a Tok
+
+    // Corresponds to BreakAfterOpenBracketBracedList
+    if (Tok.is(tok::l_brace))
+      return true;
+
+    const auto *Before = Tok.Previous;
+    if (!Before)
+      return false;
+
+    // Corresponds to BreakAfterOpenBracketIf, BreakAfterOpenBracketLoop,
+    // BreakAfterOpenBracketSwitch, BreakAfterOpenBracketFunction.
+    return Before->isIf() || Before->isLoop(Style) ||
+           Before->is(tok::kw_switch) ||
+           (!Before->is(TT_CastRParen) &&
+            !(Style.isJavaScript() && Tok.is(Keywords.kw_await)));
+  };
+
   auto IsFunctionCallParen = [](const FormatToken &Tok) {
     return Tok.is(tok::l_paren) && Tok.ParameterCount > 0 && Tok.Previous &&
            Tok.Previous->is(tok::identifier);
@@ -997,12 +1017,9 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun,
   };
   if (IsOpeningBracket(Previous) &&
       (State.Column > getNewLineColumn(State).Total ||
-       // IsOpeningBracket checks BreakAfterOpenBracketBracedList before its
-       // (!Tok.Previous) shortcut. All other true returns with a preceding
-       // token require a BreakAfterOpenBracket* option.
        // Only forbid later breaks if a break here is possible to prevent
        // alternatives from being blocked.
-       ((Previous.Previous || Previous.is(tok::l_brace)) && canBreak(State))) &&
+       (ShouldBreakAfterOpeningBracket(Previous) && canBreak(State))) &&
       // Don't do this for simple (no expressions) one-argument function calls
       // as that feels like needlessly wasting whitespace, e.g.:
       //



More information about the cfe-commits mailing list