Skip to content

Commit a0c75a9

Browse files
authored
[clang-format] Align within the level with Cpp11BracedListStyle disabled (#159140)
When the style is `{AlignConsecutiveDeclarations: true, Cpp11BracedListStyle: false}`, the program would sometimes align the lambda body with the outside. Like this. ```C++ const volatile auto result{ []() { const auto something = 1; return 2; } }; ``` This patch stops it. Now the output looks like this. ```C++ const volatile auto result{ []() { const auto something = 1; return 2; } }; ``` Fixes #53699. The problem was with how the `AlignTokenSequence` function tracked levels. The stack was pushed once when a token was more nested than the previous one. It was popped once when a token was less nested than the previous one. With the option `Cpp11BracedListStyle` disabled, the `[` token was deeper than the previous token by 2 levels. Both its indentation level and nesting level were more than that of the previous one. But the stack was only pushed once. The following tokens popped the 2 levels separately. The function treated the body of the lambda expression as on the same level as the outside. The problem is fixed this way. The function `AlignTokens` which calls the function `AlignTokenSequence` already uses a simpler and more robust way to track levels. It remembers the outermost level it should align. It compares the token's level with the outermost level. It does not need a stack. So it is immune to the problem. This patch makes the function `AlignTokenSequence` take as a parameter the indices of the tokens matched by the function `AlignTokens`. This way it does not have to contain the logic again. Now the function `AlignTokenSequence` only aligns tokens already matched by the function `AlignTokens`. It is easy to see that the assertion on line 453 holds. The workaround on line 354 is not needed any more. The test on line 20310 added at the same time as the assertion ensures that the assertion hold. The stack in the function `AlignTokenSequence` is kept for now. It is still used to figure out when things inside a level should move along with the outer level. Since the stack has the problem, the developer plans to move the logic into token annotator. It already uses a stack.
1 parent df420ee commit a0c75a9

File tree

3 files changed

+34
-27
lines changed

3 files changed

+34
-27
lines changed

clang/lib/Format/TokenAnnotator.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6496,13 +6496,14 @@ void TokenAnnotator::printDebugInfo(const AnnotatedLine &Line) const {
64966496
<< "):\n";
64976497
const FormatToken *Tok = Line.First;
64986498
while (Tok) {
6499-
llvm::errs() << " M=" << Tok->MustBreakBefore
6499+
llvm::errs() << " I=" << Tok->IndentLevel << " M=" << Tok->MustBreakBefore
65006500
<< " C=" << Tok->CanBreakBefore
65016501
<< " T=" << getTokenTypeName(Tok->getType())
65026502
<< " S=" << Tok->SpacesRequiredBefore
65036503
<< " F=" << Tok->Finalized << " B=" << Tok->BlockParameterCount
65046504
<< " BK=" << Tok->getBlockKind() << " P=" << Tok->SplitPenalty
6505-
<< " Name=" << Tok->Tok.getName() << " L=" << Tok->TotalLength
6505+
<< " Name=" << Tok->Tok.getName() << " N=" << Tok->NestingLevel
6506+
<< " L=" << Tok->TotalLength
65066507
<< " PPK=" << Tok->getPackingKind() << " FakeLParens=";
65076508
for (prec::Level LParen : Tok->FakeLParens)
65086509
llvm::errs() << LParen << "/";

clang/lib/Format/WhitespaceManager.cpp

Lines changed: 26 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -279,20 +279,19 @@ void WhitespaceManager::calculateLineBreakInformation() {
279279
}
280280

281281
// Align a single sequence of tokens, see AlignTokens below.
282-
// Column - The token for which Matches returns true is moved to this column.
282+
// Column - The tokens indexed in Matches are moved to this column.
283283
// RightJustify - Whether it is the token's right end or left end that gets
284284
// moved to that column.
285-
template <typename F>
286285
static void
287286
AlignTokenSequence(const FormatStyle &Style, unsigned Start, unsigned End,
288-
unsigned Column, bool RightJustify, F &&Matches,
287+
unsigned Column, bool RightJustify,
288+
ArrayRef<unsigned> Matches,
289289
SmallVector<WhitespaceManager::Change, 16> &Changes) {
290-
bool FoundMatchOnLine = false;
291290
int Shift = 0;
292291

293292
// ScopeStack keeps track of the current scope depth. It contains indices of
294293
// the first token on each scope.
295-
// We only run the "Matches" function on tokens from the outer-most scope.
294+
// The "Matches" indices should only have tokens from the outer-most scope.
296295
// However, we do need to pay special attention to one class of tokens
297296
// that are not in the outer-most scope, and that is function parameters
298297
// which are split across multiple lines, as illustrated by this example:
@@ -314,6 +313,9 @@ AlignTokenSequence(const FormatStyle &Style, unsigned Start, unsigned End,
314313

315314
for (unsigned i = Start; i != End; ++i) {
316315
auto &CurrentChange = Changes[i];
316+
if (!Matches.empty() && Matches[0] < i)
317+
Matches.consume_front();
318+
assert(Matches.empty() || Matches[0] >= i);
317319
if (!ScopeStack.empty() &&
318320
CurrentChange.indentAndNestingLevel() <
319321
Changes[ScopeStack.back()].indentAndNestingLevel()) {
@@ -338,26 +340,16 @@ AlignTokenSequence(const FormatStyle &Style, unsigned Start, unsigned End,
338340
Changes[i - 1].Tok->is(tok::string_literal);
339341
bool SkipMatchCheck = InsideNestedScope || ContinuedStringLiteral;
340342

341-
if (CurrentChange.NewlinesBefore > 0 && !SkipMatchCheck) {
343+
if (CurrentChange.NewlinesBefore > 0 && !SkipMatchCheck)
342344
Shift = 0;
343-
FoundMatchOnLine = false;
344-
}
345345

346346
// If this is the first matching token to be aligned, remember by how many
347347
// spaces it has to be shifted, so the rest of the changes on the line are
348348
// shifted by the same amount
349-
if (!FoundMatchOnLine && !SkipMatchCheck && Matches(CurrentChange)) {
350-
FoundMatchOnLine = true;
349+
if (!Matches.empty() && Matches[0] == i) {
351350
Shift = Column - (RightJustify ? CurrentChange.TokenLength : 0) -
352351
CurrentChange.StartOfTokenColumn;
353352
CurrentChange.Spaces += Shift;
354-
// FIXME: This is a workaround that should be removed when we fix
355-
// http://llvm.org/PR53699. An assertion later below verifies this.
356-
if (CurrentChange.NewlinesBefore == 0) {
357-
CurrentChange.Spaces =
358-
std::max(CurrentChange.Spaces,
359-
static_cast<int>(CurrentChange.Tok->SpacesRequiredBefore));
360-
}
361353
}
362354

363355
if (Shift == 0)
@@ -532,12 +524,14 @@ static unsigned AlignTokens(const FormatStyle &Style, F &&Matches,
532524
bool RightJustify = false) {
533525
// We arrange each line in 3 parts. The operator to be aligned (the anchor),
534526
// and text to its left and right. In the aligned text the width of each part
535-
// will be the maximum of that over the block that has been aligned. Maximum
536-
// widths of each part so far. When RightJustify is true and ACS.PadOperators
537-
// is false, the part from start of line to the right end of the anchor.
538-
// Otherwise, only the part to the left of the anchor. Including the space
539-
// that exists on its left from the start. Not including the padding added on
540-
// the left to right-justify the anchor.
527+
// will be the maximum of that over the block that has been aligned.
528+
529+
// Maximum widths of each part so far.
530+
// When RightJustify is true and ACS.PadOperators is false, the part from
531+
// start of line to the right end of the anchor. Otherwise, only the part to
532+
// the left of the anchor. Including the space that exists on its left from
533+
// the start. Not including the padding added on the left to right-justify the
534+
// anchor.
541535
unsigned WidthLeft = 0;
542536
// The operator to be aligned when RightJustify is true and ACS.PadOperators
543537
// is false. 0 otherwise.
@@ -550,6 +544,9 @@ static unsigned AlignTokens(const FormatStyle &Style, F &&Matches,
550544
unsigned StartOfSequence = 0;
551545
unsigned EndOfSequence = 0;
552546

547+
// The positions of the tokens to be aligned.
548+
SmallVector<unsigned> MatchedIndices;
549+
553550
// Measure the scope level (i.e. depth of (), [], {}) of the first token, and
554551
// abort when we hit any token in a higher scope than the starting one.
555552
auto IndentAndNestingLevel = StartAt < Changes.size()
@@ -578,14 +575,15 @@ static unsigned AlignTokens(const FormatStyle &Style, F &&Matches,
578575
auto AlignCurrentSequence = [&] {
579576
if (StartOfSequence > 0 && StartOfSequence < EndOfSequence) {
580577
AlignTokenSequence(Style, StartOfSequence, EndOfSequence,
581-
WidthLeft + WidthAnchor, RightJustify, Matches,
578+
WidthLeft + WidthAnchor, RightJustify, MatchedIndices,
582579
Changes);
583580
}
584581
WidthLeft = 0;
585582
WidthAnchor = 0;
586583
WidthRight = 0;
587584
StartOfSequence = 0;
588585
EndOfSequence = 0;
586+
MatchedIndices.clear();
589587
};
590588

591589
unsigned i = StartAt;
@@ -637,8 +635,10 @@ static unsigned AlignTokens(const FormatStyle &Style, F &&Matches,
637635

638636
// If there is more than one matching token per line, or if the number of
639637
// preceding commas, do not match anymore, end the sequence.
640-
if (FoundMatchOnLine || CommasBeforeMatch != CommasBeforeLastMatch)
638+
if (FoundMatchOnLine || CommasBeforeMatch != CommasBeforeLastMatch) {
639+
MatchedIndices.push_back(i);
641640
AlignCurrentSequence();
641+
}
642642

643643
CommasBeforeLastMatch = CommasBeforeMatch;
644644
FoundMatchOnLine = true;
@@ -684,6 +684,7 @@ static unsigned AlignTokens(const FormatStyle &Style, F &&Matches,
684684
WidthAnchor = NewAnchor;
685685
WidthRight = NewRight;
686686
}
687+
MatchedIndices.push_back(i);
687688
}
688689

689690
EndOfSequence = i;

clang/unittests/Format/FormatTest.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20312,6 +20312,11 @@ TEST_F(FormatTest, AlignConsecutiveDeclarations) {
2031220312
" return 2;\n"
2031320313
"} };",
2031420314
BracedAlign);
20315+
verifyFormat("const volatile auto result{ []() {\n"
20316+
" const auto something = 1;\n"
20317+
" return 2;\n"
20318+
"} };",
20319+
BracedAlign);
2031520320
verifyFormat("int foo{ []() {\n"
2031620321
" int bar{ 0 };\n"
2031720322
" return 0;\n"

0 commit comments

Comments
 (0)