diff --git a/clang/lib/Format/ContinuationIndenter.cpp b/clang/lib/Format/ContinuationIndenter.cpp --- a/clang/lib/Format/ContinuationIndenter.cpp +++ b/clang/lib/Format/ContinuationIndenter.cpp @@ -318,10 +318,11 @@ return false; // Don't create a 'hanging' indent if there are multiple blocks in a single - // statement. + // statement and we are aligning lambda blocks to their signatures. if (Previous.is(tok::l_brace) && State.Stack.size() > 1 && State.Stack[State.Stack.size() - 2].NestedBlockInlined && - State.Stack[State.Stack.size() - 2].HasMultipleNestedBlocks) { + State.Stack[State.Stack.size() - 2].HasMultipleNestedBlocks && + Style.LambdaBodyIndentation == FormatStyle::LBI_Signature) { return false; } @@ -335,6 +336,11 @@ // If binary operators are moved to the next line (including commas for some // styles of constructor initializers), that's always ok. if (!Current.isOneOf(TT_BinaryOperator, tok::comma) && + // Allow breaking opening brace of lambdas (when passed as function + // arguments) to a new line when BeforeLambdaBody brace wrapping is + // enabled. + (!Style.BraceWrapping.BeforeLambdaBody || + Current.isNot(TT_LambdaLBrace)) && CurrentState.NoLineBreakInOperand) { return false; } @@ -662,34 +668,37 @@ const FormatToken &Previous = *State.NextToken->Previous; auto &CurrentState = State.Stack.back(); - bool DisallowLineBreaksOnThisLine = Style.isCpp() && [&Current] { - // Deal with lambda arguments in C++. The aim here is to ensure that we - // don't over-indent lambda function bodies when lambdas are passed as - // arguments to function calls. We do this by ensuring that either all - // arguments (including any lambdas) go on the same line as the function - // call, or we break before the first argument. - auto PrevNonComment = Current.getPreviousNonComment(); - if (!PrevNonComment || PrevNonComment->isNot(tok::l_paren)) - return false; - if (Current.isOneOf(tok::comment, tok::l_paren, TT_LambdaLSquare)) - return false; - auto BlockParameterCount = PrevNonComment->BlockParameterCount; - if (BlockParameterCount == 0) - return false; + bool DisallowLineBreaksOnThisLine = + Style.LambdaBodyIndentation == FormatStyle::LBI_Signature && + Style.isCpp() && [&Current] { + // Deal with lambda arguments in C++. The aim here is to ensure that we + // don't over-indent lambda function bodies when lambdas are passed as + // arguments to function calls. We do this by ensuring that either all + // arguments (including any lambdas) go on the same line as the function + // call, or we break before the first argument. + auto PrevNonComment = Current.getPreviousNonComment(); + if (!PrevNonComment || PrevNonComment->isNot(tok::l_paren)) + return false; + if (Current.isOneOf(tok::comment, tok::l_paren, TT_LambdaLSquare)) + return false; + auto BlockParameterCount = PrevNonComment->BlockParameterCount; + if (BlockParameterCount == 0) + return false; - // Multiple lambdas in the same function call. - if (BlockParameterCount > 1) - return true; + // Multiple lambdas in the same function call. + if (BlockParameterCount > 1) + return true; - // A lambda followed by another arg. - if (!PrevNonComment->Role) - return false; - auto Comma = PrevNonComment->Role->lastComma(); - if (!Comma) - return false; - auto Next = Comma->getNextNonComment(); - return Next && !Next->isOneOf(TT_LambdaLSquare, tok::l_brace, tok::caret); - }(); + // A lambda followed by another arg. + if (!PrevNonComment->Role) + return false; + auto Comma = PrevNonComment->Role->lastComma(); + if (!Comma) + return false; + auto Next = Comma->getNextNonComment(); + return Next && + !Next->isOneOf(TT_LambdaLSquare, tok::l_brace, tok::caret); + }(); if (DisallowLineBreaksOnThisLine) State.NoLineBreak = true; @@ -1067,9 +1076,40 @@ NestedBlockSpecialCase || (Current.MatchingParen && Current.MatchingParen->is(TT_RequiresExpressionLBrace)); - if (!NestedBlockSpecialCase) - for (ParenState &PState : llvm::drop_end(State.Stack)) - PState.BreakBeforeParameter = true; + if (!NestedBlockSpecialCase) { + auto ParentLevelIt = std::next(State.Stack.rbegin()); + if (Style.LambdaBodyIndentation == FormatStyle::LBI_OuterScope && + Current.MatchingParen && Current.MatchingParen->is(TT_LambdaLBrace)) { + // If the first character on the new line is a lambda's closing brace, the + // stack still contains that lambda's parenthesis. As such, we need to + // recurse further down the stack than usual to find the parenthesis level + // containing the lambda, which is where we want to set + // BreakBeforeParameter. + // + // We specifically special case "OuterScope"-formatted lambdas here + // because, when using that setting, breaking before the parameter + // directly following the lambda is particularly unsightly. However, when + // "OuterScope" is not set, the logic to find the parent parenthesis level + // still appears to be sometimes incorrect. It has not been fixed yet + // because it would lead to significant changes in existing behaviour. + // + // TODO: fix the non-"OuterScope" case too. + auto FindCurrentLevel = [&](const auto &It) { + return std::find_if(It, State.Stack.rend(), [](const auto &PState) { + return PState.Tok != nullptr; // Ignore fake parens. + }); + }; + auto MaybeIncrement = [&](const auto &It) { + return It != State.Stack.rend() ? std::next(It) : It; + }; + auto LambdaLevelIt = FindCurrentLevel(State.Stack.rbegin()); + auto LevelContainingLambdaIt = + FindCurrentLevel(MaybeIncrement(LambdaLevelIt)); + ParentLevelIt = MaybeIncrement(LevelContainingLambdaIt); + } + for (auto I = ParentLevelIt, E = State.Stack.rend(); I != E; ++I) + I->BreakBeforeParameter = true; + } if (PreviousNonComment && !PreviousNonComment->isOneOf(tok::comma, tok::colon, tok::semi) && @@ -1079,7 +1119,11 @@ !PreviousNonComment->isOneOf( TT_BinaryOperator, TT_FunctionAnnotationRParen, TT_JavaAnnotation, TT_LeadingJavaAnnotation) && - Current.isNot(TT_BinaryOperator) && !PreviousNonComment->opensScope()) { + Current.isNot(TT_BinaryOperator) && !PreviousNonComment->opensScope() && + // We don't want to enforce line breaks for subsequent arguments just + // because we have been forced to break before a lambda body. + (!Style.BraceWrapping.BeforeLambdaBody || + Current.isNot(TT_LambdaLBrace))) { CurrentState.BreakBeforeParameter = true; } @@ -1098,7 +1142,7 @@ if (CurrentState.AvoidBinPacking) { // If we are breaking after '(', '{', '<', or this is the break after a ':' - // to start a member initializater list in a constructor, this should not + // to start a member initializer list in a constructor, this should not // be considered bin packing unless the relevant AllowAll option is false or // this is a dict/object literal. bool PreviousIsBreakingCtorInitializerColon = diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -22524,8 +22524,7 @@ " []() -> auto {\n" " int b = 32;\n" " return 3;\n" - " },\n" - " foo, bar)\n" + " }, foo, bar)\n" " .foo();\n" "}", Style); @@ -22551,32 +22550,12 @@ " })));\n" "}", Style); - Style.AlignAfterOpenBracket = FormatStyle::BAS_AlwaysBreak; - verifyFormat("void foo() {\n" - " aFunction(\n" - " 1, b(c(\n" - " [](d) -> Foo {\n" - " auto f = e(d);\n" - " return f;\n" - " },\n" - " foo, Bar{},\n" - " [] {\n" - " auto g = h();\n" - " return g;\n" - " },\n" - " baz)));\n" - "}", - Style); verifyFormat("void foo() {\n" " aFunction(1, b(c(foo, Bar{}, baz, [](d) -> Foo {\n" - " auto f = e(\n" - " foo,\n" - " [&] {\n" + " auto f = e(foo, [&] {\n" " auto g = h();\n" " return g;\n" - " },\n" - " qux,\n" - " [&] -> Bar {\n" + " }, qux, [&] -> Bar {\n" " auto i = j();\n" " return i;\n" " });\n" @@ -22584,28 +22563,77 @@ " })));\n" "}", Style); - verifyFormat("Namespace::Foo::Foo(\n" - " LongClassName bar, AnotherLongClassName baz)\n" + verifyFormat("Namespace::Foo::Foo(LongClassName bar,\n" + " AnotherLongClassName baz)\n" " : baz{baz}, func{[&] {\n" " auto qux = bar;\n" " return aFunkyFunctionCall(qux);\n" "}} {}", Style); + Style.AlignAfterOpenBracket = FormatStyle::BAS_AlwaysBreak; + // FIXME: The following test should pass, but fails at the time of writing. +#if 0 + // As long as all the non-lambda arguments fit on a single line, AlwaysBreak + // doesn't force an initial line break, even if lambdas span multiple lines. + verifyFormat("void foo() {\n" + " aFunction(\n" + " [](d) -> Foo {\n" + " auto f = e(d);\n" + " return f;\n" + " }, foo, Bar{}, [] {\n" + " auto g = h();\n" + " return g;\n" + " }, baz);\n" + "}", + Style); +#endif + // A long non-lambda argument forces arguments to span multiple lines and thus + // forces an initial line break when using AlwaysBreak. + verifyFormat("void foo() {\n" + " aFunction(\n" + " 1,\n" + " [](d) -> Foo {\n" + " auto f = e(d);\n" + " return f;\n" + " }, foo, Bar{},\n" + " [] {\n" + " auto g = h();\n" + " return g;\n" + " }, bazzzzz,\n" + " quuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuux);\n" + "}", + Style); + Style.BinPackArguments = false; + verifyFormat("void foo() {\n" + " aFunction(\n" + " 1,\n" + " [](d) -> Foo {\n" + " auto f = e(d);\n" + " return f;\n" + " },\n" + " foo,\n" + " Bar{},\n" + " [] {\n" + " auto g = h();\n" + " return g;\n" + " },\n" + " bazzzzz,\n" + " quuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuux);\n" + "}", + Style); + Style.BinPackArguments = true; Style.BreakBeforeBraces = FormatStyle::BS_Custom; Style.BraceWrapping.BeforeLambdaBody = true; verifyFormat("void foo() {\n" " aFunction(\n" - " 1, b(c(foo, Bar{}, baz,\n" - " [](d) -> Foo\n" + " 1, b(c(foo, Bar{}, baz, [](d) -> Foo\n" " {\n" " auto f = e(\n" " [&]\n" " {\n" " auto g = h();\n" " return g;\n" - " },\n" - " qux,\n" - " [&] -> Bar\n" + " }, qux, [&] -> Bar\n" " {\n" " auto i = j();\n" " return i;\n"