17330f729Sjoerg //===--- WhitespaceManager.cpp - Format C++ code --------------------------===//
27330f729Sjoerg //
37330f729Sjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
47330f729Sjoerg // See https://llvm.org/LICENSE.txt for license information.
57330f729Sjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
67330f729Sjoerg //
77330f729Sjoerg //===----------------------------------------------------------------------===//
87330f729Sjoerg ///
97330f729Sjoerg /// \file
107330f729Sjoerg /// This file implements WhitespaceManager class.
117330f729Sjoerg ///
127330f729Sjoerg //===----------------------------------------------------------------------===//
137330f729Sjoerg
147330f729Sjoerg #include "WhitespaceManager.h"
157330f729Sjoerg #include "llvm/ADT/STLExtras.h"
167330f729Sjoerg
177330f729Sjoerg namespace clang {
187330f729Sjoerg namespace format {
197330f729Sjoerg
operator ()(const Change & C1,const Change & C2) const207330f729Sjoerg bool WhitespaceManager::Change::IsBeforeInFile::operator()(
217330f729Sjoerg const Change &C1, const Change &C2) const {
227330f729Sjoerg return SourceMgr.isBeforeInTranslationUnit(
237330f729Sjoerg C1.OriginalWhitespaceRange.getBegin(),
247330f729Sjoerg C2.OriginalWhitespaceRange.getBegin());
257330f729Sjoerg }
267330f729Sjoerg
Change(const FormatToken & Tok,bool CreateReplacement,SourceRange OriginalWhitespaceRange,int Spaces,unsigned StartOfTokenColumn,unsigned NewlinesBefore,StringRef PreviousLinePostfix,StringRef CurrentLinePrefix,bool IsAligned,bool ContinuesPPDirective,bool IsInsideToken)277330f729Sjoerg WhitespaceManager::Change::Change(const FormatToken &Tok,
287330f729Sjoerg bool CreateReplacement,
297330f729Sjoerg SourceRange OriginalWhitespaceRange,
307330f729Sjoerg int Spaces, unsigned StartOfTokenColumn,
317330f729Sjoerg unsigned NewlinesBefore,
327330f729Sjoerg StringRef PreviousLinePostfix,
33*e038c9c4Sjoerg StringRef CurrentLinePrefix, bool IsAligned,
347330f729Sjoerg bool ContinuesPPDirective, bool IsInsideToken)
357330f729Sjoerg : Tok(&Tok), CreateReplacement(CreateReplacement),
367330f729Sjoerg OriginalWhitespaceRange(OriginalWhitespaceRange),
377330f729Sjoerg StartOfTokenColumn(StartOfTokenColumn), NewlinesBefore(NewlinesBefore),
387330f729Sjoerg PreviousLinePostfix(PreviousLinePostfix),
39*e038c9c4Sjoerg CurrentLinePrefix(CurrentLinePrefix), IsAligned(IsAligned),
407330f729Sjoerg ContinuesPPDirective(ContinuesPPDirective), Spaces(Spaces),
417330f729Sjoerg IsInsideToken(IsInsideToken), IsTrailingComment(false), TokenLength(0),
427330f729Sjoerg PreviousEndOfTokenColumn(0), EscapedNewlineColumn(0),
43*e038c9c4Sjoerg StartOfBlockComment(nullptr), IndentationOffset(0), ConditionalsLevel(0) {
44*e038c9c4Sjoerg }
457330f729Sjoerg
replaceWhitespace(FormatToken & Tok,unsigned Newlines,unsigned Spaces,unsigned StartOfTokenColumn,bool IsAligned,bool InPPDirective)467330f729Sjoerg void WhitespaceManager::replaceWhitespace(FormatToken &Tok, unsigned Newlines,
477330f729Sjoerg unsigned Spaces,
487330f729Sjoerg unsigned StartOfTokenColumn,
49*e038c9c4Sjoerg bool IsAligned, bool InPPDirective) {
507330f729Sjoerg if (Tok.Finalized)
517330f729Sjoerg return;
52*e038c9c4Sjoerg Tok.setDecision((Newlines > 0) ? FD_Break : FD_Continue);
537330f729Sjoerg Changes.push_back(Change(Tok, /*CreateReplacement=*/true, Tok.WhitespaceRange,
547330f729Sjoerg Spaces, StartOfTokenColumn, Newlines, "", "",
55*e038c9c4Sjoerg IsAligned, InPPDirective && !Tok.IsFirst,
567330f729Sjoerg /*IsInsideToken=*/false));
577330f729Sjoerg }
587330f729Sjoerg
addUntouchableToken(const FormatToken & Tok,bool InPPDirective)597330f729Sjoerg void WhitespaceManager::addUntouchableToken(const FormatToken &Tok,
607330f729Sjoerg bool InPPDirective) {
617330f729Sjoerg if (Tok.Finalized)
627330f729Sjoerg return;
637330f729Sjoerg Changes.push_back(Change(Tok, /*CreateReplacement=*/false,
647330f729Sjoerg Tok.WhitespaceRange, /*Spaces=*/0,
657330f729Sjoerg Tok.OriginalColumn, Tok.NewlinesBefore, "", "",
66*e038c9c4Sjoerg /*IsAligned=*/false, InPPDirective && !Tok.IsFirst,
677330f729Sjoerg /*IsInsideToken=*/false));
687330f729Sjoerg }
697330f729Sjoerg
707330f729Sjoerg llvm::Error
addReplacement(const tooling::Replacement & Replacement)717330f729Sjoerg WhitespaceManager::addReplacement(const tooling::Replacement &Replacement) {
727330f729Sjoerg return Replaces.add(Replacement);
737330f729Sjoerg }
747330f729Sjoerg
replaceWhitespaceInToken(const FormatToken & Tok,unsigned Offset,unsigned ReplaceChars,StringRef PreviousPostfix,StringRef CurrentPrefix,bool InPPDirective,unsigned Newlines,int Spaces)757330f729Sjoerg void WhitespaceManager::replaceWhitespaceInToken(
767330f729Sjoerg const FormatToken &Tok, unsigned Offset, unsigned ReplaceChars,
777330f729Sjoerg StringRef PreviousPostfix, StringRef CurrentPrefix, bool InPPDirective,
787330f729Sjoerg unsigned Newlines, int Spaces) {
797330f729Sjoerg if (Tok.Finalized)
807330f729Sjoerg return;
817330f729Sjoerg SourceLocation Start = Tok.getStartOfNonWhitespace().getLocWithOffset(Offset);
827330f729Sjoerg Changes.push_back(
837330f729Sjoerg Change(Tok, /*CreateReplacement=*/true,
847330f729Sjoerg SourceRange(Start, Start.getLocWithOffset(ReplaceChars)), Spaces,
857330f729Sjoerg std::max(0, Spaces), Newlines, PreviousPostfix, CurrentPrefix,
86*e038c9c4Sjoerg /*IsAligned=*/true, InPPDirective && !Tok.IsFirst,
87*e038c9c4Sjoerg /*IsInsideToken=*/true));
887330f729Sjoerg }
897330f729Sjoerg
generateReplacements()907330f729Sjoerg const tooling::Replacements &WhitespaceManager::generateReplacements() {
917330f729Sjoerg if (Changes.empty())
927330f729Sjoerg return Replaces;
937330f729Sjoerg
947330f729Sjoerg llvm::sort(Changes, Change::IsBeforeInFile(SourceMgr));
957330f729Sjoerg calculateLineBreakInformation();
967330f729Sjoerg alignConsecutiveMacros();
977330f729Sjoerg alignConsecutiveDeclarations();
98*e038c9c4Sjoerg alignConsecutiveBitFields();
997330f729Sjoerg alignConsecutiveAssignments();
100*e038c9c4Sjoerg alignChainedConditionals();
1017330f729Sjoerg alignTrailingComments();
1027330f729Sjoerg alignEscapedNewlines();
1037330f729Sjoerg generateChanges();
1047330f729Sjoerg
1057330f729Sjoerg return Replaces;
1067330f729Sjoerg }
1077330f729Sjoerg
calculateLineBreakInformation()1087330f729Sjoerg void WhitespaceManager::calculateLineBreakInformation() {
1097330f729Sjoerg Changes[0].PreviousEndOfTokenColumn = 0;
1107330f729Sjoerg Change *LastOutsideTokenChange = &Changes[0];
1117330f729Sjoerg for (unsigned i = 1, e = Changes.size(); i != e; ++i) {
1127330f729Sjoerg SourceLocation OriginalWhitespaceStart =
1137330f729Sjoerg Changes[i].OriginalWhitespaceRange.getBegin();
1147330f729Sjoerg SourceLocation PreviousOriginalWhitespaceEnd =
1157330f729Sjoerg Changes[i - 1].OriginalWhitespaceRange.getEnd();
1167330f729Sjoerg unsigned OriginalWhitespaceStartOffset =
1177330f729Sjoerg SourceMgr.getFileOffset(OriginalWhitespaceStart);
1187330f729Sjoerg unsigned PreviousOriginalWhitespaceEndOffset =
1197330f729Sjoerg SourceMgr.getFileOffset(PreviousOriginalWhitespaceEnd);
1207330f729Sjoerg assert(PreviousOriginalWhitespaceEndOffset <=
1217330f729Sjoerg OriginalWhitespaceStartOffset);
1227330f729Sjoerg const char *const PreviousOriginalWhitespaceEndData =
1237330f729Sjoerg SourceMgr.getCharacterData(PreviousOriginalWhitespaceEnd);
1247330f729Sjoerg StringRef Text(PreviousOriginalWhitespaceEndData,
1257330f729Sjoerg SourceMgr.getCharacterData(OriginalWhitespaceStart) -
1267330f729Sjoerg PreviousOriginalWhitespaceEndData);
1277330f729Sjoerg // Usually consecutive changes would occur in consecutive tokens. This is
1287330f729Sjoerg // not the case however when analyzing some preprocessor runs of the
1297330f729Sjoerg // annotated lines. For example, in this code:
1307330f729Sjoerg //
1317330f729Sjoerg // #if A // line 1
1327330f729Sjoerg // int i = 1;
1337330f729Sjoerg // #else B // line 2
1347330f729Sjoerg // int i = 2;
1357330f729Sjoerg // #endif // line 3
1367330f729Sjoerg //
1377330f729Sjoerg // one of the runs will produce the sequence of lines marked with line 1, 2
1387330f729Sjoerg // and 3. So the two consecutive whitespace changes just before '// line 2'
1397330f729Sjoerg // and before '#endif // line 3' span multiple lines and tokens:
1407330f729Sjoerg //
1417330f729Sjoerg // #else B{change X}[// line 2
1427330f729Sjoerg // int i = 2;
1437330f729Sjoerg // ]{change Y}#endif // line 3
1447330f729Sjoerg //
1457330f729Sjoerg // For this reason, if the text between consecutive changes spans multiple
1467330f729Sjoerg // newlines, the token length must be adjusted to the end of the original
1477330f729Sjoerg // line of the token.
1487330f729Sjoerg auto NewlinePos = Text.find_first_of('\n');
1497330f729Sjoerg if (NewlinePos == StringRef::npos) {
1507330f729Sjoerg Changes[i - 1].TokenLength = OriginalWhitespaceStartOffset -
1517330f729Sjoerg PreviousOriginalWhitespaceEndOffset +
1527330f729Sjoerg Changes[i].PreviousLinePostfix.size() +
1537330f729Sjoerg Changes[i - 1].CurrentLinePrefix.size();
1547330f729Sjoerg } else {
1557330f729Sjoerg Changes[i - 1].TokenLength =
1567330f729Sjoerg NewlinePos + Changes[i - 1].CurrentLinePrefix.size();
1577330f729Sjoerg }
1587330f729Sjoerg
1597330f729Sjoerg // If there are multiple changes in this token, sum up all the changes until
1607330f729Sjoerg // the end of the line.
1617330f729Sjoerg if (Changes[i - 1].IsInsideToken && Changes[i - 1].NewlinesBefore == 0)
1627330f729Sjoerg LastOutsideTokenChange->TokenLength +=
1637330f729Sjoerg Changes[i - 1].TokenLength + Changes[i - 1].Spaces;
1647330f729Sjoerg else
1657330f729Sjoerg LastOutsideTokenChange = &Changes[i - 1];
1667330f729Sjoerg
1677330f729Sjoerg Changes[i].PreviousEndOfTokenColumn =
1687330f729Sjoerg Changes[i - 1].StartOfTokenColumn + Changes[i - 1].TokenLength;
1697330f729Sjoerg
1707330f729Sjoerg Changes[i - 1].IsTrailingComment =
1717330f729Sjoerg (Changes[i].NewlinesBefore > 0 || Changes[i].Tok->is(tok::eof) ||
1727330f729Sjoerg (Changes[i].IsInsideToken && Changes[i].Tok->is(tok::comment))) &&
1737330f729Sjoerg Changes[i - 1].Tok->is(tok::comment) &&
1747330f729Sjoerg // FIXME: This is a dirty hack. The problem is that
1757330f729Sjoerg // BreakableLineCommentSection does comment reflow changes and here is
1767330f729Sjoerg // the aligning of trailing comments. Consider the case where we reflow
1777330f729Sjoerg // the second line up in this example:
1787330f729Sjoerg //
1797330f729Sjoerg // // line 1
1807330f729Sjoerg // // line 2
1817330f729Sjoerg //
1827330f729Sjoerg // That amounts to 2 changes by BreakableLineCommentSection:
1837330f729Sjoerg // - the first, delimited by (), for the whitespace between the tokens,
1847330f729Sjoerg // - and second, delimited by [], for the whitespace at the beginning
1857330f729Sjoerg // of the second token:
1867330f729Sjoerg //
1877330f729Sjoerg // // line 1(
1887330f729Sjoerg // )[// ]line 2
1897330f729Sjoerg //
1907330f729Sjoerg // So in the end we have two changes like this:
1917330f729Sjoerg //
1927330f729Sjoerg // // line1()[ ]line 2
1937330f729Sjoerg //
1947330f729Sjoerg // Note that the OriginalWhitespaceStart of the second change is the
1957330f729Sjoerg // same as the PreviousOriginalWhitespaceEnd of the first change.
1967330f729Sjoerg // In this case, the below check ensures that the second change doesn't
1977330f729Sjoerg // get treated as a trailing comment change here, since this might
1987330f729Sjoerg // trigger additional whitespace to be wrongly inserted before "line 2"
1997330f729Sjoerg // by the comment aligner here.
2007330f729Sjoerg //
2017330f729Sjoerg // For a proper solution we need a mechanism to say to WhitespaceManager
2027330f729Sjoerg // that a particular change breaks the current sequence of trailing
2037330f729Sjoerg // comments.
2047330f729Sjoerg OriginalWhitespaceStart != PreviousOriginalWhitespaceEnd;
2057330f729Sjoerg }
2067330f729Sjoerg // FIXME: The last token is currently not always an eof token; in those
2077330f729Sjoerg // cases, setting TokenLength of the last token to 0 is wrong.
2087330f729Sjoerg Changes.back().TokenLength = 0;
2097330f729Sjoerg Changes.back().IsTrailingComment = Changes.back().Tok->is(tok::comment);
2107330f729Sjoerg
2117330f729Sjoerg const WhitespaceManager::Change *LastBlockComment = nullptr;
2127330f729Sjoerg for (auto &Change : Changes) {
2137330f729Sjoerg // Reset the IsTrailingComment flag for changes inside of trailing comments
2147330f729Sjoerg // so they don't get realigned later. Comment line breaks however still need
2157330f729Sjoerg // to be aligned.
2167330f729Sjoerg if (Change.IsInsideToken && Change.NewlinesBefore == 0)
2177330f729Sjoerg Change.IsTrailingComment = false;
2187330f729Sjoerg Change.StartOfBlockComment = nullptr;
2197330f729Sjoerg Change.IndentationOffset = 0;
2207330f729Sjoerg if (Change.Tok->is(tok::comment)) {
2217330f729Sjoerg if (Change.Tok->is(TT_LineComment) || !Change.IsInsideToken)
2227330f729Sjoerg LastBlockComment = &Change;
2237330f729Sjoerg else {
2247330f729Sjoerg if ((Change.StartOfBlockComment = LastBlockComment))
2257330f729Sjoerg Change.IndentationOffset =
2267330f729Sjoerg Change.StartOfTokenColumn -
2277330f729Sjoerg Change.StartOfBlockComment->StartOfTokenColumn;
2287330f729Sjoerg }
2297330f729Sjoerg } else {
2307330f729Sjoerg LastBlockComment = nullptr;
2317330f729Sjoerg }
2327330f729Sjoerg }
233*e038c9c4Sjoerg
234*e038c9c4Sjoerg // Compute conditional nesting level
235*e038c9c4Sjoerg // Level is increased for each conditional, unless this conditional continues
236*e038c9c4Sjoerg // a chain of conditional, i.e. starts immediately after the colon of another
237*e038c9c4Sjoerg // conditional.
238*e038c9c4Sjoerg SmallVector<bool, 16> ScopeStack;
239*e038c9c4Sjoerg int ConditionalsLevel = 0;
240*e038c9c4Sjoerg for (auto &Change : Changes) {
241*e038c9c4Sjoerg for (unsigned i = 0, e = Change.Tok->FakeLParens.size(); i != e; ++i) {
242*e038c9c4Sjoerg bool isNestedConditional =
243*e038c9c4Sjoerg Change.Tok->FakeLParens[e - 1 - i] == prec::Conditional &&
244*e038c9c4Sjoerg !(i == 0 && Change.Tok->Previous &&
245*e038c9c4Sjoerg Change.Tok->Previous->is(TT_ConditionalExpr) &&
246*e038c9c4Sjoerg Change.Tok->Previous->is(tok::colon));
247*e038c9c4Sjoerg if (isNestedConditional)
248*e038c9c4Sjoerg ++ConditionalsLevel;
249*e038c9c4Sjoerg ScopeStack.push_back(isNestedConditional);
250*e038c9c4Sjoerg }
251*e038c9c4Sjoerg
252*e038c9c4Sjoerg Change.ConditionalsLevel = ConditionalsLevel;
253*e038c9c4Sjoerg
254*e038c9c4Sjoerg for (unsigned i = Change.Tok->FakeRParens; i > 0 && ScopeStack.size();
255*e038c9c4Sjoerg --i) {
256*e038c9c4Sjoerg if (ScopeStack.pop_back_val())
257*e038c9c4Sjoerg --ConditionalsLevel;
258*e038c9c4Sjoerg }
259*e038c9c4Sjoerg }
2607330f729Sjoerg }
2617330f729Sjoerg
2627330f729Sjoerg // Align a single sequence of tokens, see AlignTokens below.
2637330f729Sjoerg template <typename F>
2647330f729Sjoerg static void
AlignTokenSequence(unsigned Start,unsigned End,unsigned Column,F && Matches,SmallVector<WhitespaceManager::Change,16> & Changes)2657330f729Sjoerg AlignTokenSequence(unsigned Start, unsigned End, unsigned Column, F &&Matches,
2667330f729Sjoerg SmallVector<WhitespaceManager::Change, 16> &Changes) {
2677330f729Sjoerg bool FoundMatchOnLine = false;
2687330f729Sjoerg int Shift = 0;
2697330f729Sjoerg
2707330f729Sjoerg // ScopeStack keeps track of the current scope depth. It contains indices of
2717330f729Sjoerg // the first token on each scope.
2727330f729Sjoerg // We only run the "Matches" function on tokens from the outer-most scope.
2737330f729Sjoerg // However, we do need to pay special attention to one class of tokens
2747330f729Sjoerg // that are not in the outer-most scope, and that is function parameters
2757330f729Sjoerg // which are split across multiple lines, as illustrated by this example:
2767330f729Sjoerg // double a(int x);
2777330f729Sjoerg // int b(int y,
2787330f729Sjoerg // double z);
2797330f729Sjoerg // In the above example, we need to take special care to ensure that
2807330f729Sjoerg // 'double z' is indented along with it's owning function 'b'.
281*e038c9c4Sjoerg // The same holds for calling a function:
282*e038c9c4Sjoerg // double a = foo(x);
283*e038c9c4Sjoerg // int b = bar(foo(y),
284*e038c9c4Sjoerg // foor(z));
285*e038c9c4Sjoerg // Similar for broken string literals:
286*e038c9c4Sjoerg // double x = 3.14;
287*e038c9c4Sjoerg // auto s = "Hello"
288*e038c9c4Sjoerg // "World";
289*e038c9c4Sjoerg // Special handling is required for 'nested' ternary operators.
2907330f729Sjoerg SmallVector<unsigned, 16> ScopeStack;
2917330f729Sjoerg
2927330f729Sjoerg for (unsigned i = Start; i != End; ++i) {
2937330f729Sjoerg if (ScopeStack.size() != 0 &&
2947330f729Sjoerg Changes[i].indentAndNestingLevel() <
2957330f729Sjoerg Changes[ScopeStack.back()].indentAndNestingLevel())
2967330f729Sjoerg ScopeStack.pop_back();
2977330f729Sjoerg
2987330f729Sjoerg // Compare current token to previous non-comment token to ensure whether
2997330f729Sjoerg // it is in a deeper scope or not.
3007330f729Sjoerg unsigned PreviousNonComment = i - 1;
3017330f729Sjoerg while (PreviousNonComment > Start &&
3027330f729Sjoerg Changes[PreviousNonComment].Tok->is(tok::comment))
3037330f729Sjoerg PreviousNonComment--;
3047330f729Sjoerg if (i != Start && Changes[i].indentAndNestingLevel() >
3057330f729Sjoerg Changes[PreviousNonComment].indentAndNestingLevel())
3067330f729Sjoerg ScopeStack.push_back(i);
3077330f729Sjoerg
3087330f729Sjoerg bool InsideNestedScope = ScopeStack.size() != 0;
309*e038c9c4Sjoerg bool ContinuedStringLiteral = i > Start &&
310*e038c9c4Sjoerg Changes[i].Tok->is(tok::string_literal) &&
311*e038c9c4Sjoerg Changes[i - 1].Tok->is(tok::string_literal);
312*e038c9c4Sjoerg bool SkipMatchCheck = InsideNestedScope || ContinuedStringLiteral;
3137330f729Sjoerg
314*e038c9c4Sjoerg if (Changes[i].NewlinesBefore > 0 && !SkipMatchCheck) {
3157330f729Sjoerg Shift = 0;
3167330f729Sjoerg FoundMatchOnLine = false;
3177330f729Sjoerg }
3187330f729Sjoerg
3197330f729Sjoerg // If this is the first matching token to be aligned, remember by how many
3207330f729Sjoerg // spaces it has to be shifted, so the rest of the changes on the line are
3217330f729Sjoerg // shifted by the same amount
322*e038c9c4Sjoerg if (!FoundMatchOnLine && !SkipMatchCheck && Matches(Changes[i])) {
3237330f729Sjoerg FoundMatchOnLine = true;
3247330f729Sjoerg Shift = Column - Changes[i].StartOfTokenColumn;
3257330f729Sjoerg Changes[i].Spaces += Shift;
3267330f729Sjoerg }
3277330f729Sjoerg
3287330f729Sjoerg // This is for function parameters that are split across multiple lines,
3297330f729Sjoerg // as mentioned in the ScopeStack comment.
3307330f729Sjoerg if (InsideNestedScope && Changes[i].NewlinesBefore > 0) {
3317330f729Sjoerg unsigned ScopeStart = ScopeStack.back();
332*e038c9c4Sjoerg auto ShouldShiftBeAdded = [&] {
333*e038c9c4Sjoerg // Function declaration
334*e038c9c4Sjoerg if (Changes[ScopeStart - 1].Tok->is(TT_FunctionDeclarationName))
335*e038c9c4Sjoerg return true;
336*e038c9c4Sjoerg
337*e038c9c4Sjoerg // Continued function declaration
338*e038c9c4Sjoerg if (ScopeStart > Start + 1 &&
339*e038c9c4Sjoerg Changes[ScopeStart - 2].Tok->is(TT_FunctionDeclarationName))
340*e038c9c4Sjoerg return true;
341*e038c9c4Sjoerg
342*e038c9c4Sjoerg // Continued function call
343*e038c9c4Sjoerg if (ScopeStart > Start + 1 &&
344*e038c9c4Sjoerg Changes[ScopeStart - 2].Tok->is(tok::identifier) &&
345*e038c9c4Sjoerg Changes[ScopeStart - 1].Tok->is(tok::l_paren))
346*e038c9c4Sjoerg return true;
347*e038c9c4Sjoerg
348*e038c9c4Sjoerg // Ternary operator
349*e038c9c4Sjoerg if (Changes[i].Tok->is(TT_ConditionalExpr))
350*e038c9c4Sjoerg return true;
351*e038c9c4Sjoerg
352*e038c9c4Sjoerg // Continued ternary operator
353*e038c9c4Sjoerg if (Changes[i].Tok->Previous &&
354*e038c9c4Sjoerg Changes[i].Tok->Previous->is(TT_ConditionalExpr))
355*e038c9c4Sjoerg return true;
356*e038c9c4Sjoerg
357*e038c9c4Sjoerg return false;
358*e038c9c4Sjoerg };
359*e038c9c4Sjoerg
360*e038c9c4Sjoerg if (ShouldShiftBeAdded())
3617330f729Sjoerg Changes[i].Spaces += Shift;
3627330f729Sjoerg }
3637330f729Sjoerg
364*e038c9c4Sjoerg if (ContinuedStringLiteral)
365*e038c9c4Sjoerg Changes[i].Spaces += Shift;
366*e038c9c4Sjoerg
3677330f729Sjoerg assert(Shift >= 0);
3687330f729Sjoerg Changes[i].StartOfTokenColumn += Shift;
3697330f729Sjoerg if (i + 1 != Changes.size())
3707330f729Sjoerg Changes[i + 1].PreviousEndOfTokenColumn += Shift;
3717330f729Sjoerg }
3727330f729Sjoerg }
3737330f729Sjoerg
3747330f729Sjoerg // Walk through a subset of the changes, starting at StartAt, and find
3757330f729Sjoerg // sequences of matching tokens to align. To do so, keep track of the lines and
3767330f729Sjoerg // whether or not a matching token was found on a line. If a matching token is
3777330f729Sjoerg // found, extend the current sequence. If the current line cannot be part of a
3787330f729Sjoerg // sequence, e.g. because there is an empty line before it or it contains only
3797330f729Sjoerg // non-matching tokens, finalize the previous sequence.
3807330f729Sjoerg // The value returned is the token on which we stopped, either because we
3817330f729Sjoerg // exhausted all items inside Changes, or because we hit a scope level higher
3827330f729Sjoerg // than our initial scope.
3837330f729Sjoerg // This function is recursive. Each invocation processes only the scope level
3847330f729Sjoerg // equal to the initial level, which is the level of Changes[StartAt].
3857330f729Sjoerg // If we encounter a scope level greater than the initial level, then we call
3867330f729Sjoerg // ourselves recursively, thereby avoiding the pollution of the current state
3877330f729Sjoerg // with the alignment requirements of the nested sub-level. This recursive
3887330f729Sjoerg // behavior is necessary for aligning function prototypes that have one or more
3897330f729Sjoerg // arguments.
3907330f729Sjoerg // If this function encounters a scope level less than the initial level,
3917330f729Sjoerg // it returns the current position.
3927330f729Sjoerg // There is a non-obvious subtlety in the recursive behavior: Even though we
3937330f729Sjoerg // defer processing of nested levels to recursive invocations of this
3947330f729Sjoerg // function, when it comes time to align a sequence of tokens, we run the
3957330f729Sjoerg // alignment on the entire sequence, including the nested levels.
3967330f729Sjoerg // When doing so, most of the nested tokens are skipped, because their
3977330f729Sjoerg // alignment was already handled by the recursive invocations of this function.
3987330f729Sjoerg // However, the special exception is that we do NOT skip function parameters
3997330f729Sjoerg // that are split across multiple lines. See the test case in FormatTest.cpp
4007330f729Sjoerg // that mentions "split function parameter alignment" for an example of this.
4017330f729Sjoerg template <typename F>
AlignTokens(const FormatStyle & Style,F && Matches,SmallVector<WhitespaceManager::Change,16> & Changes,unsigned StartAt,const FormatStyle::AlignConsecutiveStyle & ACS=FormatStyle::ACS_None)402*e038c9c4Sjoerg static unsigned AlignTokens(
403*e038c9c4Sjoerg const FormatStyle &Style, F &&Matches,
404*e038c9c4Sjoerg SmallVector<WhitespaceManager::Change, 16> &Changes, unsigned StartAt,
405*e038c9c4Sjoerg const FormatStyle::AlignConsecutiveStyle &ACS = FormatStyle::ACS_None) {
4067330f729Sjoerg unsigned MinColumn = 0;
4077330f729Sjoerg unsigned MaxColumn = UINT_MAX;
4087330f729Sjoerg
4097330f729Sjoerg // Line number of the start and the end of the current token sequence.
4107330f729Sjoerg unsigned StartOfSequence = 0;
4117330f729Sjoerg unsigned EndOfSequence = 0;
4127330f729Sjoerg
4137330f729Sjoerg // Measure the scope level (i.e. depth of (), [], {}) of the first token, and
4147330f729Sjoerg // abort when we hit any token in a higher scope than the starting one.
4157330f729Sjoerg auto IndentAndNestingLevel = StartAt < Changes.size()
4167330f729Sjoerg ? Changes[StartAt].indentAndNestingLevel()
417*e038c9c4Sjoerg : std::tuple<unsigned, unsigned, unsigned>();
4187330f729Sjoerg
4197330f729Sjoerg // Keep track of the number of commas before the matching tokens, we will only
4207330f729Sjoerg // align a sequence of matching tokens if they are preceded by the same number
4217330f729Sjoerg // of commas.
4227330f729Sjoerg unsigned CommasBeforeLastMatch = 0;
4237330f729Sjoerg unsigned CommasBeforeMatch = 0;
4247330f729Sjoerg
4257330f729Sjoerg // Whether a matching token has been found on the current line.
4267330f729Sjoerg bool FoundMatchOnLine = false;
4277330f729Sjoerg
428*e038c9c4Sjoerg // Whether the current line consists purely of comments.
429*e038c9c4Sjoerg bool LineIsComment = true;
430*e038c9c4Sjoerg
4317330f729Sjoerg // Aligns a sequence of matching tokens, on the MinColumn column.
4327330f729Sjoerg //
4337330f729Sjoerg // Sequences start from the first matching token to align, and end at the
4347330f729Sjoerg // first token of the first line that doesn't need to be aligned.
4357330f729Sjoerg //
4367330f729Sjoerg // We need to adjust the StartOfTokenColumn of each Change that is on a line
4377330f729Sjoerg // containing any matching token to be aligned and located after such token.
4387330f729Sjoerg auto AlignCurrentSequence = [&] {
4397330f729Sjoerg if (StartOfSequence > 0 && StartOfSequence < EndOfSequence)
4407330f729Sjoerg AlignTokenSequence(StartOfSequence, EndOfSequence, MinColumn, Matches,
4417330f729Sjoerg Changes);
4427330f729Sjoerg MinColumn = 0;
4437330f729Sjoerg MaxColumn = UINT_MAX;
4447330f729Sjoerg StartOfSequence = 0;
4457330f729Sjoerg EndOfSequence = 0;
4467330f729Sjoerg };
4477330f729Sjoerg
4487330f729Sjoerg unsigned i = StartAt;
4497330f729Sjoerg for (unsigned e = Changes.size(); i != e; ++i) {
4507330f729Sjoerg if (Changes[i].indentAndNestingLevel() < IndentAndNestingLevel)
4517330f729Sjoerg break;
4527330f729Sjoerg
4537330f729Sjoerg if (Changes[i].NewlinesBefore != 0) {
4547330f729Sjoerg CommasBeforeMatch = 0;
4557330f729Sjoerg EndOfSequence = i;
456*e038c9c4Sjoerg
457*e038c9c4Sjoerg // Whether to break the alignment sequence because of an empty line.
458*e038c9c4Sjoerg bool EmptyLineBreak =
459*e038c9c4Sjoerg (Changes[i].NewlinesBefore > 1) &&
460*e038c9c4Sjoerg (ACS != FormatStyle::ACS_AcrossEmptyLines) &&
461*e038c9c4Sjoerg (ACS != FormatStyle::ACS_AcrossEmptyLinesAndComments);
462*e038c9c4Sjoerg
463*e038c9c4Sjoerg // Whether to break the alignment sequence because of a line without a
464*e038c9c4Sjoerg // match.
465*e038c9c4Sjoerg bool NoMatchBreak =
466*e038c9c4Sjoerg !FoundMatchOnLine &&
467*e038c9c4Sjoerg !(LineIsComment &&
468*e038c9c4Sjoerg ((ACS == FormatStyle::ACS_AcrossComments) ||
469*e038c9c4Sjoerg (ACS == FormatStyle::ACS_AcrossEmptyLinesAndComments)));
470*e038c9c4Sjoerg
471*e038c9c4Sjoerg if (EmptyLineBreak || NoMatchBreak)
4727330f729Sjoerg AlignCurrentSequence();
4737330f729Sjoerg
474*e038c9c4Sjoerg // A new line starts, re-initialize line status tracking bools.
475*e038c9c4Sjoerg // Keep the match state if a string literal is continued on this line.
476*e038c9c4Sjoerg if (i == 0 || !Changes[i].Tok->is(tok::string_literal) ||
477*e038c9c4Sjoerg !Changes[i - 1].Tok->is(tok::string_literal))
4787330f729Sjoerg FoundMatchOnLine = false;
479*e038c9c4Sjoerg LineIsComment = true;
480*e038c9c4Sjoerg }
481*e038c9c4Sjoerg
482*e038c9c4Sjoerg if (!Changes[i].Tok->is(tok::comment)) {
483*e038c9c4Sjoerg LineIsComment = false;
4847330f729Sjoerg }
4857330f729Sjoerg
4867330f729Sjoerg if (Changes[i].Tok->is(tok::comma)) {
4877330f729Sjoerg ++CommasBeforeMatch;
4887330f729Sjoerg } else if (Changes[i].indentAndNestingLevel() > IndentAndNestingLevel) {
4897330f729Sjoerg // Call AlignTokens recursively, skipping over this scope block.
490*e038c9c4Sjoerg unsigned StoppedAt = AlignTokens(Style, Matches, Changes, i, ACS);
4917330f729Sjoerg i = StoppedAt - 1;
4927330f729Sjoerg continue;
4937330f729Sjoerg }
4947330f729Sjoerg
4957330f729Sjoerg if (!Matches(Changes[i]))
4967330f729Sjoerg continue;
4977330f729Sjoerg
4987330f729Sjoerg // If there is more than one matching token per line, or if the number of
4997330f729Sjoerg // preceding commas, do not match anymore, end the sequence.
5007330f729Sjoerg if (FoundMatchOnLine || CommasBeforeMatch != CommasBeforeLastMatch)
5017330f729Sjoerg AlignCurrentSequence();
5027330f729Sjoerg
5037330f729Sjoerg CommasBeforeLastMatch = CommasBeforeMatch;
5047330f729Sjoerg FoundMatchOnLine = true;
5057330f729Sjoerg
5067330f729Sjoerg if (StartOfSequence == 0)
5077330f729Sjoerg StartOfSequence = i;
5087330f729Sjoerg
5097330f729Sjoerg unsigned ChangeMinColumn = Changes[i].StartOfTokenColumn;
510*e038c9c4Sjoerg int LineLengthAfter = Changes[i].TokenLength;
511*e038c9c4Sjoerg for (unsigned j = i + 1; j != e && Changes[j].NewlinesBefore == 0; ++j) {
512*e038c9c4Sjoerg LineLengthAfter += Changes[j].Spaces;
513*e038c9c4Sjoerg // Changes are generally 1:1 with the tokens, but a change could also be
514*e038c9c4Sjoerg // inside of a token, in which case it's counted more than once: once for
515*e038c9c4Sjoerg // the whitespace surrounding the token (!IsInsideToken) and once for
516*e038c9c4Sjoerg // each whitespace change within it (IsInsideToken).
517*e038c9c4Sjoerg // Therefore, changes inside of a token should only count the space.
518*e038c9c4Sjoerg if (!Changes[j].IsInsideToken)
519*e038c9c4Sjoerg LineLengthAfter += Changes[j].TokenLength;
520*e038c9c4Sjoerg }
5217330f729Sjoerg unsigned ChangeMaxColumn = Style.ColumnLimit - LineLengthAfter;
5227330f729Sjoerg
5237330f729Sjoerg // If we are restricted by the maximum column width, end the sequence.
5247330f729Sjoerg if (ChangeMinColumn > MaxColumn || ChangeMaxColumn < MinColumn ||
5257330f729Sjoerg CommasBeforeLastMatch != CommasBeforeMatch) {
5267330f729Sjoerg AlignCurrentSequence();
5277330f729Sjoerg StartOfSequence = i;
5287330f729Sjoerg }
5297330f729Sjoerg
5307330f729Sjoerg MinColumn = std::max(MinColumn, ChangeMinColumn);
5317330f729Sjoerg MaxColumn = std::min(MaxColumn, ChangeMaxColumn);
5327330f729Sjoerg }
5337330f729Sjoerg
5347330f729Sjoerg EndOfSequence = i;
5357330f729Sjoerg AlignCurrentSequence();
5367330f729Sjoerg return i;
5377330f729Sjoerg }
5387330f729Sjoerg
5397330f729Sjoerg // Aligns a sequence of matching tokens, on the MinColumn column.
5407330f729Sjoerg //
5417330f729Sjoerg // Sequences start from the first matching token to align, and end at the
5427330f729Sjoerg // first token of the first line that doesn't need to be aligned.
5437330f729Sjoerg //
5447330f729Sjoerg // We need to adjust the StartOfTokenColumn of each Change that is on a line
5457330f729Sjoerg // containing any matching token to be aligned and located after such token.
AlignMacroSequence(unsigned & StartOfSequence,unsigned & EndOfSequence,unsigned & MinColumn,unsigned & MaxColumn,bool & FoundMatchOnLine,std::function<bool (const WhitespaceManager::Change & C)> AlignMacrosMatches,SmallVector<WhitespaceManager::Change,16> & Changes)5467330f729Sjoerg static void AlignMacroSequence(
5477330f729Sjoerg unsigned &StartOfSequence, unsigned &EndOfSequence, unsigned &MinColumn,
5487330f729Sjoerg unsigned &MaxColumn, bool &FoundMatchOnLine,
5497330f729Sjoerg std::function<bool(const WhitespaceManager::Change &C)> AlignMacrosMatches,
5507330f729Sjoerg SmallVector<WhitespaceManager::Change, 16> &Changes) {
5517330f729Sjoerg if (StartOfSequence > 0 && StartOfSequence < EndOfSequence) {
5527330f729Sjoerg
5537330f729Sjoerg FoundMatchOnLine = false;
5547330f729Sjoerg int Shift = 0;
5557330f729Sjoerg
5567330f729Sjoerg for (unsigned I = StartOfSequence; I != EndOfSequence; ++I) {
5577330f729Sjoerg if (Changes[I].NewlinesBefore > 0) {
5587330f729Sjoerg Shift = 0;
5597330f729Sjoerg FoundMatchOnLine = false;
5607330f729Sjoerg }
5617330f729Sjoerg
5627330f729Sjoerg // If this is the first matching token to be aligned, remember by how many
5637330f729Sjoerg // spaces it has to be shifted, so the rest of the changes on the line are
5647330f729Sjoerg // shifted by the same amount
5657330f729Sjoerg if (!FoundMatchOnLine && AlignMacrosMatches(Changes[I])) {
5667330f729Sjoerg FoundMatchOnLine = true;
5677330f729Sjoerg Shift = MinColumn - Changes[I].StartOfTokenColumn;
5687330f729Sjoerg Changes[I].Spaces += Shift;
5697330f729Sjoerg }
5707330f729Sjoerg
5717330f729Sjoerg assert(Shift >= 0);
5727330f729Sjoerg Changes[I].StartOfTokenColumn += Shift;
5737330f729Sjoerg if (I + 1 != Changes.size())
5747330f729Sjoerg Changes[I + 1].PreviousEndOfTokenColumn += Shift;
5757330f729Sjoerg }
5767330f729Sjoerg }
5777330f729Sjoerg
5787330f729Sjoerg MinColumn = 0;
5797330f729Sjoerg MaxColumn = UINT_MAX;
5807330f729Sjoerg StartOfSequence = 0;
5817330f729Sjoerg EndOfSequence = 0;
5827330f729Sjoerg }
5837330f729Sjoerg
alignConsecutiveMacros()5847330f729Sjoerg void WhitespaceManager::alignConsecutiveMacros() {
585*e038c9c4Sjoerg if (Style.AlignConsecutiveMacros == FormatStyle::ACS_None)
5867330f729Sjoerg return;
5877330f729Sjoerg
5887330f729Sjoerg auto AlignMacrosMatches = [](const Change &C) {
5897330f729Sjoerg const FormatToken *Current = C.Tok;
5907330f729Sjoerg unsigned SpacesRequiredBefore = 1;
5917330f729Sjoerg
5927330f729Sjoerg if (Current->SpacesRequiredBefore == 0 || !Current->Previous)
5937330f729Sjoerg return false;
5947330f729Sjoerg
5957330f729Sjoerg Current = Current->Previous;
5967330f729Sjoerg
5977330f729Sjoerg // If token is a ")", skip over the parameter list, to the
5987330f729Sjoerg // token that precedes the "("
5997330f729Sjoerg if (Current->is(tok::r_paren) && Current->MatchingParen) {
6007330f729Sjoerg Current = Current->MatchingParen->Previous;
6017330f729Sjoerg SpacesRequiredBefore = 0;
6027330f729Sjoerg }
6037330f729Sjoerg
6047330f729Sjoerg if (!Current || !Current->is(tok::identifier))
6057330f729Sjoerg return false;
6067330f729Sjoerg
6077330f729Sjoerg if (!Current->Previous || !Current->Previous->is(tok::pp_define))
6087330f729Sjoerg return false;
6097330f729Sjoerg
6107330f729Sjoerg // For a macro function, 0 spaces are required between the
6117330f729Sjoerg // identifier and the lparen that opens the parameter list.
6127330f729Sjoerg // For a simple macro, 1 space is required between the
6137330f729Sjoerg // identifier and the first token of the defined value.
6147330f729Sjoerg return Current->Next->SpacesRequiredBefore == SpacesRequiredBefore;
6157330f729Sjoerg };
6167330f729Sjoerg
6177330f729Sjoerg unsigned MinColumn = 0;
6187330f729Sjoerg unsigned MaxColumn = UINT_MAX;
6197330f729Sjoerg
6207330f729Sjoerg // Start and end of the token sequence we're processing.
6217330f729Sjoerg unsigned StartOfSequence = 0;
6227330f729Sjoerg unsigned EndOfSequence = 0;
6237330f729Sjoerg
6247330f729Sjoerg // Whether a matching token has been found on the current line.
6257330f729Sjoerg bool FoundMatchOnLine = false;
6267330f729Sjoerg
627*e038c9c4Sjoerg // Whether the current line consists only of comments
628*e038c9c4Sjoerg bool LineIsComment = true;
629*e038c9c4Sjoerg
6307330f729Sjoerg unsigned I = 0;
6317330f729Sjoerg for (unsigned E = Changes.size(); I != E; ++I) {
6327330f729Sjoerg if (Changes[I].NewlinesBefore != 0) {
6337330f729Sjoerg EndOfSequence = I;
634*e038c9c4Sjoerg
635*e038c9c4Sjoerg // Whether to break the alignment sequence because of an empty line.
636*e038c9c4Sjoerg bool EmptyLineBreak =
637*e038c9c4Sjoerg (Changes[I].NewlinesBefore > 1) &&
638*e038c9c4Sjoerg (Style.AlignConsecutiveMacros != FormatStyle::ACS_AcrossEmptyLines) &&
639*e038c9c4Sjoerg (Style.AlignConsecutiveMacros !=
640*e038c9c4Sjoerg FormatStyle::ACS_AcrossEmptyLinesAndComments);
641*e038c9c4Sjoerg
642*e038c9c4Sjoerg // Whether to break the alignment sequence because of a line without a
643*e038c9c4Sjoerg // match.
644*e038c9c4Sjoerg bool NoMatchBreak =
645*e038c9c4Sjoerg !FoundMatchOnLine &&
646*e038c9c4Sjoerg !(LineIsComment && ((Style.AlignConsecutiveMacros ==
647*e038c9c4Sjoerg FormatStyle::ACS_AcrossComments) ||
648*e038c9c4Sjoerg (Style.AlignConsecutiveMacros ==
649*e038c9c4Sjoerg FormatStyle::ACS_AcrossEmptyLinesAndComments)));
650*e038c9c4Sjoerg
651*e038c9c4Sjoerg if (EmptyLineBreak || NoMatchBreak)
6527330f729Sjoerg AlignMacroSequence(StartOfSequence, EndOfSequence, MinColumn, MaxColumn,
6537330f729Sjoerg FoundMatchOnLine, AlignMacrosMatches, Changes);
6547330f729Sjoerg
655*e038c9c4Sjoerg // A new line starts, re-initialize line status tracking bools.
6567330f729Sjoerg FoundMatchOnLine = false;
657*e038c9c4Sjoerg LineIsComment = true;
658*e038c9c4Sjoerg }
659*e038c9c4Sjoerg
660*e038c9c4Sjoerg if (!Changes[I].Tok->is(tok::comment)) {
661*e038c9c4Sjoerg LineIsComment = false;
6627330f729Sjoerg }
6637330f729Sjoerg
6647330f729Sjoerg if (!AlignMacrosMatches(Changes[I]))
6657330f729Sjoerg continue;
6667330f729Sjoerg
6677330f729Sjoerg FoundMatchOnLine = true;
6687330f729Sjoerg
6697330f729Sjoerg if (StartOfSequence == 0)
6707330f729Sjoerg StartOfSequence = I;
6717330f729Sjoerg
6727330f729Sjoerg unsigned ChangeMinColumn = Changes[I].StartOfTokenColumn;
6737330f729Sjoerg int LineLengthAfter = -Changes[I].Spaces;
6747330f729Sjoerg for (unsigned j = I; j != E && Changes[j].NewlinesBefore == 0; ++j)
6757330f729Sjoerg LineLengthAfter += Changes[j].Spaces + Changes[j].TokenLength;
6767330f729Sjoerg unsigned ChangeMaxColumn = Style.ColumnLimit - LineLengthAfter;
6777330f729Sjoerg
6787330f729Sjoerg MinColumn = std::max(MinColumn, ChangeMinColumn);
6797330f729Sjoerg MaxColumn = std::min(MaxColumn, ChangeMaxColumn);
6807330f729Sjoerg }
6817330f729Sjoerg
6827330f729Sjoerg EndOfSequence = I;
6837330f729Sjoerg AlignMacroSequence(StartOfSequence, EndOfSequence, MinColumn, MaxColumn,
6847330f729Sjoerg FoundMatchOnLine, AlignMacrosMatches, Changes);
6857330f729Sjoerg }
6867330f729Sjoerg
alignConsecutiveAssignments()6877330f729Sjoerg void WhitespaceManager::alignConsecutiveAssignments() {
688*e038c9c4Sjoerg if (Style.AlignConsecutiveAssignments == FormatStyle::ACS_None)
6897330f729Sjoerg return;
6907330f729Sjoerg
6917330f729Sjoerg AlignTokens(
6927330f729Sjoerg Style,
6937330f729Sjoerg [&](const Change &C) {
6947330f729Sjoerg // Do not align on equal signs that are first on a line.
6957330f729Sjoerg if (C.NewlinesBefore > 0)
6967330f729Sjoerg return false;
6977330f729Sjoerg
6987330f729Sjoerg // Do not align on equal signs that are last on a line.
6997330f729Sjoerg if (&C != &Changes.back() && (&C + 1)->NewlinesBefore > 0)
7007330f729Sjoerg return false;
7017330f729Sjoerg
7027330f729Sjoerg return C.Tok->is(tok::equal);
7037330f729Sjoerg },
704*e038c9c4Sjoerg Changes, /*StartAt=*/0, Style.AlignConsecutiveAssignments);
705*e038c9c4Sjoerg }
706*e038c9c4Sjoerg
alignConsecutiveBitFields()707*e038c9c4Sjoerg void WhitespaceManager::alignConsecutiveBitFields() {
708*e038c9c4Sjoerg if (Style.AlignConsecutiveBitFields == FormatStyle::ACS_None)
709*e038c9c4Sjoerg return;
710*e038c9c4Sjoerg
711*e038c9c4Sjoerg AlignTokens(
712*e038c9c4Sjoerg Style,
713*e038c9c4Sjoerg [&](Change const &C) {
714*e038c9c4Sjoerg // Do not align on ':' that is first on a line.
715*e038c9c4Sjoerg if (C.NewlinesBefore > 0)
716*e038c9c4Sjoerg return false;
717*e038c9c4Sjoerg
718*e038c9c4Sjoerg // Do not align on ':' that is last on a line.
719*e038c9c4Sjoerg if (&C != &Changes.back() && (&C + 1)->NewlinesBefore > 0)
720*e038c9c4Sjoerg return false;
721*e038c9c4Sjoerg
722*e038c9c4Sjoerg return C.Tok->is(TT_BitFieldColon);
723*e038c9c4Sjoerg },
724*e038c9c4Sjoerg Changes, /*StartAt=*/0, Style.AlignConsecutiveBitFields);
7257330f729Sjoerg }
7267330f729Sjoerg
alignConsecutiveDeclarations()7277330f729Sjoerg void WhitespaceManager::alignConsecutiveDeclarations() {
728*e038c9c4Sjoerg if (Style.AlignConsecutiveDeclarations == FormatStyle::ACS_None)
7297330f729Sjoerg return;
7307330f729Sjoerg
7317330f729Sjoerg // FIXME: Currently we don't handle properly the PointerAlignment: Right
7327330f729Sjoerg // The * and & are not aligned and are left dangling. Something has to be done
7337330f729Sjoerg // about it, but it raises the question of alignment of code like:
7347330f729Sjoerg // const char* const* v1;
7357330f729Sjoerg // float const* v2;
7367330f729Sjoerg // SomeVeryLongType const& v3;
7377330f729Sjoerg AlignTokens(
7387330f729Sjoerg Style,
7397330f729Sjoerg [](Change const &C) {
7407330f729Sjoerg // tok::kw_operator is necessary for aligning operator overload
7417330f729Sjoerg // definitions.
7427330f729Sjoerg if (C.Tok->isOneOf(TT_FunctionDeclarationName, tok::kw_operator))
7437330f729Sjoerg return true;
7447330f729Sjoerg if (C.Tok->isNot(TT_StartOfName))
7457330f729Sjoerg return false;
746*e038c9c4Sjoerg if (C.Tok->Previous &&
747*e038c9c4Sjoerg C.Tok->Previous->is(TT_StatementAttributeLikeMacro))
748*e038c9c4Sjoerg return false;
7497330f729Sjoerg // Check if there is a subsequent name that starts the same declaration.
7507330f729Sjoerg for (FormatToken *Next = C.Tok->Next; Next; Next = Next->Next) {
7517330f729Sjoerg if (Next->is(tok::comment))
7527330f729Sjoerg continue;
753*e038c9c4Sjoerg if (Next->is(TT_PointerOrReference))
754*e038c9c4Sjoerg return false;
7557330f729Sjoerg if (!Next->Tok.getIdentifierInfo())
7567330f729Sjoerg break;
7577330f729Sjoerg if (Next->isOneOf(TT_StartOfName, TT_FunctionDeclarationName,
7587330f729Sjoerg tok::kw_operator))
7597330f729Sjoerg return false;
7607330f729Sjoerg }
7617330f729Sjoerg return true;
7627330f729Sjoerg },
763*e038c9c4Sjoerg Changes, /*StartAt=*/0, Style.AlignConsecutiveDeclarations);
764*e038c9c4Sjoerg }
765*e038c9c4Sjoerg
alignChainedConditionals()766*e038c9c4Sjoerg void WhitespaceManager::alignChainedConditionals() {
767*e038c9c4Sjoerg if (Style.BreakBeforeTernaryOperators) {
768*e038c9c4Sjoerg AlignTokens(
769*e038c9c4Sjoerg Style,
770*e038c9c4Sjoerg [](Change const &C) {
771*e038c9c4Sjoerg // Align question operators and last colon
772*e038c9c4Sjoerg return C.Tok->is(TT_ConditionalExpr) &&
773*e038c9c4Sjoerg ((C.Tok->is(tok::question) && !C.NewlinesBefore) ||
774*e038c9c4Sjoerg (C.Tok->is(tok::colon) && C.Tok->Next &&
775*e038c9c4Sjoerg (C.Tok->Next->FakeLParens.size() == 0 ||
776*e038c9c4Sjoerg C.Tok->Next->FakeLParens.back() != prec::Conditional)));
777*e038c9c4Sjoerg },
7787330f729Sjoerg Changes, /*StartAt=*/0);
779*e038c9c4Sjoerg } else {
780*e038c9c4Sjoerg static auto AlignWrappedOperand = [](Change const &C) {
781*e038c9c4Sjoerg FormatToken *Previous = C.Tok->getPreviousNonComment();
782*e038c9c4Sjoerg return C.NewlinesBefore && Previous && Previous->is(TT_ConditionalExpr) &&
783*e038c9c4Sjoerg (Previous->is(tok::colon) &&
784*e038c9c4Sjoerg (C.Tok->FakeLParens.size() == 0 ||
785*e038c9c4Sjoerg C.Tok->FakeLParens.back() != prec::Conditional));
786*e038c9c4Sjoerg };
787*e038c9c4Sjoerg // Ensure we keep alignment of wrapped operands with non-wrapped operands
788*e038c9c4Sjoerg // Since we actually align the operators, the wrapped operands need the
789*e038c9c4Sjoerg // extra offset to be properly aligned.
790*e038c9c4Sjoerg for (Change &C : Changes) {
791*e038c9c4Sjoerg if (AlignWrappedOperand(C))
792*e038c9c4Sjoerg C.StartOfTokenColumn -= 2;
793*e038c9c4Sjoerg }
794*e038c9c4Sjoerg AlignTokens(
795*e038c9c4Sjoerg Style,
796*e038c9c4Sjoerg [this](Change const &C) {
797*e038c9c4Sjoerg // Align question operators if next operand is not wrapped, as
798*e038c9c4Sjoerg // well as wrapped operands after question operator or last
799*e038c9c4Sjoerg // colon in conditional sequence
800*e038c9c4Sjoerg return (C.Tok->is(TT_ConditionalExpr) && C.Tok->is(tok::question) &&
801*e038c9c4Sjoerg &C != &Changes.back() && (&C + 1)->NewlinesBefore == 0 &&
802*e038c9c4Sjoerg !(&C + 1)->IsTrailingComment) ||
803*e038c9c4Sjoerg AlignWrappedOperand(C);
804*e038c9c4Sjoerg },
805*e038c9c4Sjoerg Changes, /*StartAt=*/0);
806*e038c9c4Sjoerg }
8077330f729Sjoerg }
8087330f729Sjoerg
alignTrailingComments()8097330f729Sjoerg void WhitespaceManager::alignTrailingComments() {
8107330f729Sjoerg unsigned MinColumn = 0;
8117330f729Sjoerg unsigned MaxColumn = UINT_MAX;
8127330f729Sjoerg unsigned StartOfSequence = 0;
8137330f729Sjoerg bool BreakBeforeNext = false;
8147330f729Sjoerg unsigned Newlines = 0;
8157330f729Sjoerg for (unsigned i = 0, e = Changes.size(); i != e; ++i) {
8167330f729Sjoerg if (Changes[i].StartOfBlockComment)
8177330f729Sjoerg continue;
8187330f729Sjoerg Newlines += Changes[i].NewlinesBefore;
8197330f729Sjoerg if (!Changes[i].IsTrailingComment)
8207330f729Sjoerg continue;
8217330f729Sjoerg
8227330f729Sjoerg unsigned ChangeMinColumn = Changes[i].StartOfTokenColumn;
8237330f729Sjoerg unsigned ChangeMaxColumn;
8247330f729Sjoerg
8257330f729Sjoerg if (Style.ColumnLimit == 0)
8267330f729Sjoerg ChangeMaxColumn = UINT_MAX;
8277330f729Sjoerg else if (Style.ColumnLimit >= Changes[i].TokenLength)
8287330f729Sjoerg ChangeMaxColumn = Style.ColumnLimit - Changes[i].TokenLength;
8297330f729Sjoerg else
8307330f729Sjoerg ChangeMaxColumn = ChangeMinColumn;
8317330f729Sjoerg
8327330f729Sjoerg // If we don't create a replacement for this change, we have to consider
8337330f729Sjoerg // it to be immovable.
8347330f729Sjoerg if (!Changes[i].CreateReplacement)
8357330f729Sjoerg ChangeMaxColumn = ChangeMinColumn;
8367330f729Sjoerg
8377330f729Sjoerg if (i + 1 != e && Changes[i + 1].ContinuesPPDirective)
8387330f729Sjoerg ChangeMaxColumn -= 2;
8397330f729Sjoerg // If this comment follows an } in column 0, it probably documents the
8407330f729Sjoerg // closing of a namespace and we don't want to align it.
8417330f729Sjoerg bool FollowsRBraceInColumn0 = i > 0 && Changes[i].NewlinesBefore == 0 &&
8427330f729Sjoerg Changes[i - 1].Tok->is(tok::r_brace) &&
8437330f729Sjoerg Changes[i - 1].StartOfTokenColumn == 0;
8447330f729Sjoerg bool WasAlignedWithStartOfNextLine = false;
8457330f729Sjoerg if (Changes[i].NewlinesBefore == 1) { // A comment on its own line.
8467330f729Sjoerg unsigned CommentColumn = SourceMgr.getSpellingColumnNumber(
8477330f729Sjoerg Changes[i].OriginalWhitespaceRange.getEnd());
8487330f729Sjoerg for (unsigned j = i + 1; j != e; ++j) {
8497330f729Sjoerg if (Changes[j].Tok->is(tok::comment))
8507330f729Sjoerg continue;
8517330f729Sjoerg
8527330f729Sjoerg unsigned NextColumn = SourceMgr.getSpellingColumnNumber(
8537330f729Sjoerg Changes[j].OriginalWhitespaceRange.getEnd());
8547330f729Sjoerg // The start of the next token was previously aligned with the
8557330f729Sjoerg // start of this comment.
8567330f729Sjoerg WasAlignedWithStartOfNextLine =
8577330f729Sjoerg CommentColumn == NextColumn ||
8587330f729Sjoerg CommentColumn == NextColumn + Style.IndentWidth;
8597330f729Sjoerg break;
8607330f729Sjoerg }
8617330f729Sjoerg }
8627330f729Sjoerg if (!Style.AlignTrailingComments || FollowsRBraceInColumn0) {
8637330f729Sjoerg alignTrailingComments(StartOfSequence, i, MinColumn);
8647330f729Sjoerg MinColumn = ChangeMinColumn;
8657330f729Sjoerg MaxColumn = ChangeMinColumn;
8667330f729Sjoerg StartOfSequence = i;
8677330f729Sjoerg } else if (BreakBeforeNext || Newlines > 1 ||
8687330f729Sjoerg (ChangeMinColumn > MaxColumn || ChangeMaxColumn < MinColumn) ||
8697330f729Sjoerg // Break the comment sequence if the previous line did not end
8707330f729Sjoerg // in a trailing comment.
8717330f729Sjoerg (Changes[i].NewlinesBefore == 1 && i > 0 &&
8727330f729Sjoerg !Changes[i - 1].IsTrailingComment) ||
8737330f729Sjoerg WasAlignedWithStartOfNextLine) {
8747330f729Sjoerg alignTrailingComments(StartOfSequence, i, MinColumn);
8757330f729Sjoerg MinColumn = ChangeMinColumn;
8767330f729Sjoerg MaxColumn = ChangeMaxColumn;
8777330f729Sjoerg StartOfSequence = i;
8787330f729Sjoerg } else {
8797330f729Sjoerg MinColumn = std::max(MinColumn, ChangeMinColumn);
8807330f729Sjoerg MaxColumn = std::min(MaxColumn, ChangeMaxColumn);
8817330f729Sjoerg }
8827330f729Sjoerg BreakBeforeNext = (i == 0) || (Changes[i].NewlinesBefore > 1) ||
8837330f729Sjoerg // Never start a sequence with a comment at the beginning
8847330f729Sjoerg // of the line.
8857330f729Sjoerg (Changes[i].NewlinesBefore == 1 && StartOfSequence == i);
8867330f729Sjoerg Newlines = 0;
8877330f729Sjoerg }
8887330f729Sjoerg alignTrailingComments(StartOfSequence, Changes.size(), MinColumn);
8897330f729Sjoerg }
8907330f729Sjoerg
alignTrailingComments(unsigned Start,unsigned End,unsigned Column)8917330f729Sjoerg void WhitespaceManager::alignTrailingComments(unsigned Start, unsigned End,
8927330f729Sjoerg unsigned Column) {
8937330f729Sjoerg for (unsigned i = Start; i != End; ++i) {
8947330f729Sjoerg int Shift = 0;
8957330f729Sjoerg if (Changes[i].IsTrailingComment) {
8967330f729Sjoerg Shift = Column - Changes[i].StartOfTokenColumn;
8977330f729Sjoerg }
8987330f729Sjoerg if (Changes[i].StartOfBlockComment) {
8997330f729Sjoerg Shift = Changes[i].IndentationOffset +
9007330f729Sjoerg Changes[i].StartOfBlockComment->StartOfTokenColumn -
9017330f729Sjoerg Changes[i].StartOfTokenColumn;
9027330f729Sjoerg }
9037330f729Sjoerg assert(Shift >= 0);
9047330f729Sjoerg Changes[i].Spaces += Shift;
9057330f729Sjoerg if (i + 1 != Changes.size())
9067330f729Sjoerg Changes[i + 1].PreviousEndOfTokenColumn += Shift;
9077330f729Sjoerg Changes[i].StartOfTokenColumn += Shift;
9087330f729Sjoerg }
9097330f729Sjoerg }
9107330f729Sjoerg
alignEscapedNewlines()9117330f729Sjoerg void WhitespaceManager::alignEscapedNewlines() {
9127330f729Sjoerg if (Style.AlignEscapedNewlines == FormatStyle::ENAS_DontAlign)
9137330f729Sjoerg return;
9147330f729Sjoerg
9157330f729Sjoerg bool AlignLeft = Style.AlignEscapedNewlines == FormatStyle::ENAS_Left;
9167330f729Sjoerg unsigned MaxEndOfLine = AlignLeft ? 0 : Style.ColumnLimit;
9177330f729Sjoerg unsigned StartOfMacro = 0;
9187330f729Sjoerg for (unsigned i = 1, e = Changes.size(); i < e; ++i) {
9197330f729Sjoerg Change &C = Changes[i];
9207330f729Sjoerg if (C.NewlinesBefore > 0) {
9217330f729Sjoerg if (C.ContinuesPPDirective) {
9227330f729Sjoerg MaxEndOfLine = std::max(C.PreviousEndOfTokenColumn + 2, MaxEndOfLine);
9237330f729Sjoerg } else {
9247330f729Sjoerg alignEscapedNewlines(StartOfMacro + 1, i, MaxEndOfLine);
9257330f729Sjoerg MaxEndOfLine = AlignLeft ? 0 : Style.ColumnLimit;
9267330f729Sjoerg StartOfMacro = i;
9277330f729Sjoerg }
9287330f729Sjoerg }
9297330f729Sjoerg }
9307330f729Sjoerg alignEscapedNewlines(StartOfMacro + 1, Changes.size(), MaxEndOfLine);
9317330f729Sjoerg }
9327330f729Sjoerg
alignEscapedNewlines(unsigned Start,unsigned End,unsigned Column)9337330f729Sjoerg void WhitespaceManager::alignEscapedNewlines(unsigned Start, unsigned End,
9347330f729Sjoerg unsigned Column) {
9357330f729Sjoerg for (unsigned i = Start; i < End; ++i) {
9367330f729Sjoerg Change &C = Changes[i];
9377330f729Sjoerg if (C.NewlinesBefore > 0) {
9387330f729Sjoerg assert(C.ContinuesPPDirective);
9397330f729Sjoerg if (C.PreviousEndOfTokenColumn + 1 > Column)
9407330f729Sjoerg C.EscapedNewlineColumn = 0;
9417330f729Sjoerg else
9427330f729Sjoerg C.EscapedNewlineColumn = Column;
9437330f729Sjoerg }
9447330f729Sjoerg }
9457330f729Sjoerg }
9467330f729Sjoerg
generateChanges()9477330f729Sjoerg void WhitespaceManager::generateChanges() {
9487330f729Sjoerg for (unsigned i = 0, e = Changes.size(); i != e; ++i) {
9497330f729Sjoerg const Change &C = Changes[i];
9507330f729Sjoerg if (i > 0) {
9517330f729Sjoerg assert(Changes[i - 1].OriginalWhitespaceRange.getBegin() !=
9527330f729Sjoerg C.OriginalWhitespaceRange.getBegin() &&
9537330f729Sjoerg "Generating two replacements for the same location");
9547330f729Sjoerg }
9557330f729Sjoerg if (C.CreateReplacement) {
9567330f729Sjoerg std::string ReplacementText = C.PreviousLinePostfix;
9577330f729Sjoerg if (C.ContinuesPPDirective)
9587330f729Sjoerg appendEscapedNewlineText(ReplacementText, C.NewlinesBefore,
9597330f729Sjoerg C.PreviousEndOfTokenColumn,
9607330f729Sjoerg C.EscapedNewlineColumn);
9617330f729Sjoerg else
9627330f729Sjoerg appendNewlineText(ReplacementText, C.NewlinesBefore);
963*e038c9c4Sjoerg appendIndentText(
964*e038c9c4Sjoerg ReplacementText, C.Tok->IndentLevel, std::max(0, C.Spaces),
965*e038c9c4Sjoerg C.StartOfTokenColumn - std::max(0, C.Spaces), C.IsAligned);
9667330f729Sjoerg ReplacementText.append(C.CurrentLinePrefix);
9677330f729Sjoerg storeReplacement(C.OriginalWhitespaceRange, ReplacementText);
9687330f729Sjoerg }
9697330f729Sjoerg }
9707330f729Sjoerg }
9717330f729Sjoerg
storeReplacement(SourceRange Range,StringRef Text)9727330f729Sjoerg void WhitespaceManager::storeReplacement(SourceRange Range, StringRef Text) {
9737330f729Sjoerg unsigned WhitespaceLength = SourceMgr.getFileOffset(Range.getEnd()) -
9747330f729Sjoerg SourceMgr.getFileOffset(Range.getBegin());
9757330f729Sjoerg // Don't create a replacement, if it does not change anything.
9767330f729Sjoerg if (StringRef(SourceMgr.getCharacterData(Range.getBegin()),
9777330f729Sjoerg WhitespaceLength) == Text)
9787330f729Sjoerg return;
9797330f729Sjoerg auto Err = Replaces.add(tooling::Replacement(
9807330f729Sjoerg SourceMgr, CharSourceRange::getCharRange(Range), Text));
9817330f729Sjoerg // FIXME: better error handling. For now, just print an error message in the
9827330f729Sjoerg // release version.
9837330f729Sjoerg if (Err) {
9847330f729Sjoerg llvm::errs() << llvm::toString(std::move(Err)) << "\n";
9857330f729Sjoerg assert(false);
9867330f729Sjoerg }
9877330f729Sjoerg }
9887330f729Sjoerg
appendNewlineText(std::string & Text,unsigned Newlines)9897330f729Sjoerg void WhitespaceManager::appendNewlineText(std::string &Text,
9907330f729Sjoerg unsigned Newlines) {
9917330f729Sjoerg for (unsigned i = 0; i < Newlines; ++i)
9927330f729Sjoerg Text.append(UseCRLF ? "\r\n" : "\n");
9937330f729Sjoerg }
9947330f729Sjoerg
appendEscapedNewlineText(std::string & Text,unsigned Newlines,unsigned PreviousEndOfTokenColumn,unsigned EscapedNewlineColumn)9957330f729Sjoerg void WhitespaceManager::appendEscapedNewlineText(
9967330f729Sjoerg std::string &Text, unsigned Newlines, unsigned PreviousEndOfTokenColumn,
9977330f729Sjoerg unsigned EscapedNewlineColumn) {
9987330f729Sjoerg if (Newlines > 0) {
9997330f729Sjoerg unsigned Spaces =
10007330f729Sjoerg std::max<int>(1, EscapedNewlineColumn - PreviousEndOfTokenColumn - 1);
10017330f729Sjoerg for (unsigned i = 0; i < Newlines; ++i) {
10027330f729Sjoerg Text.append(Spaces, ' ');
10037330f729Sjoerg Text.append(UseCRLF ? "\\\r\n" : "\\\n");
10047330f729Sjoerg Spaces = std::max<int>(0, EscapedNewlineColumn - 1);
10057330f729Sjoerg }
10067330f729Sjoerg }
10077330f729Sjoerg }
10087330f729Sjoerg
appendIndentText(std::string & Text,unsigned IndentLevel,unsigned Spaces,unsigned WhitespaceStartColumn,bool IsAligned)10097330f729Sjoerg void WhitespaceManager::appendIndentText(std::string &Text,
10107330f729Sjoerg unsigned IndentLevel, unsigned Spaces,
1011*e038c9c4Sjoerg unsigned WhitespaceStartColumn,
1012*e038c9c4Sjoerg bool IsAligned) {
10137330f729Sjoerg switch (Style.UseTab) {
10147330f729Sjoerg case FormatStyle::UT_Never:
10157330f729Sjoerg Text.append(Spaces, ' ');
10167330f729Sjoerg break;
10177330f729Sjoerg case FormatStyle::UT_Always: {
10187330f729Sjoerg if (Style.TabWidth) {
10197330f729Sjoerg unsigned FirstTabWidth =
10207330f729Sjoerg Style.TabWidth - WhitespaceStartColumn % Style.TabWidth;
10217330f729Sjoerg
10227330f729Sjoerg // Insert only spaces when we want to end up before the next tab.
10237330f729Sjoerg if (Spaces < FirstTabWidth || Spaces == 1) {
10247330f729Sjoerg Text.append(Spaces, ' ');
10257330f729Sjoerg break;
10267330f729Sjoerg }
10277330f729Sjoerg // Align to the next tab.
10287330f729Sjoerg Spaces -= FirstTabWidth;
10297330f729Sjoerg Text.append("\t");
10307330f729Sjoerg
10317330f729Sjoerg Text.append(Spaces / Style.TabWidth, '\t');
10327330f729Sjoerg Text.append(Spaces % Style.TabWidth, ' ');
10337330f729Sjoerg } else if (Spaces == 1) {
10347330f729Sjoerg Text.append(Spaces, ' ');
10357330f729Sjoerg }
10367330f729Sjoerg break;
10377330f729Sjoerg }
10387330f729Sjoerg case FormatStyle::UT_ForIndentation:
10397330f729Sjoerg if (WhitespaceStartColumn == 0) {
10407330f729Sjoerg unsigned Indentation = IndentLevel * Style.IndentWidth;
1041*e038c9c4Sjoerg Spaces = appendTabIndent(Text, Spaces, Indentation);
1042*e038c9c4Sjoerg }
1043*e038c9c4Sjoerg Text.append(Spaces, ' ');
1044*e038c9c4Sjoerg break;
1045*e038c9c4Sjoerg case FormatStyle::UT_ForContinuationAndIndentation:
1046*e038c9c4Sjoerg if (WhitespaceStartColumn == 0)
1047*e038c9c4Sjoerg Spaces = appendTabIndent(Text, Spaces, Spaces);
1048*e038c9c4Sjoerg Text.append(Spaces, ' ');
1049*e038c9c4Sjoerg break;
1050*e038c9c4Sjoerg case FormatStyle::UT_AlignWithSpaces:
1051*e038c9c4Sjoerg if (WhitespaceStartColumn == 0) {
1052*e038c9c4Sjoerg unsigned Indentation =
1053*e038c9c4Sjoerg IsAligned ? IndentLevel * Style.IndentWidth : Spaces;
1054*e038c9c4Sjoerg Spaces = appendTabIndent(Text, Spaces, Indentation);
1055*e038c9c4Sjoerg }
1056*e038c9c4Sjoerg Text.append(Spaces, ' ');
1057*e038c9c4Sjoerg break;
1058*e038c9c4Sjoerg }
1059*e038c9c4Sjoerg }
1060*e038c9c4Sjoerg
appendTabIndent(std::string & Text,unsigned Spaces,unsigned Indentation)1061*e038c9c4Sjoerg unsigned WhitespaceManager::appendTabIndent(std::string &Text, unsigned Spaces,
1062*e038c9c4Sjoerg unsigned Indentation) {
1063*e038c9c4Sjoerg // This happens, e.g. when a line in a block comment is indented less than the
1064*e038c9c4Sjoerg // first one.
10657330f729Sjoerg if (Indentation > Spaces)
10667330f729Sjoerg Indentation = Spaces;
10677330f729Sjoerg if (Style.TabWidth) {
10687330f729Sjoerg unsigned Tabs = Indentation / Style.TabWidth;
10697330f729Sjoerg Text.append(Tabs, '\t');
10707330f729Sjoerg Spaces -= Tabs * Style.TabWidth;
10717330f729Sjoerg }
1072*e038c9c4Sjoerg return Spaces;
10737330f729Sjoerg }
10747330f729Sjoerg
10757330f729Sjoerg } // namespace format
10767330f729Sjoerg } // namespace clang
1077