aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/clang/lib/Format/WhitespaceManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/clang/lib/Format/WhitespaceManager.cpp')
-rw-r--r--contrib/llvm-project/clang/lib/Format/WhitespaceManager.cpp818
1 files changed, 596 insertions, 222 deletions
diff --git a/contrib/llvm-project/clang/lib/Format/WhitespaceManager.cpp b/contrib/llvm-project/clang/lib/Format/WhitespaceManager.cpp
index a822e0aaf1f9..df84f97a8e8a 100644
--- a/contrib/llvm-project/clang/lib/Format/WhitespaceManager.cpp
+++ b/contrib/llvm-project/clang/lib/Format/WhitespaceManager.cpp
@@ -22,8 +22,13 @@ namespace format {
bool WhitespaceManager::Change::IsBeforeInFile::operator()(
const Change &C1, const Change &C2) const {
return SourceMgr.isBeforeInTranslationUnit(
- C1.OriginalWhitespaceRange.getBegin(),
- C2.OriginalWhitespaceRange.getBegin());
+ C1.OriginalWhitespaceRange.getBegin(),
+ C2.OriginalWhitespaceRange.getBegin()) ||
+ (C1.OriginalWhitespaceRange.getBegin() ==
+ C2.OriginalWhitespaceRange.getBegin() &&
+ SourceMgr.isBeforeInTranslationUnit(
+ C1.OriginalWhitespaceRange.getEnd(),
+ C2.OriginalWhitespaceRange.getEnd()));
}
WhitespaceManager::Change::Change(const FormatToken &Tok,
@@ -49,7 +54,7 @@ void WhitespaceManager::replaceWhitespace(FormatToken &Tok, unsigned Newlines,
unsigned Spaces,
unsigned StartOfTokenColumn,
bool IsAligned, bool InPPDirective) {
- if (Tok.Finalized)
+ if (Tok.Finalized || (Tok.MacroCtx && Tok.MacroCtx->Role == MR_ExpandedArg))
return;
Tok.setDecision((Newlines > 0) ? FD_Break : FD_Continue);
Changes.push_back(Change(Tok, /*CreateReplacement=*/true, Tok.WhitespaceRange,
@@ -60,7 +65,7 @@ void WhitespaceManager::replaceWhitespace(FormatToken &Tok, unsigned Newlines,
void WhitespaceManager::addUntouchableToken(const FormatToken &Tok,
bool InPPDirective) {
- if (Tok.Finalized)
+ if (Tok.Finalized || (Tok.MacroCtx && Tok.MacroCtx->Role == MR_ExpandedArg))
return;
Changes.push_back(Change(Tok, /*CreateReplacement=*/false,
Tok.WhitespaceRange, /*Spaces=*/0,
@@ -74,11 +79,17 @@ WhitespaceManager::addReplacement(const tooling::Replacement &Replacement) {
return Replaces.add(Replacement);
}
+bool WhitespaceManager::inputUsesCRLF(StringRef Text, bool DefaultToCRLF) {
+ size_t LF = Text.count('\n');
+ size_t CR = Text.count('\r') * 2;
+ return LF == CR ? DefaultToCRLF : CR > LF;
+}
+
void WhitespaceManager::replaceWhitespaceInToken(
const FormatToken &Tok, unsigned Offset, unsigned ReplaceChars,
StringRef PreviousPostfix, StringRef CurrentPrefix, bool InPPDirective,
unsigned Newlines, int Spaces) {
- if (Tok.Finalized)
+ if (Tok.Finalized || (Tok.MacroCtx && Tok.MacroCtx->Role == MR_ExpandedArg))
return;
SourceLocation Start = Tok.getStartOfNonWhitespace().getLocWithOffset(Offset);
Changes.push_back(
@@ -96,6 +107,7 @@ const tooling::Replacements &WhitespaceManager::generateReplacements() {
llvm::sort(Changes, Change::IsBeforeInFile(SourceMgr));
calculateLineBreakInformation();
alignConsecutiveMacros();
+ alignConsecutiveShortCaseStatements();
alignConsecutiveDeclarations();
alignConsecutiveBitFields();
alignConsecutiveAssignments();
@@ -161,11 +173,12 @@ void WhitespaceManager::calculateLineBreakInformation() {
// If there are multiple changes in this token, sum up all the changes until
// the end of the line.
- if (Changes[i - 1].IsInsideToken && Changes[i - 1].NewlinesBefore == 0)
+ if (Changes[i - 1].IsInsideToken && Changes[i - 1].NewlinesBefore == 0) {
LastOutsideTokenChange->TokenLength +=
Changes[i - 1].TokenLength + Changes[i - 1].Spaces;
- else
+ } else {
LastOutsideTokenChange = &Changes[i - 1];
+ }
Changes[i].PreviousEndOfTokenColumn =
Changes[i - 1].StartOfTokenColumn + Changes[i - 1].TokenLength;
@@ -221,13 +234,12 @@ void WhitespaceManager::calculateLineBreakInformation() {
Change.StartOfBlockComment = nullptr;
Change.IndentationOffset = 0;
if (Change.Tok->is(tok::comment)) {
- if (Change.Tok->is(TT_LineComment) || !Change.IsInsideToken)
+ if (Change.Tok->is(TT_LineComment) || !Change.IsInsideToken) {
LastBlockComment = &Change;
- else {
- if ((Change.StartOfBlockComment = LastBlockComment))
- Change.IndentationOffset =
- Change.StartOfTokenColumn -
- Change.StartOfBlockComment->StartOfTokenColumn;
+ } else if ((Change.StartOfBlockComment = LastBlockComment)) {
+ Change.IndentationOffset =
+ Change.StartOfTokenColumn -
+ Change.StartOfBlockComment->StartOfTokenColumn;
}
} else {
LastBlockComment = nullptr;
@@ -254,19 +266,20 @@ void WhitespaceManager::calculateLineBreakInformation() {
Change.ConditionalsLevel = ConditionalsLevel;
- for (unsigned i = Change.Tok->FakeRParens; i > 0 && ScopeStack.size();
- --i) {
+ for (unsigned i = Change.Tok->FakeRParens; i > 0 && ScopeStack.size(); --i)
if (ScopeStack.pop_back_val())
--ConditionalsLevel;
- }
}
}
// Align a single sequence of tokens, see AlignTokens below.
+// Column - The token for which Matches returns true is moved to this column.
+// RightJustify - Whether it is the token's right end or left end that gets
+// moved to that column.
template <typename F>
static void
AlignTokenSequence(const FormatStyle &Style, unsigned Start, unsigned End,
- unsigned Column, F &&Matches,
+ unsigned Column, bool RightJustify, F &&Matches,
SmallVector<WhitespaceManager::Change, 16> &Changes) {
bool FoundMatchOnLine = false;
int Shift = 0;
@@ -294,28 +307,32 @@ AlignTokenSequence(const FormatStyle &Style, unsigned Start, unsigned End,
SmallVector<unsigned, 16> ScopeStack;
for (unsigned i = Start; i != End; ++i) {
+ auto &CurrentChange = Changes[i];
if (ScopeStack.size() != 0 &&
- Changes[i].indentAndNestingLevel() <
- Changes[ScopeStack.back()].indentAndNestingLevel())
+ CurrentChange.indentAndNestingLevel() <
+ Changes[ScopeStack.back()].indentAndNestingLevel()) {
ScopeStack.pop_back();
+ }
// Compare current token to previous non-comment token to ensure whether
// it is in a deeper scope or not.
unsigned PreviousNonComment = i - 1;
while (PreviousNonComment > Start &&
- Changes[PreviousNonComment].Tok->is(tok::comment))
- PreviousNonComment--;
- if (i != Start && Changes[i].indentAndNestingLevel() >
- Changes[PreviousNonComment].indentAndNestingLevel())
+ Changes[PreviousNonComment].Tok->is(tok::comment)) {
+ --PreviousNonComment;
+ }
+ if (i != Start && CurrentChange.indentAndNestingLevel() >
+ Changes[PreviousNonComment].indentAndNestingLevel()) {
ScopeStack.push_back(i);
+ }
bool InsideNestedScope = ScopeStack.size() != 0;
bool ContinuedStringLiteral = i > Start &&
- Changes[i].Tok->is(tok::string_literal) &&
+ CurrentChange.Tok->is(tok::string_literal) &&
Changes[i - 1].Tok->is(tok::string_literal);
bool SkipMatchCheck = InsideNestedScope || ContinuedStringLiteral;
- if (Changes[i].NewlinesBefore > 0 && !SkipMatchCheck) {
+ if (CurrentChange.NewlinesBefore > 0 && !SkipMatchCheck) {
Shift = 0;
FoundMatchOnLine = false;
}
@@ -323,70 +340,146 @@ AlignTokenSequence(const FormatStyle &Style, unsigned Start, unsigned End,
// If this is the first matching token to be aligned, remember by how many
// spaces it has to be shifted, so the rest of the changes on the line are
// shifted by the same amount
- if (!FoundMatchOnLine && !SkipMatchCheck && Matches(Changes[i])) {
+ if (!FoundMatchOnLine && !SkipMatchCheck && Matches(CurrentChange)) {
FoundMatchOnLine = true;
- Shift = Column - Changes[i].StartOfTokenColumn;
- Changes[i].Spaces += Shift;
+ Shift = Column - (RightJustify ? CurrentChange.TokenLength : 0) -
+ CurrentChange.StartOfTokenColumn;
+ CurrentChange.Spaces += Shift;
+ // FIXME: This is a workaround that should be removed when we fix
+ // http://llvm.org/PR53699. An assertion later below verifies this.
+ if (CurrentChange.NewlinesBefore == 0) {
+ CurrentChange.Spaces =
+ std::max(CurrentChange.Spaces,
+ static_cast<int>(CurrentChange.Tok->SpacesRequiredBefore));
+ }
}
+ if (Shift == 0)
+ continue;
+
// This is for function parameters that are split across multiple lines,
// as mentioned in the ScopeStack comment.
- if (InsideNestedScope && Changes[i].NewlinesBefore > 0) {
+ if (InsideNestedScope && CurrentChange.NewlinesBefore > 0) {
unsigned ScopeStart = ScopeStack.back();
auto ShouldShiftBeAdded = [&] {
// Function declaration
if (Changes[ScopeStart - 1].Tok->is(TT_FunctionDeclarationName))
return true;
+ // Lambda.
+ if (Changes[ScopeStart - 1].Tok->is(TT_LambdaLBrace))
+ return false;
+
// Continued function declaration
if (ScopeStart > Start + 1 &&
- Changes[ScopeStart - 2].Tok->is(TT_FunctionDeclarationName))
+ Changes[ScopeStart - 2].Tok->is(TT_FunctionDeclarationName)) {
return true;
+ }
- // Continued function call
+ // Continued (template) function call.
if (ScopeStart > Start + 1 &&
- Changes[ScopeStart - 2].Tok->is(tok::identifier) &&
- Changes[ScopeStart - 1].Tok->is(tok::l_paren))
+ Changes[ScopeStart - 2].Tok->isOneOf(tok::identifier,
+ TT_TemplateCloser) &&
+ Changes[ScopeStart - 1].Tok->is(tok::l_paren) &&
+ Changes[ScopeStart].Tok->isNot(TT_LambdaLSquare)) {
+ if (CurrentChange.Tok->MatchingParen &&
+ CurrentChange.Tok->MatchingParen->is(TT_LambdaLBrace)) {
+ return false;
+ }
+ if (Changes[ScopeStart].NewlinesBefore > 0)
+ return false;
+ if (CurrentChange.Tok->is(tok::l_brace) &&
+ CurrentChange.Tok->is(BK_BracedInit)) {
+ return true;
+ }
return Style.BinPackArguments;
+ }
// Ternary operator
- if (Changes[i].Tok->is(TT_ConditionalExpr))
+ if (CurrentChange.Tok->is(TT_ConditionalExpr))
return true;
// Period Initializer .XXX = 1.
- if (Changes[i].Tok->is(TT_DesignatedInitializerPeriod))
+ if (CurrentChange.Tok->is(TT_DesignatedInitializerPeriod))
return true;
// Continued ternary operator
- if (Changes[i].Tok->Previous &&
- Changes[i].Tok->Previous->is(TT_ConditionalExpr))
+ if (CurrentChange.Tok->Previous &&
+ CurrentChange.Tok->Previous->is(TT_ConditionalExpr)) {
+ return true;
+ }
+
+ // Continued direct-list-initialization using braced list.
+ if (ScopeStart > Start + 1 &&
+ Changes[ScopeStart - 2].Tok->is(tok::identifier) &&
+ Changes[ScopeStart - 1].Tok->is(tok::l_brace) &&
+ CurrentChange.Tok->is(tok::l_brace) &&
+ CurrentChange.Tok->is(BK_BracedInit)) {
+ return true;
+ }
+
+ // Continued braced list.
+ if (ScopeStart > Start + 1 &&
+ Changes[ScopeStart - 2].Tok->isNot(tok::identifier) &&
+ Changes[ScopeStart - 1].Tok->is(tok::l_brace) &&
+ CurrentChange.Tok->isNot(tok::r_brace)) {
+ for (unsigned OuterScopeStart : llvm::reverse(ScopeStack)) {
+ // Lambda.
+ if (OuterScopeStart > Start &&
+ Changes[OuterScopeStart - 1].Tok->is(TT_LambdaLBrace)) {
+ return false;
+ }
+ }
+ if (Changes[ScopeStart].NewlinesBefore > 0)
+ return false;
+ return true;
+ }
+
+ // Continued template parameter.
+ if (Changes[ScopeStart - 1].Tok->is(TT_TemplateOpener))
return true;
return false;
};
if (ShouldShiftBeAdded())
- Changes[i].Spaces += Shift;
+ CurrentChange.Spaces += Shift;
}
if (ContinuedStringLiteral)
- Changes[i].Spaces += Shift;
+ CurrentChange.Spaces += Shift;
- assert(Shift >= 0);
+ // We should not remove required spaces unless we break the line before.
+ assert(Shift > 0 || Changes[i].NewlinesBefore > 0 ||
+ CurrentChange.Spaces >=
+ static_cast<int>(Changes[i].Tok->SpacesRequiredBefore) ||
+ CurrentChange.Tok->is(tok::eof));
- Changes[i].StartOfTokenColumn += Shift;
+ CurrentChange.StartOfTokenColumn += Shift;
if (i + 1 != Changes.size())
Changes[i + 1].PreviousEndOfTokenColumn += Shift;
// If PointerAlignment is PAS_Right, keep *s or &s next to the token
- if (Style.PointerAlignment == FormatStyle::PAS_Right &&
- Changes[i].Spaces != 0) {
+ if ((Style.PointerAlignment == FormatStyle::PAS_Right ||
+ Style.ReferenceAlignment == FormatStyle::RAS_Right) &&
+ CurrentChange.Spaces != 0) {
+ const bool ReferenceNotRightAligned =
+ Style.ReferenceAlignment != FormatStyle::RAS_Right &&
+ Style.ReferenceAlignment != FormatStyle::RAS_Pointer;
for (int Previous = i - 1;
Previous >= 0 &&
Changes[Previous].Tok->getType() == TT_PointerOrReference;
--Previous) {
+ assert(Changes[Previous].Tok->isPointerOrReference());
+ if (Changes[Previous].Tok->isNot(tok::star)) {
+ if (ReferenceNotRightAligned)
+ continue;
+ } else if (Style.PointerAlignment != FormatStyle::PAS_Right) {
+ continue;
+ }
Changes[Previous + 1].Spaces -= Shift;
Changes[Previous].Spaces += Shift;
+ Changes[Previous].StartOfTokenColumn += Shift;
}
}
}
@@ -419,13 +512,31 @@ AlignTokenSequence(const FormatStyle &Style, unsigned Start, unsigned End,
// However, the special exception is that we do NOT skip function parameters
// that are split across multiple lines. See the test case in FormatTest.cpp
// that mentions "split function parameter alignment" for an example of this.
+// When the parameter RightJustify is true, the operator will be
+// right-justified. It is used to align compound assignments like `+=` and `=`.
+// When RightJustify and ACS.PadOperators are true, operators in each block to
+// be aligned will be padded on the left to the same length before aligning.
template <typename F>
-static unsigned AlignTokens(
- const FormatStyle &Style, F &&Matches,
- SmallVector<WhitespaceManager::Change, 16> &Changes, unsigned StartAt,
- const FormatStyle::AlignConsecutiveStyle &ACS = FormatStyle::ACS_None) {
- unsigned MinColumn = 0;
- unsigned MaxColumn = UINT_MAX;
+static unsigned AlignTokens(const FormatStyle &Style, F &&Matches,
+ SmallVector<WhitespaceManager::Change, 16> &Changes,
+ unsigned StartAt,
+ const FormatStyle::AlignConsecutiveStyle &ACS = {},
+ bool RightJustify = false) {
+ // We arrange each line in 3 parts. The operator to be aligned (the anchor),
+ // and text to its left and right. In the aligned text the width of each part
+ // will be the maximum of that over the block that has been aligned. Maximum
+ // widths of each part so far. When RightJustify is true and ACS.PadOperators
+ // is false, the part from start of line to the right end of the anchor.
+ // Otherwise, only the part to the left of the anchor. Including the space
+ // that exists on its left from the start. Not including the padding added on
+ // the left to right-justify the anchor.
+ unsigned WidthLeft = 0;
+ // The operator to be aligned when RightJustify is true and ACS.PadOperators
+ // is false. 0 otherwise.
+ unsigned WidthAnchor = 0;
+ // Width to the right of the anchor. Plus width of the anchor when
+ // RightJustify is false.
+ unsigned WidthRight = 0;
// Line number of the start and the end of the current token sequence.
unsigned StartOfSequence = 0;
@@ -457,63 +568,63 @@ static unsigned AlignTokens(
// We need to adjust the StartOfTokenColumn of each Change that is on a line
// containing any matching token to be aligned and located after such token.
auto AlignCurrentSequence = [&] {
- if (StartOfSequence > 0 && StartOfSequence < EndOfSequence)
- AlignTokenSequence(Style, StartOfSequence, EndOfSequence, MinColumn,
- Matches, Changes);
- MinColumn = 0;
- MaxColumn = UINT_MAX;
+ if (StartOfSequence > 0 && StartOfSequence < EndOfSequence) {
+ AlignTokenSequence(Style, StartOfSequence, EndOfSequence,
+ WidthLeft + WidthAnchor, RightJustify, Matches,
+ Changes);
+ }
+ WidthLeft = 0;
+ WidthAnchor = 0;
+ WidthRight = 0;
StartOfSequence = 0;
EndOfSequence = 0;
};
unsigned i = StartAt;
for (unsigned e = Changes.size(); i != e; ++i) {
- if (Changes[i].indentAndNestingLevel() < IndentAndNestingLevel)
+ auto &CurrentChange = Changes[i];
+ if (CurrentChange.indentAndNestingLevel() < IndentAndNestingLevel)
break;
- if (Changes[i].NewlinesBefore != 0) {
+ if (CurrentChange.NewlinesBefore != 0) {
CommasBeforeMatch = 0;
EndOfSequence = i;
// Whether to break the alignment sequence because of an empty line.
bool EmptyLineBreak =
- (Changes[i].NewlinesBefore > 1) &&
- (ACS != FormatStyle::ACS_AcrossEmptyLines) &&
- (ACS != FormatStyle::ACS_AcrossEmptyLinesAndComments);
+ (CurrentChange.NewlinesBefore > 1) && !ACS.AcrossEmptyLines;
// Whether to break the alignment sequence because of a line without a
// match.
bool NoMatchBreak =
- !FoundMatchOnLine &&
- !(LineIsComment &&
- ((ACS == FormatStyle::ACS_AcrossComments) ||
- (ACS == FormatStyle::ACS_AcrossEmptyLinesAndComments)));
+ !FoundMatchOnLine && !(LineIsComment && ACS.AcrossComments);
if (EmptyLineBreak || NoMatchBreak)
AlignCurrentSequence();
// A new line starts, re-initialize line status tracking bools.
// Keep the match state if a string literal is continued on this line.
- if (i == 0 || !Changes[i].Tok->is(tok::string_literal) ||
- !Changes[i - 1].Tok->is(tok::string_literal))
+ if (i == 0 || CurrentChange.Tok->isNot(tok::string_literal) ||
+ Changes[i - 1].Tok->isNot(tok::string_literal)) {
FoundMatchOnLine = false;
+ }
LineIsComment = true;
}
- if (!Changes[i].Tok->is(tok::comment)) {
+ if (CurrentChange.Tok->isNot(tok::comment))
LineIsComment = false;
- }
- if (Changes[i].Tok->is(tok::comma)) {
+ if (CurrentChange.Tok->is(tok::comma)) {
++CommasBeforeMatch;
- } else if (Changes[i].indentAndNestingLevel() > IndentAndNestingLevel) {
+ } else if (CurrentChange.indentAndNestingLevel() > IndentAndNestingLevel) {
// Call AlignTokens recursively, skipping over this scope block.
- unsigned StoppedAt = AlignTokens(Style, Matches, Changes, i, ACS);
+ unsigned StoppedAt =
+ AlignTokens(Style, Matches, Changes, i, ACS, RightJustify);
i = StoppedAt - 1;
continue;
}
- if (!Matches(Changes[i]))
+ if (!Matches(CurrentChange))
continue;
// If there is more than one matching token per line, or if the number of
@@ -527,29 +638,44 @@ static unsigned AlignTokens(
if (StartOfSequence == 0)
StartOfSequence = i;
- unsigned ChangeMinColumn = Changes[i].StartOfTokenColumn;
- int LineLengthAfter = Changes[i].TokenLength;
+ unsigned ChangeWidthLeft = CurrentChange.StartOfTokenColumn;
+ unsigned ChangeWidthAnchor = 0;
+ unsigned ChangeWidthRight = 0;
+ if (RightJustify)
+ if (ACS.PadOperators)
+ ChangeWidthAnchor = CurrentChange.TokenLength;
+ else
+ ChangeWidthLeft += CurrentChange.TokenLength;
+ else
+ ChangeWidthRight = CurrentChange.TokenLength;
for (unsigned j = i + 1; j != e && Changes[j].NewlinesBefore == 0; ++j) {
- LineLengthAfter += Changes[j].Spaces;
+ ChangeWidthRight += Changes[j].Spaces;
// Changes are generally 1:1 with the tokens, but a change could also be
// inside of a token, in which case it's counted more than once: once for
// the whitespace surrounding the token (!IsInsideToken) and once for
// each whitespace change within it (IsInsideToken).
// Therefore, changes inside of a token should only count the space.
if (!Changes[j].IsInsideToken)
- LineLengthAfter += Changes[j].TokenLength;
+ ChangeWidthRight += Changes[j].TokenLength;
}
- unsigned ChangeMaxColumn = Style.ColumnLimit - LineLengthAfter;
// If we are restricted by the maximum column width, end the sequence.
- if (ChangeMinColumn > MaxColumn || ChangeMaxColumn < MinColumn ||
- CommasBeforeLastMatch != CommasBeforeMatch) {
+ unsigned NewLeft = std::max(ChangeWidthLeft, WidthLeft);
+ unsigned NewAnchor = std::max(ChangeWidthAnchor, WidthAnchor);
+ unsigned NewRight = std::max(ChangeWidthRight, WidthRight);
+ // `ColumnLimit == 0` means there is no column limit.
+ if (Style.ColumnLimit != 0 &&
+ Style.ColumnLimit < NewLeft + NewAnchor + NewRight) {
AlignCurrentSequence();
StartOfSequence = i;
+ WidthLeft = ChangeWidthLeft;
+ WidthAnchor = ChangeWidthAnchor;
+ WidthRight = ChangeWidthRight;
+ } else {
+ WidthLeft = NewLeft;
+ WidthAnchor = NewAnchor;
+ WidthRight = NewRight;
}
-
- MinColumn = std::max(MinColumn, ChangeMinColumn);
- MaxColumn = std::min(MaxColumn, ChangeMaxColumn);
}
EndOfSequence = i;
@@ -564,14 +690,12 @@ static unsigned AlignTokens(
//
// We need to adjust the StartOfTokenColumn of each Change that is on a line
// containing any matching token to be aligned and located after such token.
-static void AlignMacroSequence(
+static void AlignMatchingTokenSequence(
unsigned &StartOfSequence, unsigned &EndOfSequence, unsigned &MinColumn,
- unsigned &MaxColumn, bool &FoundMatchOnLine,
- std::function<bool(const WhitespaceManager::Change &C)> AlignMacrosMatches,
+ std::function<bool(const WhitespaceManager::Change &C)> Matches,
SmallVector<WhitespaceManager::Change, 16> &Changes) {
if (StartOfSequence > 0 && StartOfSequence < EndOfSequence) {
-
- FoundMatchOnLine = false;
+ bool FoundMatchOnLine = false;
int Shift = 0;
for (unsigned I = StartOfSequence; I != EndOfSequence; ++I) {
@@ -582,8 +706,8 @@ static void AlignMacroSequence(
// If this is the first matching token to be aligned, remember by how many
// spaces it has to be shifted, so the rest of the changes on the line are
- // shifted by the same amount
- if (!FoundMatchOnLine && AlignMacrosMatches(Changes[I])) {
+ // shifted by the same amount.
+ if (!FoundMatchOnLine && Matches(Changes[I])) {
FoundMatchOnLine = true;
Shift = MinColumn - Changes[I].StartOfTokenColumn;
Changes[I].Spaces += Shift;
@@ -597,13 +721,12 @@ static void AlignMacroSequence(
}
MinColumn = 0;
- MaxColumn = UINT_MAX;
StartOfSequence = 0;
EndOfSequence = 0;
}
void WhitespaceManager::alignConsecutiveMacros() {
- if (Style.AlignConsecutiveMacros == FormatStyle::ACS_None)
+ if (!Style.AlignConsecutiveMacros.Enabled)
return;
auto AlignMacrosMatches = [](const Change &C) {
@@ -622,10 +745,10 @@ void WhitespaceManager::alignConsecutiveMacros() {
SpacesRequiredBefore = 0;
}
- if (!Current || !Current->is(tok::identifier))
+ if (!Current || Current->isNot(tok::identifier))
return false;
- if (!Current->Previous || !Current->Previous->is(tok::pp_define))
+ if (!Current->Previous || Current->Previous->isNot(tok::pp_define))
return false;
// For a macro function, 0 spaces are required between the
@@ -636,7 +759,6 @@ void WhitespaceManager::alignConsecutiveMacros() {
};
unsigned MinColumn = 0;
- unsigned MaxColumn = UINT_MAX;
// Start and end of the token sequence we're processing.
unsigned StartOfSequence = 0;
@@ -654,33 +776,27 @@ void WhitespaceManager::alignConsecutiveMacros() {
EndOfSequence = I;
// Whether to break the alignment sequence because of an empty line.
- bool EmptyLineBreak =
- (Changes[I].NewlinesBefore > 1) &&
- (Style.AlignConsecutiveMacros != FormatStyle::ACS_AcrossEmptyLines) &&
- (Style.AlignConsecutiveMacros !=
- FormatStyle::ACS_AcrossEmptyLinesAndComments);
+ bool EmptyLineBreak = (Changes[I].NewlinesBefore > 1) &&
+ !Style.AlignConsecutiveMacros.AcrossEmptyLines;
// Whether to break the alignment sequence because of a line without a
// match.
bool NoMatchBreak =
!FoundMatchOnLine &&
- !(LineIsComment && ((Style.AlignConsecutiveMacros ==
- FormatStyle::ACS_AcrossComments) ||
- (Style.AlignConsecutiveMacros ==
- FormatStyle::ACS_AcrossEmptyLinesAndComments)));
+ !(LineIsComment && Style.AlignConsecutiveMacros.AcrossComments);
- if (EmptyLineBreak || NoMatchBreak)
- AlignMacroSequence(StartOfSequence, EndOfSequence, MinColumn, MaxColumn,
- FoundMatchOnLine, AlignMacrosMatches, Changes);
+ if (EmptyLineBreak || NoMatchBreak) {
+ AlignMatchingTokenSequence(StartOfSequence, EndOfSequence, MinColumn,
+ AlignMacrosMatches, Changes);
+ }
// A new line starts, re-initialize line status tracking bools.
FoundMatchOnLine = false;
LineIsComment = true;
}
- if (!Changes[I].Tok->is(tok::comment)) {
+ if (Changes[I].Tok->isNot(tok::comment))
LineIsComment = false;
- }
if (!AlignMacrosMatches(Changes[I]))
continue;
@@ -691,22 +807,16 @@ void WhitespaceManager::alignConsecutiveMacros() {
StartOfSequence = I;
unsigned ChangeMinColumn = Changes[I].StartOfTokenColumn;
- int LineLengthAfter = -Changes[I].Spaces;
- for (unsigned j = I; j != E && Changes[j].NewlinesBefore == 0; ++j)
- LineLengthAfter += Changes[j].Spaces + Changes[j].TokenLength;
- unsigned ChangeMaxColumn = Style.ColumnLimit - LineLengthAfter;
-
MinColumn = std::max(MinColumn, ChangeMinColumn);
- MaxColumn = std::min(MaxColumn, ChangeMaxColumn);
}
EndOfSequence = I;
- AlignMacroSequence(StartOfSequence, EndOfSequence, MinColumn, MaxColumn,
- FoundMatchOnLine, AlignMacrosMatches, Changes);
+ AlignMatchingTokenSequence(StartOfSequence, EndOfSequence, MinColumn,
+ AlignMacrosMatches, Changes);
}
void WhitespaceManager::alignConsecutiveAssignments() {
- if (Style.AlignConsecutiveAssignments == FormatStyle::ACS_None)
+ if (!Style.AlignConsecutiveAssignments.Enabled)
return;
AlignTokens(
@@ -720,13 +830,26 @@ void WhitespaceManager::alignConsecutiveAssignments() {
if (&C != &Changes.back() && (&C + 1)->NewlinesBefore > 0)
return false;
- return C.Tok->is(tok::equal);
+ // Do not align operator= overloads.
+ FormatToken *Previous = C.Tok->getPreviousNonComment();
+ if (Previous && Previous->is(tok::kw_operator))
+ return false;
+
+ return Style.AlignConsecutiveAssignments.AlignCompound
+ ? C.Tok->getPrecedence() == prec::Assignment
+ : (C.Tok->is(tok::equal) ||
+ // In Verilog the '<=' is not a compound assignment, thus
+ // it is aligned even when the AlignCompound option is not
+ // set.
+ (Style.isVerilog() && C.Tok->is(tok::lessequal) &&
+ C.Tok->getPrecedence() == prec::Assignment));
},
- Changes, /*StartAt=*/0, Style.AlignConsecutiveAssignments);
+ Changes, /*StartAt=*/0, Style.AlignConsecutiveAssignments,
+ /*RightJustify=*/true);
}
void WhitespaceManager::alignConsecutiveBitFields() {
- if (Style.AlignConsecutiveBitFields == FormatStyle::ACS_None)
+ if (!Style.AlignConsecutiveBitFields.Enabled)
return;
AlignTokens(
@@ -745,16 +868,125 @@ void WhitespaceManager::alignConsecutiveBitFields() {
Changes, /*StartAt=*/0, Style.AlignConsecutiveBitFields);
}
+void WhitespaceManager::alignConsecutiveShortCaseStatements() {
+ if (!Style.AlignConsecutiveShortCaseStatements.Enabled ||
+ !Style.AllowShortCaseLabelsOnASingleLine) {
+ return;
+ }
+
+ auto Matches = [&](const Change &C) {
+ if (Style.AlignConsecutiveShortCaseStatements.AlignCaseColons)
+ return C.Tok->is(TT_CaseLabelColon);
+
+ // Ignore 'IsInsideToken' to allow matching trailing comments which
+ // need to be reflowed as that causes the token to appear in two
+ // different changes, which will cause incorrect alignment as we'll
+ // reflow early due to detecting multiple aligning tokens per line.
+ return !C.IsInsideToken && C.Tok->Previous &&
+ C.Tok->Previous->is(TT_CaseLabelColon);
+ };
+
+ unsigned MinColumn = 0;
+
+ // Empty case statements don't break the alignment, but don't necessarily
+ // match our predicate, so we need to track their column so they can push out
+ // our alignment.
+ unsigned MinEmptyCaseColumn = 0;
+
+ // Start and end of the token sequence we're processing.
+ unsigned StartOfSequence = 0;
+ unsigned EndOfSequence = 0;
+
+ // Whether a matching token has been found on the current line.
+ bool FoundMatchOnLine = false;
+
+ bool LineIsComment = true;
+ bool LineIsEmptyCase = false;
+
+ unsigned I = 0;
+ for (unsigned E = Changes.size(); I != E; ++I) {
+ if (Changes[I].NewlinesBefore != 0) {
+ // Whether to break the alignment sequence because of an empty line.
+ bool EmptyLineBreak =
+ (Changes[I].NewlinesBefore > 1) &&
+ !Style.AlignConsecutiveShortCaseStatements.AcrossEmptyLines;
+
+ // Whether to break the alignment sequence because of a line without a
+ // match.
+ bool NoMatchBreak =
+ !FoundMatchOnLine &&
+ !(LineIsComment &&
+ Style.AlignConsecutiveShortCaseStatements.AcrossComments) &&
+ !LineIsEmptyCase;
+
+ if (EmptyLineBreak || NoMatchBreak) {
+ AlignMatchingTokenSequence(StartOfSequence, EndOfSequence, MinColumn,
+ Matches, Changes);
+ MinEmptyCaseColumn = 0;
+ }
+
+ // A new line starts, re-initialize line status tracking bools.
+ FoundMatchOnLine = false;
+ LineIsComment = true;
+ LineIsEmptyCase = false;
+ }
+
+ if (Changes[I].Tok->isNot(tok::comment))
+ LineIsComment = false;
+
+ if (Changes[I].Tok->is(TT_CaseLabelColon)) {
+ LineIsEmptyCase =
+ !Changes[I].Tok->Next || Changes[I].Tok->Next->isTrailingComment();
+
+ if (LineIsEmptyCase) {
+ if (Style.AlignConsecutiveShortCaseStatements.AlignCaseColons) {
+ MinEmptyCaseColumn =
+ std::max(MinEmptyCaseColumn, Changes[I].StartOfTokenColumn);
+ } else {
+ MinEmptyCaseColumn =
+ std::max(MinEmptyCaseColumn, Changes[I].StartOfTokenColumn + 2);
+ }
+ }
+ }
+
+ if (!Matches(Changes[I]))
+ continue;
+
+ if (LineIsEmptyCase)
+ continue;
+
+ FoundMatchOnLine = true;
+
+ if (StartOfSequence == 0)
+ StartOfSequence = I;
+
+ EndOfSequence = I + 1;
+
+ MinColumn = std::max(MinColumn, Changes[I].StartOfTokenColumn);
+
+ // Allow empty case statements to push out our alignment.
+ MinColumn = std::max(MinColumn, MinEmptyCaseColumn);
+ }
+
+ AlignMatchingTokenSequence(StartOfSequence, EndOfSequence, MinColumn, Matches,
+ Changes);
+}
+
void WhitespaceManager::alignConsecutiveDeclarations() {
- if (Style.AlignConsecutiveDeclarations == FormatStyle::ACS_None)
+ if (!Style.AlignConsecutiveDeclarations.Enabled)
return;
AlignTokens(
Style,
- [](Change const &C) {
- // tok::kw_operator is necessary for aligning operator overload
- // definitions.
- if (C.Tok->isOneOf(TT_FunctionDeclarationName, tok::kw_operator))
+ [&](Change const &C) {
+ if (Style.AlignConsecutiveDeclarations.AlignFunctionPointers) {
+ for (const auto *Prev = C.Tok->Previous; Prev; Prev = Prev->Previous)
+ if (Prev->is(tok::equal))
+ return false;
+ if (C.Tok->is(TT_FunctionTypeLParen))
+ return true;
+ }
+ if (C.Tok->is(TT_FunctionDeclarationName))
return true;
if (C.Tok->isNot(TT_StartOfName))
return false;
@@ -770,8 +1002,9 @@ void WhitespaceManager::alignConsecutiveDeclarations() {
if (!Next->Tok.getIdentifierInfo())
break;
if (Next->isOneOf(TT_StartOfName, TT_FunctionDeclarationName,
- tok::kw_operator))
+ tok::kw_operator)) {
return false;
+ }
}
return true;
},
@@ -802,10 +1035,9 @@ void WhitespaceManager::alignChainedConditionals() {
// Ensure we keep alignment of wrapped operands with non-wrapped operands
// Since we actually align the operators, the wrapped operands need the
// extra offset to be properly aligned.
- for (Change &C : Changes) {
+ for (Change &C : Changes)
if (AlignWrappedOperand(C))
C.StartOfTokenColumn -= 2;
- }
AlignTokens(
Style,
[this](Change const &C) {
@@ -822,50 +1054,70 @@ void WhitespaceManager::alignChainedConditionals() {
}
void WhitespaceManager::alignTrailingComments() {
- unsigned MinColumn = 0;
- unsigned MaxColumn = UINT_MAX;
- unsigned StartOfSequence = 0;
+ if (Style.AlignTrailingComments.Kind == FormatStyle::TCAS_Never)
+ return;
+
+ const int Size = Changes.size();
+ int MinColumn = 0;
+ int StartOfSequence = 0;
bool BreakBeforeNext = false;
- unsigned Newlines = 0;
- for (unsigned i = 0, e = Changes.size(); i != e; ++i) {
- if (Changes[i].StartOfBlockComment)
+ int NewLineThreshold = 1;
+ if (Style.AlignTrailingComments.Kind == FormatStyle::TCAS_Always)
+ NewLineThreshold = Style.AlignTrailingComments.OverEmptyLines + 1;
+
+ for (int I = 0, MaxColumn = INT_MAX, Newlines = 0; I < Size; ++I) {
+ auto &C = Changes[I];
+ if (C.StartOfBlockComment)
continue;
- Newlines += Changes[i].NewlinesBefore;
- if (!Changes[i].IsTrailingComment)
+ Newlines += C.NewlinesBefore;
+ if (!C.IsTrailingComment)
continue;
- unsigned ChangeMinColumn = Changes[i].StartOfTokenColumn;
- unsigned ChangeMaxColumn;
+ if (Style.AlignTrailingComments.Kind == FormatStyle::TCAS_Leave) {
+ const int OriginalSpaces =
+ C.OriginalWhitespaceRange.getEnd().getRawEncoding() -
+ C.OriginalWhitespaceRange.getBegin().getRawEncoding() -
+ C.Tok->LastNewlineOffset;
+ assert(OriginalSpaces >= 0);
+ const auto RestoredLineLength =
+ C.StartOfTokenColumn + C.TokenLength + OriginalSpaces;
+ // If leaving comments makes the line exceed the column limit, give up to
+ // leave the comments.
+ if (RestoredLineLength >= Style.ColumnLimit && Style.ColumnLimit > 0)
+ break;
+ C.Spaces = OriginalSpaces;
+ continue;
+ }
- if (Style.ColumnLimit == 0)
- ChangeMaxColumn = UINT_MAX;
- else if (Style.ColumnLimit >= Changes[i].TokenLength)
- ChangeMaxColumn = Style.ColumnLimit - Changes[i].TokenLength;
- else
- ChangeMaxColumn = ChangeMinColumn;
+ const int ChangeMinColumn = C.StartOfTokenColumn;
+ int ChangeMaxColumn;
// If we don't create a replacement for this change, we have to consider
// it to be immovable.
- if (!Changes[i].CreateReplacement)
+ if (!C.CreateReplacement)
+ ChangeMaxColumn = ChangeMinColumn;
+ else if (Style.ColumnLimit == 0)
+ ChangeMaxColumn = INT_MAX;
+ else if (Style.ColumnLimit >= C.TokenLength)
+ ChangeMaxColumn = Style.ColumnLimit - C.TokenLength;
+ else
ChangeMaxColumn = ChangeMinColumn;
- if (i + 1 != e && Changes[i + 1].ContinuesPPDirective)
+ if (I + 1 < Size && Changes[I + 1].ContinuesPPDirective &&
+ ChangeMaxColumn >= 2) {
ChangeMaxColumn -= 2;
- // If this comment follows an } in column 0, it probably documents the
- // closing of a namespace and we don't want to align it.
- bool FollowsRBraceInColumn0 = i > 0 && Changes[i].NewlinesBefore == 0 &&
- Changes[i - 1].Tok->is(tok::r_brace) &&
- Changes[i - 1].StartOfTokenColumn == 0;
+ }
+
bool WasAlignedWithStartOfNextLine = false;
- if (Changes[i].NewlinesBefore == 1) { // A comment on its own line.
- unsigned CommentColumn = SourceMgr.getSpellingColumnNumber(
- Changes[i].OriginalWhitespaceRange.getEnd());
- for (unsigned j = i + 1; j != e; ++j) {
- if (Changes[j].Tok->is(tok::comment))
+ if (C.NewlinesBefore >= 1) { // A comment on its own line.
+ const auto CommentColumn =
+ SourceMgr.getSpellingColumnNumber(C.OriginalWhitespaceRange.getEnd());
+ for (int J = I + 1; J < Size; ++J) {
+ if (Changes[J].Tok->is(tok::comment))
continue;
- unsigned NextColumn = SourceMgr.getSpellingColumnNumber(
- Changes[j].OriginalWhitespaceRange.getEnd());
+ const auto NextColumn = SourceMgr.getSpellingColumnNumber(
+ Changes[J].OriginalWhitespaceRange.getEnd());
// The start of the next token was previously aligned with the
// start of this comment.
WasAlignedWithStartOfNextLine =
@@ -874,48 +1126,86 @@ void WhitespaceManager::alignTrailingComments() {
break;
}
}
- if (!Style.AlignTrailingComments || FollowsRBraceInColumn0) {
- alignTrailingComments(StartOfSequence, i, MinColumn);
- MinColumn = ChangeMinColumn;
- MaxColumn = ChangeMinColumn;
- StartOfSequence = i;
- } else if (BreakBeforeNext || Newlines > 1 ||
+
+ // We don't want to align comments which end a scope, which are here
+ // identified by most closing braces.
+ auto DontAlignThisComment = [](const auto *Tok) {
+ if (Tok->is(tok::semi)) {
+ Tok = Tok->getPreviousNonComment();
+ if (!Tok)
+ return false;
+ }
+ if (Tok->is(tok::r_paren)) {
+ // Back up past the parentheses and a `TT_DoWhile` that may precede.
+ Tok = Tok->MatchingParen;
+ if (!Tok)
+ return false;
+ Tok = Tok->getPreviousNonComment();
+ if (!Tok)
+ return false;
+ if (Tok->is(TT_DoWhile)) {
+ const auto *Prev = Tok->getPreviousNonComment();
+ if (!Prev) {
+ // A do-while-loop without braces.
+ return true;
+ }
+ Tok = Prev;
+ }
+ }
+
+ if (Tok->isNot(tok::r_brace))
+ return false;
+
+ while (Tok->Previous && Tok->Previous->is(tok::r_brace))
+ Tok = Tok->Previous;
+ return Tok->NewlinesBefore > 0;
+ };
+
+ if (I > 0 && C.NewlinesBefore == 0 &&
+ DontAlignThisComment(Changes[I - 1].Tok)) {
+ alignTrailingComments(StartOfSequence, I, MinColumn);
+ // Reset to initial values, but skip this change for the next alignment
+ // pass.
+ MinColumn = 0;
+ MaxColumn = INT_MAX;
+ StartOfSequence = I + 1;
+ } else if (BreakBeforeNext || Newlines > NewLineThreshold ||
(ChangeMinColumn > MaxColumn || ChangeMaxColumn < MinColumn) ||
// Break the comment sequence if the previous line did not end
// in a trailing comment.
- (Changes[i].NewlinesBefore == 1 && i > 0 &&
- !Changes[i - 1].IsTrailingComment) ||
+ (C.NewlinesBefore == 1 && I > 0 &&
+ !Changes[I - 1].IsTrailingComment) ||
WasAlignedWithStartOfNextLine) {
- alignTrailingComments(StartOfSequence, i, MinColumn);
+ alignTrailingComments(StartOfSequence, I, MinColumn);
MinColumn = ChangeMinColumn;
MaxColumn = ChangeMaxColumn;
- StartOfSequence = i;
+ StartOfSequence = I;
} else {
MinColumn = std::max(MinColumn, ChangeMinColumn);
MaxColumn = std::min(MaxColumn, ChangeMaxColumn);
}
- BreakBeforeNext = (i == 0) || (Changes[i].NewlinesBefore > 1) ||
+ BreakBeforeNext = (I == 0) || (C.NewlinesBefore > 1) ||
// Never start a sequence with a comment at the beginning
// of the line.
- (Changes[i].NewlinesBefore == 1 && StartOfSequence == i);
+ (C.NewlinesBefore == 1 && StartOfSequence == I);
Newlines = 0;
}
- alignTrailingComments(StartOfSequence, Changes.size(), MinColumn);
+ alignTrailingComments(StartOfSequence, Size, MinColumn);
}
void WhitespaceManager::alignTrailingComments(unsigned Start, unsigned End,
unsigned Column) {
for (unsigned i = Start; i != End; ++i) {
int Shift = 0;
- if (Changes[i].IsTrailingComment) {
+ if (Changes[i].IsTrailingComment)
Shift = Column - Changes[i].StartOfTokenColumn;
- }
if (Changes[i].StartOfBlockComment) {
Shift = Changes[i].IndentationOffset +
Changes[i].StartOfBlockComment->StartOfTokenColumn -
Changes[i].StartOfTokenColumn;
}
- assert(Shift >= 0);
+ if (Shift <= 0)
+ continue;
Changes[i].Spaces += Shift;
if (i + 1 != Changes.size())
Changes[i + 1].PreviousEndOfTokenColumn += Shift;
@@ -993,11 +1283,14 @@ void WhitespaceManager::alignArrayInitializers(unsigned Start, unsigned End) {
void WhitespaceManager::alignArrayInitializersRightJustified(
CellDescriptions &&CellDescs) {
- auto &Cells = CellDescs.Cells;
+ if (!CellDescs.isRectangular())
+ return;
+ const int BracePadding = Style.Cpp11BracedListStyle ? 0 : 1;
+ auto &Cells = CellDescs.Cells;
// Now go through and fixup the spaces.
auto *CellIter = Cells.begin();
- for (auto i = 0U; i < CellDescs.CellCount; i++, ++CellIter) {
+ for (auto i = 0U; i < CellDescs.CellCounts[0]; ++i, ++CellIter) {
unsigned NetWidth = 0U;
if (isSplitCell(*CellIter))
NetWidth = getNetWidth(Cells.begin(), CellIter, CellDescs.InitialSpaces);
@@ -1007,28 +1300,32 @@ void WhitespaceManager::alignArrayInitializersRightJustified(
// So in here we want to see if there is a brace that falls
// on a line that was split. If so on that line we make sure that
// the spaces in front of the brace are enough.
- Changes[CellIter->Index].NewlinesBefore = 0;
- Changes[CellIter->Index].Spaces = 0;
- for (const auto *Next = CellIter->NextColumnElement; Next != nullptr;
- Next = Next->NextColumnElement) {
- Changes[Next->Index].Spaces = 0;
- Changes[Next->Index].NewlinesBefore = 0;
- }
+ const auto *Next = CellIter;
+ do {
+ const FormatToken *Previous = Changes[Next->Index].Tok->Previous;
+ if (Previous && Previous->isNot(TT_LineComment)) {
+ Changes[Next->Index].Spaces = BracePadding;
+ Changes[Next->Index].NewlinesBefore = 0;
+ }
+ Next = Next->NextColumnElement;
+ } while (Next);
// Unless the array is empty, we need the position of all the
// immediately adjacent cells
if (CellIter != Cells.begin()) {
auto ThisNetWidth =
getNetWidth(Cells.begin(), CellIter, CellDescs.InitialSpaces);
- auto MaxNetWidth =
- getMaximumNetWidth(Cells.begin(), CellIter, CellDescs.InitialSpaces,
- CellDescs.CellCount);
+ auto MaxNetWidth = getMaximumNetWidth(
+ Cells.begin(), CellIter, CellDescs.InitialSpaces,
+ CellDescs.CellCounts[0], CellDescs.CellCounts.size());
if (ThisNetWidth < MaxNetWidth)
Changes[CellIter->Index].Spaces = (MaxNetWidth - ThisNetWidth);
auto RowCount = 1U;
auto Offset = std::distance(Cells.begin(), CellIter);
- for (const auto *Next = CellIter->NextColumnElement; Next != nullptr;
+ for (const auto *Next = CellIter->NextColumnElement; Next;
Next = Next->NextColumnElement) {
- auto *Start = (Cells.begin() + RowCount * CellDescs.CellCount);
+ if (RowCount >= CellDescs.CellCounts.size())
+ break;
+ auto *Start = (Cells.begin() + RowCount * CellDescs.CellCounts[0]);
auto *End = Start + Offset;
ThisNetWidth = getNetWidth(Start, End, CellDescs.InitialSpaces);
if (ThisNetWidth < MaxNetWidth)
@@ -1042,16 +1339,16 @@ void WhitespaceManager::alignArrayInitializersRightJustified(
NetWidth;
if (Changes[CellIter->Index].NewlinesBefore == 0) {
Changes[CellIter->Index].Spaces = (CellWidth - (ThisWidth + NetWidth));
- Changes[CellIter->Index].Spaces += (i > 0) ? 1 : 0;
+ Changes[CellIter->Index].Spaces += (i > 0) ? 1 : BracePadding;
}
alignToStartOfCell(CellIter->Index, CellIter->EndIndex);
- for (const auto *Next = CellIter->NextColumnElement; Next != nullptr;
+ for (const auto *Next = CellIter->NextColumnElement; Next;
Next = Next->NextColumnElement) {
ThisWidth =
calculateCellWidth(Next->Index, Next->EndIndex, true) + NetWidth;
if (Changes[Next->Index].NewlinesBefore == 0) {
Changes[Next->Index].Spaces = (CellWidth - ThisWidth);
- Changes[Next->Index].Spaces += (i > 0) ? 1 : 0;
+ Changes[Next->Index].Spaces += (i > 0) ? 1 : BracePadding;
}
alignToStartOfCell(Next->Index, Next->EndIndex);
}
@@ -1061,37 +1358,46 @@ void WhitespaceManager::alignArrayInitializersRightJustified(
void WhitespaceManager::alignArrayInitializersLeftJustified(
CellDescriptions &&CellDescs) {
- auto &Cells = CellDescs.Cells;
+ if (!CellDescs.isRectangular())
+ return;
+
+ const int BracePadding = Style.Cpp11BracedListStyle ? 0 : 1;
+ auto &Cells = CellDescs.Cells;
// Now go through and fixup the spaces.
auto *CellIter = Cells.begin();
- // The first cell needs to be against the left brace.
- if (Changes[CellIter->Index].NewlinesBefore == 0)
- Changes[CellIter->Index].Spaces = 0;
- else
- Changes[CellIter->Index].Spaces = CellDescs.InitialSpaces;
+ // The first cell of every row needs to be against the left brace.
+ for (const auto *Next = CellIter; Next; Next = Next->NextColumnElement) {
+ auto &Change = Changes[Next->Index];
+ Change.Spaces =
+ Change.NewlinesBefore == 0 ? BracePadding : CellDescs.InitialSpaces;
+ }
++CellIter;
- for (auto i = 1U; i < CellDescs.CellCount; i++, ++CellIter) {
+ for (auto i = 1U; i < CellDescs.CellCounts[0]; i++, ++CellIter) {
auto MaxNetWidth = getMaximumNetWidth(
- Cells.begin(), CellIter, CellDescs.InitialSpaces, CellDescs.CellCount);
+ Cells.begin(), CellIter, CellDescs.InitialSpaces,
+ CellDescs.CellCounts[0], CellDescs.CellCounts.size());
auto ThisNetWidth =
getNetWidth(Cells.begin(), CellIter, CellDescs.InitialSpaces);
if (Changes[CellIter->Index].NewlinesBefore == 0) {
Changes[CellIter->Index].Spaces =
MaxNetWidth - ThisNetWidth +
- (Changes[CellIter->Index].Tok->isNot(tok::r_brace) ? 1 : 0);
+ (Changes[CellIter->Index].Tok->isNot(tok::r_brace) ? 1
+ : BracePadding);
}
auto RowCount = 1U;
auto Offset = std::distance(Cells.begin(), CellIter);
- for (const auto *Next = CellIter->NextColumnElement; Next != nullptr;
+ for (const auto *Next = CellIter->NextColumnElement; Next;
Next = Next->NextColumnElement) {
- auto *Start = (Cells.begin() + RowCount * CellDescs.CellCount);
+ if (RowCount >= CellDescs.CellCounts.size())
+ break;
+ auto *Start = (Cells.begin() + RowCount * CellDescs.CellCounts[0]);
auto *End = Start + Offset;
auto ThisNetWidth = getNetWidth(Start, End, CellDescs.InitialSpaces);
if (Changes[Next->Index].NewlinesBefore == 0) {
Changes[Next->Index].Spaces =
MaxNetWidth - ThisNetWidth +
- (Changes[Next->Index].Tok->isNot(tok::r_brace) ? 1 : 0);
+ (Changes[Next->Index].Tok->isNot(tok::r_brace) ? 1 : BracePadding);
}
++RowCount;
}
@@ -1101,7 +1407,7 @@ void WhitespaceManager::alignArrayInitializersLeftJustified(
bool WhitespaceManager::isSplitCell(const CellDescription &Cell) {
if (Cell.HasSplit)
return true;
- for (const auto *Next = Cell.NextColumnElement; Next != nullptr;
+ for (const auto *Next = Cell.NextColumnElement; Next;
Next = Next->NextColumnElement) {
if (Next->HasSplit)
return true;
@@ -1114,7 +1420,7 @@ WhitespaceManager::CellDescriptions WhitespaceManager::getCells(unsigned Start,
unsigned Depth = 0;
unsigned Cell = 0;
- unsigned CellCount = 0;
+ SmallVector<unsigned> CellCounts;
unsigned InitialSpaces = 0;
unsigned InitialTokenLength = 0;
unsigned EndSpaces = 0;
@@ -1146,28 +1452,32 @@ WhitespaceManager::CellDescriptions WhitespaceManager::getCells(unsigned Start,
} else if (C.Tok->is(tok::comma)) {
if (!Cells.empty())
Cells.back().EndIndex = i;
- Cell++;
+ if (const auto *Next = C.Tok->getNextNonComment();
+ Next && Next->isNot(tok::r_brace)) { // dangling comma
+ ++Cell;
+ }
}
} else if (Depth == 1) {
if (C.Tok == MatchingParen) {
if (!Cells.empty())
Cells.back().EndIndex = i;
Cells.push_back(CellDescription{i, ++Cell, i + 1, false, nullptr});
- CellCount = Cell + 1;
+ CellCounts.push_back(C.Tok->Previous->isNot(tok::comma) ? Cell + 1
+ : Cell);
// Go to the next non-comment and ensure there is a break in front
const auto *NextNonComment = C.Tok->getNextNonComment();
while (NextNonComment->is(tok::comma))
NextNonComment = NextNonComment->getNextNonComment();
auto j = i;
while (Changes[j].Tok != NextNonComment && j < End)
- j++;
+ ++j;
if (j < End && Changes[j].NewlinesBefore == 0 &&
Changes[j].Tok->isNot(tok::r_brace)) {
Changes[j].NewlinesBefore = 1;
// Account for the added token lengths
Changes[j].Spaces = InitialSpaces - InitialTokenLength;
}
- } else if (C.Tok->is(tok::comment)) {
+ } else if (C.Tok->is(tok::comment) && C.Tok->NewlinesBefore == 0) {
// Trailing comments stay at a space past the last token
C.Spaces = Changes[i - 1].Tok->is(tok::comma) ? 1 : 2;
} else if (C.Tok->is(tok::l_brace)) {
@@ -1190,6 +1500,17 @@ WhitespaceManager::CellDescriptions WhitespaceManager::getCells(unsigned Start,
// So if we split a line previously and the tail line + this token is
// less then the column limit we remove the split here and just put
// the column start at a space past the comma
+ //
+ // FIXME This if branch covers the cases where the column is not
+ // the first column. This leads to weird pathologies like the formatting
+ // auto foo = Items{
+ // Section{
+ // 0, bar(),
+ // }
+ // };
+ // Well if it doesn't lead to that it's indicative that the line
+ // breaking should be revisited. Unfortunately alot of other options
+ // interact with this
auto j = i - 1;
if ((j - 1) > Start && Changes[j].Tok->is(tok::comma) &&
Changes[j - 1].NewlinesBefore > 0) {
@@ -1212,7 +1533,7 @@ WhitespaceManager::CellDescriptions WhitespaceManager::getCells(unsigned Start,
}
}
- return linkCells({Cells, CellCount, InitialSpaces});
+ return linkCells({Cells, CellCounts, InitialSpaces});
}
unsigned WhitespaceManager::calculateCellWidth(unsigned Start, unsigned End,
@@ -1232,18 +1553,16 @@ void WhitespaceManager::alignToStartOfCell(unsigned Start, unsigned End) {
return;
// If the line is broken anywhere in there make sure everything
// is aligned to the parent
- for (auto i = Start + 1; i < End; i++) {
+ for (auto i = Start + 1; i < End; i++)
if (Changes[i].NewlinesBefore > 0)
Changes[i].Spaces = Changes[Start].Spaces;
- }
}
WhitespaceManager::CellDescriptions
WhitespaceManager::linkCells(CellDescriptions &&CellDesc) {
auto &Cells = CellDesc.Cells;
for (auto *CellIter = Cells.begin(); CellIter != Cells.end(); ++CellIter) {
- if (CellIter->NextColumnElement == nullptr &&
- ((CellIter + 1) != Cells.end())) {
+ if (!CellIter->NextColumnElement && (CellIter + 1) != Cells.end()) {
for (auto *NextIter = CellIter + 1; NextIter != Cells.end(); ++NextIter) {
if (NextIter->Cell == CellIter->Cell) {
CellIter->NextColumnElement = &(*NextIter);
@@ -1259,21 +1578,70 @@ void WhitespaceManager::generateChanges() {
for (unsigned i = 0, e = Changes.size(); i != e; ++i) {
const Change &C = Changes[i];
if (i > 0) {
- assert(Changes[i - 1].OriginalWhitespaceRange.getBegin() !=
- C.OriginalWhitespaceRange.getBegin() &&
- "Generating two replacements for the same location");
+ auto Last = Changes[i - 1].OriginalWhitespaceRange;
+ auto New = Changes[i].OriginalWhitespaceRange;
+ // Do not generate two replacements for the same location. As a special
+ // case, it is allowed if there is a replacement for the empty range
+ // between 2 tokens and another non-empty range at the start of the second
+ // token. We didn't implement logic to combine replacements for 2
+ // consecutive source ranges into a single replacement, because the
+ // program works fine without it.
+ //
+ // We can't eliminate empty original whitespace ranges. They appear when
+ // 2 tokens have no whitespace in between in the input. It does not
+ // matter whether whitespace is to be added. If no whitespace is to be
+ // added, the replacement will be empty, and it gets eliminated after this
+ // step in storeReplacement. For example, if the input is `foo();`,
+ // there will be a replacement for the range between every consecutive
+ // pair of tokens.
+ //
+ // A replacement at the start of a token can be added by
+ // BreakableStringLiteralUsingOperators::insertBreak when it adds braces
+ // around the string literal. Say Verilog code is being formatted and the
+ // first line is to become the next 2 lines.
+ // x("long string");
+ // x({"long ",
+ // "string"});
+ // There will be a replacement for the empty range between the parenthesis
+ // and the string and another replacement for the quote character. The
+ // replacement for the empty range between the parenthesis and the quote
+ // comes from ContinuationIndenter::addTokenOnCurrentLine when it changes
+ // the original empty range between the parenthesis and the string to
+ // another empty one. The replacement for the quote character comes from
+ // BreakableStringLiteralUsingOperators::insertBreak when it adds the
+ // brace. In the example, the replacement for the empty range is the same
+ // as the original text. However, eliminating replacements that are same
+ // as the original does not help in general. For example, a newline can
+ // be inserted, causing the first line to become the next 3 lines.
+ // xxxxxxxxxxx("long string");
+ // xxxxxxxxxxx(
+ // {"long ",
+ // "string"});
+ // In that case, the empty range between the parenthesis and the string
+ // will be replaced by a newline and 4 spaces. So we will still have to
+ // deal with a replacement for an empty source range followed by a
+ // replacement for a non-empty source range.
+ if (Last.getBegin() == New.getBegin() &&
+ (Last.getEnd() != Last.getBegin() ||
+ New.getEnd() == New.getBegin())) {
+ continue;
+ }
}
if (C.CreateReplacement) {
std::string ReplacementText = C.PreviousLinePostfix;
- if (C.ContinuesPPDirective)
+ if (C.ContinuesPPDirective) {
appendEscapedNewlineText(ReplacementText, C.NewlinesBefore,
C.PreviousEndOfTokenColumn,
C.EscapedNewlineColumn);
- else
+ } else {
appendNewlineText(ReplacementText, C.NewlinesBefore);
+ }
+ // FIXME: This assert should hold if we computed the column correctly.
+ // assert((int)C.StartOfTokenColumn >= C.Spaces);
appendIndentText(
ReplacementText, C.Tok->IndentLevel, std::max(0, C.Spaces),
- C.StartOfTokenColumn - std::max(0, C.Spaces), C.IsAligned);
+ std::max((int)C.StartOfTokenColumn, C.Spaces) - std::max(0, C.Spaces),
+ C.IsAligned);
ReplacementText.append(C.CurrentLinePrefix);
storeReplacement(C.OriginalWhitespaceRange, ReplacementText);
}
@@ -1285,8 +1653,9 @@ void WhitespaceManager::storeReplacement(SourceRange Range, StringRef Text) {
SourceMgr.getFileOffset(Range.getBegin());
// Don't create a replacement, if it does not change anything.
if (StringRef(SourceMgr.getCharacterData(Range.getBegin()),
- WhitespaceLength) == Text)
+ WhitespaceLength) == Text) {
return;
+ }
auto Err = Replaces.add(tooling::Replacement(
SourceMgr, CharSourceRange::getCharRange(Range), Text));
// FIXME: better error handling. For now, just print an error message in the
@@ -1299,8 +1668,13 @@ void WhitespaceManager::storeReplacement(SourceRange Range, StringRef Text) {
void WhitespaceManager::appendNewlineText(std::string &Text,
unsigned Newlines) {
- for (unsigned i = 0; i < Newlines; ++i)
- Text.append(UseCRLF ? "\r\n" : "\n");
+ if (UseCRLF) {
+ Text.reserve(Text.size() + 2 * Newlines);
+ for (unsigned i = 0; i < Newlines; ++i)
+ Text.append("\r\n");
+ } else {
+ Text.append(Newlines, '\n');
+ }
}
void WhitespaceManager::appendEscapedNewlineText(