Skip to content

Commit d06b923

Browse files
jp4a50owenca
authored andcommitted
[clang-format] Fix a bug that wraps before function arguments
Fixes a long-standing bug that erroneously placed function arguments on a new line despite all arguments being able to fit on the same line. The original diff that introduced the bug implemented behaviour that pushed the first argument to a function onto a new line under certain circumstances relating passing lambdas as arguments. This behaviour was implemented in TokenAnnotator::mustBreakBefore() which meant the code lacked the necessary context to figure out whether subsequent arguments might be able to all fit on one line. As such, I've moved the implementation to ContinuationIndenter and, instead of forcing a line break at the first argument in all cases, we now allow the OptimizingLineFormatter to consider placing the first argument on the same line as the function call but don't allow further line breaks in this case. The end result is that either the first argument must go on a new line (as before) or all arguments must be put on the current line. Closes llvm#44486. Differential Revision: https://reviews.llvm.org/D156259
1 parent f3958ce commit d06b923

File tree

4 files changed

+57
-28
lines changed

4 files changed

+57
-28
lines changed

Diff for: clang/lib/Format/ContinuationIndenter.cpp

+34-1
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,7 @@ LineState ContinuationIndenter::getInitialState(unsigned FirstIndent,
260260
/*NoLineBreak=*/false));
261261
State.NoContinuation = false;
262262
State.StartOfStringLiteral = 0;
263+
State.NoLineBreak = false;
263264
State.StartOfLineLevel = 0;
264265
State.LowestLevelOnLine = 0;
265266
State.IgnoreStackForComparison = false;
@@ -342,7 +343,7 @@ bool ContinuationIndenter::canBreak(const LineState &State) {
342343
return true;
343344
}
344345

345-
return !CurrentState.NoLineBreak;
346+
return !State.NoLineBreak && !CurrentState.NoLineBreak;
346347
}
347348

