diff options
Diffstat (limited to 'clang/lib/Format/WhitespaceManager.cpp')
-rw-r--r-- | clang/lib/Format/WhitespaceManager.cpp | 399 |
1 files changed, 376 insertions, 23 deletions
diff --git a/clang/lib/Format/WhitespaceManager.cpp b/clang/lib/Format/WhitespaceManager.cpp index 7d6964b7c72f..ca2222d1feff 100644 --- a/clang/lib/Format/WhitespaceManager.cpp +++ b/clang/lib/Format/WhitespaceManager.cpp @@ -13,6 +13,8 @@ #include "WhitespaceManager.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" +#include <algorithm> namespace clang { namespace format { @@ -100,6 +102,7 @@ const tooling::Replacements &WhitespaceManager::generateReplacements() { alignChainedConditionals(); alignTrailingComments(); alignEscapedNewlines(); + alignArrayInitializers(); generateChanges(); return Replaces; @@ -262,7 +265,8 @@ void WhitespaceManager::calculateLineBreakInformation() { // Align a single sequence of tokens, see AlignTokens below. template <typename F> static void -AlignTokenSequence(unsigned Start, unsigned End, unsigned Column, F &&Matches, +AlignTokenSequence(const FormatStyle &Style, unsigned Start, unsigned End, + unsigned Column, F &&Matches, SmallVector<WhitespaceManager::Change, 16> &Changes) { bool FoundMatchOnLine = false; int Shift = 0; @@ -278,6 +282,14 @@ AlignTokenSequence(unsigned Start, unsigned End, unsigned Column, F &&Matches, // double z); // In the above example, we need to take special care to ensure that // 'double z' is indented along with it's owning function 'b'. + // The same holds for calling a function: + // double a = foo(x); + // int b = bar(foo(y), + // foor(z)); + // Similar for broken string literals: + // double x = 3.14; + // auto s = "Hello" + // "World"; // Special handling is required for 'nested' ternary operators. SmallVector<unsigned, 16> ScopeStack; @@ -298,8 +310,12 @@ AlignTokenSequence(unsigned Start, unsigned End, unsigned Column, F &&Matches, ScopeStack.push_back(i); bool InsideNestedScope = ScopeStack.size() != 0; + bool ContinuedStringLiteral = i > Start && + Changes[i].Tok->is(tok::string_literal) && + Changes[i - 1].Tok->is(tok::string_literal); + bool SkipMatchCheck = InsideNestedScope || ContinuedStringLiteral; - if (Changes[i].NewlinesBefore > 0 && !InsideNestedScope) { + if (Changes[i].NewlinesBefore > 0 && !SkipMatchCheck) { Shift = 0; FoundMatchOnLine = false; } @@ -307,7 +323,7 @@ AlignTokenSequence(unsigned Start, unsigned End, unsigned Column, F &&Matches, // 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 && !InsideNestedScope && Matches(Changes[i])) { + if (!FoundMatchOnLine && !SkipMatchCheck && Matches(Changes[i])) { FoundMatchOnLine = true; Shift = Column - Changes[i].StartOfTokenColumn; Changes[i].Spaces += Shift; @@ -317,19 +333,62 @@ AlignTokenSequence(unsigned Start, unsigned End, unsigned Column, F &&Matches, // as mentioned in the ScopeStack comment. if (InsideNestedScope && Changes[i].NewlinesBefore > 0) { unsigned ScopeStart = ScopeStack.back(); - if (Changes[ScopeStart - 1].Tok->is(TT_FunctionDeclarationName) || - (ScopeStart > Start + 1 && - Changes[ScopeStart - 2].Tok->is(TT_FunctionDeclarationName)) || - Changes[i].Tok->is(TT_ConditionalExpr) || - (Changes[i].Tok->Previous && - Changes[i].Tok->Previous->is(TT_ConditionalExpr))) + auto ShouldShiftBeAdded = [&] { + // Function declaration + if (Changes[ScopeStart - 1].Tok->is(TT_FunctionDeclarationName)) + return true; + + // Continued function declaration + if (ScopeStart > Start + 1 && + Changes[ScopeStart - 2].Tok->is(TT_FunctionDeclarationName)) + return true; + + // Continued function call + if (ScopeStart > Start + 1 && + Changes[ScopeStart - 2].Tok->is(tok::identifier) && + Changes[ScopeStart - 1].Tok->is(tok::l_paren)) + return true; + + // Ternary operator + if (Changes[i].Tok->is(TT_ConditionalExpr)) + return true; + + // Period Initializer .XXX = 1. + if (Changes[i].Tok->is(TT_DesignatedInitializerPeriod)) + return true; + + // Continued ternary operator + if (Changes[i].Tok->Previous && + Changes[i].Tok->Previous->is(TT_ConditionalExpr)) + return true; + + return false; + }; + + if (ShouldShiftBeAdded()) Changes[i].Spaces += Shift; } + if (ContinuedStringLiteral) + Changes[i].Spaces += Shift; + assert(Shift >= 0); + Changes[i].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) { + for (int Previous = i - 1; + Previous >= 0 && + Changes[Previous].Tok->getType() == TT_PointerOrReference; + --Previous) { + Changes[Previous + 1].Spaces -= Shift; + Changes[Previous].Spaces += Shift; + } + } } } @@ -399,8 +458,8 @@ static unsigned AlignTokens( // containing any matching token to be aligned and located after such token. auto AlignCurrentSequence = [&] { if (StartOfSequence > 0 && StartOfSequence < EndOfSequence) - AlignTokenSequence(StartOfSequence, EndOfSequence, MinColumn, Matches, - Changes); + AlignTokenSequence(Style, StartOfSequence, EndOfSequence, MinColumn, + Matches, Changes); MinColumn = 0; MaxColumn = UINT_MAX; StartOfSequence = 0; @@ -434,7 +493,10 @@ static unsigned AlignTokens( AlignCurrentSequence(); // A new line starts, re-initialize line status tracking bools. - FoundMatchOnLine = false; + // 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)) + FoundMatchOnLine = false; LineIsComment = true; } @@ -687,12 +749,6 @@ void WhitespaceManager::alignConsecutiveDeclarations() { if (Style.AlignConsecutiveDeclarations == FormatStyle::ACS_None) return; - // FIXME: Currently we don't handle properly the PointerAlignment: Right - // The * and & are not aligned and are left dangling. Something has to be done - // about it, but it raises the question of alignment of code like: - // const char* const* v1; - // float const* v2; - // SomeVeryLongType const& v3; AlignTokens( Style, [](Change const &C) { @@ -709,6 +765,8 @@ void WhitespaceManager::alignConsecutiveDeclarations() { for (FormatToken *Next = C.Tok->Next; Next; Next = Next->Next) { if (Next->is(tok::comment)) continue; + if (Next->is(TT_PointerOrReference)) + return false; if (!Next->Tok.getIdentifierInfo()) break; if (Next->isOneOf(TT_StartOfName, TT_FunctionDeclarationName, @@ -735,12 +793,11 @@ void WhitespaceManager::alignChainedConditionals() { Changes, /*StartAt=*/0); } else { static auto AlignWrappedOperand = [](Change const &C) { - auto Previous = C.Tok->getPreviousNonComment(); // Previous; + FormatToken *Previous = C.Tok->getPreviousNonComment(); return C.NewlinesBefore && Previous && Previous->is(TT_ConditionalExpr) && - (Previous->is(tok::question) || - (Previous->is(tok::colon) && - (C.Tok->FakeLParens.size() == 0 || - C.Tok->FakeLParens.back() != prec::Conditional))); + (Previous->is(tok::colon) && + (C.Tok->FakeLParens.size() == 0 || + C.Tok->FakeLParens.back() != prec::Conditional)); }; // Ensure we keep alignment of wrapped operands with non-wrapped operands // Since we actually align the operators, the wrapped operands need the @@ -902,6 +959,302 @@ void WhitespaceManager::alignEscapedNewlines(unsigned Start, unsigned End, } } +void WhitespaceManager::alignArrayInitializers() { + if (Style.AlignArrayOfStructures == FormatStyle::AIAS_None) + return; + + for (unsigned ChangeIndex = 1U, ChangeEnd = Changes.size(); + ChangeIndex < ChangeEnd; ++ChangeIndex) { + auto &C = Changes[ChangeIndex]; + if (C.Tok->IsArrayInitializer) { + bool FoundComplete = false; + for (unsigned InsideIndex = ChangeIndex + 1; InsideIndex < ChangeEnd; + ++InsideIndex) { + if (Changes[InsideIndex].Tok == C.Tok->MatchingParen) { + alignArrayInitializers(ChangeIndex, InsideIndex + 1); + ChangeIndex = InsideIndex + 1; + FoundComplete = true; + break; + } + } + if (!FoundComplete) + ChangeIndex = ChangeEnd; + } + } +} + +void WhitespaceManager::alignArrayInitializers(unsigned Start, unsigned End) { + + if (Style.AlignArrayOfStructures == FormatStyle::AIAS_Right) + alignArrayInitializersRightJustified(getCells(Start, End)); + else if (Style.AlignArrayOfStructures == FormatStyle::AIAS_Left) + alignArrayInitializersLeftJustified(getCells(Start, End)); +} + +void WhitespaceManager::alignArrayInitializersRightJustified( + CellDescriptions &&CellDescs) { + auto &Cells = CellDescs.Cells; + + // Now go through and fixup the spaces. + auto *CellIter = Cells.begin(); + for (auto i = 0U; i < CellDescs.CellCount; i++, ++CellIter) { + unsigned NetWidth = 0U; + if (isSplitCell(*CellIter)) + NetWidth = getNetWidth(Cells.begin(), CellIter, CellDescs.InitialSpaces); + auto CellWidth = getMaximumCellWidth(CellIter, NetWidth); + + if (Changes[CellIter->Index].Tok->is(tok::r_brace)) { + // 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; + } + // 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); + 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; + Next = Next->NextColumnElement) { + auto *Start = (Cells.begin() + RowCount * CellDescs.CellCount); + auto *End = Start + Offset; + ThisNetWidth = getNetWidth(Start, End, CellDescs.InitialSpaces); + if (ThisNetWidth < MaxNetWidth) + Changes[Next->Index].Spaces = (MaxNetWidth - ThisNetWidth); + ++RowCount; + } + } + } else { + auto ThisWidth = + calculateCellWidth(CellIter->Index, CellIter->EndIndex, true) + + NetWidth; + if (Changes[CellIter->Index].NewlinesBefore == 0) { + Changes[CellIter->Index].Spaces = (CellWidth - (ThisWidth + NetWidth)); + Changes[CellIter->Index].Spaces += (i > 0) ? 1 : 0; + } + alignToStartOfCell(CellIter->Index, CellIter->EndIndex); + for (const auto *Next = CellIter->NextColumnElement; Next != nullptr; + 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; + } + alignToStartOfCell(Next->Index, Next->EndIndex); + } + } + } +} + +void WhitespaceManager::alignArrayInitializersLeftJustified( + CellDescriptions &&CellDescs) { + 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; + ++CellIter; + for (auto i = 1U; i < CellDescs.CellCount; i++, ++CellIter) { + auto MaxNetWidth = getMaximumNetWidth( + Cells.begin(), CellIter, CellDescs.InitialSpaces, CellDescs.CellCount); + 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); + } + auto RowCount = 1U; + auto Offset = std::distance(Cells.begin(), CellIter); + for (const auto *Next = CellIter->NextColumnElement; Next != nullptr; + Next = Next->NextColumnElement) { + auto *Start = (Cells.begin() + RowCount * CellDescs.CellCount); + 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); + } + ++RowCount; + } + } +} + +bool WhitespaceManager::isSplitCell(const CellDescription &Cell) { + if (Cell.HasSplit) + return true; + for (const auto *Next = Cell.NextColumnElement; Next != nullptr; + Next = Next->NextColumnElement) { + if (Next->HasSplit) + return true; + } + return false; +} + +WhitespaceManager::CellDescriptions WhitespaceManager::getCells(unsigned Start, + unsigned End) { + + unsigned Depth = 0; + unsigned Cell = 0; + unsigned CellCount = 0; + unsigned InitialSpaces = 0; + unsigned InitialTokenLength = 0; + unsigned EndSpaces = 0; + SmallVector<CellDescription> Cells; + const FormatToken *MatchingParen = nullptr; + for (unsigned i = Start; i < End; ++i) { + auto &C = Changes[i]; + if (C.Tok->is(tok::l_brace)) + ++Depth; + else if (C.Tok->is(tok::r_brace)) + --Depth; + if (Depth == 2) { + if (C.Tok->is(tok::l_brace)) { + Cell = 0; + MatchingParen = C.Tok->MatchingParen; + if (InitialSpaces == 0) { + InitialSpaces = C.Spaces + C.TokenLength; + InitialTokenLength = C.TokenLength; + auto j = i - 1; + for (; Changes[j].NewlinesBefore == 0 && j > Start; --j) { + InitialSpaces += Changes[j].Spaces + Changes[j].TokenLength; + InitialTokenLength += Changes[j].TokenLength; + } + if (C.NewlinesBefore == 0) { + InitialSpaces += Changes[j].Spaces + Changes[j].TokenLength; + InitialTokenLength += Changes[j].TokenLength; + } + } + } else if (C.Tok->is(tok::comma)) { + if (!Cells.empty()) + Cells.back().EndIndex = i; + 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; + // 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++; + 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)) { + // 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)) { + // We need to make sure that the ending braces is aligned to the + // start of our initializer + auto j = i - 1; + for (; j > 0 && !Changes[j].Tok->ArrayInitializerLineStart; --j) + ; // Nothing the loop does the work + EndSpaces = Changes[j].Spaces; + } + } else if (Depth == 0 && C.Tok->is(tok::r_brace)) { + C.NewlinesBefore = 1; + C.Spaces = EndSpaces; + } + if (C.Tok->StartsColumn) { + // This gets us past tokens that have been split over multiple + // lines + bool HasSplit = false; + if (Changes[i].NewlinesBefore > 0) { + // 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 + auto j = i - 1; + if ((j - 1) > Start && Changes[j].Tok->is(tok::comma) && + Changes[j - 1].NewlinesBefore > 0) { + --j; + auto LineLimit = Changes[j].Spaces + Changes[j].TokenLength; + if (LineLimit < Style.ColumnLimit) { + Changes[i].NewlinesBefore = 0; + Changes[i].Spaces = 1; + } + } + } + while (Changes[i].NewlinesBefore > 0 && Changes[i].Tok == C.Tok) { + Changes[i].Spaces = InitialSpaces; + ++i; + HasSplit = true; + } + if (Changes[i].Tok != C.Tok) + --i; + Cells.push_back(CellDescription{i, Cell, i, HasSplit, nullptr}); + } + } + + return linkCells({Cells, CellCount, InitialSpaces}); +} + +unsigned WhitespaceManager::calculateCellWidth(unsigned Start, unsigned End, + bool WithSpaces) const { + unsigned CellWidth = 0; + for (auto i = Start; i < End; i++) { + if (Changes[i].NewlinesBefore > 0) + CellWidth = 0; + CellWidth += Changes[i].TokenLength; + CellWidth += (WithSpaces ? Changes[i].Spaces : 0); + } + return CellWidth; +} + +void WhitespaceManager::alignToStartOfCell(unsigned Start, unsigned End) { + if ((End - Start) <= 1) + 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++) { + 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())) { + for (auto *NextIter = CellIter + 1; NextIter != Cells.end(); ++NextIter) { + if (NextIter->Cell == CellIter->Cell) { + CellIter->NextColumnElement = &(*NextIter); + break; + } + } + } + } + return std::move(CellDesc); +} + void WhitespaceManager::generateChanges() { for (unsigned i = 0, e = Changes.size(); i != e; ++i) { const Change &C = Changes[i]; |