From: Vitali Lovich Date: Tue, 22 Jun 2021 18:39:27 +0000 (+0200) Subject: [clang-format] Add new LambdaBodyIndentation option X-Git-Tag: llvmorg-14-init~3314 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=64cf5eba06bd4f81954253b1e7a10be6fe92403e;p=platform%2Fupstream%2Fllvm.git [clang-format] Add new LambdaBodyIndentation option Currently the lambda body indents relative to where the lambda signature is located. This instead lets the user choose to align the lambda body relative to the parent scope that contains the lambda declaration. Thus: someFunction([] { lambdaBody(); }); will always have the same indentation of the body even when the lambda signature goes on a new line: someFunction( [] { lambdaBody(); }); whereas before lambdaBody would be indented 6 spaces. Differential Revision: https://reviews.llvm.org/D102706 --- diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index 0d0c07f..f05e114 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -2841,6 +2841,42 @@ the configuration (without a prefix: ``Auto``). bar(); } } +**LambdaBodyIndentation** (``LambdaBodyIndentationKind``) + The indentation style of lambda bodies. ``Signature`` (the default) + causes the lambda body to be indented one additional level relative to + the indentation level of the signature. ``OuterScope`` forces the lambda + body to be indented one additional level relative to the parent scope + containing the lambda signature. For callback-heavy code, it may improve + readability to have the signature indented two levels and to use + ``OuterScope``. The KJ style guide requires ``OuterScope`. + `KJ style guide + `_ + + Possible values: + + * ``LBI_Signature`` (in configuration: ``Signature``) + Align lambda body relative to the lambda signature. This is the default. + + .. code-block:: c++ + + someMethod( + [](SomeReallyLongLambdaSignatureArgument foo) { + return; + }); + + * ``LBI_OuterScope`` (in configuration: ``OuterScope``) + Align lambda body relative to the indentation level of the outer scope + the lambda signature resides in. + + .. code-block:: c++ + + someMethod( + [](SomeReallyLongLambdaSignatureArgument foo) { + return; + }); + + + **Language** (``LanguageKind``) Language, this format style is targeted at. diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index bddaea6..4b432c1 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -259,6 +259,12 @@ clang-format - Option ``BreakInheritanceList`` gets a new style, ``AfterComma``. It breaks only after the commas that separate the base-specifiers. +- Option ``LambdaBodyIndentation`` has been added to control how the body of a + lambda is indented. The default ``Signature`` value indents the body one level + relative to whatever indentation the signature has. ``OuterScope`` lets you + change that so that the lambda body is indented one level relative to the scope + containing the lambda, regardless of where the lambda signature was placed. + - ``git-clang-format`` no longer formats changes to symbolic links. (Fixes https://llvm.org/PR46992.) diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index 164765c..d51666f 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -2490,6 +2490,38 @@ struct FormatStyle { /// Language, this format style is targeted at. LanguageKind Language; + /// Indentation logic for lambda bodies. + enum LambdaBodyIndentationKind : unsigned char { + /// Align lambda body relative to the lambda signature. This is the default. + /// \code + /// someMethod( + /// [](SomeReallyLongLambdaSignatureArgument foo) { + /// return; + /// }); + /// \endcode + LBI_Signature, + /// Align lambda body relative to the indentation level of the outer scope + /// the lambda signature resides in. + /// \code + /// someMethod( + /// [](SomeReallyLongLambdaSignatureArgument foo) { + /// return; + /// }); + /// \endcode + LBI_OuterScope, + }; + + /// The indentation style of lambda bodies. ``Signature`` (the default) + /// causes the lambda body to be indented one additional level relative to + /// the indentation level of the signature. ``OuterScope`` forces the lambda + /// body to be indented one additional level relative to the parent scope + /// containing the lambda signature. For callback-heavy code, it may improve + /// readability to have the signature indented two levels and to use + /// ``OuterScope``. The KJ style guide requires ``OuterScope`. + /// `KJ style guide + /// `_ + LambdaBodyIndentationKind LambdaBodyIndentation; + /// A regular expression matching macros that start a block. /// \code /// # With: @@ -3377,6 +3409,7 @@ struct FormatStyle { JavaScriptWrapImports == R.JavaScriptWrapImports && KeepEmptyLinesAtTheStartOfBlocks == R.KeepEmptyLinesAtTheStartOfBlocks && + LambdaBodyIndentation == R.LambdaBodyIndentation && MacroBlockBegin == R.MacroBlockBegin && MacroBlockEnd == R.MacroBlockEnd && MaxEmptyLinesToKeep == R.MaxEmptyLinesToKeep && diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index 53cbbf6..b83fcae 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -85,6 +85,15 @@ template <> struct ScalarEnumerationTraits { } }; +template <> +struct ScalarEnumerationTraits { + static void enumeration(IO &IO, + FormatStyle::LambdaBodyIndentationKind &Value) { + IO.enumCase(Value, "Signature", FormatStyle::LBI_Signature); + IO.enumCase(Value, "OuterScope", FormatStyle::LBI_OuterScope); + } +}; + template <> struct ScalarEnumerationTraits { static void enumeration(IO &IO, FormatStyle::UseTabStyle &Value) { IO.enumCase(Value, "Never", FormatStyle::UT_Never); @@ -649,6 +658,7 @@ template <> struct MappingTraits { IO.mapOptional("JavaScriptWrapImports", Style.JavaScriptWrapImports); IO.mapOptional("KeepEmptyLinesAtTheStartOfBlocks", Style.KeepEmptyLinesAtTheStartOfBlocks); + IO.mapOptional("LambdaBodyIndentation", Style.LambdaBodyIndentation); IO.mapOptional("MacroBlockBegin", Style.MacroBlockBegin); IO.mapOptional("MacroBlockEnd", Style.MacroBlockEnd); IO.mapOptional("MaxEmptyLinesToKeep", Style.MaxEmptyLinesToKeep); @@ -1040,6 +1050,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) { LLVMStyle.JavaScriptQuotes = FormatStyle::JSQS_Leave; LLVMStyle.JavaScriptWrapImports = true; LLVMStyle.TabWidth = 8; + LLVMStyle.LambdaBodyIndentation = FormatStyle::LBI_Signature; LLVMStyle.MaxEmptyLinesToKeep = 1; LLVMStyle.KeepEmptyLinesAtTheStartOfBlocks = true; LLVMStyle.NamespaceIndentation = FormatStyle::NI_None; diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp index 41fbb16..3255c71 100644 --- a/clang/lib/Format/UnwrappedLineFormatter.cpp +++ b/clang/lib/Format/UnwrappedLineFormatter.cpp @@ -823,8 +823,20 @@ protected: return true; if (NewLine) { - int AdditionalIndent = State.Stack.back().Indent - - Previous.Children[0]->Level * Style.IndentWidth; + const ParenState &P = State.Stack.back(); + + int AdditionalIndent = + P.Indent - Previous.Children[0]->Level * Style.IndentWidth; + + if (Style.LambdaBodyIndentation == FormatStyle::LBI_OuterScope && + P.NestedBlockIndent == P.LastSpace) { + if (State.NextToken->MatchingParen && + State.NextToken->MatchingParen->is(TT_LambdaLBrace)) { + State.Stack.pop_back(); + } + if (LBrace->is(TT_LambdaLBrace)) + AdditionalIndent = 0; + } Penalty += BlockFormatter->format(Previous.Children, DryRun, AdditionalIndent, diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index 6a2c0d0..108d918 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -18531,6 +18531,7 @@ TEST_F(FormatTest, FormatsLambdas) { " aaaaaaaaaaaaaaaaaaaaaaa;\n" " });", getLLVMStyleWithColumns(60)); + verifyFormat("SomeFunction({[&] {\n" " // comment\n" " },\n" @@ -19083,6 +19084,117 @@ TEST_F(FormatTest, FormatsLambdas) { " });\n" " });", LLVMWithBeforeLambdaBody); + + // Lambdas with different indentation styles. + Style = getLLVMStyleWithColumns(100); + EXPECT_EQ("SomeResult doSomething(SomeObject promise) {\n" + " return promise.then(\n" + " [this, &someVariable, someObject = " + "std::mv(s)](std::vector evaluated) mutable {\n" + " return someObject.startAsyncAction().then(\n" + " [this, &someVariable](AsyncActionResult result) " + "mutable { result.processMore(); });\n" + " });\n" + "}\n", + format("SomeResult doSomething(SomeObject promise) {\n" + " return promise.then([this, &someVariable, someObject = " + "std::mv(s)](std::vector evaluated) mutable {\n" + " return someObject.startAsyncAction().then([this, " + "&someVariable](AsyncActionResult result) mutable {\n" + " result.processMore();\n" + " });\n" + " });\n" + "}\n", + Style)); + Style.LambdaBodyIndentation = FormatStyle::LBI_OuterScope; + verifyFormat("test() {\n" + " ([]() -> {\n" + " int b = 32;\n" + " return 3;\n" + " }).foo();\n" + "}", + Style); + verifyFormat("test() {\n" + " []() -> {\n" + " int b = 32;\n" + " return 3;\n" + " }\n" + "}", + Style); + verifyFormat("std::sort(v.begin(), v.end(),\n" + " [](const auto &someLongArgumentName, const auto " + "&someOtherLongArgumentName) {\n" + " return someLongArgumentName.someMemberVariable < " + "someOtherLongArgumentName.someMemberVariable;\n" + "});", + Style); + verifyFormat("test() {\n" + " (\n" + " []() -> {\n" + " int b = 32;\n" + " return 3;\n" + " },\n" + " foo, bar)\n" + " .foo();\n" + "}", + Style); + verifyFormat("test() {\n" + " ([]() -> {\n" + " int b = 32;\n" + " return 3;\n" + " })\n" + " .foo()\n" + " .bar();\n" + "}", + Style); + EXPECT_EQ("SomeResult doSomething(SomeObject promise) {\n" + " return promise.then(\n" + " [this, &someVariable, someObject = " + "std::mv(s)](std::vector evaluated) mutable {\n" + " return someObject.startAsyncAction().then(\n" + " [this, &someVariable](AsyncActionResult result) mutable { " + "result.processMore(); });\n" + " });\n" + "}\n", + format("SomeResult doSomething(SomeObject promise) {\n" + " return promise.then([this, &someVariable, someObject = " + "std::mv(s)](std::vector evaluated) mutable {\n" + " return someObject.startAsyncAction().then([this, " + "&someVariable](AsyncActionResult result) mutable {\n" + " result.processMore();\n" + " });\n" + " });\n" + "}\n", + Style)); + EXPECT_EQ("SomeResult doSomething(SomeObject promise) {\n" + " return promise.then([this, &someVariable] {\n" + " return someObject.startAsyncAction().then(\n" + " [this, &someVariable](AsyncActionResult result) mutable { " + "result.processMore(); });\n" + " });\n" + "}\n", + format("SomeResult doSomething(SomeObject promise) {\n" + " return promise.then([this, &someVariable] {\n" + " return someObject.startAsyncAction().then([this, " + "&someVariable](AsyncActionResult result) mutable {\n" + " result.processMore();\n" + " });\n" + " });\n" + "}\n", + Style)); + Style = getGoogleStyle(); + Style.LambdaBodyIndentation = FormatStyle::LBI_OuterScope; + EXPECT_EQ("#define A \\\n" + " [] { \\\n" + " xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx( \\\n" + " xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx); \\\n" + " }", + format("#define A [] { xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx( \\\n" + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx); }", + Style)); + // TODO: The current formatting has a minor issue that's not worth fixing + // right now whereby the closing brace is indented relative to the signature + // instead of being aligned. This only happens with macros. } TEST_F(FormatTest, LambdaWithLineComments) {