[clang] [clang-format] Allow array alignment on non-rectangular arrays (PR #143781)
Ben Dunkin via cfe-commits
cfe-commits at lists.llvm.org
Mon Aug 11 11:15:44 PDT 2025
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 <bdunkin at 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) {
More information about the cfe-commits
mailing list