diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index 83716cc049ee3..55dc0b2fca4a4 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 IndentLambdaBracesNested`` Indent nested wrapped lambda braces. + + .. code-block:: c++ + + false: + function( + []() + { + return true; + }); + + true: + function( + []() + { + return true; + }); + + * ``bool IndentLambdaBracesUnnested`` 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..eb32c6f1f9d25 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -685,7 +685,7 @@ Bug Fixes in This Version - Fixed an assertion failure in serialization of constexpr structs containing unions. (#GH140130) - Fixed duplicate entries in TableGen that caused the wrong attribute to be selected. (GH#140701) - Fixed type mismatch error when 'builtin-elementwise-math' arguments have different qualifiers, this should be well-formed. (#GH141397) -- Constant evaluation now correctly runs the destructor of a variable declared in +- Constant evaluation now correctly runs the destructor of a variable declared in the second clause of a C-style ``for`` loop. (#GH139818) Bug Fixes to Compiler Builtins @@ -988,6 +988,8 @@ clang-format ``enum`` enumerator lists. - Add ``OneLineFormatOffRegex`` option for turning formatting off for one line. - Add ``SpaceAfterOperatorKeyword`` option. +- Add ``IndentLambdaBracesNested`` and ``IndentLambdaBracesUnnested`` 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..3795467aadf26 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 IndentLambdaBracesNested; + /// Indent unnested wrapped lambda braces. + /// \code + /// false: + /// auto foo = []() + /// { + /// return true; + /// }; + /// + /// true: + /// auto foo = []() + /// { + /// return true; + /// }; + /// \endcode + bool IndentLambdaBracesUnnested; /// 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..5c41d51473ebf 100644 --- a/clang/lib/Format/ContinuationIndenter.cpp +++ b/clang/lib/Format/ContinuationIndenter.cpp @@ -1334,12 +1334,14 @@ 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 = Nested ? Style.BraceWrapping.IndentLambdaBracesNested + : Style.BraceWrapping.IndentLambdaBracesUnnested; + return From + (Indent * Style.IndentWidth); } if ((NextNonComment->is(tok::l_brace) && NextNonComment->is(BK_Block)) || @@ -2123,8 +2125,11 @@ 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 = Nested ? Style.BraceWrapping.IndentLambdaBracesNested + : Style.BraceWrapping.IndentLambdaBracesUnnested; + 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..b254421c3ba3d 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -196,6 +196,10 @@ template <> struct MappingTraits { IO.mapOptional("BeforeLambdaBody", Wrapping.BeforeLambdaBody); IO.mapOptional("BeforeWhile", Wrapping.BeforeWhile); IO.mapOptional("IndentBraces", Wrapping.IndentBraces); + IO.mapOptional("IndentLambdaBracesNested", + Wrapping.IndentLambdaBracesNested); + IO.mapOptional("IndentLambdaBracesUnnested", + Wrapping.IndentLambdaBracesUnnested); 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, + /*IndentLambdaBracesNested=*/false, + /*IndentLambdaBracesUnnested=*/false, /*SplitEmptyFunction=*/true, /*SplitEmptyRecord=*/true, /*SplitEmptyNamespace=*/true}; @@ -1452,6 +1458,8 @@ static void expandPresetsBraceWrapping(FormatStyle &Expanded) { /*BeforeLambdaBody=*/true, /*BeforeWhile=*/true, /*IndentBraces=*/true, + /*IndentLambdaBracesNested=*/true, + /*IndentLambdaBracesUnnested=*/true, /*SplitEmptyFunction=*/true, /*SplitEmptyRecord=*/true, /*SplitEmptyNamespace=*/true}; @@ -1552,6 +1560,8 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) { /*BeforeLambdaBody=*/false, /*BeforeWhile=*/false, /*IndentBraces=*/false, + /*IndentLambdaBracesNested=*/false, + /*IndentLambdaBracesUnnested=*/false, /*SplitEmptyFunction=*/true, /*SplitEmptyRecord=*/true, /*SplitEmptyNamespace=*/true}; diff --git a/clang/unittests/Format/ConfigParseTest.cpp b/clang/unittests/Format/ConfigParseTest.cpp index aedfdd151d6d3..38eed55b7b6f6 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, IndentLambdaBracesNested); + CHECK_PARSE_NESTED_BOOL(BraceWrapping, IndentLambdaBracesUnnested); 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..d4c3eb3b7979d 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.IndentLambdaBracesNested = 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.IndentLambdaBracesNested = 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.IndentLambdaBracesUnnested = 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.IndentLambdaBracesUnnested = 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;