1750 lines · cpp
1//===--- WhitespaceManager.cpp - Format C++ code --------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8///9/// \file10/// This file implements WhitespaceManager class.11///12//===----------------------------------------------------------------------===//13 14#include "WhitespaceManager.h"15#include "llvm/ADT/STLExtras.h"16#include "llvm/ADT/SmallVector.h"17#include <algorithm>18 19namespace clang {20namespace format {21 22bool WhitespaceManager::Change::IsBeforeInFile::operator()(23 const Change &C1, const Change &C2) const {24 return SourceMgr.isBeforeInTranslationUnit(25 C1.OriginalWhitespaceRange.getBegin(),26 C2.OriginalWhitespaceRange.getBegin()) ||27 (C1.OriginalWhitespaceRange.getBegin() ==28 C2.OriginalWhitespaceRange.getBegin() &&29 SourceMgr.isBeforeInTranslationUnit(30 C1.OriginalWhitespaceRange.getEnd(),31 C2.OriginalWhitespaceRange.getEnd()));32}33 34WhitespaceManager::Change::Change(const FormatToken &Tok,35 bool CreateReplacement,36 SourceRange OriginalWhitespaceRange,37 int Spaces, unsigned StartOfTokenColumn,38 unsigned NewlinesBefore,39 StringRef PreviousLinePostfix,40 StringRef CurrentLinePrefix, bool IsAligned,41 bool ContinuesPPDirective, bool IsInsideToken)42 : Tok(&Tok), CreateReplacement(CreateReplacement),43 OriginalWhitespaceRange(OriginalWhitespaceRange),44 StartOfTokenColumn(StartOfTokenColumn), NewlinesBefore(NewlinesBefore),45 PreviousLinePostfix(PreviousLinePostfix),46 CurrentLinePrefix(CurrentLinePrefix), IsAligned(IsAligned),47 ContinuesPPDirective(ContinuesPPDirective), Spaces(Spaces),48 IsInsideToken(IsInsideToken), IsTrailingComment(false), TokenLength(0),49 PreviousEndOfTokenColumn(0), EscapedNewlineColumn(0),50 StartOfBlockComment(nullptr), IndentationOffset(0), ConditionalsLevel(0) {51}52 53void WhitespaceManager::replaceWhitespace(FormatToken &Tok, unsigned Newlines,54 unsigned Spaces,55 unsigned StartOfTokenColumn,56 bool IsAligned, bool InPPDirective) {57 if (Tok.Finalized || (Tok.MacroCtx && Tok.MacroCtx->Role == MR_ExpandedArg))58 return;59 Tok.setDecision((Newlines > 0) ? FD_Break : FD_Continue);60 Changes.push_back(Change(Tok, /*CreateReplacement=*/true, Tok.WhitespaceRange,61 Spaces, StartOfTokenColumn, Newlines, "", "",62 IsAligned, InPPDirective && !Tok.IsFirst,63 /*IsInsideToken=*/false));64}65 66void WhitespaceManager::addUntouchableToken(const FormatToken &Tok,67 bool InPPDirective) {68 if (Tok.Finalized || (Tok.MacroCtx && Tok.MacroCtx->Role == MR_ExpandedArg))69 return;70 Changes.push_back(Change(Tok, /*CreateReplacement=*/false,71 Tok.WhitespaceRange, /*Spaces=*/0,72 Tok.OriginalColumn, Tok.NewlinesBefore, "", "",73 /*IsAligned=*/false, InPPDirective && !Tok.IsFirst,74 /*IsInsideToken=*/false));75}76 77llvm::Error78WhitespaceManager::addReplacement(const tooling::Replacement &Replacement) {79 return Replaces.add(Replacement);80}81 82bool WhitespaceManager::inputUsesCRLF(StringRef Text, bool DefaultToCRLF) {83 size_t LF = Text.count('\n');84 size_t CR = Text.count('\r') * 2;85 return LF == CR ? DefaultToCRLF : CR > LF;86}87 88void WhitespaceManager::replaceWhitespaceInToken(89 const FormatToken &Tok, unsigned Offset, unsigned ReplaceChars,90 StringRef PreviousPostfix, StringRef CurrentPrefix, bool InPPDirective,91 unsigned Newlines, int Spaces) {92 if (Tok.Finalized || (Tok.MacroCtx && Tok.MacroCtx->Role == MR_ExpandedArg))93 return;94 SourceLocation Start = Tok.getStartOfNonWhitespace().getLocWithOffset(Offset);95 Changes.push_back(96 Change(Tok, /*CreateReplacement=*/true,97 SourceRange(Start, Start.getLocWithOffset(ReplaceChars)), Spaces,98 std::max(0, Spaces), Newlines, PreviousPostfix, CurrentPrefix,99 /*IsAligned=*/true, InPPDirective && !Tok.IsFirst,100 /*IsInsideToken=*/true));101}102 103const tooling::Replacements &WhitespaceManager::generateReplacements() {104 if (Changes.empty())105 return Replaces;106 107 llvm::sort(Changes, Change::IsBeforeInFile(SourceMgr));108 calculateLineBreakInformation();109 alignConsecutiveMacros();110 alignConsecutiveShortCaseStatements(/*IsExpr=*/true);111 alignConsecutiveShortCaseStatements(/*IsExpr=*/false);112 alignConsecutiveDeclarations();113 alignConsecutiveBitFields();114 alignConsecutiveAssignments();115 if (Style.isTableGen()) {116 alignConsecutiveTableGenBreakingDAGArgColons();117 alignConsecutiveTableGenCondOperatorColons();118 alignConsecutiveTableGenDefinitions();119 }120 alignChainedConditionals();121 alignTrailingComments();122 alignEscapedNewlines();123 alignArrayInitializers();124 generateChanges();125 126 return Replaces;127}128 129void WhitespaceManager::calculateLineBreakInformation() {130 Changes[0].PreviousEndOfTokenColumn = 0;131 Change *LastOutsideTokenChange = &Changes[0];132 for (unsigned I = 1, e = Changes.size(); I != e; ++I) {133 auto &C = Changes[I];134 auto &P = Changes[I - 1];135 auto &PrevTokLength = P.TokenLength;136 SourceLocation OriginalWhitespaceStart =137 C.OriginalWhitespaceRange.getBegin();138 SourceLocation PreviousOriginalWhitespaceEnd =139 P.OriginalWhitespaceRange.getEnd();140 unsigned OriginalWhitespaceStartOffset =141 SourceMgr.getFileOffset(OriginalWhitespaceStart);142 unsigned PreviousOriginalWhitespaceEndOffset =143 SourceMgr.getFileOffset(PreviousOriginalWhitespaceEnd);144 assert(PreviousOriginalWhitespaceEndOffset <=145 OriginalWhitespaceStartOffset);146 const char *const PreviousOriginalWhitespaceEndData =147 SourceMgr.getCharacterData(PreviousOriginalWhitespaceEnd);148 StringRef Text(PreviousOriginalWhitespaceEndData,149 SourceMgr.getCharacterData(OriginalWhitespaceStart) -150 PreviousOriginalWhitespaceEndData);151 // Usually consecutive changes would occur in consecutive tokens. This is152 // not the case however when analyzing some preprocessor runs of the153 // annotated lines. For example, in this code:154 //155 // #if A // line 1156 // int i = 1;157 // #else B // line 2158 // int i = 2;159 // #endif // line 3160 //161 // one of the runs will produce the sequence of lines marked with line 1, 2162 // and 3. So the two consecutive whitespace changes just before '// line 2'163 // and before '#endif // line 3' span multiple lines and tokens:164 //165 // #else B{change X}[// line 2166 // int i = 2;167 // ]{change Y}#endif // line 3168 //169 // For this reason, if the text between consecutive changes spans multiple170 // newlines, the token length must be adjusted to the end of the original171 // line of the token.172 auto NewlinePos = Text.find_first_of('\n');173 if (NewlinePos == StringRef::npos) {174 PrevTokLength = OriginalWhitespaceStartOffset -175 PreviousOriginalWhitespaceEndOffset +176 C.PreviousLinePostfix.size() + P.CurrentLinePrefix.size();177 if (!P.IsInsideToken)178 PrevTokLength = std::min(PrevTokLength, P.Tok->ColumnWidth);179 } else {180 PrevTokLength = NewlinePos + P.CurrentLinePrefix.size();181 }182 183 // If there are multiple changes in this token, sum up all the changes until184 // the end of the line.185 if (P.IsInsideToken && P.NewlinesBefore == 0)186 LastOutsideTokenChange->TokenLength += PrevTokLength + P.Spaces;187 else188 LastOutsideTokenChange = &P;189 190 C.PreviousEndOfTokenColumn = P.StartOfTokenColumn + PrevTokLength;191 192 P.IsTrailingComment =193 (C.NewlinesBefore > 0 || C.Tok->is(tok::eof) ||194 (C.IsInsideToken && C.Tok->is(tok::comment))) &&195 P.Tok->is(tok::comment) &&196 // FIXME: This is a dirty hack. The problem is that197 // BreakableLineCommentSection does comment reflow changes and here is198 // the aligning of trailing comments. Consider the case where we reflow199 // the second line up in this example:200 //201 // // line 1202 // // line 2203 //204 // That amounts to 2 changes by BreakableLineCommentSection:205 // - the first, delimited by (), for the whitespace between the tokens,206 // - and second, delimited by [], for the whitespace at the beginning207 // of the second token:208 //209 // // line 1(210 // )[// ]line 2211 //212 // So in the end we have two changes like this:213 //214 // // line1()[ ]line 2215 //216 // Note that the OriginalWhitespaceStart of the second change is the217 // same as the PreviousOriginalWhitespaceEnd of the first change.218 // In this case, the below check ensures that the second change doesn't219 // get treated as a trailing comment change here, since this might220 // trigger additional whitespace to be wrongly inserted before "line 2"221 // by the comment aligner here.222 //223 // For a proper solution we need a mechanism to say to WhitespaceManager224 // that a particular change breaks the current sequence of trailing225 // comments.226 OriginalWhitespaceStart != PreviousOriginalWhitespaceEnd;227 }228 // FIXME: The last token is currently not always an eof token; in those229 // cases, setting TokenLength of the last token to 0 is wrong.230 Changes.back().TokenLength = 0;231 Changes.back().IsTrailingComment = Changes.back().Tok->is(tok::comment);232 233 const WhitespaceManager::Change *LastBlockComment = nullptr;234 for (auto &Change : Changes) {235 // Reset the IsTrailingComment flag for changes inside of trailing comments236 // so they don't get realigned later. Comment line breaks however still need237 // to be aligned.238 if (Change.IsInsideToken && Change.NewlinesBefore == 0)239 Change.IsTrailingComment = false;240 Change.StartOfBlockComment = nullptr;241 Change.IndentationOffset = 0;242 if (Change.Tok->is(tok::comment)) {243 if (Change.Tok->is(TT_LineComment) || !Change.IsInsideToken) {244 LastBlockComment = &Change;245 } else if ((Change.StartOfBlockComment = LastBlockComment)) {246 Change.IndentationOffset =247 Change.StartOfTokenColumn -248 Change.StartOfBlockComment->StartOfTokenColumn;249 }250 } else {251 LastBlockComment = nullptr;252 }253 }254 255 // Compute conditional nesting level256 // Level is increased for each conditional, unless this conditional continues257 // a chain of conditional, i.e. starts immediately after the colon of another258 // conditional.259 SmallVector<bool, 16> ScopeStack;260 int ConditionalsLevel = 0;261 for (auto &Change : Changes) {262 for (unsigned i = 0, e = Change.Tok->FakeLParens.size(); i != e; ++i) {263 bool isNestedConditional =264 Change.Tok->FakeLParens[e - 1 - i] == prec::Conditional &&265 !(i == 0 && Change.Tok->Previous &&266 Change.Tok->Previous->is(TT_ConditionalExpr) &&267 Change.Tok->Previous->is(tok::colon));268 if (isNestedConditional)269 ++ConditionalsLevel;270 ScopeStack.push_back(isNestedConditional);271 }272 273 Change.ConditionalsLevel = ConditionalsLevel;274 275 for (unsigned i = Change.Tok->FakeRParens; i > 0 && ScopeStack.size(); --i)276 if (ScopeStack.pop_back_val())277 --ConditionalsLevel;278 }279}280 281// Align a single sequence of tokens, see AlignTokens below.282// Column - The tokens indexed in Matches are moved to this column.283// RightJustify - Whether it is the token's right end or left end that gets284// moved to that column.285static void286AlignTokenSequence(const FormatStyle &Style, unsigned Start, unsigned End,287 unsigned Column, bool RightJustify,288 ArrayRef<unsigned> Matches,289 SmallVector<WhitespaceManager::Change, 16> &Changes) {290 int Shift = 0;291 // Set when the shift is applied anywhere in the line. Cleared when the line292 // ends.293 bool LineShifted = false;294 295 // ScopeStack keeps track of the current scope depth. It contains the levels296 // of at most 2 scopes. The first one is the one that the matched token is297 // in. The second one is the one that should not be moved by this procedure.298 // The "Matches" indices should only have tokens from the outer-most scope.299 // However, we do need to pay special attention to one class of tokens300 // that are not in the outer-most scope, and that is the continuations of an301 // unwrapped line whose positions are derived from a token to the right of the302 // aligned token, as illustrated by this example:303 // double a(int x);304 // int b(int y,305 // double z);306 // In the above example, we need to take special care to ensure that307 // 'double z' is indented along with its owning function 'b', because its308 // position is derived from the '(' token to the right of the 'b' token.309 // The same holds for calling a function:310 // double a = foo(x);311 // int b = bar(foo(y),312 // foor(z));313 // Similar for broken string literals:314 // double x = 3.14;315 // auto s = "Hello"316 // "World";317 // Special handling is required for 'nested' ternary operators.318 SmallVector<std::tuple<unsigned, unsigned, unsigned>, 2> ScopeStack;319 320 for (unsigned i = Start; i != End; ++i) {321 auto &CurrentChange = Changes[i];322 if (!Matches.empty() && Matches[0] < i)323 Matches.consume_front();324 assert(Matches.empty() || Matches[0] >= i);325 while (!ScopeStack.empty() &&326 CurrentChange.indentAndNestingLevel() < ScopeStack.back()) {327 ScopeStack.pop_back();328 }329 330 // Keep track of the level that should not move with the aligned token.331 if (ScopeStack.size() == 1u && CurrentChange.NewlinesBefore != 0u &&332 CurrentChange.indentAndNestingLevel() > ScopeStack[0] &&333 !CurrentChange.IsAligned) {334 ScopeStack.push_back(CurrentChange.indentAndNestingLevel());335 }336 337 bool InsideNestedScope =338 !ScopeStack.empty() &&339 CurrentChange.indentAndNestingLevel() > ScopeStack[0];340 bool ContinuedStringLiteral = i > Start &&341 CurrentChange.Tok->is(tok::string_literal) &&342 Changes[i - 1].Tok->is(tok::string_literal);343 bool SkipMatchCheck = InsideNestedScope || ContinuedStringLiteral;344 345 if (CurrentChange.NewlinesBefore > 0) {346 LineShifted = false;347 if (!SkipMatchCheck)348 Shift = 0;349 }350 351 // If this is the first matching token to be aligned, remember by how many352 // spaces it has to be shifted, so the rest of the changes on the line are353 // shifted by the same amount354 if (!Matches.empty() && Matches[0] == i) {355 Shift = Column - (RightJustify ? CurrentChange.TokenLength : 0) -356 CurrentChange.StartOfTokenColumn;357 ScopeStack = {CurrentChange.indentAndNestingLevel()};358 }359 360 if (Shift == 0)361 continue;362 363 // This is for lines that are split across multiple lines, as mentioned in364 // the ScopeStack comment. The stack size being 1 means that the token is365 // not in a scope that should not move.366 if ((!Matches.empty() && Matches[0] == i) ||367 (ScopeStack.size() == 1u && CurrentChange.NewlinesBefore > 0 &&368 (ContinuedStringLiteral || InsideNestedScope))) {369 LineShifted = true;370 CurrentChange.Spaces += Shift;371 }372 373 // We should not remove required spaces unless we break the line before.374 assert(Shift > 0 || Changes[i].NewlinesBefore > 0 ||375 CurrentChange.Spaces >=376 static_cast<int>(Changes[i].Tok->SpacesRequiredBefore) ||377 CurrentChange.Tok->is(tok::eof));378 379 if (LineShifted) {380 CurrentChange.StartOfTokenColumn += Shift;381 if (i + 1 != Changes.size())382 Changes[i + 1].PreviousEndOfTokenColumn += Shift;383 }384 385 // If PointerAlignment is PAS_Right, keep *s or &s next to the token,386 // except if the token is equal, then a space is needed.387 if ((Style.PointerAlignment == FormatStyle::PAS_Right ||388 Style.ReferenceAlignment == FormatStyle::RAS_Right) &&389 CurrentChange.Spaces != 0 &&390 CurrentChange.Tok->isNoneOf(tok::equal, tok::r_paren,391 TT_TemplateCloser)) {392 const bool ReferenceNotRightAligned =393 Style.ReferenceAlignment != FormatStyle::RAS_Right &&394 Style.ReferenceAlignment != FormatStyle::RAS_Pointer;395 for (int Previous = i - 1;396 Previous >= 0 && Changes[Previous].Tok->is(TT_PointerOrReference);397 --Previous) {398 assert(Changes[Previous].Tok->isPointerOrReference());399 if (Changes[Previous].Tok->isNot(tok::star)) {400 if (ReferenceNotRightAligned)401 continue;402 } else if (Style.PointerAlignment != FormatStyle::PAS_Right) {403 continue;404 }405 Changes[Previous + 1].Spaces -= Shift;406 Changes[Previous].Spaces += Shift;407 Changes[Previous].StartOfTokenColumn += Shift;408 }409 }410 }411}412 413// Walk through a subset of the changes, starting at StartAt, and find414// sequences of matching tokens to align. To do so, keep track of the lines and415// whether or not a matching token was found on a line. If a matching token is416// found, extend the current sequence. If the current line cannot be part of a417// sequence, e.g. because there is an empty line before it or it contains only418// non-matching tokens, finalize the previous sequence.419// The value returned is the token on which we stopped, either because we420// exhausted all items inside Changes, or because we hit a scope level higher421// than our initial scope.422// This function is recursive. Each invocation processes only the scope level423// equal to the initial level, which is the level of Changes[StartAt].424// If we encounter a scope level greater than the initial level, then we call425// ourselves recursively, thereby avoiding the pollution of the current state426// with the alignment requirements of the nested sub-level. This recursive427// behavior is necessary for aligning function prototypes that have one or more428// arguments.429// If this function encounters a scope level less than the initial level,430// it returns the current position.431// There is a non-obvious subtlety in the recursive behavior: Even though we432// defer processing of nested levels to recursive invocations of this433// function, when it comes time to align a sequence of tokens, we run the434// alignment on the entire sequence, including the nested levels.435// When doing so, most of the nested tokens are skipped, because their436// alignment was already handled by the recursive invocations of this function.437// However, the special exception is that we do NOT skip function parameters438// that are split across multiple lines. See the test case in FormatTest.cpp439// that mentions "split function parameter alignment" for an example of this.440// When the parameter RightJustify is true, the operator will be441// right-justified. It is used to align compound assignments like `+=` and `=`.442// When RightJustify and ACS.PadOperators are true, operators in each block to443// be aligned will be padded on the left to the same length before aligning.444//445// The simple check will not look at the indentaion and nesting level to recurse446// into the line for alignment. It will also not count the commas. This is e.g.447// for aligning macro definitions.448template <typename F, bool SimpleCheck = false>449static unsigned AlignTokens(const FormatStyle &Style, F &&Matches,450 SmallVector<WhitespaceManager::Change, 16> &Changes,451 unsigned StartAt,452 const FormatStyle::AlignConsecutiveStyle &ACS = {},453 bool RightJustify = false) {454 // We arrange each line in 3 parts. The operator to be aligned (the anchor),455 // and text to its left and right. In the aligned text the width of each part456 // will be the maximum of that over the block that has been aligned.457 458 // Maximum widths of each part so far.459 // When RightJustify is true and ACS.PadOperators is false, the part from460 // start of line to the right end of the anchor. Otherwise, only the part to461 // the left of the anchor. Including the space that exists on its left from462 // the start. Not including the padding added on the left to right-justify the463 // anchor.464 unsigned WidthLeft = 0;465 // The operator to be aligned when RightJustify is true and ACS.PadOperators466 // is false. 0 otherwise.467 unsigned WidthAnchor = 0;468 // Width to the right of the anchor. Plus width of the anchor when469 // RightJustify is false.470 unsigned WidthRight = 0;471 472 // Line number of the start and the end of the current token sequence.473 unsigned StartOfSequence = 0;474 unsigned EndOfSequence = 0;475 476 // The positions of the tokens to be aligned.477 SmallVector<unsigned> MatchedIndices;478 479 // Measure the scope level (i.e. depth of (), [], {}) of the first token, and480 // abort when we hit any token in a higher scope than the starting one.481 const auto IndentAndNestingLevel =482 StartAt < Changes.size() ? Changes[StartAt].indentAndNestingLevel()483 : std::tuple<unsigned, unsigned, unsigned>();484 485 // Keep track of the number of commas before the matching tokens, we will only486 // align a sequence of matching tokens if they are preceded by the same number487 // of commas.488 unsigned CommasBeforeLastMatch = 0;489 unsigned CommasBeforeMatch = 0;490 491 // Whether a matching token has been found on the current line.492 bool FoundMatchOnLine = false;493 494 // Whether the current line consists purely of comments.495 bool LineIsComment = true;496 497 // Aligns a sequence of matching tokens, on the MinColumn column.498 //499 // Sequences start from the first matching token to align, and end at the500 // first token of the first line that doesn't need to be aligned.501 //502 // We need to adjust the StartOfTokenColumn of each Change that is on a line503 // containing any matching token to be aligned and located after such token.504 auto AlignCurrentSequence = [&] {505 if (StartOfSequence > 0 && StartOfSequence < EndOfSequence) {506 AlignTokenSequence(Style, StartOfSequence, EndOfSequence,507 WidthLeft + WidthAnchor, RightJustify, MatchedIndices,508 Changes);509 }510 WidthLeft = 0;511 WidthAnchor = 0;512 WidthRight = 0;513 StartOfSequence = 0;514 EndOfSequence = 0;515 MatchedIndices.clear();516 };517 518 unsigned I = StartAt;519 const auto E = Changes.size();520 for (; I != E; ++I) {521 auto &CurrentChange = Changes[I];522 if (CurrentChange.indentAndNestingLevel() < IndentAndNestingLevel)523 break;524 525 if (CurrentChange.NewlinesBefore != 0) {526 CommasBeforeMatch = 0;527 EndOfSequence = I;528 529 // Whether to break the alignment sequence because of an empty line.530 bool EmptyLineBreak =531 (CurrentChange.NewlinesBefore > 1) && !ACS.AcrossEmptyLines;532 533 // Whether to break the alignment sequence because of a line without a534 // match.535 bool NoMatchBreak =536 !FoundMatchOnLine && !(LineIsComment && ACS.AcrossComments);537 538 if (EmptyLineBreak || NoMatchBreak)539 AlignCurrentSequence();540 541 // A new line starts, re-initialize line status tracking bools.542 // Keep the match state if a string literal is continued on this line.543 if (I == 0 || CurrentChange.Tok->isNot(tok::string_literal) ||544 Changes[I - 1].Tok->isNot(tok::string_literal)) {545 FoundMatchOnLine = false;546 }547 LineIsComment = true;548 }549 550 if (CurrentChange.Tok->isNot(tok::comment))551 LineIsComment = false;552 553 if (!SimpleCheck) {554 if (CurrentChange.Tok->is(tok::comma)) {555 ++CommasBeforeMatch;556 } else if (CurrentChange.indentAndNestingLevel() >557 IndentAndNestingLevel) {558 // Call AlignTokens recursively, skipping over this scope block.559 const auto StoppedAt =560 AlignTokens(Style, Matches, Changes, I, ACS, RightJustify);561 I = StoppedAt - 1;562 continue;563 }564 }565 566 if (!Matches(CurrentChange))567 continue;568 569 // If there is more than one matching token per line, or if the number of570 // preceding commas, do not match anymore, end the sequence.571 if (FoundMatchOnLine || CommasBeforeMatch != CommasBeforeLastMatch) {572 MatchedIndices.push_back(I);573 AlignCurrentSequence();574 }575 576 CommasBeforeLastMatch = CommasBeforeMatch;577 FoundMatchOnLine = true;578 579 if (StartOfSequence == 0)580 StartOfSequence = I;581 582 unsigned ChangeWidthLeft = CurrentChange.StartOfTokenColumn;583 unsigned ChangeWidthAnchor = 0;584 unsigned ChangeWidthRight = 0;585 unsigned CurrentChangeWidthRight = 0;586 if (RightJustify)587 if (ACS.PadOperators)588 ChangeWidthAnchor = CurrentChange.TokenLength;589 else590 ChangeWidthLeft += CurrentChange.TokenLength;591 else592 CurrentChangeWidthRight = CurrentChange.TokenLength;593 const FormatToken *MatchingParenToEncounter = nullptr;594 for (unsigned J = I + 1;595 J != E && (Changes[J].NewlinesBefore == 0 ||596 MatchingParenToEncounter || Changes[J].IsAligned);597 ++J) {598 const auto &Change = Changes[J];599 const auto *Tok = Change.Tok;600 601 if (Tok->MatchingParen) {602 if (Tok->isOneOf(tok::l_paren, tok::l_brace, tok::l_square,603 TT_TemplateOpener) &&604 !MatchingParenToEncounter) {605 // If the next token is on the next line, we probably don't need to606 // check the following lengths, because it most likely isn't aligned607 // with the rest.608 if (J + 1 != E && Changes[J + 1].NewlinesBefore == 0)609 MatchingParenToEncounter = Tok->MatchingParen;610 } else if (MatchingParenToEncounter == Tok->MatchingParen) {611 MatchingParenToEncounter = nullptr;612 }613 }614 615 if (Change.NewlinesBefore != 0) {616 ChangeWidthRight = std::max(ChangeWidthRight, CurrentChangeWidthRight);617 const auto ChangeWidthStart = ChangeWidthLeft + ChangeWidthAnchor;618 // If the position of the current token is columnwise before the begin619 // of the alignment, we drop out here, because the next line does not620 // have to be moved with the previous one(s) for the alignment. E.g.:621 // int i1 = 1; | <- ColumnLimit | int i1 = 1;622 // int j = 0; | Without the break -> | int j = 0;623 // int k = bar( | We still want to align the = | int k = bar(624 // argument1, | here, even if we can't move | argument1,625 // argument2); | the following lines. | argument2);626 if (static_cast<unsigned>(Change.Spaces) < ChangeWidthStart)627 break;628 CurrentChangeWidthRight = Change.Spaces - ChangeWidthStart;629 } else {630 CurrentChangeWidthRight += Change.Spaces;631 }632 633 // Changes are generally 1:1 with the tokens, but a change could also be634 // inside of a token, in which case it's counted more than once: once for635 // the whitespace surrounding the token (!IsInsideToken) and once for636 // each whitespace change within it (IsInsideToken).637 // Therefore, changes inside of a token should only count the space.638 if (!Change.IsInsideToken)639 CurrentChangeWidthRight += Change.TokenLength;640 }641 642 ChangeWidthRight = std::max(ChangeWidthRight, CurrentChangeWidthRight);643 644 // If we are restricted by the maximum column width, end the sequence.645 unsigned NewLeft = std::max(ChangeWidthLeft, WidthLeft);646 unsigned NewAnchor = std::max(ChangeWidthAnchor, WidthAnchor);647 unsigned NewRight = std::max(ChangeWidthRight, WidthRight);648 // `ColumnLimit == 0` means there is no column limit.649 if (Style.ColumnLimit != 0 &&650 Style.ColumnLimit < NewLeft + NewAnchor + NewRight) {651 AlignCurrentSequence();652 StartOfSequence = I;653 WidthLeft = ChangeWidthLeft;654 WidthAnchor = ChangeWidthAnchor;655 WidthRight = ChangeWidthRight;656 } else {657 WidthLeft = NewLeft;658 WidthAnchor = NewAnchor;659 WidthRight = NewRight;660 }661 MatchedIndices.push_back(I);662 }663 664 // Pass entire lines to the function so that it can update the state of all665 // tokens that move.666 for (EndOfSequence = I;667 EndOfSequence < E && Changes[EndOfSequence].NewlinesBefore == 0;668 ++EndOfSequence) {669 }670 AlignCurrentSequence();671 // The return value should still be where the level ends. The rest of the line672 // may contain stuff to be aligned within an outer level.673 return I;674}675 676// Aligns a sequence of matching tokens, on the MinColumn column.677//678// Sequences start from the first matching token to align, and end at the679// first token of the first line that doesn't need to be aligned.680//681// We need to adjust the StartOfTokenColumn of each Change that is on a line682// containing any matching token to be aligned and located after such token.683static void AlignMatchingTokenSequence(684 unsigned &StartOfSequence, unsigned &EndOfSequence, unsigned &MinColumn,685 std::function<bool(const WhitespaceManager::Change &C)> Matches,686 SmallVector<WhitespaceManager::Change, 16> &Changes) {687 if (StartOfSequence > 0 && StartOfSequence < EndOfSequence) {688 bool FoundMatchOnLine = false;689 int Shift = 0;690 691 for (unsigned I = StartOfSequence; I != EndOfSequence; ++I) {692 if (Changes[I].NewlinesBefore > 0) {693 Shift = 0;694 FoundMatchOnLine = false;695 }696 697 // If this is the first matching token to be aligned, remember by how many698 // spaces it has to be shifted, so the rest of the changes on the line are699 // shifted by the same amount.700 if (!FoundMatchOnLine && Matches(Changes[I])) {701 FoundMatchOnLine = true;702 Shift = MinColumn - Changes[I].StartOfTokenColumn;703 Changes[I].Spaces += Shift;704 }705 706 assert(Shift >= 0);707 Changes[I].StartOfTokenColumn += Shift;708 if (I + 1 != Changes.size())709 Changes[I + 1].PreviousEndOfTokenColumn += Shift;710 }711 }712 713 MinColumn = 0;714 StartOfSequence = 0;715 EndOfSequence = 0;716}717 718void WhitespaceManager::alignConsecutiveMacros() {719 if (!Style.AlignConsecutiveMacros.Enabled)720 return;721 722 auto AlignMacrosMatches = [](const Change &C) {723 const FormatToken *Current = C.Tok;724 assert(Current);725 726 if (Current->SpacesRequiredBefore == 0 || !Current->Previous)727 return false;728 729 Current = Current->Previous;730 731 // If token is a ")", skip over the parameter list, to the732 // token that precedes the "("733 if (Current->is(tok::r_paren)) {734 const auto *MatchingParen = Current->MatchingParen;735 // For a macro function, 0 spaces are required between the736 // identifier and the lparen that opens the parameter list.737 if (!MatchingParen || MatchingParen->SpacesRequiredBefore > 0 ||738 !MatchingParen->Previous) {739 return false;740 }741 Current = MatchingParen->Previous;742 } else if (Current->Next->SpacesRequiredBefore != 1) {743 // For a simple macro, 1 space is required between the744 // identifier and the first token of the defined value.745 return false;746 }747 748 return Current->endsSequence(tok::identifier, tok::pp_define);749 };750 751 AlignTokens<decltype(AlignMacrosMatches) &, /*SimpleCheck=*/true>(752 Style, AlignMacrosMatches, Changes, 0, Style.AlignConsecutiveMacros);753}754 755void WhitespaceManager::alignConsecutiveAssignments() {756 if (!Style.AlignConsecutiveAssignments.Enabled)757 return;758 759 AlignTokens(760 Style,761 [&](const Change &C) {762 // Do not align on equal signs that are first on a line.763 if (C.NewlinesBefore > 0)764 return false;765 766 // Do not align on equal signs that are last on a line.767 if (&C != &Changes.back() && (&C + 1)->NewlinesBefore > 0)768 return false;769 770 // Do not align operator= overloads.771 FormatToken *Previous = C.Tok->getPreviousNonComment();772 if (Previous && Previous->is(tok::kw_operator))773 return false;774 775 return Style.AlignConsecutiveAssignments.AlignCompound776 ? C.Tok->getPrecedence() == prec::Assignment777 : (C.Tok->is(tok::equal) ||778 // In Verilog the '<=' is not a compound assignment, thus779 // it is aligned even when the AlignCompound option is not780 // set.781 (Style.isVerilog() && C.Tok->is(tok::lessequal) &&782 C.Tok->getPrecedence() == prec::Assignment));783 },784 Changes, /*StartAt=*/0, Style.AlignConsecutiveAssignments,785 /*RightJustify=*/true);786}787 788void WhitespaceManager::alignConsecutiveBitFields() {789 alignConsecutiveColons(Style.AlignConsecutiveBitFields, TT_BitFieldColon);790}791 792void WhitespaceManager::alignConsecutiveColons(793 const FormatStyle::AlignConsecutiveStyle &AlignStyle, TokenType Type) {794 if (!AlignStyle.Enabled)795 return;796 797 AlignTokens(798 Style,799 [&](Change const &C) {800 // Do not align on ':' that is first on a line.801 if (C.NewlinesBefore > 0)802 return false;803 804 // Do not align on ':' that is last on a line.805 if (&C != &Changes.back() && (&C + 1)->NewlinesBefore > 0)806 return false;807 808 return C.Tok->is(Type);809 },810 Changes, /*StartAt=*/0, AlignStyle);811}812 813void WhitespaceManager::alignConsecutiveShortCaseStatements(bool IsExpr) {814 if (!Style.AlignConsecutiveShortCaseStatements.Enabled ||815 !(IsExpr ? Style.AllowShortCaseExpressionOnASingleLine816 : Style.AllowShortCaseLabelsOnASingleLine)) {817 return;818 }819 820 const auto Type = IsExpr ? TT_CaseLabelArrow : TT_CaseLabelColon;821 const auto &Option = Style.AlignConsecutiveShortCaseStatements;822 const bool AlignArrowOrColon =823 IsExpr ? Option.AlignCaseArrows : Option.AlignCaseColons;824 825 auto Matches = [&](const Change &C) {826 if (AlignArrowOrColon)827 return C.Tok->is(Type);828 829 // Ignore 'IsInsideToken' to allow matching trailing comments which830 // need to be reflowed as that causes the token to appear in two831 // different changes, which will cause incorrect alignment as we'll832 // reflow early due to detecting multiple aligning tokens per line.833 return !C.IsInsideToken && C.Tok->Previous && C.Tok->Previous->is(Type);834 };835 836 unsigned MinColumn = 0;837 838 // Empty case statements don't break the alignment, but don't necessarily839 // match our predicate, so we need to track their column so they can push out840 // our alignment.841 unsigned MinEmptyCaseColumn = 0;842 843 // Start and end of the token sequence we're processing.844 unsigned StartOfSequence = 0;845 unsigned EndOfSequence = 0;846 847 // Whether a matching token has been found on the current line.848 bool FoundMatchOnLine = false;849 850 bool LineIsComment = true;851 bool LineIsEmptyCase = false;852 853 unsigned I = 0;854 for (unsigned E = Changes.size(); I != E; ++I) {855 if (Changes[I].NewlinesBefore != 0) {856 // Whether to break the alignment sequence because of an empty line.857 bool EmptyLineBreak =858 (Changes[I].NewlinesBefore > 1) &&859 !Style.AlignConsecutiveShortCaseStatements.AcrossEmptyLines;860 861 // Whether to break the alignment sequence because of a line without a862 // match.863 bool NoMatchBreak =864 !FoundMatchOnLine &&865 !(LineIsComment &&866 Style.AlignConsecutiveShortCaseStatements.AcrossComments) &&867 !LineIsEmptyCase;868 869 if (EmptyLineBreak || NoMatchBreak) {870 AlignMatchingTokenSequence(StartOfSequence, EndOfSequence, MinColumn,871 Matches, Changes);872 MinEmptyCaseColumn = 0;873 }874 875 // A new line starts, re-initialize line status tracking bools.876 FoundMatchOnLine = false;877 LineIsComment = true;878 LineIsEmptyCase = false;879 }880 881 if (Changes[I].Tok->isNot(tok::comment))882 LineIsComment = false;883 884 if (Changes[I].Tok->is(Type)) {885 LineIsEmptyCase =886 !Changes[I].Tok->Next || Changes[I].Tok->Next->isTrailingComment();887 888 if (LineIsEmptyCase) {889 if (Style.AlignConsecutiveShortCaseStatements.AlignCaseColons) {890 MinEmptyCaseColumn =891 std::max(MinEmptyCaseColumn, Changes[I].StartOfTokenColumn);892 } else {893 MinEmptyCaseColumn =894 std::max(MinEmptyCaseColumn, Changes[I].StartOfTokenColumn + 2);895 }896 }897 }898 899 if (!Matches(Changes[I]))900 continue;901 902 if (LineIsEmptyCase)903 continue;904 905 FoundMatchOnLine = true;906 907 if (StartOfSequence == 0)908 StartOfSequence = I;909 910 EndOfSequence = I + 1;911 912 MinColumn = std::max(MinColumn, Changes[I].StartOfTokenColumn);913 914 // Allow empty case statements to push out our alignment.915 MinColumn = std::max(MinColumn, MinEmptyCaseColumn);916 }917 918 AlignMatchingTokenSequence(StartOfSequence, EndOfSequence, MinColumn, Matches,919 Changes);920}921 922void WhitespaceManager::alignConsecutiveTableGenBreakingDAGArgColons() {923 alignConsecutiveColons(Style.AlignConsecutiveTableGenBreakingDAGArgColons,924 TT_TableGenDAGArgListColonToAlign);925}926 927void WhitespaceManager::alignConsecutiveTableGenCondOperatorColons() {928 alignConsecutiveColons(Style.AlignConsecutiveTableGenCondOperatorColons,929 TT_TableGenCondOperatorColon);930}931 932void WhitespaceManager::alignConsecutiveTableGenDefinitions() {933 alignConsecutiveColons(Style.AlignConsecutiveTableGenDefinitionColons,934 TT_InheritanceColon);935}936 937void WhitespaceManager::alignConsecutiveDeclarations() {938 if (!Style.AlignConsecutiveDeclarations.Enabled)939 return;940 941 AlignTokens(942 Style,943 [&](Change const &C) {944 if (C.Tok->is(TT_FunctionTypeLParen))945 return Style.AlignConsecutiveDeclarations.AlignFunctionPointers;946 if (C.Tok->is(TT_FunctionDeclarationName))947 return Style.AlignConsecutiveDeclarations.AlignFunctionDeclarations;948 if (C.Tok->isNot(TT_StartOfName))949 return false;950 if (C.Tok->Previous &&951 C.Tok->Previous->is(TT_StatementAttributeLikeMacro))952 return false;953 // Check if there is a subsequent name that starts the same declaration.954 for (FormatToken *Next = C.Tok->Next; Next; Next = Next->Next) {955 if (Next->is(tok::comment))956 continue;957 if (Next->is(TT_PointerOrReference))958 return false;959 if (!Next->Tok.getIdentifierInfo())960 break;961 if (Next->isOneOf(TT_StartOfName, TT_FunctionDeclarationName,962 tok::kw_operator)) {963 return false;964 }965 }966 return true;967 },968 Changes, /*StartAt=*/0, Style.AlignConsecutiveDeclarations);969}970 971void WhitespaceManager::alignChainedConditionals() {972 if (Style.BreakBeforeTernaryOperators) {973 AlignTokens(974 Style,975 [](Change const &C) {976 // Align question operators and last colon977 return C.Tok->is(TT_ConditionalExpr) &&978 ((C.Tok->is(tok::question) && !C.NewlinesBefore) ||979 (C.Tok->is(tok::colon) && C.Tok->Next &&980 (C.Tok->Next->FakeLParens.empty() ||981 C.Tok->Next->FakeLParens.back() != prec::Conditional)));982 },983 Changes, /*StartAt=*/0);984 } else {985 static auto AlignWrappedOperand = [](Change const &C) {986 FormatToken *Previous = C.Tok->getPreviousNonComment();987 return C.NewlinesBefore && Previous && Previous->is(TT_ConditionalExpr) &&988 (Previous->is(tok::colon) &&989 (C.Tok->FakeLParens.empty() ||990 C.Tok->FakeLParens.back() != prec::Conditional));991 };992 // Ensure we keep alignment of wrapped operands with non-wrapped operands993 // Since we actually align the operators, the wrapped operands need the994 // extra offset to be properly aligned.995 for (Change &C : Changes)996 if (AlignWrappedOperand(C))997 C.StartOfTokenColumn -= 2;998 AlignTokens(999 Style,1000 [this](Change const &C) {1001 // Align question operators if next operand is not wrapped, as1002 // well as wrapped operands after question operator or last1003 // colon in conditional sequence1004 return (C.Tok->is(TT_ConditionalExpr) && C.Tok->is(tok::question) &&1005 &C != &Changes.back() && (&C + 1)->NewlinesBefore == 0 &&1006 !(&C + 1)->IsTrailingComment) ||1007 AlignWrappedOperand(C);1008 },1009 Changes, /*StartAt=*/0);1010 }1011}1012 1013void WhitespaceManager::alignTrailingComments() {1014 if (Style.AlignTrailingComments.Kind == FormatStyle::TCAS_Never)1015 return;1016 1017 const int Size = Changes.size();1018 if (Size == 0)1019 return;1020 1021 int MinColumn = 0;1022 int StartOfSequence = 0;1023 bool BreakBeforeNext = false;1024 bool IsInPP = Changes.front().Tok->Tok.is(tok::hash);1025 int NewLineThreshold = 1;1026 if (Style.AlignTrailingComments.Kind == FormatStyle::TCAS_Always)1027 NewLineThreshold = Style.AlignTrailingComments.OverEmptyLines + 1;1028 1029 for (int I = 0, MaxColumn = INT_MAX, Newlines = 0; I < Size; ++I) {1030 auto &C = Changes[I];1031 if (C.StartOfBlockComment)1032 continue;1033 if (C.NewlinesBefore != 0) {1034 Newlines += C.NewlinesBefore;1035 const bool WasInPP = std::exchange(1036 IsInPP, C.Tok->Tok.is(tok::hash) || (IsInPP && C.IsTrailingComment) ||1037 C.ContinuesPPDirective);1038 if (IsInPP != WasInPP && !Style.AlignTrailingComments.AlignPPAndNotPP) {1039 alignTrailingComments(StartOfSequence, I, MinColumn);1040 MinColumn = 0;1041 MaxColumn = INT_MAX;1042 StartOfSequence = I;1043 Newlines = 0;1044 }1045 }1046 if (!C.IsTrailingComment)1047 continue;1048 1049 if (Style.AlignTrailingComments.Kind == FormatStyle::TCAS_Leave) {1050 const int OriginalSpaces =1051 C.OriginalWhitespaceRange.getEnd().getRawEncoding() -1052 C.OriginalWhitespaceRange.getBegin().getRawEncoding() -1053 C.Tok->LastNewlineOffset;1054 assert(OriginalSpaces >= 0);1055 const auto RestoredLineLength =1056 C.StartOfTokenColumn + C.TokenLength + OriginalSpaces;1057 // If leaving comments makes the line exceed the column limit, give up to1058 // leave the comments.1059 if (RestoredLineLength >= Style.ColumnLimit && Style.ColumnLimit > 0)1060 break;1061 C.Spaces = C.NewlinesBefore > 0 ? C.Tok->OriginalColumn : OriginalSpaces;1062 continue;1063 }1064 1065 const int ChangeMinColumn = C.StartOfTokenColumn;1066 int ChangeMaxColumn;1067 1068 // If we don't create a replacement for this change, we have to consider1069 // it to be immovable.1070 if (!C.CreateReplacement)1071 ChangeMaxColumn = ChangeMinColumn;1072 else if (Style.ColumnLimit == 0)1073 ChangeMaxColumn = INT_MAX;1074 else if (Style.ColumnLimit >= C.TokenLength)1075 ChangeMaxColumn = Style.ColumnLimit - C.TokenLength;1076 else1077 ChangeMaxColumn = ChangeMinColumn;1078 1079 if (I + 1 < Size && Changes[I + 1].ContinuesPPDirective &&1080 ChangeMaxColumn >= 2) {1081 ChangeMaxColumn -= 2;1082 }1083 1084 bool WasAlignedWithStartOfNextLine = false;1085 if (C.NewlinesBefore >= 1) { // A comment on its own line.1086 const auto CommentColumn =1087 SourceMgr.getSpellingColumnNumber(C.OriginalWhitespaceRange.getEnd());1088 for (int J = I + 1; J < Size; ++J) {1089 if (Changes[J].Tok->is(tok::comment))1090 continue;1091 1092 const auto NextColumn = SourceMgr.getSpellingColumnNumber(1093 Changes[J].OriginalWhitespaceRange.getEnd());1094 // The start of the next token was previously aligned with the1095 // start of this comment.1096 WasAlignedWithStartOfNextLine =1097 CommentColumn == NextColumn ||1098 CommentColumn == NextColumn + Style.IndentWidth;1099 break;1100 }1101 }1102 1103 // We don't want to align comments which end a scope, which are here1104 // identified by most closing braces.1105 auto DontAlignThisComment = [](const auto *Tok) {1106 if (Tok->is(tok::semi)) {1107 Tok = Tok->getPreviousNonComment();1108 if (!Tok)1109 return false;1110 }1111 if (Tok->is(tok::r_paren)) {1112 // Back up past the parentheses and a `TT_DoWhile` that may precede.1113 Tok = Tok->MatchingParen;1114 if (!Tok)1115 return false;1116 Tok = Tok->getPreviousNonComment();1117 if (!Tok)1118 return false;1119 if (Tok->is(TT_DoWhile)) {1120 const auto *Prev = Tok->getPreviousNonComment();1121 if (!Prev) {1122 // A do-while-loop without braces.1123 return true;1124 }1125 Tok = Prev;1126 }1127 }1128 1129 if (Tok->isNot(tok::r_brace))1130 return false;1131 1132 while (Tok->Previous && Tok->Previous->is(tok::r_brace))1133 Tok = Tok->Previous;1134 return Tok->NewlinesBefore > 0;1135 };1136 1137 if (I > 0 && C.NewlinesBefore == 0 &&1138 DontAlignThisComment(Changes[I - 1].Tok)) {1139 alignTrailingComments(StartOfSequence, I, MinColumn);1140 // Reset to initial values, but skip this change for the next alignment1141 // pass.1142 MinColumn = 0;1143 MaxColumn = INT_MAX;1144 StartOfSequence = I + 1;1145 } else if (BreakBeforeNext || Newlines > NewLineThreshold ||1146 (ChangeMinColumn > MaxColumn || ChangeMaxColumn < MinColumn) ||1147 // Break the comment sequence if the previous line did not end1148 // in a trailing comment.1149 (C.NewlinesBefore == 1 && I > 0 &&1150 !Changes[I - 1].IsTrailingComment) ||1151 WasAlignedWithStartOfNextLine) {1152 alignTrailingComments(StartOfSequence, I, MinColumn);1153 MinColumn = ChangeMinColumn;1154 MaxColumn = ChangeMaxColumn;1155 StartOfSequence = I;1156 } else {1157 MinColumn = std::max(MinColumn, ChangeMinColumn);1158 MaxColumn = std::min(MaxColumn, ChangeMaxColumn);1159 }1160 BreakBeforeNext = (I == 0) || (C.NewlinesBefore > 1) ||1161 // Never start a sequence with a comment at the beginning1162 // of the line.1163 (C.NewlinesBefore == 1 && StartOfSequence == I);1164 Newlines = 0;1165 }1166 alignTrailingComments(StartOfSequence, Size, MinColumn);1167}1168 1169void WhitespaceManager::alignTrailingComments(unsigned Start, unsigned End,1170 unsigned Column) {1171 for (unsigned i = Start; i != End; ++i) {1172 int Shift = 0;1173 if (Changes[i].IsTrailingComment)1174 Shift = Column - Changes[i].StartOfTokenColumn;1175 if (Changes[i].StartOfBlockComment) {1176 Shift = Changes[i].IndentationOffset +1177 Changes[i].StartOfBlockComment->StartOfTokenColumn -1178 Changes[i].StartOfTokenColumn;1179 }1180 if (Shift <= 0)1181 continue;1182 Changes[i].Spaces += Shift;1183 if (i + 1 != Changes.size())1184 Changes[i + 1].PreviousEndOfTokenColumn += Shift;1185 Changes[i].StartOfTokenColumn += Shift;1186 }1187}1188 1189void WhitespaceManager::alignEscapedNewlines() {1190 const auto Align = Style.AlignEscapedNewlines;1191 if (Align == FormatStyle::ENAS_DontAlign)1192 return;1193 1194 const bool WithLastLine = Align == FormatStyle::ENAS_LeftWithLastLine;1195 const bool AlignLeft = Align == FormatStyle::ENAS_Left || WithLastLine;1196 const auto MaxColumn = Style.ColumnLimit;1197 unsigned MaxEndOfLine = AlignLeft ? 0 : MaxColumn;1198 unsigned StartOfMacro = 0;1199 for (unsigned i = 1, e = Changes.size(); i < e; ++i) {1200 Change &C = Changes[i];1201 if (C.NewlinesBefore == 0 && (!WithLastLine || C.Tok->isNot(tok::eof)))1202 continue;1203 const bool InPPDirective = C.ContinuesPPDirective;1204 const auto BackslashColumn = C.PreviousEndOfTokenColumn + 2;1205 if (InPPDirective ||1206 (WithLastLine && (MaxColumn == 0 || BackslashColumn <= MaxColumn))) {1207 MaxEndOfLine = std::max(BackslashColumn, MaxEndOfLine);1208 }1209 if (!InPPDirective) {1210 alignEscapedNewlines(StartOfMacro + 1, i, MaxEndOfLine);1211 MaxEndOfLine = AlignLeft ? 0 : MaxColumn;1212 StartOfMacro = i;1213 }1214 }1215 alignEscapedNewlines(StartOfMacro + 1, Changes.size(), MaxEndOfLine);1216}1217 1218void WhitespaceManager::alignEscapedNewlines(unsigned Start, unsigned End,1219 unsigned Column) {1220 for (unsigned i = Start; i < End; ++i) {1221 Change &C = Changes[i];1222 if (C.NewlinesBefore > 0) {1223 assert(C.ContinuesPPDirective);1224 if (C.PreviousEndOfTokenColumn + 1 > Column)1225 C.EscapedNewlineColumn = 0;1226 else1227 C.EscapedNewlineColumn = Column;1228 }1229 }1230}1231 1232void WhitespaceManager::alignArrayInitializers() {1233 if (Style.AlignArrayOfStructures == FormatStyle::AIAS_None)1234 return;1235 1236 for (unsigned ChangeIndex = 1U, ChangeEnd = Changes.size();1237 ChangeIndex < ChangeEnd; ++ChangeIndex) {1238 auto &C = Changes[ChangeIndex];1239 if (C.Tok->IsArrayInitializer) {1240 bool FoundComplete = false;1241 for (unsigned InsideIndex = ChangeIndex + 1; InsideIndex < ChangeEnd;1242 ++InsideIndex) {1243 const auto *Tok = Changes[InsideIndex].Tok;1244 if (Tok->is(tok::pp_define))1245 break;1246 if (Tok == C.Tok->MatchingParen) {1247 alignArrayInitializers(ChangeIndex, InsideIndex + 1);1248 ChangeIndex = InsideIndex + 1;1249 FoundComplete = true;1250 break;1251 }1252 }1253 if (!FoundComplete)1254 ChangeIndex = ChangeEnd;1255 }1256 }1257}1258 1259void WhitespaceManager::alignArrayInitializers(unsigned Start, unsigned End) {1260 1261 if (Style.AlignArrayOfStructures == FormatStyle::AIAS_Right)1262 alignArrayInitializersRightJustified(getCells(Start, End));1263 else if (Style.AlignArrayOfStructures == FormatStyle::AIAS_Left)1264 alignArrayInitializersLeftJustified(getCells(Start, End));1265}1266 1267void WhitespaceManager::alignArrayInitializersRightJustified(1268 CellDescriptions &&CellDescs) {1269 if (!CellDescs.isRectangular())1270 return;1271 1272 const int BracePadding =1273 Style.Cpp11BracedListStyle != FormatStyle::BLS_Block ? 0 : 1;1274 auto &Cells = CellDescs.Cells;1275 // Now go through and fixup the spaces.1276 auto *CellIter = Cells.begin();1277 for (auto i = 0U; i < CellDescs.CellCounts[0]; ++i, ++CellIter) {1278 unsigned NetWidth = 0U;1279 if (isSplitCell(*CellIter))1280 NetWidth = getNetWidth(Cells.begin(), CellIter, CellDescs.InitialSpaces);1281 auto CellWidth = getMaximumCellWidth(CellIter, NetWidth);1282 1283 if (Changes[CellIter->Index].Tok->is(tok::r_brace)) {1284 // So in here we want to see if there is a brace that falls1285 // on a line that was split. If so on that line we make sure that1286 // the spaces in front of the brace are enough.1287 const auto *Next = CellIter;1288 do {1289 const FormatToken *Previous = Changes[Next->Index].Tok->Previous;1290 if (Previous && Previous->isNot(TT_LineComment)) {1291 Changes[Next->Index].Spaces = BracePadding;1292 Changes[Next->Index].NewlinesBefore = 0;1293 }1294 Next = Next->NextColumnElement;1295 } while (Next);1296 // Unless the array is empty, we need the position of all the1297 // immediately adjacent cells1298 if (CellIter != Cells.begin()) {1299 auto ThisNetWidth =1300 getNetWidth(Cells.begin(), CellIter, CellDescs.InitialSpaces);1301 auto MaxNetWidth = getMaximumNetWidth(1302 Cells.begin(), CellIter, CellDescs.InitialSpaces,1303 CellDescs.CellCounts[0], CellDescs.CellCounts.size());1304 if (ThisNetWidth < MaxNetWidth)1305 Changes[CellIter->Index].Spaces = (MaxNetWidth - ThisNetWidth);1306 auto RowCount = 1U;1307 auto Offset = std::distance(Cells.begin(), CellIter);1308 for (const auto *Next = CellIter->NextColumnElement; Next;1309 Next = Next->NextColumnElement) {1310 if (RowCount >= CellDescs.CellCounts.size())1311 break;1312 auto *Start = (Cells.begin() + RowCount * CellDescs.CellCounts[0]);1313 auto *End = Start + Offset;1314 ThisNetWidth = getNetWidth(Start, End, CellDescs.InitialSpaces);1315 if (ThisNetWidth < MaxNetWidth)1316 Changes[Next->Index].Spaces = (MaxNetWidth - ThisNetWidth);1317 ++RowCount;1318 }1319 }1320 } else {1321 auto ThisWidth =1322 calculateCellWidth(CellIter->Index, CellIter->EndIndex, true) +1323 NetWidth;1324 if (Changes[CellIter->Index].NewlinesBefore == 0) {1325 Changes[CellIter->Index].Spaces = (CellWidth - (ThisWidth + NetWidth));1326 Changes[CellIter->Index].Spaces += (i > 0) ? 1 : BracePadding;1327 }1328 alignToStartOfCell(CellIter->Index, CellIter->EndIndex);1329 for (const auto *Next = CellIter->NextColumnElement; Next;1330 Next = Next->NextColumnElement) {1331 ThisWidth =1332 calculateCellWidth(Next->Index, Next->EndIndex, true) + NetWidth;1333 if (Changes[Next->Index].NewlinesBefore == 0) {1334 Changes[Next->Index].Spaces = (CellWidth - ThisWidth);1335 Changes[Next->Index].Spaces += (i > 0) ? 1 : BracePadding;1336 }1337 alignToStartOfCell(Next->Index, Next->EndIndex);1338 }1339 }1340 }1341}1342 1343void WhitespaceManager::alignArrayInitializersLeftJustified(1344 CellDescriptions &&CellDescs) {1345 1346 if (!CellDescs.isRectangular())1347 return;1348 1349 const int BracePadding =1350 Style.Cpp11BracedListStyle != FormatStyle::BLS_Block ? 0 : 1;1351 auto &Cells = CellDescs.Cells;1352 // Now go through and fixup the spaces.1353 auto *CellIter = Cells.begin();1354 // The first cell of every row needs to be against the left brace.1355 for (const auto *Next = CellIter; Next; Next = Next->NextColumnElement) {1356 auto &Change = Changes[Next->Index];1357 Change.Spaces =1358 Change.NewlinesBefore == 0 ? BracePadding : CellDescs.InitialSpaces;1359 }1360 ++CellIter;1361 for (auto i = 1U; i < CellDescs.CellCounts[0]; i++, ++CellIter) {1362 auto MaxNetWidth = getMaximumNetWidth(1363 Cells.begin(), CellIter, CellDescs.InitialSpaces,1364 CellDescs.CellCounts[0], CellDescs.CellCounts.size());1365 auto ThisNetWidth =1366 getNetWidth(Cells.begin(), CellIter, CellDescs.InitialSpaces);1367 if (Changes[CellIter->Index].NewlinesBefore == 0) {1368 Changes[CellIter->Index].Spaces =1369 MaxNetWidth - ThisNetWidth +1370 (Changes[CellIter->Index].Tok->isNot(tok::r_brace) ? 11371 : BracePadding);1372 }1373 auto RowCount = 1U;1374 auto Offset = std::distance(Cells.begin(), CellIter);1375 for (const auto *Next = CellIter->NextColumnElement; Next;1376 Next = Next->NextColumnElement) {1377 if (RowCount >= CellDescs.CellCounts.size())1378 break;1379 auto *Start = (Cells.begin() + RowCount * CellDescs.CellCounts[0]);1380 auto *End = Start + Offset;1381 auto ThisNetWidth = getNetWidth(Start, End, CellDescs.InitialSpaces);1382 if (Changes[Next->Index].NewlinesBefore == 0) {1383 Changes[Next->Index].Spaces =1384 MaxNetWidth - ThisNetWidth +1385 (Changes[Next->Index].Tok->isNot(tok::r_brace) ? 1 : BracePadding);1386 }1387 ++RowCount;1388 }1389 }1390}1391 1392bool WhitespaceManager::isSplitCell(const CellDescription &Cell) {1393 if (Cell.HasSplit)1394 return true;1395 for (const auto *Next = Cell.NextColumnElement; Next;1396 Next = Next->NextColumnElement) {1397 if (Next->HasSplit)1398 return true;1399 }1400 return false;1401}1402 1403WhitespaceManager::CellDescriptions WhitespaceManager::getCells(unsigned Start,1404 unsigned End) {1405 1406 unsigned Depth = 0;1407 unsigned Cell = 0;1408 SmallVector<unsigned> CellCounts;1409 unsigned InitialSpaces = 0;1410 unsigned InitialTokenLength = 0;1411 unsigned EndSpaces = 0;1412 SmallVector<CellDescription> Cells;1413 const FormatToken *MatchingParen = nullptr;1414 for (unsigned i = Start; i < End; ++i) {1415 auto &C = Changes[i];1416 if (C.Tok->is(tok::l_brace))1417 ++Depth;1418 else if (C.Tok->is(tok::r_brace))1419 --Depth;1420 if (Depth == 2) {1421 if (C.Tok->is(tok::l_brace)) {1422 Cell = 0;1423 MatchingParen = C.Tok->MatchingParen;1424 if (InitialSpaces == 0) {1425 InitialSpaces = C.Spaces + C.TokenLength;1426 InitialTokenLength = C.TokenLength;1427 auto j = i - 1;1428 for (; Changes[j].NewlinesBefore == 0 && j > Start; --j) {1429 InitialSpaces += Changes[j].Spaces + Changes[j].TokenLength;1430 InitialTokenLength += Changes[j].TokenLength;1431 }1432 if (C.NewlinesBefore == 0) {1433 InitialSpaces += Changes[j].Spaces + Changes[j].TokenLength;1434 InitialTokenLength += Changes[j].TokenLength;1435 }1436 }1437 } else if (C.Tok->is(tok::comma)) {1438 if (!Cells.empty())1439 Cells.back().EndIndex = i;1440 if (const auto *Next = C.Tok->getNextNonComment();1441 Next && Next->isNot(tok::r_brace)) { // dangling comma1442 ++Cell;1443 }1444 }1445 } else if (Depth == 1) {1446 if (C.Tok == MatchingParen) {1447 if (!Cells.empty())1448 Cells.back().EndIndex = i;1449 Cells.push_back(CellDescription{i, ++Cell, i + 1, false, nullptr});1450 CellCounts.push_back(C.Tok->Previous->isNot(tok::comma) ? Cell + 11451 : Cell);1452 // Go to the next non-comment and ensure there is a break in front1453 const auto *NextNonComment = C.Tok->getNextNonComment();1454 while (NextNonComment && NextNonComment->is(tok::comma))1455 NextNonComment = NextNonComment->getNextNonComment();1456 auto j = i;1457 while (j < End && Changes[j].Tok != NextNonComment)1458 ++j;1459 if (j < End && Changes[j].NewlinesBefore == 0 &&1460 Changes[j].Tok->isNot(tok::r_brace)) {1461 Changes[j].NewlinesBefore = 1;1462 // Account for the added token lengths1463 Changes[j].Spaces = InitialSpaces - InitialTokenLength;1464 }1465 } else if (C.Tok->is(tok::comment) && C.Tok->NewlinesBefore == 0) {1466 // Trailing comments stay at a space past the last token1467 C.Spaces = Changes[i - 1].Tok->is(tok::comma) ? 1 : 2;1468 } else if (C.Tok->is(tok::l_brace)) {1469 // We need to make sure that the ending braces is aligned to the1470 // start of our initializer1471 auto j = i - 1;1472 for (; j > 0 && !Changes[j].Tok->ArrayInitializerLineStart; --j)1473 ; // Nothing the loop does the work1474 EndSpaces = Changes[j].Spaces;1475 }1476 } else if (Depth == 0 && C.Tok->is(tok::r_brace)) {1477 C.NewlinesBefore = 1;1478 C.Spaces = EndSpaces;1479 }1480 if (C.Tok->StartsColumn) {1481 // This gets us past tokens that have been split over multiple1482 // lines1483 bool HasSplit = false;1484 if (Changes[i].NewlinesBefore > 0) {1485 // So if we split a line previously and the tail line + this token is1486 // less then the column limit we remove the split here and just put1487 // the column start at a space past the comma1488 //1489 // FIXME This if branch covers the cases where the column is not1490 // the first column. This leads to weird pathologies like the formatting1491 // auto foo = Items{1492 // Section{1493 // 0, bar(),1494 // }1495 // };1496 // Well if it doesn't lead to that it's indicative that the line1497 // breaking should be revisited. Unfortunately alot of other options1498 // interact with this1499 auto j = i - 1;1500 if ((j - 1) > Start && Changes[j].Tok->is(tok::comma) &&1501 Changes[j - 1].NewlinesBefore > 0) {1502 --j;1503 auto LineLimit = Changes[j].Spaces + Changes[j].TokenLength;1504 if (LineLimit < Style.ColumnLimit) {1505 Changes[i].NewlinesBefore = 0;1506 Changes[i].Spaces = 1;1507 }1508 }1509 }1510 while (Changes[i].NewlinesBefore > 0 && Changes[i].Tok == C.Tok) {1511 Changes[i].Spaces = InitialSpaces;1512 ++i;1513 HasSplit = true;1514 }1515 if (Changes[i].Tok != C.Tok)1516 --i;1517 Cells.push_back(CellDescription{i, Cell, i, HasSplit, nullptr});1518 }1519 }1520 1521 return linkCells({Cells, CellCounts, InitialSpaces});1522}1523 1524unsigned WhitespaceManager::calculateCellWidth(unsigned Start, unsigned End,1525 bool WithSpaces) const {1526 unsigned CellWidth = 0;1527 for (auto i = Start; i < End; i++) {1528 if (Changes[i].NewlinesBefore > 0)1529 CellWidth = 0;1530 CellWidth += Changes[i].TokenLength;1531 CellWidth += (WithSpaces ? Changes[i].Spaces : 0);1532 }1533 return CellWidth;1534}1535 1536void WhitespaceManager::alignToStartOfCell(unsigned Start, unsigned End) {1537 if ((End - Start) <= 1)1538 return;1539 // If the line is broken anywhere in there make sure everything1540 // is aligned to the parent1541 for (auto i = Start + 1; i < End; i++)1542 if (Changes[i].NewlinesBefore > 0)1543 Changes[i].Spaces = Changes[Start].Spaces;1544}1545 1546WhitespaceManager::CellDescriptions1547WhitespaceManager::linkCells(CellDescriptions &&CellDesc) {1548 auto &Cells = CellDesc.Cells;1549 for (auto *CellIter = Cells.begin(); CellIter != Cells.end(); ++CellIter) {1550 if (!CellIter->NextColumnElement && (CellIter + 1) != Cells.end()) {1551 for (auto *NextIter = CellIter + 1; NextIter != Cells.end(); ++NextIter) {1552 if (NextIter->Cell == CellIter->Cell) {1553 CellIter->NextColumnElement = &(*NextIter);1554 break;1555 }1556 }1557 }1558 }1559 return std::move(CellDesc);1560}1561 1562void WhitespaceManager::generateChanges() {1563 for (unsigned i = 0, e = Changes.size(); i != e; ++i) {1564 const Change &C = Changes[i];1565 if (i > 0) {1566 auto Last = Changes[i - 1].OriginalWhitespaceRange;1567 auto New = Changes[i].OriginalWhitespaceRange;1568 // Do not generate two replacements for the same location. As a special1569 // case, it is allowed if there is a replacement for the empty range1570 // between 2 tokens and another non-empty range at the start of the second1571 // token. We didn't implement logic to combine replacements for 21572 // consecutive source ranges into a single replacement, because the1573 // program works fine without it.1574 //1575 // We can't eliminate empty original whitespace ranges. They appear when1576 // 2 tokens have no whitespace in between in the input. It does not1577 // matter whether whitespace is to be added. If no whitespace is to be1578 // added, the replacement will be empty, and it gets eliminated after this1579 // step in storeReplacement. For example, if the input is `foo();`,1580 // there will be a replacement for the range between every consecutive1581 // pair of tokens.1582 //1583 // A replacement at the start of a token can be added by1584 // BreakableStringLiteralUsingOperators::insertBreak when it adds braces1585 // around the string literal. Say Verilog code is being formatted and the1586 // first line is to become the next 2 lines.1587 // x("long string");1588 // x({"long ",1589 // "string"});1590 // There will be a replacement for the empty range between the parenthesis1591 // and the string and another replacement for the quote character. The1592 // replacement for the empty range between the parenthesis and the quote1593 // comes from ContinuationIndenter::addTokenOnCurrentLine when it changes1594 // the original empty range between the parenthesis and the string to1595 // another empty one. The replacement for the quote character comes from1596 // BreakableStringLiteralUsingOperators::insertBreak when it adds the1597 // brace. In the example, the replacement for the empty range is the same1598 // as the original text. However, eliminating replacements that are same1599 // as the original does not help in general. For example, a newline can1600 // be inserted, causing the first line to become the next 3 lines.1601 // xxxxxxxxxxx("long string");1602 // xxxxxxxxxxx(1603 // {"long ",1604 // "string"});1605 // In that case, the empty range between the parenthesis and the string1606 // will be replaced by a newline and 4 spaces. So we will still have to1607 // deal with a replacement for an empty source range followed by a1608 // replacement for a non-empty source range.1609 if (Last.getBegin() == New.getBegin() &&1610 (Last.getEnd() != Last.getBegin() ||1611 New.getEnd() == New.getBegin())) {1612 continue;1613 }1614 }1615 if (C.CreateReplacement) {1616 std::string ReplacementText = C.PreviousLinePostfix;1617 if (C.ContinuesPPDirective) {1618 appendEscapedNewlineText(ReplacementText, C.NewlinesBefore,1619 C.PreviousEndOfTokenColumn,1620 C.EscapedNewlineColumn);1621 } else {1622 appendNewlineText(ReplacementText, C);1623 }1624 // FIXME: This assert should hold if we computed the column correctly.1625 // assert((int)C.StartOfTokenColumn >= C.Spaces);1626 appendIndentText(1627 ReplacementText, C.Tok->IndentLevel, std::max(0, C.Spaces),1628 std::max((int)C.StartOfTokenColumn, C.Spaces) - std::max(0, C.Spaces),1629 C.IsAligned);1630 ReplacementText.append(C.CurrentLinePrefix);1631 storeReplacement(C.OriginalWhitespaceRange, ReplacementText);1632 }1633 }1634}1635 1636void WhitespaceManager::storeReplacement(SourceRange Range, StringRef Text) {1637 unsigned WhitespaceLength = SourceMgr.getFileOffset(Range.getEnd()) -1638 SourceMgr.getFileOffset(Range.getBegin());1639 // Don't create a replacement, if it does not change anything.1640 if (StringRef(SourceMgr.getCharacterData(Range.getBegin()),1641 WhitespaceLength) == Text) {1642 return;1643 }1644 auto Err = Replaces.add(tooling::Replacement(1645 SourceMgr, CharSourceRange::getCharRange(Range), Text));1646 // FIXME: better error handling. For now, just print an error message in the1647 // release version.1648 if (Err) {1649 llvm::errs() << llvm::toString(std::move(Err)) << "\n";1650 assert(false);1651 }1652}1653 1654void WhitespaceManager::appendNewlineText(std::string &Text, const Change &C) {1655 if (C.NewlinesBefore <= 0)1656 return;1657 1658 StringRef Newline = UseCRLF ? "\r\n" : "\n";1659 Text.append(Newline);1660 1661 if (C.Tok->HasFormFeedBefore)1662 Text.append("\f");1663 1664 for (unsigned I = 1; I < C.NewlinesBefore; ++I)1665 Text.append(Newline);1666}1667 1668void WhitespaceManager::appendEscapedNewlineText(1669 std::string &Text, unsigned Newlines, unsigned PreviousEndOfTokenColumn,1670 unsigned EscapedNewlineColumn) {1671 if (Newlines > 0) {1672 unsigned Spaces =1673 std::max<int>(1, EscapedNewlineColumn - PreviousEndOfTokenColumn - 1);1674 for (unsigned i = 0; i < Newlines; ++i) {1675 Text.append(Spaces, ' ');1676 Text.append(UseCRLF ? "\\\r\n" : "\\\n");1677 Spaces = std::max<int>(0, EscapedNewlineColumn - 1);1678 }1679 }1680}1681 1682void WhitespaceManager::appendIndentText(std::string &Text,1683 unsigned IndentLevel, unsigned Spaces,1684 unsigned WhitespaceStartColumn,1685 bool IsAligned) {1686 switch (Style.UseTab) {1687 case FormatStyle::UT_Never:1688 Text.append(Spaces, ' ');1689 break;1690 case FormatStyle::UT_Always: {1691 if (Style.TabWidth) {1692 unsigned FirstTabWidth =1693 Style.TabWidth - WhitespaceStartColumn % Style.TabWidth;1694 1695 // Insert only spaces when we want to end up before the next tab.1696 if (Spaces < FirstTabWidth || Spaces == 1) {1697 Text.append(Spaces, ' ');1698 break;1699 }1700 // Align to the next tab.1701 Spaces -= FirstTabWidth;1702 Text.append("\t");1703 1704 Text.append(Spaces / Style.TabWidth, '\t');1705 Text.append(Spaces % Style.TabWidth, ' ');1706 } else if (Spaces == 1) {1707 Text.append(Spaces, ' ');1708 }1709 break;1710 }1711 case FormatStyle::UT_ForIndentation:1712 if (WhitespaceStartColumn == 0) {1713 unsigned Indentation = IndentLevel * Style.IndentWidth;1714 Spaces = appendTabIndent(Text, Spaces, Indentation);1715 }1716 Text.append(Spaces, ' ');1717 break;1718 case FormatStyle::UT_ForContinuationAndIndentation:1719 if (WhitespaceStartColumn == 0)1720 Spaces = appendTabIndent(Text, Spaces, Spaces);1721 Text.append(Spaces, ' ');1722 break;1723 case FormatStyle::UT_AlignWithSpaces:1724 if (WhitespaceStartColumn == 0) {1725 unsigned Indentation =1726 IsAligned ? IndentLevel * Style.IndentWidth : Spaces;1727 Spaces = appendTabIndent(Text, Spaces, Indentation);1728 }1729 Text.append(Spaces, ' ');1730 break;1731 }1732}1733 1734unsigned WhitespaceManager::appendTabIndent(std::string &Text, unsigned Spaces,1735 unsigned Indentation) {1736 // This happens, e.g. when a line in a block comment is indented less than the1737 // first one.1738 if (Indentation > Spaces)1739 Indentation = Spaces;1740 if (Style.TabWidth) {1741 unsigned Tabs = Indentation / Style.TabWidth;1742 Text.append(Tabs, '\t');1743 Spaces -= Tabs * Style.TabWidth;1744 }1745 return Spaces;1746}1747 1748} // namespace format1749} // namespace clang1750