348349
bool ContinuationIndenter::mustBreak(const LineState &State) {
@@ -653,6 +654,38 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun,
653654
const FormatToken &Previous = *State.NextToken->Previous;
654655
auto &CurrentState = State.Stack.back();
655656

657+
bool DisallowLineBreaksOnThisLine = Style.isCpp() && [&Current] {
658+
// Deal with lambda arguments in C++. The aim here is to ensure that we
659+
// don't over-indent lambda function bodies when lambdas are passed as
660+
// arguments to function calls. We do this by ensuring that either all
661+
// arguments (including any lambdas) go on the same line as the function
662+
// call, or we break before the first argument.
663+
auto PrevNonComment = Current.getPreviousNonComment();
664+
if (!PrevNonComment || PrevNonComment->isNot(tok::l_paren))
665+
return false;
666+
if (Current.isOneOf(tok::comment, tok::l_paren, TT_LambdaLSquare))
667+
return false;
668+
auto BlockParameterCount = PrevNonComment->BlockParameterCount;
669+
if (BlockParameterCount == 0)
670+
return false;
671+
672+
// Multiple lambdas in the same function call.
673+
if (BlockParameterCount > 1)
674+
return true;
675+
676+
// A lambda followed by another arg.
677+
if (!PrevNonComment->Role)
678+
return false;
679+
auto Comma = PrevNonComment->Role->lastComma();
680+
if (!Comma)
681+
return false;
682+
auto Next = Comma->getNextNonComment();
683+
return Next && !Next->isOneOf(TT_LambdaLSquare, tok::l_brace, tok::caret);
684+
}();
685+
686+
if (DisallowLineBreaksOnThisLine)
687+
State.NoLineBreak = true;
688+
656689
if (Current.is(tok::equal) &&
657690
(State.Line->First->is(tok::kw_for) || Current.NestingLevel == 0) &&
658691
CurrentState.VariablePos == 0) {

Diff for: clang/lib/Format/ContinuationIndenter.h

+3
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,9 @@ struct LineState {
433433
/// literal sequence, 0 otherwise.
434434
unsigned StartOfStringLiteral;
435435

436+
/// Disallow line breaks for this line.
437+
bool NoLineBreak;
438+
436439
/// A stack keeping track of properties applying to parenthesis
437440
/// levels.
438441
SmallVector<ParenState> Stack;

Diff for: clang/lib/Format/TokenAnnotator.cpp

-24
Original file line numberDiff line numberDiff line change
@@ -5193,30 +5193,6 @@ bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line,
51935193
return true;
51945194
}
51955195

5196-
// Deal with lambda arguments in C++ - we want consistent line breaks whether
5197-
// they happen to be at arg0, arg1 or argN. The selection is a bit nuanced
5198-
// as aggressive line breaks are placed when the lambda is not the last arg.
5199-
if ((Style.Language == FormatStyle::LK_Cpp ||
5200-
Style.Language == FormatStyle::LK_ObjC) &&
5201-
Left.is(tok::l_paren) && Left.BlockParameterCount > 0 &&
5202-
!Right.isOneOf(tok::l_paren, TT_LambdaLSquare)) {
5203-
// Multiple lambdas in the same function call force line breaks.
5204-
if (Left.BlockParameterCount > 1)
5205-
return true;
5206-
5207-
// A lambda followed by another arg forces a line break.
5208-
if (!Left.Role)
5209-
return false;
5210-
auto Comma = Left.Role->lastComma();
5211-
if (!Comma)
5212-
return false;
5213-
auto Next = Comma->getNextNonComment();
5214-
if (!Next)
5215-
return false;
5216-
if (!Next->isOneOf(TT_LambdaLSquare, tok::l_brace, tok::caret))
5217-
return true;
5218-
}
5219-
52205196
return false;
52215197
}
52225198

Diff for: clang/unittests/Format/FormatTest.cpp

+20-3
Original file line numberDiff line numberDiff line change
@@ -22224,8 +22224,25 @@ TEST_F(FormatTest, FormatsLambdas) {
2222422224
" }\n"
2222522225
"};");
2222622226

22227-
// Multiple lambdas in the same parentheses change indentation rules. These
22228-
// lambdas are forced to start on new lines.
22227+
// Lambdas that fit on a single line within an argument list are not forced
22228+
// onto new lines.
22229+
verifyFormat("SomeFunction([] {});");
22230+
verifyFormat("SomeFunction(0, [] {});");
22231+
verifyFormat("SomeFunction([] {}, 0);");
22232+
verifyFormat("SomeFunction(0, [] {}, 0);");
22233+
verifyFormat("SomeFunction([] { return 0; }, 0);");
22234+
verifyFormat("SomeFunction(a, [] { return 0; }, b);");
22235+
verifyFormat("SomeFunction([] { return 0; }, [] { return 0; });");
22236+
verifyFormat("SomeFunction([] { return 0; }, [] { return 0; }, b);");
22237+
verifyFormat("auto loooooooooooooooooooooooooooong =\n"
22238+
" SomeFunction([] { return 0; }, [] { return 0; }, b);");
22239+
// Exceeded column limit. We need to break.
22240+
verifyFormat("auto loooooooooooooooooooooooooooongName = SomeFunction(\n"
22241+
" [] { return anotherLooooooooooonoooooooongName; }, [] { "
22242+
"return 0; }, b);");
22243+
22244+
// Multiple multi-line lambdas in the same parentheses change indentation
22245+
// rules. These lambdas are always forced to start on new lines.
2222922246
verifyFormat("SomeFunction(\n"
2223022247
" []() {\n"
2223122248
" //\n"
@@ -22234,7 +22251,7 @@ TEST_F(FormatTest, FormatsLambdas) {
2223422251
" //\n"
2223522252
" });");
2223622253

22237-
// A lambda passed as arg0 is always pushed to the next line.
22254+
// A multi-line lambda passed as arg0 is always pushed to the next line.
2223822255
verifyFormat("SomeFunction(\n"
2223922256
" [this] {\n"
2224022257
" //\n"

0 commit comments

Comments
 (0)