Path: blob/main/contrib/llvm-project/clang/lib/Format/WhitespaceManager.cpp
35233 views
//===--- WhitespaceManager.cpp - Format C++ code --------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7///8/// \file9/// This file implements WhitespaceManager class.10///11//===----------------------------------------------------------------------===//1213#include "WhitespaceManager.h"14#include "llvm/ADT/STLExtras.h"15#include "llvm/ADT/SmallVector.h"16#include <algorithm>1718namespace clang {19namespace format {2021bool WhitespaceManager::Change::IsBeforeInFile::operator()(22const Change &C1, const Change &C2) const {23return SourceMgr.isBeforeInTranslationUnit(24C1.OriginalWhitespaceRange.getBegin(),25C2.OriginalWhitespaceRange.getBegin()) ||26(C1.OriginalWhitespaceRange.getBegin() ==27C2.OriginalWhitespaceRange.getBegin() &&28SourceMgr.isBeforeInTranslationUnit(29C1.OriginalWhitespaceRange.getEnd(),30C2.OriginalWhitespaceRange.getEnd()));31}3233WhitespaceManager::Change::Change(const FormatToken &Tok,34bool CreateReplacement,35SourceRange OriginalWhitespaceRange,36int Spaces, unsigned StartOfTokenColumn,37unsigned NewlinesBefore,38StringRef PreviousLinePostfix,39StringRef CurrentLinePrefix, bool IsAligned,40bool ContinuesPPDirective, bool IsInsideToken)41: Tok(&Tok), CreateReplacement(CreateReplacement),42OriginalWhitespaceRange(OriginalWhitespaceRange),43StartOfTokenColumn(StartOfTokenColumn), NewlinesBefore(NewlinesBefore),44PreviousLinePostfix(PreviousLinePostfix),45CurrentLinePrefix(CurrentLinePrefix), IsAligned(IsAligned),46ContinuesPPDirective(ContinuesPPDirective), Spaces(Spaces),47IsInsideToken(IsInsideToken), IsTrailingComment(false), TokenLength(0),48PreviousEndOfTokenColumn(0), EscapedNewlineColumn(0),49StartOfBlockComment(nullptr), IndentationOffset(0), ConditionalsLevel(0) {50}5152void WhitespaceManager::replaceWhitespace(FormatToken &Tok, unsigned Newlines,53unsigned Spaces,54unsigned StartOfTokenColumn,55bool IsAligned, bool InPPDirective) {56if (Tok.Finalized || (Tok.MacroCtx && Tok.MacroCtx->Role == MR_ExpandedArg))57return;58Tok.setDecision((Newlines > 0) ? FD_Break : FD_Continue);59Changes.push_back(Change(Tok, /*CreateReplacement=*/true, Tok.WhitespaceRange,60Spaces, StartOfTokenColumn, Newlines, "", "",61IsAligned, InPPDirective && !Tok.IsFirst,62/*IsInsideToken=*/false));63}6465void WhitespaceManager::addUntouchableToken(const FormatToken &Tok,66bool InPPDirective) {67if (Tok.Finalized || (Tok.MacroCtx && Tok.MacroCtx->Role == MR_ExpandedArg))68return;69Changes.push_back(Change(Tok, /*CreateReplacement=*/false,70Tok.WhitespaceRange, /*Spaces=*/0,71Tok.OriginalColumn, Tok.NewlinesBefore, "", "",72/*IsAligned=*/false, InPPDirective && !Tok.IsFirst,73/*IsInsideToken=*/false));74}7576llvm::Error77WhitespaceManager::addReplacement(const tooling::Replacement &Replacement) {78return Replaces.add(Replacement);79}8081bool WhitespaceManager::inputUsesCRLF(StringRef Text, bool DefaultToCRLF) {82size_t LF = Text.count('\n');83size_t CR = Text.count('\r') * 2;84return LF == CR ? DefaultToCRLF : CR > LF;85}8687void WhitespaceManager::replaceWhitespaceInToken(88const FormatToken &Tok, unsigned Offset, unsigned ReplaceChars,89StringRef PreviousPostfix, StringRef CurrentPrefix, bool InPPDirective,90unsigned Newlines, int Spaces) {91if (Tok.Finalized || (Tok.MacroCtx && Tok.MacroCtx->Role == MR_ExpandedArg))92return;93SourceLocation Start = Tok.getStartOfNonWhitespace().getLocWithOffset(Offset);94Changes.push_back(95Change(Tok, /*CreateReplacement=*/true,96SourceRange(Start, Start.getLocWithOffset(ReplaceChars)), Spaces,97std::max(0, Spaces), Newlines, PreviousPostfix, CurrentPrefix,98/*IsAligned=*/true, InPPDirective && !Tok.IsFirst,99/*IsInsideToken=*/true));100}101102const tooling::Replacements &WhitespaceManager::generateReplacements() {103if (Changes.empty())104return Replaces;105106llvm::sort(Changes, Change::IsBeforeInFile(SourceMgr));107calculateLineBreakInformation();108alignConsecutiveMacros();109alignConsecutiveShortCaseStatements(/*IsExpr=*/true);110alignConsecutiveShortCaseStatements(/*IsExpr=*/false);111alignConsecutiveDeclarations();112alignConsecutiveBitFields();113alignConsecutiveAssignments();114if (Style.isTableGen()) {115alignConsecutiveTableGenBreakingDAGArgColons();116alignConsecutiveTableGenCondOperatorColons();117alignConsecutiveTableGenDefinitions();118}119alignChainedConditionals();120alignTrailingComments();121alignEscapedNewlines();122alignArrayInitializers();123generateChanges();124125return Replaces;126}127128void WhitespaceManager::calculateLineBreakInformation() {129Changes[0].PreviousEndOfTokenColumn = 0;130Change *LastOutsideTokenChange = &Changes[0];131for (unsigned I = 1, e = Changes.size(); I != e; ++I) {132auto &C = Changes[I];133auto &P = Changes[I - 1];134auto &PrevTokLength = P.TokenLength;135SourceLocation OriginalWhitespaceStart =136C.OriginalWhitespaceRange.getBegin();137SourceLocation PreviousOriginalWhitespaceEnd =138P.OriginalWhitespaceRange.getEnd();139unsigned OriginalWhitespaceStartOffset =140SourceMgr.getFileOffset(OriginalWhitespaceStart);141unsigned PreviousOriginalWhitespaceEndOffset =142SourceMgr.getFileOffset(PreviousOriginalWhitespaceEnd);143assert(PreviousOriginalWhitespaceEndOffset <=144OriginalWhitespaceStartOffset);145const char *const PreviousOriginalWhitespaceEndData =146SourceMgr.getCharacterData(PreviousOriginalWhitespaceEnd);147StringRef Text(PreviousOriginalWhitespaceEndData,148SourceMgr.getCharacterData(OriginalWhitespaceStart) -149PreviousOriginalWhitespaceEndData);150// Usually consecutive changes would occur in consecutive tokens. This is151// not the case however when analyzing some preprocessor runs of the152// annotated lines. For example, in this code:153//154// #if A // line 1155// int i = 1;156// #else B // line 2157// int i = 2;158// #endif // line 3159//160// one of the runs will produce the sequence of lines marked with line 1, 2161// and 3. So the two consecutive whitespace changes just before '// line 2'162// and before '#endif // line 3' span multiple lines and tokens:163//164// #else B{change X}[// line 2165// int i = 2;166// ]{change Y}#endif // line 3167//168// For this reason, if the text between consecutive changes spans multiple169// newlines, the token length must be adjusted to the end of the original170// line of the token.171auto NewlinePos = Text.find_first_of('\n');172if (NewlinePos == StringRef::npos) {173PrevTokLength = OriginalWhitespaceStartOffset -174PreviousOriginalWhitespaceEndOffset +175C.PreviousLinePostfix.size() + P.CurrentLinePrefix.size();176if (!P.IsInsideToken)177PrevTokLength = std::min(PrevTokLength, P.Tok->ColumnWidth);178} else {179PrevTokLength = NewlinePos + P.CurrentLinePrefix.size();180}181182// If there are multiple changes in this token, sum up all the changes until183// the end of the line.184if (P.IsInsideToken && P.NewlinesBefore == 0)185LastOutsideTokenChange->TokenLength += PrevTokLength + P.Spaces;186else187LastOutsideTokenChange = &P;188189C.PreviousEndOfTokenColumn = P.StartOfTokenColumn + PrevTokLength;190191P.IsTrailingComment =192(C.NewlinesBefore > 0 || C.Tok->is(tok::eof) ||193(C.IsInsideToken && C.Tok->is(tok::comment))) &&194P.Tok->is(tok::comment) &&195// FIXME: This is a dirty hack. The problem is that196// BreakableLineCommentSection does comment reflow changes and here is197// the aligning of trailing comments. Consider the case where we reflow198// the second line up in this example:199//200// // line 1201// // line 2202//203// That amounts to 2 changes by BreakableLineCommentSection:204// - the first, delimited by (), for the whitespace between the tokens,205// - and second, delimited by [], for the whitespace at the beginning206// of the second token:207//208// // line 1(209// )[// ]line 2210//211// So in the end we have two changes like this:212//213// // line1()[ ]line 2214//215// Note that the OriginalWhitespaceStart of the second change is the216// same as the PreviousOriginalWhitespaceEnd of the first change.217// In this case, the below check ensures that the second change doesn't218// get treated as a trailing comment change here, since this might219// trigger additional whitespace to be wrongly inserted before "line 2"220// by the comment aligner here.221//222// For a proper solution we need a mechanism to say to WhitespaceManager223// that a particular change breaks the current sequence of trailing224// comments.225OriginalWhitespaceStart != PreviousOriginalWhitespaceEnd;226}227// FIXME: The last token is currently not always an eof token; in those228// cases, setting TokenLength of the last token to 0 is wrong.229Changes.back().TokenLength = 0;230Changes.back().IsTrailingComment = Changes.back().Tok->is(tok::comment);231232const WhitespaceManager::Change *LastBlockComment = nullptr;233for (auto &Change : Changes) {234// Reset the IsTrailingComment flag for changes inside of trailing comments235// so they don't get realigned later. Comment line breaks however still need236// to be aligned.237if (Change.IsInsideToken && Change.NewlinesBefore == 0)238Change.IsTrailingComment = false;239Change.StartOfBlockComment = nullptr;240Change.IndentationOffset = 0;241if (Change.Tok->is(tok::comment)) {242if (Change.Tok->is(TT_LineComment) || !Change.IsInsideToken) {243LastBlockComment = &Change;244} else if ((Change.StartOfBlockComment = LastBlockComment)) {245Change.IndentationOffset =246Change.StartOfTokenColumn -247Change.StartOfBlockComment->StartOfTokenColumn;248}249} else {250LastBlockComment = nullptr;251}252}253254// Compute conditional nesting level255// Level is increased for each conditional, unless this conditional continues256// a chain of conditional, i.e. starts immediately after the colon of another257// conditional.258SmallVector<bool, 16> ScopeStack;259int ConditionalsLevel = 0;260for (auto &Change : Changes) {261for (unsigned i = 0, e = Change.Tok->FakeLParens.size(); i != e; ++i) {262bool isNestedConditional =263Change.Tok->FakeLParens[e - 1 - i] == prec::Conditional &&264!(i == 0 && Change.Tok->Previous &&265Change.Tok->Previous->is(TT_ConditionalExpr) &&266Change.Tok->Previous->is(tok::colon));267if (isNestedConditional)268++ConditionalsLevel;269ScopeStack.push_back(isNestedConditional);270}271272Change.ConditionalsLevel = ConditionalsLevel;273274for (unsigned i = Change.Tok->FakeRParens; i > 0 && ScopeStack.size(); --i)275if (ScopeStack.pop_back_val())276--ConditionalsLevel;277}278}279280// Align a single sequence of tokens, see AlignTokens below.281// Column - The token for which Matches returns true is moved to this column.282// RightJustify - Whether it is the token's right end or left end that gets283// moved to that column.284template <typename F>285static void286AlignTokenSequence(const FormatStyle &Style, unsigned Start, unsigned End,287unsigned Column, bool RightJustify, F &&Matches,288SmallVector<WhitespaceManager::Change, 16> &Changes) {289bool FoundMatchOnLine = false;290int Shift = 0;291292// ScopeStack keeps track of the current scope depth. It contains indices of293// the first token on each scope.294// We only run the "Matches" function on tokens from the outer-most scope.295// However, we do need to pay special attention to one class of tokens296// that are not in the outer-most scope, and that is function parameters297// which are split across multiple lines, as illustrated by this example:298// double a(int x);299// int b(int y,300// double z);301// In the above example, we need to take special care to ensure that302// 'double z' is indented along with it's owning function 'b'.303// The same holds for calling a function:304// double a = foo(x);305// int b = bar(foo(y),306// foor(z));307// Similar for broken string literals:308// double x = 3.14;309// auto s = "Hello"310// "World";311// Special handling is required for 'nested' ternary operators.312SmallVector<unsigned, 16> ScopeStack;313314for (unsigned i = Start; i != End; ++i) {315auto &CurrentChange = Changes[i];316if (ScopeStack.size() != 0 &&317CurrentChange.indentAndNestingLevel() <318Changes[ScopeStack.back()].indentAndNestingLevel()) {319ScopeStack.pop_back();320}321322// Compare current token to previous non-comment token to ensure whether323// it is in a deeper scope or not.324unsigned PreviousNonComment = i - 1;325while (PreviousNonComment > Start &&326Changes[PreviousNonComment].Tok->is(tok::comment)) {327--PreviousNonComment;328}329if (i != Start && CurrentChange.indentAndNestingLevel() >330Changes[PreviousNonComment].indentAndNestingLevel()) {331ScopeStack.push_back(i);332}333334bool InsideNestedScope = ScopeStack.size() != 0;335bool ContinuedStringLiteral = i > Start &&336CurrentChange.Tok->is(tok::string_literal) &&337Changes[i - 1].Tok->is(tok::string_literal);338bool SkipMatchCheck = InsideNestedScope || ContinuedStringLiteral;339340if (CurrentChange.NewlinesBefore > 0 && !SkipMatchCheck) {341Shift = 0;342FoundMatchOnLine = false;343}344345// If this is the first matching token to be aligned, remember by how many346// spaces it has to be shifted, so the rest of the changes on the line are347// shifted by the same amount348if (!FoundMatchOnLine && !SkipMatchCheck && Matches(CurrentChange)) {349FoundMatchOnLine = true;350Shift = Column - (RightJustify ? CurrentChange.TokenLength : 0) -351CurrentChange.StartOfTokenColumn;352CurrentChange.Spaces += Shift;353// FIXME: This is a workaround that should be removed when we fix354// http://llvm.org/PR53699. An assertion later below verifies this.355if (CurrentChange.NewlinesBefore == 0) {356CurrentChange.Spaces =357std::max(CurrentChange.Spaces,358static_cast<int>(CurrentChange.Tok->SpacesRequiredBefore));359}360}361362if (Shift == 0)363continue;364365// This is for function parameters that are split across multiple lines,366// as mentioned in the ScopeStack comment.367if (InsideNestedScope && CurrentChange.NewlinesBefore > 0) {368unsigned ScopeStart = ScopeStack.back();369auto ShouldShiftBeAdded = [&] {370// Function declaration371if (Changes[ScopeStart - 1].Tok->is(TT_FunctionDeclarationName))372return true;373374// Lambda.375if (Changes[ScopeStart - 1].Tok->is(TT_LambdaLBrace))376return false;377378// Continued function declaration379if (ScopeStart > Start + 1 &&380Changes[ScopeStart - 2].Tok->is(TT_FunctionDeclarationName)) {381return true;382}383384// Continued (template) function call.385if (ScopeStart > Start + 1 &&386Changes[ScopeStart - 2].Tok->isOneOf(tok::identifier,387TT_TemplateCloser) &&388Changes[ScopeStart - 1].Tok->is(tok::l_paren) &&389Changes[ScopeStart].Tok->isNot(TT_LambdaLSquare)) {390if (CurrentChange.Tok->MatchingParen &&391CurrentChange.Tok->MatchingParen->is(TT_LambdaLBrace)) {392return false;393}394if (Changes[ScopeStart].NewlinesBefore > 0)395return false;396if (CurrentChange.Tok->is(tok::l_brace) &&397CurrentChange.Tok->is(BK_BracedInit)) {398return true;399}400return Style.BinPackArguments;401}402403// Ternary operator404if (CurrentChange.Tok->is(TT_ConditionalExpr))405return true;406407// Period Initializer .XXX = 1.408if (CurrentChange.Tok->is(TT_DesignatedInitializerPeriod))409return true;410411// Continued ternary operator412if (CurrentChange.Tok->Previous &&413CurrentChange.Tok->Previous->is(TT_ConditionalExpr)) {414return true;415}416417// Continued direct-list-initialization using braced list.418if (ScopeStart > Start + 1 &&419Changes[ScopeStart - 2].Tok->is(tok::identifier) &&420Changes[ScopeStart - 1].Tok->is(tok::l_brace) &&421CurrentChange.Tok->is(tok::l_brace) &&422CurrentChange.Tok->is(BK_BracedInit)) {423return true;424}425426// Continued braced list.427if (ScopeStart > Start + 1 &&428Changes[ScopeStart - 2].Tok->isNot(tok::identifier) &&429Changes[ScopeStart - 1].Tok->is(tok::l_brace) &&430CurrentChange.Tok->isNot(tok::r_brace)) {431for (unsigned OuterScopeStart : llvm::reverse(ScopeStack)) {432// Lambda.433if (OuterScopeStart > Start &&434Changes[OuterScopeStart - 1].Tok->is(TT_LambdaLBrace)) {435return false;436}437}438if (Changes[ScopeStart].NewlinesBefore > 0)439return false;440return true;441}442443// Continued template parameter.444if (Changes[ScopeStart - 1].Tok->is(TT_TemplateOpener))445return true;446447return false;448};449450if (ShouldShiftBeAdded())451CurrentChange.Spaces += Shift;452}453454if (ContinuedStringLiteral)455CurrentChange.Spaces += Shift;456457// We should not remove required spaces unless we break the line before.458assert(Shift > 0 || Changes[i].NewlinesBefore > 0 ||459CurrentChange.Spaces >=460static_cast<int>(Changes[i].Tok->SpacesRequiredBefore) ||461CurrentChange.Tok->is(tok::eof));462463CurrentChange.StartOfTokenColumn += Shift;464if (i + 1 != Changes.size())465Changes[i + 1].PreviousEndOfTokenColumn += Shift;466467// If PointerAlignment is PAS_Right, keep *s or &s next to the token,468// except if the token is equal, then a space is needed.469if ((Style.PointerAlignment == FormatStyle::PAS_Right ||470Style.ReferenceAlignment == FormatStyle::RAS_Right) &&471CurrentChange.Spaces != 0 &&472!CurrentChange.Tok->isOneOf(tok::equal, tok::r_paren,473TT_TemplateCloser)) {474const bool ReferenceNotRightAligned =475Style.ReferenceAlignment != FormatStyle::RAS_Right &&476Style.ReferenceAlignment != FormatStyle::RAS_Pointer;477for (int Previous = i - 1;478Previous >= 0 && Changes[Previous].Tok->is(TT_PointerOrReference);479--Previous) {480assert(Changes[Previous].Tok->isPointerOrReference());481if (Changes[Previous].Tok->isNot(tok::star)) {482if (ReferenceNotRightAligned)483continue;484} else if (Style.PointerAlignment != FormatStyle::PAS_Right) {485continue;486}487Changes[Previous + 1].Spaces -= Shift;488Changes[Previous].Spaces += Shift;489Changes[Previous].StartOfTokenColumn += Shift;490}491}492}493}494495// Walk through a subset of the changes, starting at StartAt, and find496// sequences of matching tokens to align. To do so, keep track of the lines and497// whether or not a matching token was found on a line. If a matching token is498// found, extend the current sequence. If the current line cannot be part of a499// sequence, e.g. because there is an empty line before it or it contains only500// non-matching tokens, finalize the previous sequence.501// The value returned is the token on which we stopped, either because we502// exhausted all items inside Changes, or because we hit a scope level higher503// than our initial scope.504// This function is recursive. Each invocation processes only the scope level505// equal to the initial level, which is the level of Changes[StartAt].506// If we encounter a scope level greater than the initial level, then we call507// ourselves recursively, thereby avoiding the pollution of the current state508// with the alignment requirements of the nested sub-level. This recursive509// behavior is necessary for aligning function prototypes that have one or more510// arguments.511// If this function encounters a scope level less than the initial level,512// it returns the current position.513// There is a non-obvious subtlety in the recursive behavior: Even though we514// defer processing of nested levels to recursive invocations of this515// function, when it comes time to align a sequence of tokens, we run the516// alignment on the entire sequence, including the nested levels.517// When doing so, most of the nested tokens are skipped, because their518// alignment was already handled by the recursive invocations of this function.519// However, the special exception is that we do NOT skip function parameters520// that are split across multiple lines. See the test case in FormatTest.cpp521// that mentions "split function parameter alignment" for an example of this.522// When the parameter RightJustify is true, the operator will be523// right-justified. It is used to align compound assignments like `+=` and `=`.524// When RightJustify and ACS.PadOperators are true, operators in each block to525// be aligned will be padded on the left to the same length before aligning.526template <typename F>527static unsigned AlignTokens(const FormatStyle &Style, F &&Matches,528SmallVector<WhitespaceManager::Change, 16> &Changes,529unsigned StartAt,530const FormatStyle::AlignConsecutiveStyle &ACS = {},531bool RightJustify = false) {532// We arrange each line in 3 parts. The operator to be aligned (the anchor),533// and text to its left and right. In the aligned text the width of each part534// will be the maximum of that over the block that has been aligned. Maximum535// widths of each part so far. When RightJustify is true and ACS.PadOperators536// is false, the part from start of line to the right end of the anchor.537// Otherwise, only the part to the left of the anchor. Including the space538// that exists on its left from the start. Not including the padding added on539// the left to right-justify the anchor.540unsigned WidthLeft = 0;541// The operator to be aligned when RightJustify is true and ACS.PadOperators542// is false. 0 otherwise.543unsigned WidthAnchor = 0;544// Width to the right of the anchor. Plus width of the anchor when545// RightJustify is false.546unsigned WidthRight = 0;547548// Line number of the start and the end of the current token sequence.549unsigned StartOfSequence = 0;550unsigned EndOfSequence = 0;551552// Measure the scope level (i.e. depth of (), [], {}) of the first token, and553// abort when we hit any token in a higher scope than the starting one.554auto IndentAndNestingLevel = StartAt < Changes.size()555? Changes[StartAt].indentAndNestingLevel()556: std::tuple<unsigned, unsigned, unsigned>();557558// Keep track of the number of commas before the matching tokens, we will only559// align a sequence of matching tokens if they are preceded by the same number560// of commas.561unsigned CommasBeforeLastMatch = 0;562unsigned CommasBeforeMatch = 0;563564// Whether a matching token has been found on the current line.565bool FoundMatchOnLine = false;566567// Whether the current line consists purely of comments.568bool LineIsComment = true;569570// Aligns a sequence of matching tokens, on the MinColumn column.571//572// Sequences start from the first matching token to align, and end at the573// first token of the first line that doesn't need to be aligned.574//575// We need to adjust the StartOfTokenColumn of each Change that is on a line576// containing any matching token to be aligned and located after such token.577auto AlignCurrentSequence = [&] {578if (StartOfSequence > 0 && StartOfSequence < EndOfSequence) {579AlignTokenSequence(Style, StartOfSequence, EndOfSequence,580WidthLeft + WidthAnchor, RightJustify, Matches,581Changes);582}583WidthLeft = 0;584WidthAnchor = 0;585WidthRight = 0;586StartOfSequence = 0;587EndOfSequence = 0;588};589590unsigned i = StartAt;591for (unsigned e = Changes.size(); i != e; ++i) {592auto &CurrentChange = Changes[i];593if (CurrentChange.indentAndNestingLevel() < IndentAndNestingLevel)594break;595596if (CurrentChange.NewlinesBefore != 0) {597CommasBeforeMatch = 0;598EndOfSequence = i;599600// Whether to break the alignment sequence because of an empty line.601bool EmptyLineBreak =602(CurrentChange.NewlinesBefore > 1) && !ACS.AcrossEmptyLines;603604// Whether to break the alignment sequence because of a line without a605// match.606bool NoMatchBreak =607!FoundMatchOnLine && !(LineIsComment && ACS.AcrossComments);608609if (EmptyLineBreak || NoMatchBreak)610AlignCurrentSequence();611612// A new line starts, re-initialize line status tracking bools.613// Keep the match state if a string literal is continued on this line.614if (i == 0 || CurrentChange.Tok->isNot(tok::string_literal) ||615Changes[i - 1].Tok->isNot(tok::string_literal)) {616FoundMatchOnLine = false;617}618LineIsComment = true;619}620621if (CurrentChange.Tok->isNot(tok::comment))622LineIsComment = false;623624if (CurrentChange.Tok->is(tok::comma)) {625++CommasBeforeMatch;626} else if (CurrentChange.indentAndNestingLevel() > IndentAndNestingLevel) {627// Call AlignTokens recursively, skipping over this scope block.628unsigned StoppedAt =629AlignTokens(Style, Matches, Changes, i, ACS, RightJustify);630i = StoppedAt - 1;631continue;632}633634if (!Matches(CurrentChange))635continue;636637// If there is more than one matching token per line, or if the number of638// preceding commas, do not match anymore, end the sequence.639if (FoundMatchOnLine || CommasBeforeMatch != CommasBeforeLastMatch)640AlignCurrentSequence();641642CommasBeforeLastMatch = CommasBeforeMatch;643FoundMatchOnLine = true;644645if (StartOfSequence == 0)646StartOfSequence = i;647648unsigned ChangeWidthLeft = CurrentChange.StartOfTokenColumn;649unsigned ChangeWidthAnchor = 0;650unsigned ChangeWidthRight = 0;651if (RightJustify)652if (ACS.PadOperators)653ChangeWidthAnchor = CurrentChange.TokenLength;654else655ChangeWidthLeft += CurrentChange.TokenLength;656else657ChangeWidthRight = CurrentChange.TokenLength;658for (unsigned j = i + 1; j != e && Changes[j].NewlinesBefore == 0; ++j) {659ChangeWidthRight += Changes[j].Spaces;660// Changes are generally 1:1 with the tokens, but a change could also be661// inside of a token, in which case it's counted more than once: once for662// the whitespace surrounding the token (!IsInsideToken) and once for663// each whitespace change within it (IsInsideToken).664// Therefore, changes inside of a token should only count the space.665if (!Changes[j].IsInsideToken)666ChangeWidthRight += Changes[j].TokenLength;667}668669// If we are restricted by the maximum column width, end the sequence.670unsigned NewLeft = std::max(ChangeWidthLeft, WidthLeft);671unsigned NewAnchor = std::max(ChangeWidthAnchor, WidthAnchor);672unsigned NewRight = std::max(ChangeWidthRight, WidthRight);673// `ColumnLimit == 0` means there is no column limit.674if (Style.ColumnLimit != 0 &&675Style.ColumnLimit < NewLeft + NewAnchor + NewRight) {676AlignCurrentSequence();677StartOfSequence = i;678WidthLeft = ChangeWidthLeft;679WidthAnchor = ChangeWidthAnchor;680WidthRight = ChangeWidthRight;681} else {682WidthLeft = NewLeft;683WidthAnchor = NewAnchor;684WidthRight = NewRight;685}686}687688EndOfSequence = i;689AlignCurrentSequence();690return i;691}692693// Aligns a sequence of matching tokens, on the MinColumn column.694//695// Sequences start from the first matching token to align, and end at the696// first token of the first line that doesn't need to be aligned.697//698// We need to adjust the StartOfTokenColumn of each Change that is on a line699// containing any matching token to be aligned and located after such token.700static void AlignMatchingTokenSequence(701unsigned &StartOfSequence, unsigned &EndOfSequence, unsigned &MinColumn,702std::function<bool(const WhitespaceManager::Change &C)> Matches,703SmallVector<WhitespaceManager::Change, 16> &Changes) {704if (StartOfSequence > 0 && StartOfSequence < EndOfSequence) {705bool FoundMatchOnLine = false;706int Shift = 0;707708for (unsigned I = StartOfSequence; I != EndOfSequence; ++I) {709if (Changes[I].NewlinesBefore > 0) {710Shift = 0;711FoundMatchOnLine = false;712}713714// If this is the first matching token to be aligned, remember by how many715// spaces it has to be shifted, so the rest of the changes on the line are716// shifted by the same amount.717if (!FoundMatchOnLine && Matches(Changes[I])) {718FoundMatchOnLine = true;719Shift = MinColumn - Changes[I].StartOfTokenColumn;720Changes[I].Spaces += Shift;721}722723assert(Shift >= 0);724Changes[I].StartOfTokenColumn += Shift;725if (I + 1 != Changes.size())726Changes[I + 1].PreviousEndOfTokenColumn += Shift;727}728}729730MinColumn = 0;731StartOfSequence = 0;732EndOfSequence = 0;733}734735void WhitespaceManager::alignConsecutiveMacros() {736if (!Style.AlignConsecutiveMacros.Enabled)737return;738739auto AlignMacrosMatches = [](const Change &C) {740const FormatToken *Current = C.Tok;741unsigned SpacesRequiredBefore = 1;742743if (Current->SpacesRequiredBefore == 0 || !Current->Previous)744return false;745746Current = Current->Previous;747748// If token is a ")", skip over the parameter list, to the749// token that precedes the "("750if (Current->is(tok::r_paren) && Current->MatchingParen) {751Current = Current->MatchingParen->Previous;752SpacesRequiredBefore = 0;753}754755if (!Current || Current->isNot(tok::identifier))756return false;757758if (!Current->Previous || Current->Previous->isNot(tok::pp_define))759return false;760761// For a macro function, 0 spaces are required between the762// identifier and the lparen that opens the parameter list.763// For a simple macro, 1 space is required between the764// identifier and the first token of the defined value.765return Current->Next->SpacesRequiredBefore == SpacesRequiredBefore;766};767768unsigned MinColumn = 0;769770// Start and end of the token sequence we're processing.771unsigned StartOfSequence = 0;772unsigned EndOfSequence = 0;773774// Whether a matching token has been found on the current line.775bool FoundMatchOnLine = false;776777// Whether the current line consists only of comments778bool LineIsComment = true;779780unsigned I = 0;781for (unsigned E = Changes.size(); I != E; ++I) {782if (Changes[I].NewlinesBefore != 0) {783EndOfSequence = I;784785// Whether to break the alignment sequence because of an empty line.786bool EmptyLineBreak = (Changes[I].NewlinesBefore > 1) &&787!Style.AlignConsecutiveMacros.AcrossEmptyLines;788789// Whether to break the alignment sequence because of a line without a790// match.791bool NoMatchBreak =792!FoundMatchOnLine &&793!(LineIsComment && Style.AlignConsecutiveMacros.AcrossComments);794795if (EmptyLineBreak || NoMatchBreak) {796AlignMatchingTokenSequence(StartOfSequence, EndOfSequence, MinColumn,797AlignMacrosMatches, Changes);798}799800// A new line starts, re-initialize line status tracking bools.801FoundMatchOnLine = false;802LineIsComment = true;803}804805if (Changes[I].Tok->isNot(tok::comment))806LineIsComment = false;807808if (!AlignMacrosMatches(Changes[I]))809continue;810811FoundMatchOnLine = true;812813if (StartOfSequence == 0)814StartOfSequence = I;815816unsigned ChangeMinColumn = Changes[I].StartOfTokenColumn;817MinColumn = std::max(MinColumn, ChangeMinColumn);818}819820EndOfSequence = I;821AlignMatchingTokenSequence(StartOfSequence, EndOfSequence, MinColumn,822AlignMacrosMatches, Changes);823}824825void WhitespaceManager::alignConsecutiveAssignments() {826if (!Style.AlignConsecutiveAssignments.Enabled)827return;828829AlignTokens(830Style,831[&](const Change &C) {832// Do not align on equal signs that are first on a line.833if (C.NewlinesBefore > 0)834return false;835836// Do not align on equal signs that are last on a line.837if (&C != &Changes.back() && (&C + 1)->NewlinesBefore > 0)838return false;839840// Do not align operator= overloads.841FormatToken *Previous = C.Tok->getPreviousNonComment();842if (Previous && Previous->is(tok::kw_operator))843return false;844845return Style.AlignConsecutiveAssignments.AlignCompound846? C.Tok->getPrecedence() == prec::Assignment847: (C.Tok->is(tok::equal) ||848// In Verilog the '<=' is not a compound assignment, thus849// it is aligned even when the AlignCompound option is not850// set.851(Style.isVerilog() && C.Tok->is(tok::lessequal) &&852C.Tok->getPrecedence() == prec::Assignment));853},854Changes, /*StartAt=*/0, Style.AlignConsecutiveAssignments,855/*RightJustify=*/true);856}857858void WhitespaceManager::alignConsecutiveBitFields() {859alignConsecutiveColons(Style.AlignConsecutiveBitFields, TT_BitFieldColon);860}861862void WhitespaceManager::alignConsecutiveColons(863const FormatStyle::AlignConsecutiveStyle &AlignStyle, TokenType Type) {864if (!AlignStyle.Enabled)865return;866867AlignTokens(868Style,869[&](Change const &C) {870// Do not align on ':' that is first on a line.871if (C.NewlinesBefore > 0)872return false;873874// Do not align on ':' that is last on a line.875if (&C != &Changes.back() && (&C + 1)->NewlinesBefore > 0)876return false;877878return C.Tok->is(Type);879},880Changes, /*StartAt=*/0, AlignStyle);881}882883void WhitespaceManager::alignConsecutiveShortCaseStatements(bool IsExpr) {884if (!Style.AlignConsecutiveShortCaseStatements.Enabled ||885!(IsExpr ? Style.AllowShortCaseExpressionOnASingleLine886: Style.AllowShortCaseLabelsOnASingleLine)) {887return;888}889890const auto Type = IsExpr ? TT_CaseLabelArrow : TT_CaseLabelColon;891const auto &Option = Style.AlignConsecutiveShortCaseStatements;892const bool AlignArrowOrColon =893IsExpr ? Option.AlignCaseArrows : Option.AlignCaseColons;894895auto Matches = [&](const Change &C) {896if (AlignArrowOrColon)897return C.Tok->is(Type);898899// Ignore 'IsInsideToken' to allow matching trailing comments which900// need to be reflowed as that causes the token to appear in two901// different changes, which will cause incorrect alignment as we'll902// reflow early due to detecting multiple aligning tokens per line.903return !C.IsInsideToken && C.Tok->Previous && C.Tok->Previous->is(Type);904};905906unsigned MinColumn = 0;907908// Empty case statements don't break the alignment, but don't necessarily909// match our predicate, so we need to track their column so they can push out910// our alignment.911unsigned MinEmptyCaseColumn = 0;912913// Start and end of the token sequence we're processing.914unsigned StartOfSequence = 0;915unsigned EndOfSequence = 0;916917// Whether a matching token has been found on the current line.918bool FoundMatchOnLine = false;919920bool LineIsComment = true;921bool LineIsEmptyCase = false;922923unsigned I = 0;924for (unsigned E = Changes.size(); I != E; ++I) {925if (Changes[I].NewlinesBefore != 0) {926// Whether to break the alignment sequence because of an empty line.927bool EmptyLineBreak =928(Changes[I].NewlinesBefore > 1) &&929!Style.AlignConsecutiveShortCaseStatements.AcrossEmptyLines;930931// Whether to break the alignment sequence because of a line without a932// match.933bool NoMatchBreak =934!FoundMatchOnLine &&935!(LineIsComment &&936Style.AlignConsecutiveShortCaseStatements.AcrossComments) &&937!LineIsEmptyCase;938939if (EmptyLineBreak || NoMatchBreak) {940AlignMatchingTokenSequence(StartOfSequence, EndOfSequence, MinColumn,941Matches, Changes);942MinEmptyCaseColumn = 0;943}944945// A new line starts, re-initialize line status tracking bools.946FoundMatchOnLine = false;947LineIsComment = true;948LineIsEmptyCase = false;949}950951if (Changes[I].Tok->isNot(tok::comment))952LineIsComment = false;953954if (Changes[I].Tok->is(Type)) {955LineIsEmptyCase =956!Changes[I].Tok->Next || Changes[I].Tok->Next->isTrailingComment();957958if (LineIsEmptyCase) {959if (Style.AlignConsecutiveShortCaseStatements.AlignCaseColons) {960MinEmptyCaseColumn =961std::max(MinEmptyCaseColumn, Changes[I].StartOfTokenColumn);962} else {963MinEmptyCaseColumn =964std::max(MinEmptyCaseColumn, Changes[I].StartOfTokenColumn + 2);965}966}967}968969if (!Matches(Changes[I]))970continue;971972if (LineIsEmptyCase)973continue;974975FoundMatchOnLine = true;976977if (StartOfSequence == 0)978StartOfSequence = I;979980EndOfSequence = I + 1;981982MinColumn = std::max(MinColumn, Changes[I].StartOfTokenColumn);983984// Allow empty case statements to push out our alignment.985MinColumn = std::max(MinColumn, MinEmptyCaseColumn);986}987988AlignMatchingTokenSequence(StartOfSequence, EndOfSequence, MinColumn, Matches,989Changes);990}991992void WhitespaceManager::alignConsecutiveTableGenBreakingDAGArgColons() {993alignConsecutiveColons(Style.AlignConsecutiveTableGenBreakingDAGArgColons,994TT_TableGenDAGArgListColonToAlign);995}996997void WhitespaceManager::alignConsecutiveTableGenCondOperatorColons() {998alignConsecutiveColons(Style.AlignConsecutiveTableGenCondOperatorColons,999TT_TableGenCondOperatorColon);1000}10011002void WhitespaceManager::alignConsecutiveTableGenDefinitions() {1003alignConsecutiveColons(Style.AlignConsecutiveTableGenDefinitionColons,1004TT_InheritanceColon);1005}10061007void WhitespaceManager::alignConsecutiveDeclarations() {1008if (!Style.AlignConsecutiveDeclarations.Enabled)1009return;10101011AlignTokens(1012Style,1013[&](Change const &C) {1014if (Style.AlignConsecutiveDeclarations.AlignFunctionPointers) {1015for (const auto *Prev = C.Tok->Previous; Prev; Prev = Prev->Previous)1016if (Prev->is(tok::equal))1017return false;1018if (C.Tok->is(TT_FunctionTypeLParen))1019return true;1020}1021if (C.Tok->is(TT_FunctionDeclarationName))1022return true;1023if (C.Tok->isNot(TT_StartOfName))1024return false;1025if (C.Tok->Previous &&1026C.Tok->Previous->is(TT_StatementAttributeLikeMacro))1027return false;1028// Check if there is a subsequent name that starts the same declaration.1029for (FormatToken *Next = C.Tok->Next; Next; Next = Next->Next) {1030if (Next->is(tok::comment))1031continue;1032if (Next->is(TT_PointerOrReference))1033return false;1034if (!Next->Tok.getIdentifierInfo())1035break;1036if (Next->isOneOf(TT_StartOfName, TT_FunctionDeclarationName,1037tok::kw_operator)) {1038return false;1039}1040}1041return true;1042},1043Changes, /*StartAt=*/0, Style.AlignConsecutiveDeclarations);1044}10451046void WhitespaceManager::alignChainedConditionals() {1047if (Style.BreakBeforeTernaryOperators) {1048AlignTokens(1049Style,1050[](Change const &C) {1051// Align question operators and last colon1052return C.Tok->is(TT_ConditionalExpr) &&1053((C.Tok->is(tok::question) && !C.NewlinesBefore) ||1054(C.Tok->is(tok::colon) && C.Tok->Next &&1055(C.Tok->Next->FakeLParens.size() == 0 ||1056C.Tok->Next->FakeLParens.back() != prec::Conditional)));1057},1058Changes, /*StartAt=*/0);1059} else {1060static auto AlignWrappedOperand = [](Change const &C) {1061FormatToken *Previous = C.Tok->getPreviousNonComment();1062return C.NewlinesBefore && Previous && Previous->is(TT_ConditionalExpr) &&1063(Previous->is(tok::colon) &&1064(C.Tok->FakeLParens.size() == 0 ||1065C.Tok->FakeLParens.back() != prec::Conditional));1066};1067// Ensure we keep alignment of wrapped operands with non-wrapped operands1068// Since we actually align the operators, the wrapped operands need the1069// extra offset to be properly aligned.1070for (Change &C : Changes)1071if (AlignWrappedOperand(C))1072C.StartOfTokenColumn -= 2;1073AlignTokens(1074Style,1075[this](Change const &C) {1076// Align question operators if next operand is not wrapped, as1077// well as wrapped operands after question operator or last1078// colon in conditional sequence1079return (C.Tok->is(TT_ConditionalExpr) && C.Tok->is(tok::question) &&1080&C != &Changes.back() && (&C + 1)->NewlinesBefore == 0 &&1081!(&C + 1)->IsTrailingComment) ||1082AlignWrappedOperand(C);1083},1084Changes, /*StartAt=*/0);1085}1086}10871088void WhitespaceManager::alignTrailingComments() {1089if (Style.AlignTrailingComments.Kind == FormatStyle::TCAS_Never)1090return;10911092const int Size = Changes.size();1093int MinColumn = 0;1094int StartOfSequence = 0;1095bool BreakBeforeNext = false;1096int NewLineThreshold = 1;1097if (Style.AlignTrailingComments.Kind == FormatStyle::TCAS_Always)1098NewLineThreshold = Style.AlignTrailingComments.OverEmptyLines + 1;10991100for (int I = 0, MaxColumn = INT_MAX, Newlines = 0; I < Size; ++I) {1101auto &C = Changes[I];1102if (C.StartOfBlockComment)1103continue;1104Newlines += C.NewlinesBefore;1105if (!C.IsTrailingComment)1106continue;11071108if (Style.AlignTrailingComments.Kind == FormatStyle::TCAS_Leave) {1109const int OriginalSpaces =1110C.OriginalWhitespaceRange.getEnd().getRawEncoding() -1111C.OriginalWhitespaceRange.getBegin().getRawEncoding() -1112C.Tok->LastNewlineOffset;1113assert(OriginalSpaces >= 0);1114const auto RestoredLineLength =1115C.StartOfTokenColumn + C.TokenLength + OriginalSpaces;1116// If leaving comments makes the line exceed the column limit, give up to1117// leave the comments.1118if (RestoredLineLength >= Style.ColumnLimit && Style.ColumnLimit > 0)1119break;1120C.Spaces = C.NewlinesBefore > 0 ? C.Tok->OriginalColumn : OriginalSpaces;1121continue;1122}11231124const int ChangeMinColumn = C.StartOfTokenColumn;1125int ChangeMaxColumn;11261127// If we don't create a replacement for this change, we have to consider1128// it to be immovable.1129if (!C.CreateReplacement)1130ChangeMaxColumn = ChangeMinColumn;1131else if (Style.ColumnLimit == 0)1132ChangeMaxColumn = INT_MAX;1133else if (Style.ColumnLimit >= C.TokenLength)1134ChangeMaxColumn = Style.ColumnLimit - C.TokenLength;1135else1136ChangeMaxColumn = ChangeMinColumn;11371138if (I + 1 < Size && Changes[I + 1].ContinuesPPDirective &&1139ChangeMaxColumn >= 2) {1140ChangeMaxColumn -= 2;1141}11421143bool WasAlignedWithStartOfNextLine = false;1144if (C.NewlinesBefore >= 1) { // A comment on its own line.1145const auto CommentColumn =1146SourceMgr.getSpellingColumnNumber(C.OriginalWhitespaceRange.getEnd());1147for (int J = I + 1; J < Size; ++J) {1148if (Changes[J].Tok->is(tok::comment))1149continue;11501151const auto NextColumn = SourceMgr.getSpellingColumnNumber(1152Changes[J].OriginalWhitespaceRange.getEnd());1153// The start of the next token was previously aligned with the1154// start of this comment.1155WasAlignedWithStartOfNextLine =1156CommentColumn == NextColumn ||1157CommentColumn == NextColumn + Style.IndentWidth;1158break;1159}1160}11611162// We don't want to align comments which end a scope, which are here1163// identified by most closing braces.1164auto DontAlignThisComment = [](const auto *Tok) {1165if (Tok->is(tok::semi)) {1166Tok = Tok->getPreviousNonComment();1167if (!Tok)1168return false;1169}1170if (Tok->is(tok::r_paren)) {1171// Back up past the parentheses and a `TT_DoWhile` that may precede.1172Tok = Tok->MatchingParen;1173if (!Tok)1174return false;1175Tok = Tok->getPreviousNonComment();1176if (!Tok)1177return false;1178if (Tok->is(TT_DoWhile)) {1179const auto *Prev = Tok->getPreviousNonComment();1180if (!Prev) {1181// A do-while-loop without braces.1182return true;1183}1184Tok = Prev;1185}1186}11871188if (Tok->isNot(tok::r_brace))1189return false;11901191while (Tok->Previous && Tok->Previous->is(tok::r_brace))1192Tok = Tok->Previous;1193return Tok->NewlinesBefore > 0;1194};11951196if (I > 0 && C.NewlinesBefore == 0 &&1197DontAlignThisComment(Changes[I - 1].Tok)) {1198alignTrailingComments(StartOfSequence, I, MinColumn);1199// Reset to initial values, but skip this change for the next alignment1200// pass.1201MinColumn = 0;1202MaxColumn = INT_MAX;1203StartOfSequence = I + 1;1204} else if (BreakBeforeNext || Newlines > NewLineThreshold ||1205(ChangeMinColumn > MaxColumn || ChangeMaxColumn < MinColumn) ||1206// Break the comment sequence if the previous line did not end1207// in a trailing comment.1208(C.NewlinesBefore == 1 && I > 0 &&1209!Changes[I - 1].IsTrailingComment) ||1210WasAlignedWithStartOfNextLine) {1211alignTrailingComments(StartOfSequence, I, MinColumn);1212MinColumn = ChangeMinColumn;1213MaxColumn = ChangeMaxColumn;1214StartOfSequence = I;1215} else {1216MinColumn = std::max(MinColumn, ChangeMinColumn);1217MaxColumn = std::min(MaxColumn, ChangeMaxColumn);1218}1219BreakBeforeNext = (I == 0) || (C.NewlinesBefore > 1) ||1220// Never start a sequence with a comment at the beginning1221// of the line.1222(C.NewlinesBefore == 1 && StartOfSequence == I);1223Newlines = 0;1224}1225alignTrailingComments(StartOfSequence, Size, MinColumn);1226}12271228void WhitespaceManager::alignTrailingComments(unsigned Start, unsigned End,1229unsigned Column) {1230for (unsigned i = Start; i != End; ++i) {1231int Shift = 0;1232if (Changes[i].IsTrailingComment)1233Shift = Column - Changes[i].StartOfTokenColumn;1234if (Changes[i].StartOfBlockComment) {1235Shift = Changes[i].IndentationOffset +1236Changes[i].StartOfBlockComment->StartOfTokenColumn -1237Changes[i].StartOfTokenColumn;1238}1239if (Shift <= 0)1240continue;1241Changes[i].Spaces += Shift;1242if (i + 1 != Changes.size())1243Changes[i + 1].PreviousEndOfTokenColumn += Shift;1244Changes[i].StartOfTokenColumn += Shift;1245}1246}12471248void WhitespaceManager::alignEscapedNewlines() {1249const auto Align = Style.AlignEscapedNewlines;1250if (Align == FormatStyle::ENAS_DontAlign)1251return;12521253const bool WithLastLine = Align == FormatStyle::ENAS_LeftWithLastLine;1254const bool AlignLeft = Align == FormatStyle::ENAS_Left || WithLastLine;1255const auto MaxColumn = Style.ColumnLimit;1256unsigned MaxEndOfLine = AlignLeft ? 0 : MaxColumn;1257unsigned StartOfMacro = 0;1258for (unsigned i = 1, e = Changes.size(); i < e; ++i) {1259Change &C = Changes[i];1260if (C.NewlinesBefore == 0 && (!WithLastLine || C.Tok->isNot(tok::eof)))1261continue;1262const bool InPPDirective = C.ContinuesPPDirective;1263const auto BackslashColumn = C.PreviousEndOfTokenColumn + 2;1264if (InPPDirective ||1265(WithLastLine && (MaxColumn == 0 || BackslashColumn <= MaxColumn))) {1266MaxEndOfLine = std::max(BackslashColumn, MaxEndOfLine);1267}1268if (!InPPDirective) {1269alignEscapedNewlines(StartOfMacro + 1, i, MaxEndOfLine);1270MaxEndOfLine = AlignLeft ? 0 : MaxColumn;1271StartOfMacro = i;1272}1273}1274alignEscapedNewlines(StartOfMacro + 1, Changes.size(), MaxEndOfLine);1275}12761277void WhitespaceManager::alignEscapedNewlines(unsigned Start, unsigned End,1278unsigned Column) {1279for (unsigned i = Start; i < End; ++i) {1280Change &C = Changes[i];1281if (C.NewlinesBefore > 0) {1282assert(C.ContinuesPPDirective);1283if (C.PreviousEndOfTokenColumn + 1 > Column)1284C.EscapedNewlineColumn = 0;1285else1286C.EscapedNewlineColumn = Column;1287}1288}1289}12901291void WhitespaceManager::alignArrayInitializers() {1292if (Style.AlignArrayOfStructures == FormatStyle::AIAS_None)1293return;12941295for (unsigned ChangeIndex = 1U, ChangeEnd = Changes.size();1296ChangeIndex < ChangeEnd; ++ChangeIndex) {1297auto &C = Changes[ChangeIndex];1298if (C.Tok->IsArrayInitializer) {1299bool FoundComplete = false;1300for (unsigned InsideIndex = ChangeIndex + 1; InsideIndex < ChangeEnd;1301++InsideIndex) {1302if (Changes[InsideIndex].Tok == C.Tok->MatchingParen) {1303alignArrayInitializers(ChangeIndex, InsideIndex + 1);1304ChangeIndex = InsideIndex + 1;1305FoundComplete = true;1306break;1307}1308}1309if (!FoundComplete)1310ChangeIndex = ChangeEnd;1311}1312}1313}13141315void WhitespaceManager::alignArrayInitializers(unsigned Start, unsigned End) {13161317if (Style.AlignArrayOfStructures == FormatStyle::AIAS_Right)1318alignArrayInitializersRightJustified(getCells(Start, End));1319else if (Style.AlignArrayOfStructures == FormatStyle::AIAS_Left)1320alignArrayInitializersLeftJustified(getCells(Start, End));1321}13221323void WhitespaceManager::alignArrayInitializersRightJustified(1324CellDescriptions &&CellDescs) {1325if (!CellDescs.isRectangular())1326return;13271328const int BracePadding = Style.Cpp11BracedListStyle ? 0 : 1;1329auto &Cells = CellDescs.Cells;1330// Now go through and fixup the spaces.1331auto *CellIter = Cells.begin();1332for (auto i = 0U; i < CellDescs.CellCounts[0]; ++i, ++CellIter) {1333unsigned NetWidth = 0U;1334if (isSplitCell(*CellIter))1335NetWidth = getNetWidth(Cells.begin(), CellIter, CellDescs.InitialSpaces);1336auto CellWidth = getMaximumCellWidth(CellIter, NetWidth);13371338if (Changes[CellIter->Index].Tok->is(tok::r_brace)) {1339// So in here we want to see if there is a brace that falls1340// on a line that was split. If so on that line we make sure that1341// the spaces in front of the brace are enough.1342const auto *Next = CellIter;1343do {1344const FormatToken *Previous = Changes[Next->Index].Tok->Previous;1345if (Previous && Previous->isNot(TT_LineComment)) {1346Changes[Next->Index].Spaces = BracePadding;1347Changes[Next->Index].NewlinesBefore = 0;1348}1349Next = Next->NextColumnElement;1350} while (Next);1351// Unless the array is empty, we need the position of all the1352// immediately adjacent cells1353if (CellIter != Cells.begin()) {1354auto ThisNetWidth =1355getNetWidth(Cells.begin(), CellIter, CellDescs.InitialSpaces);1356auto MaxNetWidth = getMaximumNetWidth(1357Cells.begin(), CellIter, CellDescs.InitialSpaces,1358CellDescs.CellCounts[0], CellDescs.CellCounts.size());1359if (ThisNetWidth < MaxNetWidth)1360Changes[CellIter->Index].Spaces = (MaxNetWidth - ThisNetWidth);1361auto RowCount = 1U;1362auto Offset = std::distance(Cells.begin(), CellIter);1363for (const auto *Next = CellIter->NextColumnElement; Next;1364Next = Next->NextColumnElement) {1365if (RowCount >= CellDescs.CellCounts.size())1366break;1367auto *Start = (Cells.begin() + RowCount * CellDescs.CellCounts[0]);1368auto *End = Start + Offset;1369ThisNetWidth = getNetWidth(Start, End, CellDescs.InitialSpaces);1370if (ThisNetWidth < MaxNetWidth)1371Changes[Next->Index].Spaces = (MaxNetWidth - ThisNetWidth);1372++RowCount;1373}1374}1375} else {1376auto ThisWidth =1377calculateCellWidth(CellIter->Index, CellIter->EndIndex, true) +1378NetWidth;1379if (Changes[CellIter->Index].NewlinesBefore == 0) {1380Changes[CellIter->Index].Spaces = (CellWidth - (ThisWidth + NetWidth));1381Changes[CellIter->Index].Spaces += (i > 0) ? 1 : BracePadding;1382}1383alignToStartOfCell(CellIter->Index, CellIter->EndIndex);1384for (const auto *Next = CellIter->NextColumnElement; Next;1385Next = Next->NextColumnElement) {1386ThisWidth =1387calculateCellWidth(Next->Index, Next->EndIndex, true) + NetWidth;1388if (Changes[Next->Index].NewlinesBefore == 0) {1389Changes[Next->Index].Spaces = (CellWidth - ThisWidth);1390Changes[Next->Index].Spaces += (i > 0) ? 1 : BracePadding;1391}1392alignToStartOfCell(Next->Index, Next->EndIndex);1393}1394}1395}1396}13971398void WhitespaceManager::alignArrayInitializersLeftJustified(1399CellDescriptions &&CellDescs) {14001401if (!CellDescs.isRectangular())1402return;14031404const int BracePadding = Style.Cpp11BracedListStyle ? 0 : 1;1405auto &Cells = CellDescs.Cells;1406// Now go through and fixup the spaces.1407auto *CellIter = Cells.begin();1408// The first cell of every row needs to be against the left brace.1409for (const auto *Next = CellIter; Next; Next = Next->NextColumnElement) {1410auto &Change = Changes[Next->Index];1411Change.Spaces =1412Change.NewlinesBefore == 0 ? BracePadding : CellDescs.InitialSpaces;1413}1414++CellIter;1415for (auto i = 1U; i < CellDescs.CellCounts[0]; i++, ++CellIter) {1416auto MaxNetWidth = getMaximumNetWidth(1417Cells.begin(), CellIter, CellDescs.InitialSpaces,1418CellDescs.CellCounts[0], CellDescs.CellCounts.size());1419auto ThisNetWidth =1420getNetWidth(Cells.begin(), CellIter, CellDescs.InitialSpaces);1421if (Changes[CellIter->Index].NewlinesBefore == 0) {1422Changes[CellIter->Index].Spaces =1423MaxNetWidth - ThisNetWidth +1424(Changes[CellIter->Index].Tok->isNot(tok::r_brace) ? 11425: BracePadding);1426}1427auto RowCount = 1U;1428auto Offset = std::distance(Cells.begin(), CellIter);1429for (const auto *Next = CellIter->NextColumnElement; Next;1430Next = Next->NextColumnElement) {1431if (RowCount >= CellDescs.CellCounts.size())1432break;1433auto *Start = (Cells.begin() + RowCount * CellDescs.CellCounts[0]);1434auto *End = Start + Offset;1435auto ThisNetWidth = getNetWidth(Start, End, CellDescs.InitialSpaces);1436if (Changes[Next->Index].NewlinesBefore == 0) {1437Changes[Next->Index].Spaces =1438MaxNetWidth - ThisNetWidth +1439(Changes[Next->Index].Tok->isNot(tok::r_brace) ? 1 : BracePadding);1440}1441++RowCount;1442}1443}1444}14451446bool WhitespaceManager::isSplitCell(const CellDescription &Cell) {1447if (Cell.HasSplit)1448return true;1449for (const auto *Next = Cell.NextColumnElement; Next;1450Next = Next->NextColumnElement) {1451if (Next->HasSplit)1452return true;1453}1454return false;1455}14561457WhitespaceManager::CellDescriptions WhitespaceManager::getCells(unsigned Start,1458unsigned End) {14591460unsigned Depth = 0;1461unsigned Cell = 0;1462SmallVector<unsigned> CellCounts;1463unsigned InitialSpaces = 0;1464unsigned InitialTokenLength = 0;1465unsigned EndSpaces = 0;1466SmallVector<CellDescription> Cells;1467const FormatToken *MatchingParen = nullptr;1468for (unsigned i = Start; i < End; ++i) {1469auto &C = Changes[i];1470if (C.Tok->is(tok::l_brace))1471++Depth;1472else if (C.Tok->is(tok::r_brace))1473--Depth;1474if (Depth == 2) {1475if (C.Tok->is(tok::l_brace)) {1476Cell = 0;1477MatchingParen = C.Tok->MatchingParen;1478if (InitialSpaces == 0) {1479InitialSpaces = C.Spaces + C.TokenLength;1480InitialTokenLength = C.TokenLength;1481auto j = i - 1;1482for (; Changes[j].NewlinesBefore == 0 && j > Start; --j) {1483InitialSpaces += Changes[j].Spaces + Changes[j].TokenLength;1484InitialTokenLength += Changes[j].TokenLength;1485}1486if (C.NewlinesBefore == 0) {1487InitialSpaces += Changes[j].Spaces + Changes[j].TokenLength;1488InitialTokenLength += Changes[j].TokenLength;1489}1490}1491} else if (C.Tok->is(tok::comma)) {1492if (!Cells.empty())1493Cells.back().EndIndex = i;1494if (const auto *Next = C.Tok->getNextNonComment();1495Next && Next->isNot(tok::r_brace)) { // dangling comma1496++Cell;1497}1498}1499} else if (Depth == 1) {1500if (C.Tok == MatchingParen) {1501if (!Cells.empty())1502Cells.back().EndIndex = i;1503Cells.push_back(CellDescription{i, ++Cell, i + 1, false, nullptr});1504CellCounts.push_back(C.Tok->Previous->isNot(tok::comma) ? Cell + 11505: Cell);1506// Go to the next non-comment and ensure there is a break in front1507const auto *NextNonComment = C.Tok->getNextNonComment();1508while (NextNonComment && NextNonComment->is(tok::comma))1509NextNonComment = NextNonComment->getNextNonComment();1510auto j = i;1511while (j < End && Changes[j].Tok != NextNonComment)1512++j;1513if (j < End && Changes[j].NewlinesBefore == 0 &&1514Changes[j].Tok->isNot(tok::r_brace)) {1515Changes[j].NewlinesBefore = 1;1516// Account for the added token lengths1517Changes[j].Spaces = InitialSpaces - InitialTokenLength;1518}1519} else if (C.Tok->is(tok::comment) && C.Tok->NewlinesBefore == 0) {1520// Trailing comments stay at a space past the last token1521C.Spaces = Changes[i - 1].Tok->is(tok::comma) ? 1 : 2;1522} else if (C.Tok->is(tok::l_brace)) {1523// We need to make sure that the ending braces is aligned to the1524// start of our initializer1525auto j = i - 1;1526for (; j > 0 && !Changes[j].Tok->ArrayInitializerLineStart; --j)1527; // Nothing the loop does the work1528EndSpaces = Changes[j].Spaces;1529}1530} else if (Depth == 0 && C.Tok->is(tok::r_brace)) {1531C.NewlinesBefore = 1;1532C.Spaces = EndSpaces;1533}1534if (C.Tok->StartsColumn) {1535// This gets us past tokens that have been split over multiple1536// lines1537bool HasSplit = false;1538if (Changes[i].NewlinesBefore > 0) {1539// So if we split a line previously and the tail line + this token is1540// less then the column limit we remove the split here and just put1541// the column start at a space past the comma1542//1543// FIXME This if branch covers the cases where the column is not1544// the first column. This leads to weird pathologies like the formatting1545// auto foo = Items{1546// Section{1547// 0, bar(),1548// }1549// };1550// Well if it doesn't lead to that it's indicative that the line1551// breaking should be revisited. Unfortunately alot of other options1552// interact with this1553auto j = i - 1;1554if ((j - 1) > Start && Changes[j].Tok->is(tok::comma) &&1555Changes[j - 1].NewlinesBefore > 0) {1556--j;1557auto LineLimit = Changes[j].Spaces + Changes[j].TokenLength;1558if (LineLimit < Style.ColumnLimit) {1559Changes[i].NewlinesBefore = 0;1560Changes[i].Spaces = 1;1561}1562}1563}1564while (Changes[i].NewlinesBefore > 0 && Changes[i].Tok == C.Tok) {1565Changes[i].Spaces = InitialSpaces;1566++i;1567HasSplit = true;1568}1569if (Changes[i].Tok != C.Tok)1570--i;1571Cells.push_back(CellDescription{i, Cell, i, HasSplit, nullptr});1572}1573}15741575return linkCells({Cells, CellCounts, InitialSpaces});1576}15771578unsigned WhitespaceManager::calculateCellWidth(unsigned Start, unsigned End,1579bool WithSpaces) const {1580unsigned CellWidth = 0;1581for (auto i = Start; i < End; i++) {1582if (Changes[i].NewlinesBefore > 0)1583CellWidth = 0;1584CellWidth += Changes[i].TokenLength;1585CellWidth += (WithSpaces ? Changes[i].Spaces : 0);1586}1587return CellWidth;1588}15891590void WhitespaceManager::alignToStartOfCell(unsigned Start, unsigned End) {1591if ((End - Start) <= 1)1592return;1593// If the line is broken anywhere in there make sure everything1594// is aligned to the parent1595for (auto i = Start + 1; i < End; i++)1596if (Changes[i].NewlinesBefore > 0)1597Changes[i].Spaces = Changes[Start].Spaces;1598}15991600WhitespaceManager::CellDescriptions1601WhitespaceManager::linkCells(CellDescriptions &&CellDesc) {1602auto &Cells = CellDesc.Cells;1603for (auto *CellIter = Cells.begin(); CellIter != Cells.end(); ++CellIter) {1604if (!CellIter->NextColumnElement && (CellIter + 1) != Cells.end()) {1605for (auto *NextIter = CellIter + 1; NextIter != Cells.end(); ++NextIter) {1606if (NextIter->Cell == CellIter->Cell) {1607CellIter->NextColumnElement = &(*NextIter);1608break;1609}1610}1611}1612}1613return std::move(CellDesc);1614}16151616void WhitespaceManager::generateChanges() {1617for (unsigned i = 0, e = Changes.size(); i != e; ++i) {1618const Change &C = Changes[i];1619if (i > 0) {1620auto Last = Changes[i - 1].OriginalWhitespaceRange;1621auto New = Changes[i].OriginalWhitespaceRange;1622// Do not generate two replacements for the same location. As a special1623// case, it is allowed if there is a replacement for the empty range1624// between 2 tokens and another non-empty range at the start of the second1625// token. We didn't implement logic to combine replacements for 21626// consecutive source ranges into a single replacement, because the1627// program works fine without it.1628//1629// We can't eliminate empty original whitespace ranges. They appear when1630// 2 tokens have no whitespace in between in the input. It does not1631// matter whether whitespace is to be added. If no whitespace is to be1632// added, the replacement will be empty, and it gets eliminated after this1633// step in storeReplacement. For example, if the input is `foo();`,1634// there will be a replacement for the range between every consecutive1635// pair of tokens.1636//1637// A replacement at the start of a token can be added by1638// BreakableStringLiteralUsingOperators::insertBreak when it adds braces1639// around the string literal. Say Verilog code is being formatted and the1640// first line is to become the next 2 lines.1641// x("long string");1642// x({"long ",1643// "string"});1644// There will be a replacement for the empty range between the parenthesis1645// and the string and another replacement for the quote character. The1646// replacement for the empty range between the parenthesis and the quote1647// comes from ContinuationIndenter::addTokenOnCurrentLine when it changes1648// the original empty range between the parenthesis and the string to1649// another empty one. The replacement for the quote character comes from1650// BreakableStringLiteralUsingOperators::insertBreak when it adds the1651// brace. In the example, the replacement for the empty range is the same1652// as the original text. However, eliminating replacements that are same1653// as the original does not help in general. For example, a newline can1654// be inserted, causing the first line to become the next 3 lines.1655// xxxxxxxxxxx("long string");1656// xxxxxxxxxxx(1657// {"long ",1658// "string"});1659// In that case, the empty range between the parenthesis and the string1660// will be replaced by a newline and 4 spaces. So we will still have to1661// deal with a replacement for an empty source range followed by a1662// replacement for a non-empty source range.1663if (Last.getBegin() == New.getBegin() &&1664(Last.getEnd() != Last.getBegin() ||1665New.getEnd() == New.getBegin())) {1666continue;1667}1668}1669if (C.CreateReplacement) {1670std::string ReplacementText = C.PreviousLinePostfix;1671if (C.ContinuesPPDirective) {1672appendEscapedNewlineText(ReplacementText, C.NewlinesBefore,1673C.PreviousEndOfTokenColumn,1674C.EscapedNewlineColumn);1675} else {1676appendNewlineText(ReplacementText, C.NewlinesBefore);1677}1678// FIXME: This assert should hold if we computed the column correctly.1679// assert((int)C.StartOfTokenColumn >= C.Spaces);1680appendIndentText(1681ReplacementText, C.Tok->IndentLevel, std::max(0, C.Spaces),1682std::max((int)C.StartOfTokenColumn, C.Spaces) - std::max(0, C.Spaces),1683C.IsAligned);1684ReplacementText.append(C.CurrentLinePrefix);1685storeReplacement(C.OriginalWhitespaceRange, ReplacementText);1686}1687}1688}16891690void WhitespaceManager::storeReplacement(SourceRange Range, StringRef Text) {1691unsigned WhitespaceLength = SourceMgr.getFileOffset(Range.getEnd()) -1692SourceMgr.getFileOffset(Range.getBegin());1693// Don't create a replacement, if it does not change anything.1694if (StringRef(SourceMgr.getCharacterData(Range.getBegin()),1695WhitespaceLength) == Text) {1696return;1697}1698auto Err = Replaces.add(tooling::Replacement(1699SourceMgr, CharSourceRange::getCharRange(Range), Text));1700// FIXME: better error handling. For now, just print an error message in the1701// release version.1702if (Err) {1703llvm::errs() << llvm::toString(std::move(Err)) << "\n";1704assert(false);1705}1706}17071708void WhitespaceManager::appendNewlineText(std::string &Text,1709unsigned Newlines) {1710if (UseCRLF) {1711Text.reserve(Text.size() + 2 * Newlines);1712for (unsigned i = 0; i < Newlines; ++i)1713Text.append("\r\n");1714} else {1715Text.append(Newlines, '\n');1716}1717}17181719void WhitespaceManager::appendEscapedNewlineText(1720std::string &Text, unsigned Newlines, unsigned PreviousEndOfTokenColumn,1721unsigned EscapedNewlineColumn) {1722if (Newlines > 0) {1723unsigned Spaces =1724std::max<int>(1, EscapedNewlineColumn - PreviousEndOfTokenColumn - 1);1725for (unsigned i = 0; i < Newlines; ++i) {1726Text.append(Spaces, ' ');1727Text.append(UseCRLF ? "\\\r\n" : "\\\n");1728Spaces = std::max<int>(0, EscapedNewlineColumn - 1);1729}1730}1731}17321733void WhitespaceManager::appendIndentText(std::string &Text,1734unsigned IndentLevel, unsigned Spaces,1735unsigned WhitespaceStartColumn,1736bool IsAligned) {1737switch (Style.UseTab) {1738case FormatStyle::UT_Never:1739Text.append(Spaces, ' ');1740break;1741case FormatStyle::UT_Always: {1742if (Style.TabWidth) {1743unsigned FirstTabWidth =1744Style.TabWidth - WhitespaceStartColumn % Style.TabWidth;17451746// Insert only spaces when we want to end up before the next tab.1747if (Spaces < FirstTabWidth || Spaces == 1) {1748Text.append(Spaces, ' ');1749break;1750}1751// Align to the next tab.1752Spaces -= FirstTabWidth;1753Text.append("\t");17541755Text.append(Spaces / Style.TabWidth, '\t');1756Text.append(Spaces % Style.TabWidth, ' ');1757} else if (Spaces == 1) {1758Text.append(Spaces, ' ');1759}1760break;1761}1762case FormatStyle::UT_ForIndentation:1763if (WhitespaceStartColumn == 0) {1764unsigned Indentation = IndentLevel * Style.IndentWidth;1765Spaces = appendTabIndent(Text, Spaces, Indentation);1766}1767Text.append(Spaces, ' ');1768break;1769case FormatStyle::UT_ForContinuationAndIndentation:1770if (WhitespaceStartColumn == 0)1771Spaces = appendTabIndent(Text, Spaces, Spaces);1772Text.append(Spaces, ' ');1773break;1774case FormatStyle::UT_AlignWithSpaces:1775if (WhitespaceStartColumn == 0) {1776unsigned Indentation =1777IsAligned ? IndentLevel * Style.IndentWidth : Spaces;1778Spaces = appendTabIndent(Text, Spaces, Indentation);1779}1780Text.append(Spaces, ' ');1781break;1782}1783}17841785unsigned WhitespaceManager::appendTabIndent(std::string &Text, unsigned Spaces,1786unsigned Indentation) {1787// This happens, e.g. when a line in a block comment is indented less than the1788// first one.1789if (Indentation > Spaces)1790Indentation = Spaces;1791if (Style.TabWidth) {1792unsigned Tabs = Indentation / Style.TabWidth;1793Text.append(Tabs, '\t');1794Spaces -= Tabs * Style.TabWidth;1795}1796return Spaces;1797}17981799} // namespace format1800} // namespace clang180118021803