[clang] 64cf5eb - [clang-format] Add new LambdaBodyIndentation option
Björn Schäpers via cfe-commits
cfe-commits at lists.llvm.org
Tue Jun 22 12:46:23 PDT 2021
Author: Vitali Lovich
Date: 2021-06-22T21:46:16+02:00
New Revision: 64cf5eba06bd4f81954253b1e7a10be6fe92403e
URL: https://github.com/llvm/llvm-project/commit/64cf5eba06bd4f81954253b1e7a10be6fe92403e
DIFF: https://github.com/llvm/llvm-project/commit/64cf5eba06bd4f81954253b1e7a10be6fe92403e.diff
LOG: [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
Added:
Modified:
clang/docs/ClangFormatStyleOptions.rst
clang/docs/ReleaseNotes.rst
clang/include/clang/Format/Format.h
clang/lib/Format/Format.cpp
clang/lib/Format/UnwrappedLineFormatter.cpp
clang/unittests/Format/FormatTest.cpp
Removed:
################################################################################
diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst
index 0d0c07fa350f..f05e11469a7b 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
+ <https://github.com/capnproto/capnproto/blob/master/kjdoc/style-guide.md>`_
+
+ 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 bddaea6e4846..4b432c1f3bb8 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 164765ca1a1a..d51666fe9a1b 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
+ /// <https://github.com/capnproto/capnproto/blob/master/kjdoc/style-guide.md>`_
+ 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 53cbbf66e85a..b83fcaebf18c 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -85,6 +85,15 @@ template <> struct ScalarEnumerationTraits<FormatStyle::LanguageStandard> {
}
};
+template <>
+struct ScalarEnumerationTraits<FormatStyle::LambdaBodyIndentationKind> {
+ 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<FormatStyle::UseTabStyle> {
static void enumeration(IO &IO, FormatStyle::UseTabStyle &Value) {
IO.enumCase(Value, "Never", FormatStyle::UT_Never);
@@ -649,6 +658,7 @@ template <> struct MappingTraits<FormatStyle> {
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 41fbb16f0db1..3255c71d3407 100644
--- a/clang/lib/Format/UnwrappedLineFormatter.cpp
+++ b/clang/lib/Format/UnwrappedLineFormatter.cpp
@@ -823,8 +823,20 @@ class LineFormatter {
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 6a2c0d007589..108d918ce345 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
diff erent indentation styles.
+ Style = getLLVMStyleWithColumns(100);
+ EXPECT_EQ("SomeResult doSomething(SomeObject promise) {\n"
+ " return promise.then(\n"
+ " [this, &someVariable, someObject = "
+ "std::mv(s)](std::vector<int> 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<int> 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<int> 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<int> 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) {
More information about the cfe-commits
mailing list