Skip to content

[clang-format] Disable OuterScope lambda indentation behaviour for constructor initializers #66755

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

Merged

Conversation

jp4a50
Copy link
Contributor

@jp4a50 jp4a50 commented Sep 19, 2023

By default, OuterScope aligns lambdas to the beginning of the current line. This makes sense for most types of statements within code blocks but leads to unappealing and misleading indentation for lambdas within constructor initializers.

constructor initializers

By default, OuterScope aligns lambdas to the beginning of the current
line. This makes sense for most types of statements within code blocks
but leads to unappealing and misleading indentation for lambdas within
constructor initializers.
@llvmbot
Copy link
Member

llvmbot commented Sep 19, 2023

@llvm/pr-subscribers-clang

@llvm/pr-subscribers-clang-format

Changes

By default, OuterScope aligns lambdas to the beginning of the current line. This makes sense for most types of statements within code blocks but leads to unappealing and misleading indentation for lambdas within constructor initializers.


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

2 Files Affected:

  • (modified) clang/lib/Format/ContinuationIndenter.cpp (+2-1)
  • (modified) clang/unittests/Format/FormatTest.cpp (+23-7)
diff --git a/clang/lib/Format/ContinuationIndenter.cpp b/clang/lib/Format/ContinuationIndenter.cpp
index deb3e554fdc124b..0fa70eace90f416 100644
--- a/clang/lib/Format/ContinuationIndenter.cpp
+++ b/clang/lib/Format/ContinuationIndenter.cpp
@@ -1955,7 +1955,8 @@ void ContinuationIndenter::moveStatePastScopeCloser(LineState &State) {
 
 void ContinuationIndenter::moveStateToNewBlock(LineState &State) {
   if (Style.LambdaBodyIndentation == FormatStyle::LBI_OuterScope &&
-      State.NextToken->is(TT_LambdaLBrace)) {
+      State.NextToken->is(TT_LambdaLBrace) && State.Line &&
+      !State.Line->MightBeFunctionDecl) {
     State.Stack.back().NestedBlockIndent = State.FirstIndent;
   }
   unsigned NestedBlockIndent = State.Stack.back().NestedBlockIndent;
diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index 0d0fbdb84e3271b..cb6b22b32e63350 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -22537,10 +22537,12 @@ TEST_F(FormatTest, FormatsLambdas) {
                "  }\n"
                "}",
                Style);
-  verifyFormat("std::sort(v.begin(), v.end(),\n"
-               "          [](const auto &foo, const auto &bar) {\n"
-               "  return foo.baz < bar.baz;\n"
-               "});",
+  verifyFormat("void test() {\n"
+               "  std::sort(v.begin(), v.end(),\n"
+               "            [](const auto &foo, const auto &bar) {\n"
+               "    return foo.baz < bar.baz;\n"
+               "  });\n"
+               "};",
                Style);
   verifyFormat("void test() {\n"
                "  (\n"
@@ -22589,9 +22591,23 @@ TEST_F(FormatTest, FormatsLambdas) {
   verifyFormat("Namespace::Foo::Foo(LongClassName bar,\n"
                "                    AnotherLongClassName baz)\n"
                "    : baz{baz}, func{[&] {\n"
-               "  auto qux = bar;\n"
-               "  return aFunkyFunctionCall(qux);\n"
-               "}} {}",
+               "        auto qux = bar;\n"
+               "        return aFunkyFunctionCall(qux);\n"
+               "      }} {}",
+               Style);
+  verifyFormat("void foo() {\n"
+               "  class Foo {\n"
+               "  public:\n"
+               "    Foo()\n"
+               "        : qux{[](int quux) {\n"
+               "            auto tmp = quux;\n"
+               "            return tmp;\n"
+               "          }} {}\n"
+               "\n"
+               "  private:\n"
+               "    std::function<void(int quux)> qux;\n"
+               "  };\n"
+               "}\n",
                Style);
   Style.AlignAfterOpenBracket = FormatStyle::BAS_AlwaysBreak;
   // FIXME: The following test should pass, but fails at the time of writing.

Copy link
Contributor

@owenca owenca left a comment

Choose a reason for hiding this comment

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

Can you add test cases with BreakConstructorInitializer set to AfterColon?

@mydeveloperday
Copy link
Contributor

I'm not sure I understand, looking at https://clang.llvm.org/docs/ClangFormatStyleOptions.html#lambdabodyindentation it seems to explicitly state it should be at the outer function scope, can you point to a github issue this is trying to solve or is this just personal preference?

@jp4a50
Copy link
Contributor Author

jp4a50 commented Sep 20, 2023

I'm not sure I understand, looking at https://clang.llvm.org/docs/ClangFormatStyleOptions.html#lambdabodyindentation it seems to explicitly state it should be at the outer function scope, can you point to a github issue this is trying to solve or is this just personal preference?

When my team originally proposed and authored this option the intention was for it to apply to expression statements within blocks such that statements such that the bodies of lambdas as function call arguments (like the example in the docs) and on the RHS of assignment expressions (etc.) would align with the start of the expression, minimizing indentation.

We just didn't think of or consider cases such as lambdas used within constructor initializers where the start of the constructor is not the start of the expression so aligning with respect to the start of the constructor is confusing.

So I'd say that this can be considered personal preference to a certain extent but also an attempt to honour the intended behaviour of the option when we proposed it.

I accept that implementing this as an edge case is clunkier than I'd like, though.

when using OuterScope lambda body indentation.
Copy link
Contributor

@owenca owenca left a comment

Choose a reason for hiding this comment

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

LGTM, but please wait for @mydeveloperday.

@HazardyKnusperkeks
Copy link
Contributor

You should adapt the documentation.

@llvmbot llvmbot added the clang Clang issues not falling into any other category label Sep 26, 2023
@owenca owenca removed the clang Clang issues not falling into any other category label Sep 27, 2023
@jp4a50
Copy link
Contributor Author

jp4a50 commented Sep 27, 2023

You should adapt the documentation.

Done. Tried to capture the original intention of the option which is to minimize (and make consistent) indentation of lambdas at block scope. This PR addresses the most obvious exception to block scope but there may be more that we can address going forwards.

@jp4a50
Copy link
Contributor Author

jp4a50 commented Sep 28, 2023

Thanks for the reviews! Can someone please merge for me? @HazardyKnusperkeks @owenca

@HazardyKnusperkeks HazardyKnusperkeks merged commit 82001e0 into llvm:main Sep 28, 2023
legrosbuffle pushed a commit to legrosbuffle/llvm-project that referenced this pull request Sep 29, 2023
…nstructor initializers (llvm#66755)

By default, OuterScope aligns lambdas to the beginning of the current
line. This makes sense for most types of statements within code blocks
but leads to unappealing and misleading indentation for lambdas within
constructor initializers.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants