https://github.com/bdunkin updated https://github.com/llvm/llvm-project/pull/143781
>From f0a9d10d5306e5f074259c88426725b5adfe6994 Mon Sep 17 00:00:00 2001 From: Ben Dunkin <bdun...@arena.net> Date: Fri, 6 Jun 2025 14:23:29 -0700 Subject: [PATCH] Rewrite AlignArrayOfStructures implementation to allow non-rectangular arrays, and fix numerous formatting bugs. --- clang/lib/Format/ContinuationIndenter.cpp | 48 +- clang/lib/Format/FormatToken.cpp | 10 + clang/lib/Format/FormatToken.h | 13 +- clang/lib/Format/TokenAnnotator.cpp | 40 +- clang/lib/Format/WhitespaceManager.cpp | 546 ++++++------- clang/lib/Format/WhitespaceManager.h | 100 +-- clang/unittests/Format/FormatTest.cpp | 883 ++++++++++++++++++---- 7 files changed, 1147 insertions(+), 493 deletions(-) diff --git a/clang/lib/Format/ContinuationIndenter.cpp b/clang/lib/Format/ContinuationIndenter.cpp index 9a10403b858f9..38db977632533 100644 --- a/clang/lib/Format/ContinuationIndenter.cpp +++ b/clang/lib/Format/ContinuationIndenter.cpp @@ -304,12 +304,15 @@ bool ContinuationIndenter::canBreak(const LineState &State) { Current.closesBlockOrBlockTypeList(Style))) { return false; } + // The opening "{" of a braced list has to be on the same line as the first - // element if it is nested in another braced init list or function call. + // element if it is nested in another braced init list or function call, + // unless it is an array initializer that needs to be aligned. if (!Current.MustBreakBefore && Previous.is(tok::l_brace) && Previous.isNot(TT_DictLiteral) && Previous.is(BK_BracedInit) && Previous.Previous && - Previous.Previous->isOneOf(tok::l_brace, tok::l_paren, tok::comma)) { + Previous.Previous->isOneOf(tok::l_brace, tok::l_paren, tok::comma) && + !Previous.opensAlignedArrayInitializer(Style)) { return false; } // This prevents breaks like: @@ -411,6 +414,7 @@ bool ContinuationIndenter::mustBreak(const LineState &State) { } if (CurrentState.BreakBeforeClosingBrace && (Current.closesBlockOrBlockTypeList(Style) || + Current.closesAlignedArrayInitializer(Style) || (Current.is(tok::r_brace) && Current.isBlockIndentedInitRBrace(Style)))) { return true; @@ -805,6 +809,13 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun, CurrentState.NoLineBreak = true; } + // If this is the first element of an array initializer that needs to have + // it's columns aligned, do not allow any further elements to break the line. + // If we need to break the line for some reason, then this token must be + // placed on its own line as well, and the current state should be discarded. + if (Previous.opensAlignedArrayInitializer(Style) && Style.ColumnLimit > 0) + CurrentState.NoLineBreak = true; + if (Current.is(TT_SelectorName) && !CurrentState.ObjCSelectorNameFound) { unsigned MinIndent = std::max( State.FirstIndent + Style.ContinuationIndentWidth, CurrentState.Indent); @@ -1959,12 +1970,35 @@ void ContinuationIndenter::moveStatePastScopeOpener(LineState &State, NewIndent = CurrentState.LastSpace + Style.ContinuationIndentWidth; } const FormatToken *NextNonComment = Current.getNextNonComment(); - AvoidBinPacking = EndsInComma || Current.is(TT_DictLiteral) || - Style.isProto() || !Style.BinPackArguments || - (NextNonComment && NextNonComment->isOneOf( - TT_DesignatedInitializerPeriod, - TT_DesignatedInitializerLSquare)); + + bool AlignedArrayInitializer = Current.opensAlignedArrayInitializer(Style); + + AvoidBinPacking = + EndsInComma || Current.is(TT_DictLiteral) || Style.isProto() || + !Style.BinPackArguments || + (NextNonComment && + NextNonComment->isOneOf(TT_DesignatedInitializerPeriod, + TT_DesignatedInitializerLSquare)) || + (Style.AlignArrayOfStructures != FormatStyle::AIAS_None && + State.Line->Type == LineType::LT_ArrayOfStructInitializer) || + AlignedArrayInitializer; + BreakBeforeParameter = EndsInComma; + + // If this is an array initializer that will have it's columns aligned, and + // the value is too long to fit on a line, we break before each parameter + // because trying to align columns across multiple lines has too many corner + // cases to do properly. This way, either we align columns all on the same + // line, or we don't align columns at all because they are all on their own + // line. + if (AlignedArrayInitializer && Style.ColumnLimit) { + const unsigned LengthToMatchingParen = + getLengthToMatchingParen(Current, State.Stack) + State.Column; + + if (LengthToMatchingParen > getColumnLimit(State)) + BreakBeforeParameter = true; + } + if (Current.ParameterCount > 1) NestedBlockIndent = std::max(NestedBlockIndent, State.Column + 1); } else { diff --git a/clang/lib/Format/FormatToken.cpp b/clang/lib/Format/FormatToken.cpp index 0d8ae1c4a77eb..54b7cbd4c85e1 100644 --- a/clang/lib/Format/FormatToken.cpp +++ b/clang/lib/Format/FormatToken.cpp @@ -79,6 +79,16 @@ bool FormatToken::opensBlockOrBlockTypeList(const FormatStyle &Style) const { (is(tok::less) && Style.isProto()); } +bool FormatToken::opensAlignedArrayInitializer(const FormatStyle &Style) const { + if (isNot(tok::l_brace) || + Style.AlignArrayOfStructures == FormatStyle::AIAS_None) { + return false; + } + + const FormatToken *Next = getNextNonComment(); + return Next != nullptr && Next->StartsColumn; +} + TokenRole::~TokenRole() {} void TokenRole::precomputeFormattingInfos(const FormatToken *Token) {} diff --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h index 9252a795a0b5e..c54aac64b3b65 100644 --- a/clang/lib/Format/FormatToken.h +++ b/clang/lib/Format/FormatToken.h @@ -580,9 +580,6 @@ struct FormatToken { /// The first token in set of column elements. bool StartsColumn = false; - /// This notes the start of the line of an array initializer. - bool ArrayInitializerLineStart = false; - /// This starts an array initializer. bool IsArrayInitializer = false; @@ -859,6 +856,11 @@ struct FormatToken { /// list that should be indented with a block indent. [[nodiscard]] bool opensBlockOrBlockTypeList(const FormatStyle &Style) const; + /// Returns \c true if this tokens starts an array initializer that needs to + /// have it's elements be aligned + [[nodiscard]] bool + opensAlignedArrayInitializer(const FormatStyle &Style) const; + /// Returns whether the token is the left square bracket of a C++ /// structured binding declaration. bool isCppStructuredBinding(bool IsCpp) const { @@ -879,6 +881,11 @@ struct FormatToken { return MatchingParen && MatchingParen->opensBlockOrBlockTypeList(Style); } + /// Same as opensAlignedArrayInitializer, but for the closing token. + bool closesAlignedArrayInitializer(const FormatStyle &Style) const { + return MatchingParen && MatchingParen->opensAlignedArrayInitializer(Style); + } + /// Return the actual namespace token, if this token starts a namespace /// block. const FormatToken *getNamespaceToken() const { diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index 4801d27b1395a..3135aa0d22660 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -4227,13 +4227,17 @@ void TokenAnnotator::calculateArrayInitializerColumnList( if (Line.First == Line.Last) return; auto *CurrentToken = Line.First; - CurrentToken->ArrayInitializerLineStart = true; unsigned Depth = 0; while (CurrentToken && CurrentToken != Line.Last) { if (CurrentToken->is(tok::l_brace)) { CurrentToken->IsArrayInitializer = true; if (CurrentToken->Next) CurrentToken->Next->MustBreakBefore = true; + + // Ensure the end brace of the outer array is on its own line + if (CurrentToken->MatchingParen) + CurrentToken->MatchingParen->MustBreakBefore = true; + CurrentToken = calculateInitializerColumnList(Line, CurrentToken->Next, Depth + 1); } else { @@ -4249,11 +4253,40 @@ FormatToken *TokenAnnotator::calculateInitializerColumnList( ++Depth; else if (CurrentToken->is(tok::r_brace)) --Depth; + + // Ensure each outer array element starts on its own line + if (Depth == 1 && CurrentToken->is(tok::comma)) { + auto *NextNonComment = CurrentToken->getNextNonComment(); + if (NextNonComment) + NextNonComment->MustBreakBefore = true; + } + if (Depth == 2 && CurrentToken->isOneOf(tok::l_brace, tok::comma)) { CurrentToken = CurrentToken->Next; if (!CurrentToken) break; - CurrentToken->StartsColumn = true; + + // Right (closing) braces should not count as starting a column because + // they are aligned using separate logic. + + // Note: This uses startsSequence() so that trailing comments are skipped + // when checking if the token after a comma/l-brace is a r_brace. We can't + // just ignore comments in general, because an inline comment with + // something else after it should still count as starting a column. + // IE: + // + // { // a + // 4 + // } + // + // vs. + // + // { /* a */ 4 } + // + // In the first case, the comment does not start a column, but in the + // second it does. + CurrentToken->StartsColumn = !CurrentToken->startsSequence(tok::r_brace); + CurrentToken = CurrentToken->Previous; } CurrentToken = CurrentToken->Next; @@ -6199,7 +6232,8 @@ bool TokenAnnotator::canBreakBefore(const AnnotatedLine &Line, // block-indented initialization list. if (Right.is(tok::r_brace)) { return Right.MatchingParen && (Right.MatchingParen->is(BK_Block) || - (Right.isBlockIndentedInitRBrace(Style))); + Right.isBlockIndentedInitRBrace(Style) || + Right.closesAlignedArrayInitializer(Style)); } // We only break before r_paren if we're in a block indented context. diff --git a/clang/lib/Format/WhitespaceManager.cpp b/clang/lib/Format/WhitespaceManager.cpp index cc3cc0f6906cc..da0615a7325cf 100644 --- a/clang/lib/Format/WhitespaceManager.cpp +++ b/clang/lib/Format/WhitespaceManager.cpp @@ -31,19 +31,18 @@ bool WhitespaceManager::Change::IsBeforeInFile::operator()( C2.OriginalWhitespaceRange.getEnd())); } -WhitespaceManager::Change::Change(const FormatToken &Tok, - bool CreateReplacement, - SourceRange OriginalWhitespaceRange, - int Spaces, unsigned StartOfTokenColumn, - unsigned NewlinesBefore, - StringRef PreviousLinePostfix, - StringRef CurrentLinePrefix, bool IsAligned, - bool ContinuesPPDirective, bool IsInsideToken) +WhitespaceManager::Change::Change( + const FormatToken &Tok, bool CreateReplacement, + SourceRange OriginalWhitespaceRange, int Spaces, + unsigned StartOfTokenColumn, unsigned NewlinesBefore, + StringRef PreviousLinePostfix, StringRef CurrentLinePrefix, + bool IsIndentationAligned, bool ContinuesPPDirective, bool IsInsideToken) : Tok(&Tok), CreateReplacement(CreateReplacement), OriginalWhitespaceRange(OriginalWhitespaceRange), StartOfTokenColumn(StartOfTokenColumn), NewlinesBefore(NewlinesBefore), PreviousLinePostfix(PreviousLinePostfix), - CurrentLinePrefix(CurrentLinePrefix), IsAligned(IsAligned), + CurrentLinePrefix(CurrentLinePrefix), + IsIndentationAligned(IsIndentationAligned), IsWhitespaceAligned(false), ContinuesPPDirective(ContinuesPPDirective), Spaces(Spaces), IsInsideToken(IsInsideToken), IsTrailingComment(false), TokenLength(0), PreviousEndOfTokenColumn(0), EscapedNewlineColumn(0), @@ -53,13 +52,14 @@ WhitespaceManager::Change::Change(const FormatToken &Tok, void WhitespaceManager::replaceWhitespace(FormatToken &Tok, unsigned Newlines, unsigned Spaces, unsigned StartOfTokenColumn, - bool IsAligned, bool InPPDirective) { + bool IsIndentationAligned, + bool InPPDirective) { 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, Spaces, StartOfTokenColumn, Newlines, "", "", - IsAligned, InPPDirective && !Tok.IsFirst, + IsIndentationAligned, InPPDirective && !Tok.IsFirst, /*IsInsideToken=*/false)); } @@ -67,11 +67,11 @@ void WhitespaceManager::addUntouchableToken(const FormatToken &Tok, bool InPPDirective) { if (Tok.Finalized || (Tok.MacroCtx && Tok.MacroCtx->Role == MR_ExpandedArg)) return; - Changes.push_back(Change(Tok, /*CreateReplacement=*/false, - Tok.WhitespaceRange, /*Spaces=*/0, - Tok.OriginalColumn, Tok.NewlinesBefore, "", "", - /*IsAligned=*/false, InPPDirective && !Tok.IsFirst, - /*IsInsideToken=*/false)); + Changes.push_back( + Change(Tok, /*CreateReplacement=*/false, Tok.WhitespaceRange, + /*Spaces=*/0, Tok.OriginalColumn, Tok.NewlinesBefore, "", "", + /*IsIndentationAligned=*/false, InPPDirective && !Tok.IsFirst, + /*IsInsideToken=*/false)); } llvm::Error @@ -96,7 +96,7 @@ void WhitespaceManager::replaceWhitespaceInToken( Change(Tok, /*CreateReplacement=*/true, SourceRange(Start, Start.getLocWithOffset(ReplaceChars)), Spaces, std::max(0, Spaces), Newlines, PreviousPostfix, CurrentPrefix, - /*IsAligned=*/true, InPPDirective && !Tok.IsFirst, + /*IsIndentationAligned=*/true, InPPDirective && !Tok.IsFirst, /*IsInsideToken=*/true)); } @@ -118,9 +118,9 @@ const tooling::Replacements &WhitespaceManager::generateReplacements() { alignConsecutiveTableGenDefinitions(); } alignChainedConditionals(); + alignArrayInitializers(); alignTrailingComments(); alignEscapedNewlines(); - alignArrayInitializers(); generateChanges(); return Replaces; @@ -278,6 +278,33 @@ void WhitespaceManager::calculateLineBreakInformation() { } } +// Sets the spaces in front of a Change, and updates the start/end columns of +// subsequent tokens so that trailing comments and escaped newlines can be +// aligned properly +static void +SetChangeSpaces(unsigned Start, unsigned Spaces, + SmallVector<WhitespaceManager::Change, 16> &Changes) { + WhitespaceManager::Change &FirstChange = Changes[Start]; + const int ColumnChange = Spaces - FirstChange.Spaces; + + if (ColumnChange == 0) + return; + + FirstChange.Spaces += ColumnChange; + FirstChange.StartOfTokenColumn += ColumnChange; + + for (unsigned i = Start + 1; i < Changes.size(); i++) { + WhitespaceManager::Change &C = Changes[i]; + + C.PreviousEndOfTokenColumn += ColumnChange; + + if (C.NewlinesBefore > 0) + break; + + C.StartOfTokenColumn += ColumnChange; + } +} + // 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 @@ -350,13 +377,15 @@ AlignTokenSequence(const FormatStyle &Style, unsigned Start, unsigned End, FoundMatchOnLine = true; Shift = Column - (RightJustify ? CurrentChange.TokenLength : 0) - CurrentChange.StartOfTokenColumn; - CurrentChange.Spaces += Shift; + SetChangeSpaces(i, CurrentChange.Spaces + Shift, Changes); // 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 = + SetChangeSpaces( + i, std::max(CurrentChange.Spaces, - static_cast<int>(CurrentChange.Tok->SpacesRequiredBefore)); + static_cast<int>(CurrentChange.Tok->SpacesRequiredBefore)), + Changes); } } @@ -449,11 +478,11 @@ AlignTokenSequence(const FormatStyle &Style, unsigned Start, unsigned End, }; if (ShouldShiftBeAdded()) - CurrentChange.Spaces += Shift; + SetChangeSpaces(i, CurrentChange.Spaces + Shift, Changes); } if (ContinuedStringLiteral) - CurrentChange.Spaces += Shift; + SetChangeSpaces(i, CurrentChange.Spaces + Shift, Changes); // We should not remove required spaces unless we break the line before. assert(Shift > 0 || Changes[i].NewlinesBefore > 0 || @@ -461,10 +490,6 @@ AlignTokenSequence(const FormatStyle &Style, unsigned Start, unsigned End, static_cast<int>(Changes[i].Tok->SpacesRequiredBefore) || CurrentChange.Tok->is(tok::eof)); - 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, // except if the token is equal, then a space is needed. if ((Style.PointerAlignment == FormatStyle::PAS_Right || @@ -485,9 +510,9 @@ AlignTokenSequence(const FormatStyle &Style, unsigned Start, unsigned End, } else if (Style.PointerAlignment != FormatStyle::PAS_Right) { continue; } - Changes[Previous + 1].Spaces -= Shift; - Changes[Previous].Spaces += Shift; - Changes[Previous].StartOfTokenColumn += Shift; + SetChangeSpaces(Previous + 1, Changes[Previous + 1].Spaces - Shift, + Changes); + SetChangeSpaces(Previous, Changes[Previous].Spaces + Shift, Changes); } } } @@ -1318,175 +1343,188 @@ void WhitespaceManager::alignArrayInitializers(unsigned Start, unsigned End) { void WhitespaceManager::alignArrayInitializersRightJustified( CellDescriptions &&CellDescs) { - if (!CellDescs.isRectangular()) + + // If there are less than two rows, there is nothing to align. + if (CellDescs.Rows.size() < 2) + return; + + // If there are less than 2 columns, there is nothing to align. + const int ColumnCount = CellDescs.ColumnStartingCellIndices.size(); + if (ColumnCount < 2) return; const int BracePadding = Style.Cpp11BracedListStyle ? 0 : 1; + auto &ColumnStartingIndices = CellDescs.ColumnStartingCellIndices; auto &Cells = CellDescs.Cells; - // Now go through and fixup the spaces. - auto *CellIter = Cells.begin(); - for (auto i = 0U; i < CellDescs.CellCounts[0]; ++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. - 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.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; - Next = Next->NextColumnElement) { - 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) - 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 : BracePadding; - } - alignToStartOfCell(CellIter->Index, CellIter->EndIndex); - 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 : BracePadding; - } - alignToStartOfCell(Next->Index, Next->EndIndex); - } + + // Calculate column widths. + SmallVector<unsigned> ColumnWidths; // Widths from the previous column + SmallVector<unsigned> SummedColumnWidths; // Widths from the start of the row + + unsigned CurrentWidth = 0; + for (unsigned CellIndex : ColumnStartingIndices) { + const CellDescription *current = &Cells[CellIndex]; + + unsigned MaxWidth = 0; + while (current != nullptr) { + unsigned CellWidth = calculateCellWidth(*current); + + // +1 for the space after the comma in the previous column in all but + // the first column which has brace padding from the opening + // brace instead. + CellWidth += (current->Cell > 0) ? 1 : BracePadding; + + MaxWidth = std::max(MaxWidth, CellWidth); + current = current->NextColumnElement; } + + ColumnWidths.push_back(MaxWidth); + + CurrentWidth += MaxWidth; + SummedColumnWidths.push_back(CurrentWidth); + + // +1 for the comma between cells. + CurrentWidth++; + } + + // Fixup spaces. + for (RowDescription &Row : CellDescs.Rows) { + unsigned WidthSoFarInRow = 0; + for (unsigned i = Row.StartCellIndex; i < Row.EndCellIndex; i++) { + const CellDescription &Cell = Cells[i]; + + const unsigned CellWidth = calculateCellWidth(Cell); + const unsigned AlignmentSpaces = ColumnWidths[Cell.Cell] - CellWidth; + setChangeSpaces(Cell.Index, AlignmentSpaces); + Changes[Cell.Index].IsWhitespaceAligned = true; + + WidthSoFarInRow = SummedColumnWidths[Cell.Cell]; + + // +1 for the comma after columns in all but the last column + // Note: this can't check Cell.Cell because a row may not have a full + // set of columns. + if (i < Row.EndCellIndex - 1) + WidthSoFarInRow++; + } + + // Align the end brace. + const unsigned AlignmentSpaces = + (SummedColumnWidths.back() - WidthSoFarInRow) + BracePadding; + setChangeSpaces(Row.ClosingBraceChangeIndex, AlignmentSpaces); + Changes[Row.ClosingBraceChangeIndex].IsWhitespaceAligned = true; } } void WhitespaceManager::alignArrayInitializersLeftJustified( CellDescriptions &&CellDescs) { - if (!CellDescs.isRectangular()) + // If there are less than two rows, there is nothing to align. + if (CellDescs.Rows.size() < 2) + return; + + // If there are less than 2 columns, there is nothing to align. + const unsigned ColumnCount = CellDescs.ColumnStartingCellIndices.size(); + if (ColumnCount < 2) return; + const unsigned LastColumnIndex = ColumnCount - 1; 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 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.CellCounts[0]; i++, ++CellIter) { - auto MaxNetWidth = getMaximumNetWidth( - 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 - : BracePadding); - } - auto RowCount = 1U; - auto Offset = std::distance(Cells.begin(), CellIter); - for (const auto *Next = CellIter->NextColumnElement; Next; - Next = Next->NextColumnElement) { - 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 : BracePadding); - } - ++RowCount; + const auto &ColumnStartingIndices = CellDescs.ColumnStartingCellIndices; + const auto &Cells = CellDescs.Cells; + + // Calculate column starting widths. + SmallVector<unsigned> StartWidths; + + // The first column starts after the opening brace's padding. + StartWidths.push_back(BracePadding); + + for (unsigned i = 0; i < ColumnCount; i++) { + const CellDescription *current = &Cells[ColumnStartingIndices[i]]; + + unsigned MaxWidth = 0; + while (current != nullptr) { + // calculateCellWidth returns relative column count from the previous + // cell, but we want it relative to the opening brace so we add + // starting width for this cell. + unsigned CellWidth = calculateCellWidth(*current) + StartWidths[i]; + + // +1 for the comma after the cell if it exists. + if (Changes[current->EndIndex].Tok->is(tok::comma)) + CellWidth++; + + // +1 for the space after the column if this is not the last column. + if (i < LastColumnIndex) + CellWidth++; + + MaxWidth = std::max(MaxWidth, CellWidth); + current = current->NextColumnElement; } + + // If this is the last column, add the brace padding to the width so that + // the end brace gets the necessary padding. + if (i == LastColumnIndex) + MaxWidth += BracePadding; + + StartWidths.push_back(MaxWidth); } -} -bool WhitespaceManager::isSplitCell(const CellDescription &Cell) { - if (Cell.HasSplit) - return true; - for (const auto *Next = Cell.NextColumnElement; Next; - Next = Next->NextColumnElement) { - if (Next->HasSplit) - return true; + // Fixup spaces. + for (RowDescription &Row : CellDescs.Rows) { + unsigned WidthSoFarInRow = 0; + + for (unsigned i = Row.StartCellIndex; i < Row.EndCellIndex; i++) { + const CellDescription &Cell = Cells[i]; + + Change &Change = Changes[Cell.Index]; + const unsigned AlignmentSpaces = StartWidths[Cell.Cell] - WidthSoFarInRow; + setChangeSpaces(Cell.Index, AlignmentSpaces); + Change.IsWhitespaceAligned = true; + + WidthSoFarInRow += calculateCellWidth(Cell) + Change.Spaces; + + // +1 for the comma after columns in all but the last column + // Note: this can't check Cell.Cell because a row may not have a full + // set of columns. + if (i < Row.EndCellIndex - 1) + WidthSoFarInRow += 1; + } + + // Align the end brace. + const unsigned AlignmentSpaces = StartWidths.back() - WidthSoFarInRow; + setChangeSpaces(Row.ClosingBraceChangeIndex, AlignmentSpaces); + Changes[Row.ClosingBraceChangeIndex].IsWhitespaceAligned = true; } - return false; } WhitespaceManager::CellDescriptions WhitespaceManager::getCells(unsigned Start, unsigned End) { - unsigned Depth = 0; unsigned Cell = 0; - SmallVector<unsigned> CellCounts; - unsigned InitialSpaces = 0; - unsigned InitialTokenLength = 0; - unsigned EndSpaces = 0; SmallVector<CellDescription> Cells; + SmallVector<RowDescription> Rows; + SmallVector<unsigned> StartingCellIndices; const FormatToken *MatchingParen = nullptr; + RowDescription CurrentRow; + bool SkipCurrentRow = false; 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; + SkipCurrentRow = false; + CurrentRow = RowDescription{unsigned(Cells.size()), 0, 0, + C.StartOfTokenColumn + C.TokenLength}; 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; + if (const auto *Next = C.Tok->getNextNonComment(); Next && Next->isNot(tok::r_brace)) { // dangling comma ++Cell; @@ -1494,105 +1532,64 @@ WhitespaceManager::CellDescriptions WhitespaceManager::getCells(unsigned Start, } } 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}); - 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 && NextNonComment->is(tok::comma)) - NextNonComment = NextNonComment->getNextNonComment(); - auto j = i; - while (j < End && Changes[j].Tok != NextNonComment) - ++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; + // Rows with trailing commas are not aligned because they have each cell + // on a separate line + if (C.Tok->getPreviousNonComment()->is(tok::comma)) + SkipCurrentRow = true; + + if (SkipCurrentRow) { + // If we are skipping this row, we also need to remove the cells. We + // may have added cells before we found out the row needed to be + // skipped, so we just remove them at the end of the row for + // simplicity. + Cells.pop_back_n(Cells.size() - CurrentRow.StartCellIndex); + } else { + CurrentRow.ClosingBraceChangeIndex = i; + CurrentRow.EndCellIndex = Cells.size(); + Rows.push_back(CurrentRow); + + // If this is an empty row, just push back the cell. + if (Cell == 0) { + Cells.push_back(CellDescription{i, Cell, i + 1, nullptr}); + } else { + if (!Cells.empty()) + Cells.back().EndIndex = i; + Cells.push_back(CellDescription{i, ++Cell, i + 1, nullptr}); + } } - } 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)) { - // 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 - // - // 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) { - --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 (C.NewlinesBefore > 0) { + SkipCurrentRow = true; + } else { + if (Cell >= StartingCellIndices.size()) + StartingCellIndices.push_back(Cells.size()); + + Cells.push_back(CellDescription{i, Cell, i, nullptr}); } - if (Changes[i].Tok != C.Tok) - --i; - Cells.push_back(CellDescription{i, Cell, i, HasSplit, nullptr}); } } - return linkCells({Cells, CellCounts, InitialSpaces}); + return linkCells({Cells, Rows, StartingCellIndices}); } -unsigned WhitespaceManager::calculateCellWidth(unsigned Start, unsigned End, - bool WithSpaces) const { +unsigned +WhitespaceManager::calculateCellWidth(const CellDescription &Cell) const { unsigned CellWidth = 0; - for (auto i = Start; i < End; i++) { + for (auto i = Cell.Index; i < Cell.EndIndex; i++) { if (Changes[i].NewlinesBefore > 0) CellWidth = 0; + + if (CellWidth != 0) + CellWidth += Changes[i].Spaces; + 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; @@ -1609,6 +1606,10 @@ WhitespaceManager::linkCells(CellDescriptions &&CellDesc) { return std::move(CellDesc); } +void WhitespaceManager::setChangeSpaces(unsigned Start, unsigned Spaces) { + SetChangeSpaces(Start, Spaces, Changes); +} + void WhitespaceManager::generateChanges() { for (unsigned i = 0, e = Changes.size(); i != e; ++i) { const Change &C = Changes[i]; @@ -1617,37 +1618,39 @@ void WhitespaceManager::generateChanges() { 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 + // 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. + // 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. + // 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. + // 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 ", @@ -1676,7 +1679,7 @@ void WhitespaceManager::generateChanges() { appendIndentText( ReplacementText, C.Tok->IndentLevel, std::max(0, C.Spaces), std::max((int)C.StartOfTokenColumn, C.Spaces) - std::max(0, C.Spaces), - C.IsAligned); + C.IsIndentationAligned, C.IsWhitespaceAligned); ReplacementText.append(C.CurrentLinePrefix); storeReplacement(C.OriginalWhitespaceRange, ReplacementText); } @@ -1732,7 +1735,8 @@ void WhitespaceManager::appendEscapedNewlineText( void WhitespaceManager::appendIndentText(std::string &Text, unsigned IndentLevel, unsigned Spaces, unsigned WhitespaceStartColumn, - bool IsAligned) { + bool IsIndentationAligned, + bool IsWhitespaceAligned) { switch (Style.UseTab) { case FormatStyle::UT_Never: Text.append(Spaces, ' '); @@ -1772,8 +1776,10 @@ void WhitespaceManager::appendIndentText(std::string &Text, break; case FormatStyle::UT_AlignWithSpaces: if (WhitespaceStartColumn == 0) { - unsigned Indentation = - IsAligned ? IndentLevel * Style.IndentWidth : Spaces; + unsigned Indentation = IsWhitespaceAligned ? 0 + : IsIndentationAligned + ? IndentLevel * Style.IndentWidth + : Spaces; Spaces = appendTabIndent(Text, Spaces, Indentation); } Text.append(Spaces, ' '); @@ -1783,8 +1789,8 @@ void WhitespaceManager::appendIndentText(std::string &Text, unsigned WhitespaceManager::appendTabIndent(std::string &Text, unsigned Spaces, unsigned Indentation) { - // This happens, e.g. when a line in a block comment is indented less than the - // first one. + // This happens, e.g. when a line in a block comment is indented less than + // the first one. if (Indentation > Spaces) Indentation = Spaces; if (Style.TabWidth) { diff --git a/clang/lib/Format/WhitespaceManager.h b/clang/lib/Format/WhitespaceManager.h index 6d18db74cd2e4..54b9160e9f24a 100644 --- a/clang/lib/Format/WhitespaceManager.h +++ b/clang/lib/Format/WhitespaceManager.h @@ -50,7 +50,8 @@ class WhitespaceManager { /// this replacement. It is needed for determining how \p Spaces is turned /// into tabs and spaces for some format styles. void replaceWhitespace(FormatToken &Tok, unsigned Newlines, unsigned Spaces, - unsigned StartOfTokenColumn, bool IsAligned = false, + unsigned StartOfTokenColumn, + bool IsIndentationAligned = false, bool InPPDirective = false); /// Adds information about an unchangeable token's whitespace. @@ -110,7 +111,8 @@ class WhitespaceManager { SourceRange OriginalWhitespaceRange, int Spaces, unsigned StartOfTokenColumn, unsigned NewlinesBefore, StringRef PreviousLinePostfix, StringRef CurrentLinePrefix, - bool IsAligned, bool ContinuesPPDirective, bool IsInsideToken); + bool IsIndentationAligned, bool ContinuesPPDirective, + bool IsInsideToken); // The kind of the token whose whitespace this change replaces, or in which // this change inserts whitespace. @@ -126,7 +128,8 @@ class WhitespaceManager { unsigned NewlinesBefore; std::string PreviousLinePostfix; std::string CurrentLinePrefix; - bool IsAligned; + bool IsIndentationAligned; + bool IsWhitespaceAligned; bool ContinuesPPDirective; // The number of spaces in front of the token or broken part of the token. @@ -177,7 +180,6 @@ class WhitespaceManager { unsigned Index = 0; unsigned Cell = 0; unsigned EndIndex = 0; - bool HasSplit = false; CellDescription *NextColumnElement = nullptr; constexpr bool operator==(const CellDescription &Other) const { @@ -189,22 +191,17 @@ class WhitespaceManager { } }; + struct RowDescription { + unsigned StartCellIndex = 0; + unsigned EndCellIndex = 0; + unsigned ClosingBraceChangeIndex = 0; + unsigned StartColumn = 0; + }; + struct CellDescriptions { SmallVector<CellDescription> Cells; - SmallVector<unsigned> CellCounts; - unsigned InitialSpaces = 0; - - // Determine if every row in the array - // has the same number of columns. - bool isRectangular() const { - if (CellCounts.size() < 2) - return false; - - for (auto NumberOfColumns : CellCounts) - if (NumberOfColumns != CellCounts[0]) - return false; - return true; - } + SmallVector<RowDescription> Rows; + SmallVector<unsigned> ColumnStartingCellIndices; }; /// Calculate \c IsTrailingComment, \c TokenLength for the last tokens @@ -274,76 +271,17 @@ class WhitespaceManager { void alignArrayInitializersLeftJustified(CellDescriptions &&CellDescs); /// Calculate the cell width between two indexes. - unsigned calculateCellWidth(unsigned Start, unsigned End, - bool WithSpaces = false) const; + unsigned calculateCellWidth(const CellDescription &Cell) const; /// Get a set of fully specified CellDescriptions between \p Start and /// \p End of the change list. CellDescriptions getCells(unsigned Start, unsigned End); - /// Does this \p Cell contain a split element? - static bool isSplitCell(const CellDescription &Cell); - - /// Get the width of the preceding cells from \p Start to \p End. - template <typename I> - auto getNetWidth(const I &Start, const I &End, unsigned InitialSpaces) const { - auto NetWidth = InitialSpaces; - for (auto PrevIter = Start; PrevIter != End; ++PrevIter) { - // If we broke the line the initial spaces are already - // accounted for. - assert(PrevIter->Index < Changes.size()); - if (Changes[PrevIter->Index].NewlinesBefore > 0) - NetWidth = 0; - NetWidth += - calculateCellWidth(PrevIter->Index, PrevIter->EndIndex, true) + 1; - } - return NetWidth; - } - - /// Get the maximum width of a cell in a sequence of columns. - template <typename I> - unsigned getMaximumCellWidth(I CellIter, unsigned NetWidth) const { - unsigned CellWidth = - calculateCellWidth(CellIter->Index, CellIter->EndIndex, true); - if (Changes[CellIter->Index].NewlinesBefore == 0) - CellWidth += NetWidth; - for (const auto *Next = CellIter->NextColumnElement; Next; - Next = Next->NextColumnElement) { - auto ThisWidth = calculateCellWidth(Next->Index, Next->EndIndex, true); - if (Changes[Next->Index].NewlinesBefore == 0) - ThisWidth += NetWidth; - CellWidth = std::max(CellWidth, ThisWidth); - } - return CellWidth; - } - - /// Get The maximum width of all columns to a given cell. - template <typename I> - unsigned getMaximumNetWidth(const I &CellStart, const I &CellStop, - unsigned InitialSpaces, unsigned CellCount, - unsigned MaxRowCount) const { - auto MaxNetWidth = getNetWidth(CellStart, CellStop, InitialSpaces); - auto RowCount = 1U; - auto Offset = std::distance(CellStart, CellStop); - for (const auto *Next = CellStop->NextColumnElement; Next; - Next = Next->NextColumnElement) { - if (RowCount >= MaxRowCount) - break; - auto Start = (CellStart + RowCount * CellCount); - auto End = Start + Offset; - MaxNetWidth = - std::max(MaxNetWidth, getNetWidth(Start, End, InitialSpaces)); - ++RowCount; - } - return MaxNetWidth; - } - - /// Align a split cell with a newline to the first element in the cell. - void alignToStartOfCell(unsigned Start, unsigned End); - /// Link the Cell pointers in the list of Cells. static CellDescriptions linkCells(CellDescriptions &&CellDesc); + void setChangeSpaces(unsigned Start, unsigned Spaces); + /// Fill \c Replaces with the replacements for all effective changes. void generateChanges(); @@ -355,7 +293,7 @@ class WhitespaceManager { unsigned EscapedNewlineColumn); void appendIndentText(std::string &Text, unsigned IndentLevel, unsigned Spaces, unsigned WhitespaceStartColumn, - bool IsAligned); + bool IsIndentationAligned, bool IsWhitespaceAligned); unsigned appendTabIndent(std::string &Text, unsigned Spaces, unsigned Indentation); diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index 96cc650f52a5d..860fcaa783946 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -22043,6 +22043,21 @@ TEST_F(FormatTest, CatchExceptionReferenceBinding) { TEST_F(FormatTest, CatchAlignArrayOfStructuresRightAlignment) { auto Style = getLLVMStyle(); Style.AlignArrayOfStructures = FormatStyle::AIAS_Right; + + verifyFormat("auto foo = Items{\n" + " Section{0, barbar()},\n" + " Section{\n" + " 1,\n" + " boo(),\n" + " }\n" + "};", + Style); + + verifyFormat("auto foo = Items{\n" + " Section{0, bar()}\n" + "};", + Style); + verifyNoCrash("f({\n" "table({}, table({{\"\", false}}, {}))\n" "});", @@ -22118,11 +22133,14 @@ TEST_F(FormatTest, CatchAlignArrayOfStructuresRightAlignment) { verifyFormat( "test demo[] = {\n" - " { 7, 23,\n" - " \"hello world i am a very long line that really, in any\"\n" - " \"just world, ought to be split over multiple lines\"},\n" - " {-1, 93463, \"world\"},\n" - " {56, 5, \"!!\"}\n" + " {\n" + " 7,\n" + " 23,\n" + " \"hello world i am a very long line that really, in any\"\n" + " \"just world, ought to be split over multiple lines\"\n" + " },\n" + " {-1, 93463, \"world\"},\n" + " {56, 5, \"!!\"}\n" "};", Style); @@ -22149,7 +22167,7 @@ TEST_F(FormatTest, CatchAlignArrayOfStructuresRightAlignment) { " {\"x\", \"dy\"}},\n" " { {\"dx\"}, \"Mul\", {\"dy\"" ", \"sign\"}},\n" - "});", + " });", Style); Style.Cpp11BracedListStyle = false; @@ -22163,17 +22181,16 @@ TEST_F(FormatTest, CatchAlignArrayOfStructuresRightAlignment) { Style.ColumnLimit = 0; verifyFormat( - "test demo[] = {\n" - " {56, 23, \"hello world i am a very long line that really, " - "in any just world, ought to be split over multiple lines\"},\n" - " {-1, 93463, " - " \"world\"},\n" - " { 7, 5, " - " \"!!\"},\n" - "};", - "test demo[] = {{56, 23, \"hello world i am a very long line " - "that really, in any just world, ought to be split over multiple " - "lines\"},{-1, 93463, \"world\"},{7, 5, \"!!\"},};", + R"( +test demo[] = { + {56, 23, "hello world i am a very long line that really, in any just world, ought to be split over multiple lines"}, + {-1, 93463, "world"}, + { 7, 5, "!!"}, +}; +)", + R"( +test demo[] = {{56, 23, "hello world i am a very long line that really, in any just world, ought to be split over multiple lines"},{-1, 93463, "world"},{7, 5, "!!"},}; +)", Style); Style.ColumnLimit = 80; @@ -22202,25 +22219,33 @@ TEST_F(FormatTest, CatchAlignArrayOfStructuresRightAlignment) { verifyFormat("demo = std::array<\n" " struct test, 3>{\n" " test{\n" - " 56, 23,\n" - " \"hello \"\n" - " \"world i \"\n" - " \"am a very \"\n" - " \"long line \"\n" - " \"that \"\n" - " \"really, \"\n" - " \"in any \"\n" - " \"just \"\n" - " \"world, \"\n" - " \"ought to \"\n" - " \"be split \"\n" - " \"over \"\n" - " \"multiple \"\n" - " \"lines\"},\n" - " test{-1, 93463,\n" - " \"world\"},\n" - " test{ 7, 5,\n" - " \"!!\" },\n" + " 56,\n" + " 23,\n" + " \"hello \"\n" + " \"world i \"\n" + " \"am a very \"\n" + " \"long line \"\n" + " \"that \"\n" + " \"really, \"\n" + " \"in any \"\n" + " \"just \"\n" + " \"world, \"\n" + " \"ought to \"\n" + " \"be split \"\n" + " \"over \"\n" + " \"multiple \"\n" + " \"lines\"\n" + " },\n" + " test{\n" + " -1,\n" + " 93463,\n" + " \"world\"\n" + " },\n" + " test{\n" + " 7,\n" + " 5,\n" + " \"!!\"\n" + " },\n" "};", "demo = std::array<struct test, 3>{test{56, 23, \"hello world " "i am a very long line that really, in any just world, ought " @@ -22231,8 +22256,10 @@ TEST_F(FormatTest, CatchAlignArrayOfStructuresRightAlignment) { Style = getLLVMStyleWithColumns(50); Style.AlignArrayOfStructures = FormatStyle::AIAS_Right; verifyFormat("static A x = {\n" - " {{init1, init2, init3, init4},\n" - " {init1, init2, init3, init4}}\n" + " {\n" + " {init1, init2, init3, init4},\n" + " {init1, init2, init3, init4}\n" + " }\n" "};", Style); // TODO: Fix the indentations below when this option is fully functional. @@ -22248,10 +22275,13 @@ TEST_F(FormatTest, CatchAlignArrayOfStructuresRightAlignment) { Style.ColumnLimit = 100; verifyFormat( "test demo[] = {\n" - " {56, 23,\n" - " \"hello world i am a very long line that really, in any just world" - ", ought to be split over \"\n" - " \"multiple lines\" },\n" + " {\n" + " 56,\n" + " 23,\n" + " \"hello world i am a very long line that really, in any just " + "world, ought to be split over \"\n" + " \"multiple lines\"\n" + " },\n" " {-1, 93463, \"world\"},\n" " { 7, 5, \"!!\"},\n" "};", @@ -22268,8 +22298,10 @@ TEST_F(FormatTest, CatchAlignArrayOfStructuresRightAlignment) { " { 7, 5, \"!!\"}\n" "};\n" "static A x = {\n" - " {{init1, init2, init3, init4},\n" - " {init1, init2, init3, init4}}\n" + " {\n" + " {init1, init2, init3, init4},\n" + " {init1, init2, init3, init4}\n" + " }\n" "};", Style); Style.ColumnLimit = 100; @@ -22287,37 +22319,535 @@ TEST_F(FormatTest, CatchAlignArrayOfStructuresRightAlignment) { " {234, 5, 1, \"gracious\"} // fourth line\n" "};", Style); + + verifyFormat(R"( +test demo[] = { + { + 56, + "hello world i am a very long line that really, in any just world, ought to be split over " + "multiple lines", + 23 + }, + {-1, "world", 93463}, + { 7, "!!", 5}, +}; +)", + R"( +test demo[] = {{56, "hello world i am a very long line that really, in any just world, ought to be split over multiple lines", 23},{-1, "world", 93463},{7, "!!", 5},}; +)", + Style); + + verifyFormat(R"( +test demo[] = { + {-1, "world", 93463}, + { + 56, + "hello world i am a very long line that really, in any just world, ought to be split over " + "multiple lines", + 23 + }, + { 7, "!!", 5}, +}; +)", + R"( +test demo[] = {{-1, "world", 93463},{56, "hello world i am a very long line that really, in any just world, ought to be split over multiple lines", 23},{7, "!!", 5},}; +)", + Style); +} + +TEST_F(FormatTest, AlignArrayOfStructuresGithubIssues) { + + // https://github.com/llvm/llvm-project/issues/148833 + // Summary: Aligning across macro statments doesn't work + // + // Notes: It looks like we never even see the tokens in the else branch, so + // its not the alignment code thats busted, but the code that collects tokens + // to format. See UnwrappedLineParser::addUnwrappedLine for where the tokens + // are initially added. + FormatStyle Style = getLLVMStyleWithColumns(120); + Style.AlignArrayOfStructures = FormatStyle::AIAS_Left; + + // verifyFormat("const struct reg we_WANT[] = {\n" + // "#if A\n" + // " {abc, 0, format, code},\n" + // " {abcd2, 0, format, code},\n" + // "#else\n" + // " {aaaa, 0, why, why },\n" + // " {whyyyy, 0, why, why },\n" + // "#endif\n" + // "}\n", + // Style); + + // FIXED - https://github.com/llvm/llvm-project/issues/138151 + // Summary: Aligning arrays of structures with UseTab: AlignWithSpaces does + // not use spaces to align columns + Style = getGoogleStyle(); + Style.AlignArrayOfStructures = FormatStyle::AIAS_Left; + Style.UseTab = FormatStyle::UT_AlignWithSpaces; + Style.IndentWidth = 4; + Style.TabWidth = 4; + verifyFormat( - "test demo[] = {\n" - " {56,\n" - " \"hello world i am a very long line that really, in any just world" - ", ought to be split over \"\n" - " \"multiple lines\", 23},\n" - " {-1, \"world\", 93463},\n" - " { 7, \"!!\", 5},\n" - "};", - "test demo[] = {{56, \"hello world i am a very long line " - "that really, in any just world, ought to be split over multiple " - "lines\", 23},{-1, \"world\", 93463},{7, \"!!\", 5},};", + "std::vector<Foo> foos = {\n" + "\t{LONG_NAME, 0, i | j},\n" + "\t{LONG_NAME, 0, i | j},\n" + "\t{LONGER_NAME, 0, i | j},\n" + "\t{LONGER_NAME, 0, i },\n" + "\t{THIS_IS_A_VERY_LONG_NAME, 0, j },\n" + "\t{LONGER_NAME, THIS_IS_A_VERY_LONG_NAME, i },\n" + "\t{LONG_NAME, THIS_IS_A_VERY_LONG_NAME, j }\n" + "};\n", + Style); + + // FIXED - https://github.com/llvm/llvm-project/issues/85937 + // Summary: Macro escaped newlines are not aligned properly when both + // AlignEscapedNewLines and AlignArrayOfStructures are used + Style = getLLVMStyleWithColumns(80); + Style.AlignEscapedNewlines = FormatStyle::ENAS_Left; + Style.AlignArrayOfStructures = FormatStyle::AIAS_Left; + + verifyFormat(R"( +#define DEFINE_COMMAND_PROCESS_TABLE(Enum) \ + const STExample TCommand::EXPL_MAIN[] = { \ + {Enum::GetName(), " shows help " }, \ + {Enum::GetAttribute(), " do something "}, \ + {Enum::GetState(), " do whatever " }, \ + }; +)", + Style); + + // FIXED - https://github.com/llvm/llvm-project/issues/76803 + // Summary: constructor member initializer list indentation is weird + Style = getLLVMStyle(); + Style.AlignArrayOfStructures = FormatStyle::AIAS_Left; + Style.IndentWidth = 4; + + verifyFormat("struct Foo {\n" + " explicit Foo()\n" + " : data({\n" + " {1, 2},\n" + " }) {}\n" + " const std::map<int, int> data;\n" + "};\n", + Style); + + // FIXED - https://github.com/llvm/llvm-project/issues/76803 + // Summary: Array opening and closing brackets are busted even when + // AlignArrayOfStructures is None + Style = getLLVMStyleWithColumns(100); + Style.AlignArrayOfStructures = FormatStyle::AIAS_None; + Style.IndentWidth = 2; + Style.TabWidth = 2; + Style.ContinuationIndentWidth = 2; + Style.UseTab = FormatStyle::UT_AlignWithSpaces; + Style.AlignAfterOpenBracket = FormatStyle::BAS_BlockIndent; + Style.PointerAlignment = FormatStyle::PAS_Left; + Style.BreakBeforeBinaryOperators = FormatStyle::BOS_All; + Style.AlignAfterOpenBracket = FormatStyle::BAS_BlockIndent; + Style.AlignOperands = FormatStyle::OAS_DontAlign; + Style.BreakBeforeTernaryOperators = true; + Style.BreakBeforeBraces = FormatStyle::BS_Attach; + Style.BinPackArguments = false; + Style.BinPackParameters = FormatStyle::BPPS_AlwaysOnePerLine; + + verifyFormat( + R"(VBuffer Renderer::allocateBuffer(AllocateBufferInfo const& info) { + VBuffer buf; + VkSharingMode sharingMode + = info.sharedQueueFamilies.size() >= 2 ? VK_SHARING_MODE_CONCURRENT : VK_SHARING_MODE_EXCLUSIVE; + CHECKVK(vmaCreateBuffer( + allocator_, + (VkBufferCreateInfo const[1]){{ + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .size = info.size, + .usage = info.bufferUsage, + .sharingMode = sharingMode, + .queueFamilyIndexCount = uint32_t(info.sharedQueueFamilies.size()), + .pQueueFamilyIndices = info.sharedQueueFamilies.data(), + }}, + (VmaAllocationCreateInfo const[1]){{ + .flags = info.allocationFlags, + .usage = info.memoryUsage, + .requiredFlags = info.requiredMemoryPropertyFlags, + }}, + &buf.buffer, + &buf.allocation, + info.allocationInfo + )); + return buf; +} +)", Style); + + // FIXED - https://github.com/llvm/llvm-project/issues/76717 + // Summary: Braced initializer of struct is badly aligned + Style = getLLVMStyle(); + Style.AlignArrayOfStructures = FormatStyle::AIAS_Left; + Style.ColumnLimit = 0; + Style.Cpp11BracedListStyle = false; + Style.IndentWidth = 4; + + verifyFormat("static const auto messages = Messages{\n" + " {\n" + " Code::X,\n" + " {\n" + " \"data1\",\n" + " Code::Y,\n" + " },\n" + " },\n" + " {\n" + " Code::Y,\n" + " {\n" + " \"data1\",\n" + " Code::Z,\n" + " },\n" + " },\n" + "};\n" + "static const Entries entry = {\n" + " { \"\data\", Code::X },\n" + " { \"\data1\", Code::Y },\n" + " { \"\data12\", Code::Z },\n" + "};\n", + Style); + + Style.AlignArrayOfStructures = FormatStyle::AIAS_Right; + + verifyFormat("static const auto messages = Messages{\n" + " {\n" + " Code::X,\n" + " {\n" + " \"data1\",\n" + " Code::Y,\n" + " },\n" + " },\n" + " {\n" + " Code::Y,\n" + " {\n" + " \"data1\",\n" + " Code::Z,\n" + " },\n" + " },\n" + "};\n" + "static const Entries entry = {\n" + " { \"\data\", Code::X },\n" + " { \"\data1\", Code::Y },\n" + " { \"\data12\", Code::Z },\n" + "};\n", + Style); + + // FIXED - https://github.com/llvm/llvm-project/issues/55477 + // Summary: tabs are not used for the indentation of the closing brace in the + // following when UseTab: ForContinuationAndIndentation + Style = getLLVMStyle(); + Style.AlignArrayOfStructures = FormatStyle::AIAS_Right; + Style.AllowAllArgumentsOnNextLine = false; + Style.ContinuationIndentWidth = 1; + Style.IndentWidth = 1; + Style.Standard = FormatStyle::LS_Latest; + Style.TabWidth = 1; + Style.LineEnding = FormatStyle::LE_LF; + Style.UseTab = FormatStyle::UT_ForContinuationAndIndentation; + + verifyFormat("void func() {\n" + "\tstd::vector<std::pair<int, std::string>> in = {\n" + "\t\t{13, \"13\"},\n" + "\t\t{14, \"14\"},\n" + "\t\t{ 1, \"1\"}\n" + "\t};\n" + "}\n", + Style); + + // FIXED - https://github.com/llvm/llvm-project/issues/55154 + // Summary: inconsistent use of tabs vs spaces for indentation when both + // SpaceBeforeCpp11BracedList is true and UseTab is + // ForContinuationAndIndentation + Style = getLLVMStyle(); + Style.AlignArrayOfStructures = FormatStyle::AIAS_Left; + Style.IndentWidth = 4; + Style.SpaceBeforeCpp11BracedList = true; + Style.Cpp11BracedListStyle = false; + Style.TabWidth = 4; + Style.UseTab = FormatStyle::UT_ForContinuationAndIndentation; + + verifyFormat("static type arr[] = {\n" + "\t{ fun(arg), arg },\n" + "\t{ fun(arg), arg },\n" + "\t{ fun(arg), arg },\n" + "\t{ fun(arg), arg },\n" + "\t{ fun(arg), arg },\n" + "\t{ fun(arg), arg },\n" + "\t{ fun(arg), arg },\n" + "};\n", + Style); + + // FIXED - https://github.com/llvm/llvm-project/issues/53442 + // Summary: alignment of columns does not use spaces when UseTab: + // AlignWithSpaces + Style = getLLVMStyle(); + Style.AlignArrayOfStructures = FormatStyle::AIAS_Left; + Style.IndentWidth = 4; + Style.TabWidth = 4; + Style.UseTab = FormatStyle::UT_AlignWithSpaces; + Style.BreakBeforeBraces = FormatStyle::BS_Allman; + + verifyFormat( + "const map<string, int64_t> CoreReport::GetGameCountersRolloverInfo()\n" + "{\n" + "\tstatic map<string, int64_t> counterRolloverInfo{\n" + "\t\t{\"CashIn\", 4000000000},\n" + "\t\t{\"CoinIn\", 4000000000},\n" + "\t\t{\"QuantityMultiProgressive\", 65535 },\n" + "\t};\n" + "\treturn counterRolloverInfo;\n" + "}\n", + Style); + + // FIXED - https://github.com/llvm/llvm-project/issues/55485 + // Summary: Alignment with tabs is busted + Style = getLLVMStyleWithColumns(120); + Style.AlignAfterOpenBracket = FormatStyle::BAS_BlockIndent; + Style.AlignArrayOfStructures = FormatStyle::AIAS_Right; + Style.BinPackArguments = false; + Style.ContinuationIndentWidth = 1; + Style.IndentWidth = 1; + Style.TabWidth = 1; + Style.LineEnding = FormatStyle::LE_LF; + Style.UseTab = FormatStyle::UT_ForContinuationAndIndentation; + + verifyFormat("void func() {\n" + "\tint array = {\n" + "\t\t//\n" + "\t\t10,\n" + "\t\t20\n" + "\t};\n" + "\tmy_class a{\n" + "\t\t//\n" + "\t\t10,\n" + "\t\t20\n" + "\t};\n" + "}\n", + Style); + + // FIXED - https://github.com/llvm/llvm-project/issues/55477 + // Summary: Alignment doesn't use tabs + Style = getLLVMStyleWithColumns(120); + Style.AlignArrayOfStructures = FormatStyle::AIAS_Right; + Style.AllowAllArgumentsOnNextLine = false; + Style.ContinuationIndentWidth = 1; + Style.IndentWidth = 1; + Style.TabWidth = 1; + Style.LineEnding = FormatStyle::LE_LF; + Style.UseTab = FormatStyle::UT_ForContinuationAndIndentation; + + verifyFormat("void func() {\n" + "\tstd::vector<std::pair<int, std::string>> in = {\n" + "\t\t{13, \" 13 \"},\n" + "\t\t{14, \" 14 \"},\n" + "\t\t{ 1, \" 1 \"}\n" + "\t};\n" + "}\n", + Style); + + // FIXED - https://github.com/llvm/llvm-project/issues/54354 + // Summary: The comment and array elements do not get indented consistently + // Note: The issue does not contain any settings, so these are a guess + Style = getLLVMStyleWithColumns(120); + Style.AlignArrayOfStructures = FormatStyle::AIAS_Right; + Style.AllowAllArgumentsOnNextLine = false; + Style.ContinuationIndentWidth = 1; + Style.TabWidth = 1; + Style.LineEnding = FormatStyle::LE_LF; + Style.UseTab = FormatStyle::UT_ForContinuationAndIndentation; + verifyFormat("std::vector<std::tuple<int, int, int>> input{\n" + "\t// Node a, node b, weightage\n" + "\t{1, 2, 5},\n" + "\t{1, 3, 3},\n" + "\t{1, 4, 7},\n" + "\t{2, 4, 3},\n" + "\t{2, 5, 2},\n" + "\t{3, 4, 1},\n" + "\t{4, 5, 2}\n" + "};\n", + Style); + + // FIXED - https://github.com/llvm/llvm-project/issues/51766 + // Summary: Single row does not have it's columns aligned nicely (they bin + // pack??) + Style = getMicrosoftStyle(FormatStyle::LK_Cpp); + Style.AlignArrayOfStructures = FormatStyle::AIAS_Right; + verifyFormat("LZ4F_preferences_t prefs = {\n" + " {\n" + " LZ4F_default,\n" + " LZ4F_blockLinked,\n" + " LZ4F_contentChecksumEnabled,\n" + " LZ4F_frame,\n" + " cbSource,\n" + " 0,\n" + " LZ4F_blockChecksumEnabled,\n" + " }\n" + "};\n", + Style); + + verifyFormat("class Derived : Base\n" + "{\n" + " void foo()\n" + " {\n" + "#ifndef _MSC_VER\n" + " const Object object = {\n" + " {\n" + " SOME_MACRO,\n" + " { opt,\n" + " someMember }\n" + " }\n" + " };\n" + "#else\n" + "#endif\n" + " }\n" + "};\n", + Style); + + // FIXED - https://github.com/llvm/llvm-project/issues/86439 + // Summary: Alignment is messed up (appears to be because UseTab: Always) + Style = getMicrosoftStyle(FormatStyle::LK_Cpp); + Style.IndentWidth = 4; + Style.ColumnLimit = 100; + Style.SpacesBeforeTrailingComments = 2; + Style.BreakBeforeBraces = FormatStyle::BS_Allman; + Style.BinPackArguments = false; + Style.BinPackParameters = FormatStyle::BPPS_OnePerLine; + Style.PackConstructorInitializers = FormatStyle::PCIS_Never; + Style.AlignArrayOfStructures = FormatStyle::AIAS_Left; + Style.AlignAfterOpenBracket = FormatStyle::BAS_BlockIndent; + Style.ContinuationIndentWidth = 4; + Style.Cpp11BracedListStyle = true; + Style.UseTab = FormatStyle::UT_Always; + + verifyFormat("struct test demo[] = {\n" + "\t{56, 23,\t\" hello \"},\n" + "\t{-1, 93463, \" world \"},\n" + "\t{7,\t 5,\t\t\" !!\"\t }\n" + "};\n", + Style); + + // FIXED - https://github.com/llvm/llvm-project/issues/142072 + // Summary: Closing brace is aligned with the return, but should be with the + // opening brace + Style = getLLVMStyleWithColumns(100); + Style.BreakBeforeBraces = FormatStyle::BS_Allman; + Style.IndentWidth = 4; + Style.UseTab = FormatStyle::UT_Never; + Style.AlignAfterOpenBracket = FormatStyle::BAS_AlwaysBreak; + Style.BinPackArguments = false; + Style.BinPackParameters = FormatStyle::BPPS_OnePerLine; + Style.ContinuationIndentWidth = 4; + Style.PenaltyReturnTypeOnItsOwnLine = 1000; + Style.AlignArrayOfStructures = FormatStyle::AIAS_Left; + Style.PointerAlignment = FormatStyle::PAS_Left; + verifyFormat("int test(int argc, char* argv[])\n" + "{\n" + " return dispatch(\n" + " argc,\n" + " argv,\n" + " {\n" + " {\"option1\", &test_option1},\n" + " {\"option2\", &test_option2}\n" + " });\n" + "}\n", + Style); + + // FIXED - https://github.com/llvm/llvm-project/issues/89721 + // Summary: Really long columns mess up alignment for subsequent columns + Style = getLLVMStyleWithColumns(120); + Style.AlignAfterOpenBracket = FormatStyle::BAS_Align; + Style.AlignArrayOfStructures = FormatStyle::AIAS_Left; + Style.AlignConsecutiveAssignments.Enabled = true; + Style.BinPackArguments = false; + Style.BinPackParameters = FormatStyle::BPPS_OnePerLine; + Style.BreakArrays = true; + Style.TabWidth = 4; + Style.UseTab = FormatStyle::UT_Never; + verifyFormat(R"(const auto test_cases = std::vector<test_configuration>{ + { + "some_long_path/some_folder/111111/some_other_folder/another_folder/11111111/" + "some_really_long_file_name.bin", + 111e1, + -1111111, + 1.11e1, + 1111111 + }, + { + "some_long_path/some_folder/111111/some_other_folder/another_folder/11111111/" + "some_other_really_long_file_name.bin", + 111e1, + 11.1111e1, + 11.11e1, + 11111111.11 + }, + {"short_folder/folder/other/some_long_file_name.bin", 111111111.111111, 1111111, 111111, 111111}, +}; +)", + Style); + + verifyFormat(R"(const auto test_cases = std::vector<test_configuration>{ + { + .filepath_suffix = "some_long_path/some_folder/111111/some_other_folder/another_folder/11111111/" + "some_really_long_file_name.bin", + .double1 = 111e1, + .double2 = -1111111, + .double3 = 1.11e1, + .double4 = 1111111, + }, + { + .filepath_suffix = "some_long_path/some_folder/111111/some_other_folder/another_folder/11111111/" + "some_other_really_long_file_name.bin", + .double1 = 111e1, + .double2 = 11.1111e1, + .double3 = 11.11e1, + .double4 = 11111111.11, + }, + { + .filepath_suffix = "short_folder/folder/other/some_long_file_name.bin", + .double1 = 111111111.111111, + .double2 = 1111111, + .double3 = 111111, + .double4 = 111111, + }, +}; +)", + Style); } TEST_F(FormatTest, CatchAlignArrayOfStructuresLeftAlignment) { auto Style = getLLVMStyle(); Style.AlignArrayOfStructures = FormatStyle::AIAS_Left; - /* FIXME: This case gets misformatted. verifyFormat("auto foo = Items{\n" - " Section{0, bar(), },\n" - " Section{1, boo() }\n" + " Section{\n" + " 0,\n" + " bar(),\n" + " },\n" + " Section{1, boo()}\n" "};", Style); - */ + verifyFormat("auto foo = Items{\n" " Section{\n" - " 0, bar(),\n" - " }\n" + " 0,\n" + " bar(),\n" + " }\n" + "};", + Style); + + verifyFormat("auto foo = Items{\n" + " Section{0, barbar()},\n" + " Section{1, boo() }\n" + "};", + Style); + + verifyFormat("auto foo = Items{\n" + " Section{0, bar()}\n" "};", Style); + verifyFormat("struct test demo[] = {\n" " {56, 23, \"hello\"},\n" " {-1, 93463, \"world\"},\n" @@ -22376,16 +22906,37 @@ TEST_F(FormatTest, CatchAlignArrayOfStructuresLeftAlignment) { " {7, 5, \"!!\" }\n" "};", Style); + verifyFormat( "test demo[] = {\n" - " {7, 23,\n" - " \"hello world i am a very long line that really, in any\"\n" - " \"just world, ought to be split over multiple lines\"},\n" - " {-1, 93463, \"world\" },\n" - " {56, 5, \"!!\" }\n" + " {\n" + " 7,\n" + " 23,\n" + " \"hello world i am a very long line that really, in any\"\n" + " \"just world, ought to be split over multiple lines\"\n" + " },\n" + " {-1, 93463, \"world\"},\n" + " {56, 5, \"!!\" }\n" "};", Style); + Style = getLLVMStyleWithColumns(100); + Style.AlignArrayOfStructures = FormatStyle::AIAS_Left; + verifyFormat(R"(test demo[] = { + { + 4, + 5, + [](int a) { + const char *x = "djdjdjdjdjdjdjdjdjdj"; + return x; + } + }, + {6, -442, "asd" }, + {14124, 4324234, "dasdoijoiajsodijoaisjodijoaisjdoijoaijsd"} +}; +)", + Style); + verifyNoCrash("Foo f[] = {\n" " [0] = { 1, },\n" " [i] { 1, },\n" @@ -22409,7 +22960,7 @@ TEST_F(FormatTest, CatchAlignArrayOfStructuresLeftAlignment) { "\"dy\"} },\n" " {{\"dx\"}, \"Mul\", " "{\"dy\", \"sign\"}},\n" - "});", + " });", Style); Style.AlignEscapedNewlines = FormatStyle::ENAS_DontAlign; @@ -22430,17 +22981,12 @@ TEST_F(FormatTest, CatchAlignArrayOfStructuresLeftAlignment) { Style.ColumnLimit = 0; verifyFormat( - "test demo[] = {\n" - " {56, 23, \"hello world i am a very long line that really, in any " - "just world, ought to be split over multiple lines\"},\n" - " {-1, 93463, \"world\" " - " },\n" - " {7, 5, \"!!\" " - " },\n" - "};", - "test demo[] = {{56, 23, \"hello world i am a very long line " - "that really, in any just world, ought to be split over multiple " - "lines\"},{-1, 93463, \"world\"},{7, 5, \"!!\"},};", + R"(test demo[] = { + {56, 23, "hello world i am a very long line that really, in any just world, ought to be split over multiple lines"}, + {-1, 93463, "world" }, + {7, 5, "!!" }, +};)", + R"(test demo[] = {{56, 23, "hello world i am a very long line that really, in any just world, ought to be split over multiple lines"},{-1, 93463, "world"},{7, 5, "!!"},};)", Style); Style.ColumnLimit = 80; @@ -22476,25 +23022,33 @@ TEST_F(FormatTest, CatchAlignArrayOfStructuresLeftAlignment) { "demo = std::array<\n" " struct test, 3>{\n" " test{\n" - " 56, 23,\n" - " \"hello \"\n" - " \"world i \"\n" - " \"am a very \"\n" - " \"long line \"\n" - " \"that \"\n" - " \"really, \"\n" - " \"in any \"\n" - " \"just \"\n" - " \"world, \"\n" - " \"ought to \"\n" - " \"be split \"\n" - " \"over \"\n" - " \"multiple \"\n" - " \"lines\"},\n" - " test{-1, 93463,\n" - " \"world\"},\n" - " test{7, 5,\n" - " \"!!\" },\n" + " 56,\n" + " 23,\n" + " \"hello \"\n" + " \"world i \"\n" + " \"am a very \"\n" + " \"long line \"\n" + " \"that \"\n" + " \"really, \"\n" + " \"in any \"\n" + " \"just \"\n" + " \"world, \"\n" + " \"ought to \"\n" + " \"be split \"\n" + " \"over \"\n" + " \"multiple \"\n" + " \"lines\"\n" + " },\n" + " test{\n" + " -1,\n" + " 93463,\n" + " \"world\"\n" + " },\n" + " test{\n" + " 7,\n" + " 5,\n" + " \"!!\"\n" + " },\n" "};", format("demo = std::array<struct test, 3>{test{56, 23, \"hello world " "i am a very long line that really, in any just world, ought " @@ -22506,17 +23060,22 @@ TEST_F(FormatTest, CatchAlignArrayOfStructuresLeftAlignment) { Style = getLLVMStyleWithColumns(50); Style.AlignArrayOfStructures = FormatStyle::AIAS_Left; verifyFormat("static A x = {\n" - " {{init1, init2, init3, init4},\n" - " {init1, init2, init3, init4}}\n" + " {\n" + " {init1, init2, init3, init4},\n" + " {init1, init2, init3, init4}\n" + " }\n" "};", Style); Style.ColumnLimit = 100; verifyFormat( "test demo[] = {\n" - " {56, 23,\n" - " \"hello world i am a very long line that really, in any just world" - ", ought to be split over \"\n" - " \"multiple lines\" },\n" + " {\n" + " 56,\n" + " 23,\n" + " \"hello world i am a very long line that really, in any just " + "world, ought to be split over \"\n" + " \"multiple lines\"\n" + " },\n" " {-1, 93463, \"world\"},\n" " {7, 5, \"!!\" },\n" "};", @@ -27955,51 +28514,84 @@ TEST_F(FormatTest, AlignArrayOfStructuresLeftAlignmentNonSquare) { // crashes, these tests assert that the array is not changed but will // also act as regression tests for when it is properly fixed verifyFormat("struct test demo[] = {\n" - " {1, 2},\n" + " {1, 2 },\n" " {3, 4, 5},\n" " {6, 7, 8}\n" "};", Style); verifyFormat("struct test demo[] = {\n" " {1, 2, 3, 4, 5},\n" - " {3, 4, 5},\n" - " {6, 7, 8}\n" + " {3, 4, 5 },\n" + " {6, 7, 8 }\n" "};", Style); verifyFormat("struct test demo[] = {\n" - " {1, 2, 3, 4, 5},\n" - " {3, 4, 5},\n" + " {1, 2, 3, 4, 5 },\n" + " {3, 4, 5 },\n" " {6, 7, 8, 9, 10, 11, 12}\n" "};", Style); verifyFormat("struct test demo[] = {\n" - " {1, 2, 3},\n" - " {3, 4, 5},\n" + " {1, 2, 3 },\n" + " {3, 4, 5 },\n" " {6, 7, 8, 9, 10, 11, 12}\n" "};", Style); verifyFormat("S{\n" - " {},\n" - " {},\n" + " { },\n" + " { },\n" " {a, b}\n" "};", Style); verifyFormat("S{\n" - " {},\n" - " {},\n" + " { },\n" + " { },\n" " {a, b},\n" "};", Style); verifyFormat("void foo() {\n" " auto thing = test{\n" " {\n" - " {13},\n" - " {something}, // A\n" + " {13},\n" + " {something}, // A\n" " }\n" " };\n" "}", Style); + verifyFormat("test demo::a[] = {\n" + " {abcde, foo(\"something\"), extra_bit},\n" + " {\n" + " ab,\n" + " foo(\"something else\"),\n" + " },\n" + " {\n" + " abc,\n" + " foo(\"\"),\n" + " }\n" + "};", + Style); + verifyFormat("struct test demo = {\n" + " {abcde, foo(\"something\"), extra_bit},\n" + " {ab, foo(\"something else\") },\n" + " {abc, foo(\"\") }\n" + "};", + Style); + verifyFormat("struct test demo = {\n" + " {abcde, foo(\"something\"), extra_bit},\n" + " {ab, foo(\"something else\") },\n" + " {ad, foo(\"something else\"), extra },\n" + " {abc, foo(\"\") }\n" + "};", + Style); + verifyFormat("struct test demo = {\n" + " {\n" + " very_long_identifier_number_1,\n" + " shorter_identifier,\n" + " very_long_identifier_number_3,\n" + " }\n" + "};", + Style); } TEST_F(FormatTest, AlignArrayOfStructuresRightAlignmentNonSquare) { @@ -28012,51 +28604,84 @@ TEST_F(FormatTest, AlignArrayOfStructuresRightAlignmentNonSquare) { // crashes, these tests assert that the array is not changed but will // also act as regression tests for when it is properly fixed verifyFormat("struct test demo[] = {\n" - " {1, 2},\n" + " {1, 2 },\n" " {3, 4, 5},\n" " {6, 7, 8}\n" "};", Style); verifyFormat("struct test demo[] = {\n" " {1, 2, 3, 4, 5},\n" - " {3, 4, 5},\n" - " {6, 7, 8}\n" + " {3, 4, 5 },\n" + " {6, 7, 8 }\n" "};", Style); verifyFormat("struct test demo[] = {\n" - " {1, 2, 3, 4, 5},\n" - " {3, 4, 5},\n" + " {1, 2, 3, 4, 5 },\n" + " {3, 4, 5 },\n" " {6, 7, 8, 9, 10, 11, 12}\n" "};", Style); verifyFormat("struct test demo[] = {\n" - " {1, 2, 3},\n" - " {3, 4, 5},\n" + " {1, 2, 3 },\n" + " {3, 4, 5 },\n" " {6, 7, 8, 9, 10, 11, 12}\n" "};", Style); verifyFormat("S{\n" - " {},\n" - " {},\n" + " { },\n" + " { },\n" " {a, b}\n" "};", Style); verifyFormat("S{\n" - " {},\n" - " {},\n" + " { },\n" + " { },\n" " {a, b},\n" "};", Style); verifyFormat("void foo() {\n" " auto thing = test{\n" " {\n" - " {13},\n" - " {something}, // A\n" + " {13},\n" + " {something}, // A\n" " }\n" " };\n" "}", Style); + verifyFormat("test demo::a[] = {\n" + " {abcde, foo(\"something\"), extra_bit},\n" + " {\n" + " ab,\n" + " foo(\"something else\"),\n" + " },\n" + " {\n" + " abc,\n" + " foo(\"\"),\n" + " }\n" + "};", + Style); + verifyFormat("struct test demo = {\n" + " {abcde, foo(\"something\"), extra_bit},\n" + " { ab, foo(\"something else\") },\n" + " { abc, foo(\"\") }\n" + "};", + Style); + verifyFormat("struct test demo = {\n" + " {abcde, foo(\"something\"), extra_bit},\n" + " { ab, foo(\"something else\") },\n" + " { ad, foo(\"something else\"), extra},\n" + " { abc, foo(\"\") }\n" + "};", + Style); + verifyFormat("struct test demo = {\n" + " {\n" + " very_long_identifier_number_1,\n" + " shorter_identifier,\n" + " very_long_identifier_number_3,\n" + " }\n" + "};", + Style); } TEST_F(FormatTest, FormatsVariableTemplates) { _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits