Skip to content

[clang-format] Add options to control wrapped lambda brace indent. #143249

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

rmarker
Copy link
Contributor

@rmarker rmarker commented Jun 7, 2025

Adds options to customise how wrapped lambda braces are indented when using custom brace wrapping.

IndentBraces was recently updated to also indent wrapped lambda braces. This has been changed to be controlled separately to allow the old behaviour to be maintained. Ideally before a release is made with the new behaviour.

In order to further increase flexibility, the indentation can be controlled separately for both nested and unnested lambdas.

Resolves #143248

Adds options to customise how wrapped lambda braces are indented when
using custom brace wrapping.
IndentBraces was recently changed to also indent wrapped lambda braces.
This has been changed to be controlled separately to allow the old
behaviour to be maintained.
Especially before a release is made with the new behaviour.
In order to further increase flexibility, the indentation can be
controlled separately for both nested and unnested lambdas.
@llvmbot llvmbot added clang Clang issues not falling into any other category clang-format labels Jun 7, 2025
@llvmbot
Copy link
Member

llvmbot commented Jun 7, 2025

@llvm/pr-subscribers-clang

Author: None (rmarker)

Changes

Adds options to customise how wrapped lambda braces are indented when using custom brace wrapping.

IndentBraces was recently updated to also indent wrapped lambda braces. This has been changed to be controlled separately to allow the old behaviour to be maintained. Ideally before a release is made with the new behaviour.

In order to further increase flexibility, the indentation can be controlled separately for both nested and unnested lambdas.

Resolves #143248


Full diff: https://github.com/llvm/llvm-project/pull/143249.diff

7 Files Affected:

  • (modified) clang/docs/ClangFormatStyleOptions.rst (+34)
  • (modified) clang/docs/ReleaseNotes.rst (+2)
  • (modified) clang/include/clang/Format/Format.h (+32)
  • (modified) clang/lib/Format/ContinuationIndenter.cpp (+12-5)
  • (modified) clang/lib/Format/Format.cpp (+10)
  • (modified) clang/unittests/Format/ConfigParseTest.cpp (+2)
  • (modified) clang/unittests/Format/FormatTest.cpp (+78)
diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst
index 83716cc049ee3..5294c77020945 100644
--- a/clang/docs/ClangFormatStyleOptions.rst
+++ b/clang/docs/ClangFormatStyleOptions.rst
@@ -2541,6 +2541,40 @@ the configuration (without a prefix: ``Auto``).
 
   * ``bool IndentBraces`` Indent the wrapped braces themselves.
 
+  * ``bool IndentBracesLambdaNested`` Indent nested wrapped lambda braces.
+
+    .. code-block:: c++
+
+      false:
+      function(
+          []()
+          {
+            return true;
+          });
+
+      true:
+      function(
+          []()
+            {
+              return true;
+            });
+
+  * ``bool IndentBracesLambdaUnnested`` Indent unnested wrapped lambda braces.
+
+    .. code-block:: c++
+
+      false:
+      auto foo = []()
+      {
+        return true;
+      };
+
+      true:
+      auto foo = []()
+        {
+          return true;
+        };
+
   * ``bool SplitEmptyFunction`` If ``false``, empty function body can be put on a single line.
     This option is used only if the opening brace of the function has
     already been wrapped, i.e. the ``AfterFunction`` brace wrapping mode is
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 9ab69320f0368..03e33c4fcdc49 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -988,6 +988,8 @@ clang-format
   ``enum`` enumerator lists.
 - Add ``OneLineFormatOffRegex`` option for turning formatting off for one line.
 - Add ``SpaceAfterOperatorKeyword`` option.
+- Add ``IndentBracesLambdaNested`` and ``IndentBracesLambdaUnnested`` to
+  ``BraceWrapping`` options for controlling wrapped lambda brace indentation.
 
 clang-refactor
 --------------
diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h
index 127b1d08919de..86291d6fa845c 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -1547,6 +1547,38 @@ struct FormatStyle {
     bool BeforeWhile;
     /// Indent the wrapped braces themselves.
     bool IndentBraces;
+    /// Indent nested wrapped lambda braces.
+    /// \code
+    ///   false:
+    ///   function(
+    ///       []()
+    ///       {
+    ///         return true;
+    ///       });
+    ///
+    ///   true:
+    ///   function(
+    ///       []()
+    ///         {
+    ///           return true;
+    ///         });
+    /// \endcode
+    bool IndentBracesLambdaNested;
+    /// Indent unnested wrapped lambda braces.
+    /// \code
+    ///   false:
+    ///   auto foo = []()
+    ///   {
+    ///     return true;
+    ///   };
+    ///
+    ///   true:
+    ///   auto foo = []()
+    ///     {
+    ///       return true;
+    ///     };
+    /// \endcode
+    bool IndentBracesLambdaUnnested;
     /// If ``false``, empty function body can be put on a single line.
     /// This option is used only if the opening brace of the function has
     /// already been wrapped, i.e. the ``AfterFunction`` brace wrapping mode is
diff --git a/clang/lib/Format/ContinuationIndenter.cpp b/clang/lib/Format/ContinuationIndenter.cpp
index 4e4e48f90a89f..d37b075da61ae 100644
--- a/clang/lib/Format/ContinuationIndenter.cpp
+++ b/clang/lib/Format/ContinuationIndenter.cpp
@@ -1334,12 +1334,15 @@ unsigned ContinuationIndenter::getNewLineColumn(const LineState &State) {
            Style.IndentWidth;
   }
 
-  if (Style.BraceWrapping.BeforeLambdaBody &&
-      Style.BraceWrapping.IndentBraces && Current.is(TT_LambdaLBrace)) {
+  if (Style.BraceWrapping.BeforeLambdaBody && Current.is(TT_LambdaLBrace)) {
+    const auto Nested = Current.NestingLevel != 0;
     const auto From = Style.LambdaBodyIndentation == FormatStyle::LBI_Signature
                           ? CurrentState.Indent
                           : State.FirstIndent;
-    return From + Style.IndentWidth;
+    const auto Indent =
+        (Style.BraceWrapping.IndentBracesLambdaNested && Nested) ||
+        (Style.BraceWrapping.IndentBracesLambdaUnnested && !Nested);
+    return From + (Indent * Style.IndentWidth);
   }
 
   if ((NextNonComment->is(tok::l_brace) && NextNonComment->is(BK_Block)) ||
@@ -2123,8 +2126,12 @@ void ContinuationIndenter::moveStateToNewBlock(LineState &State, bool NewLine) {
   if (Style.LambdaBodyIndentation == FormatStyle::LBI_OuterScope &&
       State.NextToken->is(TT_LambdaLBrace) &&
       !State.Line->MightBeFunctionDecl) {
-    const auto Indent = Style.IndentWidth * Style.BraceWrapping.IndentBraces;
-    State.Stack.back().NestedBlockIndent = State.FirstIndent + Indent;
+    const auto Nested = State.NextToken->NestingLevel != 0;
+    const auto Indent =
+        (Style.BraceWrapping.IndentBracesLambdaNested && Nested) ||
+        (Style.BraceWrapping.IndentBracesLambdaUnnested && !Nested);
+    State.Stack.back().NestedBlockIndent =
+        State.FirstIndent + (Indent * Style.IndentWidth);
   }
   unsigned NestedBlockIndent = State.Stack.back().NestedBlockIndent;
   // ObjC block sometimes follow special indentation rules.
diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index bdaf264e9adce..db0f9f87eeb0a 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -196,6 +196,10 @@ template <> struct MappingTraits<FormatStyle::BraceWrappingFlags> {
     IO.mapOptional("BeforeLambdaBody", Wrapping.BeforeLambdaBody);
     IO.mapOptional("BeforeWhile", Wrapping.BeforeWhile);
     IO.mapOptional("IndentBraces", Wrapping.IndentBraces);
+    IO.mapOptional("IndentBracesLambdaNested",
+                   Wrapping.IndentBracesLambdaNested);
+    IO.mapOptional("IndentBracesLambdaUnnested",
+                   Wrapping.IndentBracesLambdaUnnested);
     IO.mapOptional("SplitEmptyFunction", Wrapping.SplitEmptyFunction);
     IO.mapOptional("SplitEmptyRecord", Wrapping.SplitEmptyRecord);
     IO.mapOptional("SplitEmptyNamespace", Wrapping.SplitEmptyNamespace);
@@ -1382,6 +1386,8 @@ static void expandPresetsBraceWrapping(FormatStyle &Expanded) {
                             /*BeforeLambdaBody=*/false,
                             /*BeforeWhile=*/false,
                             /*IndentBraces=*/false,
+                            /*IndentBracesLambdaNested=*/false,
+                            /*IndentBracesLambdaUnnested=*/false,
                             /*SplitEmptyFunction=*/true,
                             /*SplitEmptyRecord=*/true,
                             /*SplitEmptyNamespace=*/true};
@@ -1452,6 +1458,8 @@ static void expandPresetsBraceWrapping(FormatStyle &Expanded) {
         /*BeforeLambdaBody=*/true,
         /*BeforeWhile=*/true,
         /*IndentBraces=*/true,
+        /*IndentBracesLambdaNested=*/true,
+        /*IndentBracesLambdaUnnested=*/true,
         /*SplitEmptyFunction=*/true,
         /*SplitEmptyRecord=*/true,
         /*SplitEmptyNamespace=*/true};
@@ -1552,6 +1560,8 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
                              /*BeforeLambdaBody=*/false,
                              /*BeforeWhile=*/false,
                              /*IndentBraces=*/false,
+                             /*IndentBracesLambdaNested=*/false,
+                             /*IndentBracesLambdaUnnested=*/false,
                              /*SplitEmptyFunction=*/true,
                              /*SplitEmptyRecord=*/true,
                              /*SplitEmptyNamespace=*/true};
diff --git a/clang/unittests/Format/ConfigParseTest.cpp b/clang/unittests/Format/ConfigParseTest.cpp
index aedfdd151d6d3..4436e5ef4904f 100644
--- a/clang/unittests/Format/ConfigParseTest.cpp
+++ b/clang/unittests/Format/ConfigParseTest.cpp
@@ -236,6 +236,8 @@ TEST(ConfigParseTest, ParsesConfigurationBools) {
   CHECK_PARSE_NESTED_BOOL(BraceWrapping, BeforeLambdaBody);
   CHECK_PARSE_NESTED_BOOL(BraceWrapping, BeforeWhile);
   CHECK_PARSE_NESTED_BOOL(BraceWrapping, IndentBraces);
+  CHECK_PARSE_NESTED_BOOL(BraceWrapping, IndentBracesLambdaNested);
+  CHECK_PARSE_NESTED_BOOL(BraceWrapping, IndentBracesLambdaUnnested);
   CHECK_PARSE_NESTED_BOOL(BraceWrapping, SplitEmptyFunction);
   CHECK_PARSE_NESTED_BOOL(BraceWrapping, SplitEmptyRecord);
   CHECK_PARSE_NESTED_BOOL(BraceWrapping, SplitEmptyNamespace);
diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index c0633ba3c29b3..fa06554ed584c 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -24358,6 +24358,84 @@ TEST_F(FormatTest, LambdaBracesInGNU) {
                Style);
 }
 
+TEST_F(FormatTest, LambdaBracesIndentationNested) {
+  auto Style = getLLVMStyle();
+  EXPECT_EQ(Style.LambdaBodyIndentation, FormatStyle::LBI_Signature);
+
+  Style.BreakBeforeBraces = FormatStyle::BS_Custom;
+  Style.BraceWrapping.BeforeLambdaBody = true;
+  verifyFormat("function(\n"
+               "    [&]()\n"
+               "    {\n"
+               "      for (int i = 0; i < y; ++i)\n"
+               "        return 97;\n"
+               "    });",
+               Style);
+
+  Style.BraceWrapping.IndentBracesLambdaNested = true;
+  verifyFormat("function(\n"
+               "    [&]()\n"
+               "      {\n"
+               "        for (int i = 0; i < y; ++i)\n"
+               "          return 97;\n"
+               "      });",
+               Style);
+
+  Style.LambdaBodyIndentation = FormatStyle::LBI_OuterScope;
+  verifyFormat("function([&]()\n"
+               "  {\n"
+               "    for (int i = 0; i < y; ++i)\n"
+               "      return 97;\n"
+               "  });",
+               Style);
+
+  Style.BraceWrapping.IndentBracesLambdaNested = false;
+  verifyFormat("function([&]()\n"
+               "{\n"
+               "  for (int i = 0; i < y; ++i)\n"
+               "    return 97;\n"
+               "});",
+               Style);
+}
+
+TEST_F(FormatTest, LambdaBracesIndentationUnnested) {
+  auto Style = getLLVMStyle();
+  EXPECT_EQ(Style.LambdaBodyIndentation, FormatStyle::LBI_Signature);
+
+  Style.BreakBeforeBraces = FormatStyle::BS_Custom;
+  Style.BraceWrapping.BeforeLambdaBody = true;
+  verifyFormat("auto x = [&]()\n"
+               "{\n"
+               "  for (int i = 0; i < y; ++i)\n"
+               "    return 97;\n"
+               "};",
+               Style);
+
+  Style.BraceWrapping.IndentBracesLambdaUnnested = true;
+  verifyFormat("auto x = [&]()\n"
+               "  {\n"
+               "    for (int i = 0; i < y; ++i)\n"
+               "      return 97;\n"
+               "  };",
+               Style);
+
+  Style.LambdaBodyIndentation = FormatStyle::LBI_OuterScope;
+  verifyFormat("auto x = [&]()\n"
+               "  {\n"
+               "    for (int i = 0; i < y; ++i)\n"
+               "      return 97;\n"
+               "  };",
+               Style);
+
+  Style.BraceWrapping.IndentBracesLambdaUnnested = false;
+  verifyFormat("auto x = [&]()\n"
+               "{\n"
+               "  for (int i = 0; i < y; ++i)\n"
+               "    return 97;\n"
+               "};",
+               Style);
+}
+
 TEST_F(FormatTest, FormatsBlocks) {
   FormatStyle ShortBlocks = getLLVMStyle();
   ShortBlocks.AllowShortBlocksOnASingleLine = FormatStyle::SBS_Always;

@llvmbot
Copy link
Member

llvmbot commented Jun 7, 2025

@llvm/pr-subscribers-clang-format

Author: None (rmarker)

Changes

Adds options to customise how wrapped lambda braces are indented when using custom brace wrapping.

IndentBraces was recently updated to also indent wrapped lambda braces. This has been changed to be controlled separately to allow the old behaviour to be maintained. Ideally before a release is made with the new behaviour.

In order to further increase flexibility, the indentation can be controlled separately for both nested and unnested lambdas.

Resolves #143248


Full diff: https://github.com/llvm/llvm-project/pull/143249.diff

7 Files Affected:

  • (modified) clang/docs/ClangFormatStyleOptions.rst (+34)
  • (modified) clang/docs/ReleaseNotes.rst (+2)
  • (modified) clang/include/clang/Format/Format.h (+32)
  • (modified) clang/lib/Format/ContinuationIndenter.cpp (+12-5)
  • (modified) clang/lib/Format/Format.cpp (+10)
  • (modified) clang/unittests/Format/ConfigParseTest.cpp (+2)
  • (modified) clang/unittests/Format/FormatTest.cpp (+78)
diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst
index 83716cc049ee3..5294c77020945 100644
--- a/clang/docs/ClangFormatStyleOptions.rst
+++ b/clang/docs/ClangFormatStyleOptions.rst
@@ -2541,6 +2541,40 @@ the configuration (without a prefix: ``Auto``).
 
   * ``bool IndentBraces`` Indent the wrapped braces themselves.
 
+  * ``bool IndentBracesLambdaNested`` Indent nested wrapped lambda braces.
+
+    .. code-block:: c++
+
+      false:
+      function(
+          []()
+          {
+            return true;
+          });
+
+      true:
+      function(
+          []()
+            {
+              return true;
+            });
+
+  * ``bool IndentBracesLambdaUnnested`` Indent unnested wrapped lambda braces.
+
+    .. code-block:: c++
+
+      false:
+      auto foo = []()
+      {
+        return true;
+      };
+
+      true:
+      auto foo = []()
+        {
+          return true;
+        };
+
   * ``bool SplitEmptyFunction`` If ``false``, empty function body can be put on a single line.
     This option is used only if the opening brace of the function has
     already been wrapped, i.e. the ``AfterFunction`` brace wrapping mode is
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 9ab69320f0368..03e33c4fcdc49 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -988,6 +988,8 @@ clang-format
   ``enum`` enumerator lists.
 - Add ``OneLineFormatOffRegex`` option for turning formatting off for one line.
 - Add ``SpaceAfterOperatorKeyword`` option.
+- Add ``IndentBracesLambdaNested`` and ``IndentBracesLambdaUnnested`` to
+  ``BraceWrapping`` options for controlling wrapped lambda brace indentation.
 
 clang-refactor
 --------------
diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h
index 127b1d08919de..86291d6fa845c 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -1547,6 +1547,38 @@ struct FormatStyle {
     bool BeforeWhile;
     /// Indent the wrapped braces themselves.
     bool IndentBraces;
+    /// Indent nested wrapped lambda braces.
+    /// \code
+    ///   false:
+    ///   function(
+    ///       []()
+    ///       {
+    ///         return true;
+    ///       });
+    ///
+    ///   true:
+    ///   function(
+    ///       []()
+    ///         {
+    ///           return true;
+    ///         });
+    /// \endcode
+    bool IndentBracesLambdaNested;
+    /// Indent unnested wrapped lambda braces.
+    /// \code
+    ///   false:
+    ///   auto foo = []()
+    ///   {
+    ///     return true;
+    ///   };
+    ///
+    ///   true:
+    ///   auto foo = []()
+    ///     {
+    ///       return true;
+    ///     };
+    /// \endcode
+    bool IndentBracesLambdaUnnested;
     /// If ``false``, empty function body can be put on a single line.
     /// This option is used only if the opening brace of the function has
     /// already been wrapped, i.e. the ``AfterFunction`` brace wrapping mode is
diff --git a/clang/lib/Format/ContinuationIndenter.cpp b/clang/lib/Format/ContinuationIndenter.cpp
index 4e4e48f90a89f..d37b075da61ae 100644
--- a/clang/lib/Format/ContinuationIndenter.cpp
+++ b/clang/lib/Format/ContinuationIndenter.cpp
@@ -1334,12 +1334,15 @@ unsigned ContinuationIndenter::getNewLineColumn(const LineState &State) {
            Style.IndentWidth;
   }
 
-  if (Style.BraceWrapping.BeforeLambdaBody &&
-      Style.BraceWrapping.IndentBraces && Current.is(TT_LambdaLBrace)) {
+  if (Style.BraceWrapping.BeforeLambdaBody && Current.is(TT_LambdaLBrace)) {
+    const auto Nested = Current.NestingLevel != 0;
     const auto From = Style.LambdaBodyIndentation == FormatStyle::LBI_Signature
                           ? CurrentState.Indent
                           : State.FirstIndent;
-    return From + Style.IndentWidth;
+    const auto Indent =
+        (Style.BraceWrapping.IndentBracesLambdaNested && Nested) ||
+        (Style.BraceWrapping.IndentBracesLambdaUnnested && !Nested);
+    return From + (Indent * Style.IndentWidth);
   }
 
   if ((NextNonComment->is(tok::l_brace) && NextNonComment->is(BK_Block)) ||
@@ -2123,8 +2126,12 @@ void ContinuationIndenter::moveStateToNewBlock(LineState &State, bool NewLine) {
   if (Style.LambdaBodyIndentation == FormatStyle::LBI_OuterScope &&
       State.NextToken->is(TT_LambdaLBrace) &&
       !State.Line->MightBeFunctionDecl) {
-    const auto Indent = Style.IndentWidth * Style.BraceWrapping.IndentBraces;
-    State.Stack.back().NestedBlockIndent = State.FirstIndent + Indent;
+    const auto Nested = State.NextToken->NestingLevel != 0;
+    const auto Indent =
+        (Style.BraceWrapping.IndentBracesLambdaNested && Nested) ||
+        (Style.BraceWrapping.IndentBracesLambdaUnnested && !Nested);
+    State.Stack.back().NestedBlockIndent =
+        State.FirstIndent + (Indent * Style.IndentWidth);
   }
   unsigned NestedBlockIndent = State.Stack.back().NestedBlockIndent;
   // ObjC block sometimes follow special indentation rules.
diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index bdaf264e9adce..db0f9f87eeb0a 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -196,6 +196,10 @@ template <> struct MappingTraits<FormatStyle::BraceWrappingFlags> {
     IO.mapOptional("BeforeLambdaBody", Wrapping.BeforeLambdaBody);
     IO.mapOptional("BeforeWhile", Wrapping.BeforeWhile);
     IO.mapOptional("IndentBraces", Wrapping.IndentBraces);
+    IO.mapOptional("IndentBracesLambdaNested",
+                   Wrapping.IndentBracesLambdaNested);
+    IO.mapOptional("IndentBracesLambdaUnnested",
+                   Wrapping.IndentBracesLambdaUnnested);
     IO.mapOptional("SplitEmptyFunction", Wrapping.SplitEmptyFunction);
     IO.mapOptional("SplitEmptyRecord", Wrapping.SplitEmptyRecord);
     IO.mapOptional("SplitEmptyNamespace", Wrapping.SplitEmptyNamespace);
@@ -1382,6 +1386,8 @@ static void expandPresetsBraceWrapping(FormatStyle &Expanded) {
                             /*BeforeLambdaBody=*/false,
                             /*BeforeWhile=*/false,
                             /*IndentBraces=*/false,
+                            /*IndentBracesLambdaNested=*/false,
+                            /*IndentBracesLambdaUnnested=*/false,
                             /*SplitEmptyFunction=*/true,
                             /*SplitEmptyRecord=*/true,
                             /*SplitEmptyNamespace=*/true};
@@ -1452,6 +1458,8 @@ static void expandPresetsBraceWrapping(FormatStyle &Expanded) {
         /*BeforeLambdaBody=*/true,
         /*BeforeWhile=*/true,
         /*IndentBraces=*/true,
+        /*IndentBracesLambdaNested=*/true,
+        /*IndentBracesLambdaUnnested=*/true,
         /*SplitEmptyFunction=*/true,
         /*SplitEmptyRecord=*/true,
         /*SplitEmptyNamespace=*/true};
@@ -1552,6 +1560,8 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
                              /*BeforeLambdaBody=*/false,
                              /*BeforeWhile=*/false,
                              /*IndentBraces=*/false,
+                             /*IndentBracesLambdaNested=*/false,
+                             /*IndentBracesLambdaUnnested=*/false,
                              /*SplitEmptyFunction=*/true,
                              /*SplitEmptyRecord=*/true,
                              /*SplitEmptyNamespace=*/true};
diff --git a/clang/unittests/Format/ConfigParseTest.cpp b/clang/unittests/Format/ConfigParseTest.cpp
index aedfdd151d6d3..4436e5ef4904f 100644
--- a/clang/unittests/Format/ConfigParseTest.cpp
+++ b/clang/unittests/Format/ConfigParseTest.cpp
@@ -236,6 +236,8 @@ TEST(ConfigParseTest, ParsesConfigurationBools) {
   CHECK_PARSE_NESTED_BOOL(BraceWrapping, BeforeLambdaBody);
   CHECK_PARSE_NESTED_BOOL(BraceWrapping, BeforeWhile);
   CHECK_PARSE_NESTED_BOOL(BraceWrapping, IndentBraces);
+  CHECK_PARSE_NESTED_BOOL(BraceWrapping, IndentBracesLambdaNested);
+  CHECK_PARSE_NESTED_BOOL(BraceWrapping, IndentBracesLambdaUnnested);
   CHECK_PARSE_NESTED_BOOL(BraceWrapping, SplitEmptyFunction);
   CHECK_PARSE_NESTED_BOOL(BraceWrapping, SplitEmptyRecord);
   CHECK_PARSE_NESTED_BOOL(BraceWrapping, SplitEmptyNamespace);
diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index c0633ba3c29b3..fa06554ed584c 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -24358,6 +24358,84 @@ TEST_F(FormatTest, LambdaBracesInGNU) {
                Style);
 }
 
+TEST_F(FormatTest, LambdaBracesIndentationNested) {
+  auto Style = getLLVMStyle();
+  EXPECT_EQ(Style.LambdaBodyIndentation, FormatStyle::LBI_Signature);
+
+  Style.BreakBeforeBraces = FormatStyle::BS_Custom;
+  Style.BraceWrapping.BeforeLambdaBody = true;
+  verifyFormat("function(\n"
+               "    [&]()\n"
+               "    {\n"
+               "      for (int i = 0; i < y; ++i)\n"
+               "        return 97;\n"
+               "    });",
+               Style);
+
+  Style.BraceWrapping.IndentBracesLambdaNested = true;
+  verifyFormat("function(\n"
+               "    [&]()\n"
+               "      {\n"
+               "        for (int i = 0; i < y; ++i)\n"
+               "          return 97;\n"
+               "      });",
+               Style);
+
+  Style.LambdaBodyIndentation = FormatStyle::LBI_OuterScope;
+  verifyFormat("function([&]()\n"
+               "  {\n"
+               "    for (int i = 0; i < y; ++i)\n"
+               "      return 97;\n"
+               "  });",
+               Style);
+
+  Style.BraceWrapping.IndentBracesLambdaNested = false;
+  verifyFormat("function([&]()\n"
+               "{\n"
+               "  for (int i = 0; i < y; ++i)\n"
+               "    return 97;\n"
+               "});",
+               Style);
+}
+
+TEST_F(FormatTest, LambdaBracesIndentationUnnested) {
+  auto Style = getLLVMStyle();
+  EXPECT_EQ(Style.LambdaBodyIndentation, FormatStyle::LBI_Signature);
+
+  Style.BreakBeforeBraces = FormatStyle::BS_Custom;
+  Style.BraceWrapping.BeforeLambdaBody = true;
+  verifyFormat("auto x = [&]()\n"
+               "{\n"
+               "  for (int i = 0; i < y; ++i)\n"
+               "    return 97;\n"
+               "};",
+               Style);
+
+  Style.BraceWrapping.IndentBracesLambdaUnnested = true;
+  verifyFormat("auto x = [&]()\n"
+               "  {\n"
+               "    for (int i = 0; i < y; ++i)\n"
+               "      return 97;\n"
+               "  };",
+               Style);
+
+  Style.LambdaBodyIndentation = FormatStyle::LBI_OuterScope;
+  verifyFormat("auto x = [&]()\n"
+               "  {\n"
+               "    for (int i = 0; i < y; ++i)\n"
+               "      return 97;\n"
+               "  };",
+               Style);
+
+  Style.BraceWrapping.IndentBracesLambdaUnnested = false;
+  verifyFormat("auto x = [&]()\n"
+               "{\n"
+               "  for (int i = 0; i < y; ++i)\n"
+               "    return 97;\n"
+               "};",
+               Style);
+}
+
 TEST_F(FormatTest, FormatsBlocks) {
   FormatStyle ShortBlocks = getLLVMStyle();
   ShortBlocks.AllowShortBlocksOnASingleLine = FormatStyle::SBS_Always;

@@ -1547,6 +1547,38 @@ struct FormatStyle {
bool BeforeWhile;
/// Indent the wrapped braces themselves.
bool IndentBraces;
/// Indent nested wrapped lambda braces.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a better name is in order, when reading nested lambda, I thought of a lambda within a lambda.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, yeah, I based the name off of the description in the code. I.e. that it uses Current.NestingLevel.
I hadn't thought about the lambda in lambda.
Do you have any ideas of something clearer?
Maybe IndentBracesLambdaTopLevel for the unnested and then just IndentBracesLambda for the others? (I cannot think of an immediately obvious name for the nested case).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe IndentBracesLambdaScoped could work? Or IndentBracesLambdaInner? Could be paired with IndentBracesLambdaOuter.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really have something, but you should swap Lambda and Braces => LambdaBraces.

When is NestingLevel > 0?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When it is inside some form of brackets.
From the documentation in FormatToken.h.

/// The nesting level of this token, i.e. the number of surrounding (),
/// [], {} or <>.
unsigned NestingLevel = 0;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I can't think of a nice name here... @owenca do you have an idea?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we allow this new option, I want a separate NFC patch that upgrades the IndentBraces sub-option to an enum or a struct first. See e.g. #140497.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created a patch to upgrade IndentBraces. #143663

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had an idea about the name.
When looking again at the projects at work that needed the nested/unnested distinction, I saw that only indenting the unnested version was required.
Confusion could be eliminated by switching to an enum for IndentLambdaBraces with None, All and TopLevel.

Copy link
Contributor

@HazardyKnusperkeks HazardyKnusperkeks left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A general note, if you comment in the Files changed tab and Start a review it should only send one email for all your comments.

@@ -1547,6 +1547,38 @@ struct FormatStyle {
bool BeforeWhile;
/// Indent the wrapped braces themselves.
bool IndentBraces;
/// Indent nested wrapped lambda braces.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really have something, but you should swap Lambda and Braces => LambdaBraces.

When is NestingLevel > 0?

@owenca
Copy link
Contributor

owenca commented Jun 10, 2025

See https://clang.llvm.org/docs/ClangFormatStyleOptions.html#adding-additional-style-options. IMO, when it's difficult to name the new options, it will be hard for people to understand what they do. @mydeveloperday WDYT?

@rmarker
Copy link
Contributor Author

rmarker commented Jun 11, 2025

See https://clang.llvm.org/docs/ClangFormatStyleOptions.html#adding-additional-style-options. IMO, when it's difficult to name the new options, it will be hard for people to understand what they do. @mydeveloperday WDYT?

I'll have to see if we can get our style guide public. Argh, that will be fun company politics ;)
Trying to first get the style guide to align with clang-format went down in flames.

I think that switching to only including the option for indenting the top level lambdas ((#143249 (comment))) does go a long way to making it a lot more straightforward and clearer to remove any confusion.

In any case, I started work on upgrading to a struct (#143663).
I think keeping support for the previous formatting, with at least a bool for lambda brace indentation is worth it.
Regardless of any small additional nuance that is also possible to include in the option.

Copy link
Contributor

@mydeveloperday mydeveloperday left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something here doesn't feel right..aren't these brace Wapping options?

@rmarker
Copy link
Contributor Author

rmarker commented Jun 15, 2025

Something here doesn't feel right..aren't these brace Wapping options?

Yes, the BraceWrapping options contain IndentBraces.
This is so that BreakBeforeBraces: Custom can also indent braces, similar to BreakBeforeBraces: GNU.
GNU was recently updated (#135479) to also indent lambda braces.
The aim of this PR is to allow for more control of how lambda braces are indented when using the Custom mode.

Holistically, it might make more sense to split indentation, and thus GNU and Whitesmiths, out of brace wrapping. But I'm not sure whether a change like that would be worthwhile at this point.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang Clang issues not falling into any other category clang-format
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[clang-format] More control of wrapped lambda brace indentation
5 participants