[clang] 673c5ba - [clang-format] Adds a formatter for aligning arrays of structs
Björn Schäpers via cfe-commits
cfe-commits at lists.llvm.org
Sun Jun 13 12:14:49 PDT 2021
Author: Fred Grim
Date: 2021-06-13T21:14:37+02:00
New Revision: 673c5ba58497298a684f8b8dfddbfb11cd89950e
URL: https://github.com/llvm/llvm-project/commit/673c5ba58497298a684f8b8dfddbfb11cd89950e
DIFF: https://github.com/llvm/llvm-project/commit/673c5ba58497298a684f8b8dfddbfb11cd89950e.diff
LOG: [clang-format] Adds a formatter for aligning arrays of structs
This adds a new formatter to arrange array of struct initializers into
neat columns.
Differential Revision: https://reviews.llvm.org/D101868
Added:
clang/test/Format/struct-array-initializer.cpp
Modified:
clang/docs/ClangFormatStyleOptions.rst
clang/docs/ReleaseNotes.rst
clang/include/clang/Format/Format.h
clang/lib/Format/Format.cpp
clang/lib/Format/FormatToken.h
clang/lib/Format/TokenAnnotator.cpp
clang/lib/Format/TokenAnnotator.h
clang/lib/Format/WhitespaceManager.cpp
clang/lib/Format/WhitespaceManager.h
clang/unittests/Format/FormatTest.cpp
Removed:
################################################################################
diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst
index 9686830c3bd3e..0d0c07fa350fd 100644
--- a/clang/docs/ClangFormatStyleOptions.rst
+++ b/clang/docs/ClangFormatStyleOptions.rst
@@ -204,6 +204,41 @@ the configuration (without a prefix: ``Auto``).
+**AlignArrayOfStructures** (``ArrayInitializerAlignmentStyle``)
+ if not ``None``, when using initialization for an array of structs
+ aligns the fields into columns.
+
+ Possible values:
+
+ * ``AIAS_Left`` (in configuration: ``Left``)
+ Align array column and left justify the columns e.g.:
+
+ .. code-block:: c++
+
+ struct test demo[] =
+ {
+ {56, 23, "hello"},
+ {-1, 93463, "world"},
+ {7, 5, "!!" }
+ };
+
+ * ``AIAS_Right`` (in configuration: ``Right``)
+ Align array column and right justify the columns e.g.:
+
+ .. code-block:: c++
+
+ struct test demo[] =
+ {
+ {56, 23, "hello"},
+ {-1, 93463, "world"},
+ { 7, 5, "!!"}
+ };
+
+ * ``AIAS_None`` (in configuration: ``None``)
+ Don't align array initializer columns.
+
+
+
**AlignConsecutiveAssignments** (``AlignConsecutiveStyle``)
Style of aligning consecutive assignments.
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index f5835dd2f7c63..bddaea6e48461 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -265,6 +265,9 @@ clang-format
- Makes ``PointerAligment: Right`` working with ``AlignConsecutiveDeclarations``.
(Fixes https://llvm.org/PR27353)
+- Option ``AlignArrayOfStructure`` has been added to allow for ordering array-like
+ initializers.
+
libclang
--------
diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h
index 503f0e446bff3..164765ca1a1a3 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -90,6 +90,35 @@ struct FormatStyle {
/// brackets.
BracketAlignmentStyle AlignAfterOpenBracket;
+ /// Different style for aligning array initializers.
+ enum ArrayInitializerAlignmentStyle {
+ /// Align array column and left justify the columns e.g.:
+ /// \code
+ /// struct test demo[] =
+ /// {
+ /// {56, 23, "hello"},
+ /// {-1, 93463, "world"},
+ /// {7, 5, "!!" }
+ /// };
+ /// \endcode
+ AIAS_Left,
+ /// Align array column and right justify the columns e.g.:
+ /// \code
+ /// struct test demo[] =
+ /// {
+ /// {56, 23, "hello"},
+ /// {-1, 93463, "world"},
+ /// { 7, 5, "!!"}
+ /// };
+ /// \endcode
+ AIAS_Right,
+ /// Don't align array initializer columns.
+ AIAS_None
+ };
+ /// if not ``None``, when using initialization for an array of structs
+ /// aligns the fields into columns.
+ ArrayInitializerAlignmentStyle AlignArrayOfStructures;
+
/// Styles for alignment of consecutive tokens. Tokens can be assignment signs
/// (see
/// ``AlignConsecutiveAssignments``), bitfield member separators (see
@@ -3272,6 +3301,7 @@ struct FormatStyle {
bool operator==(const FormatStyle &R) const {
return AccessModifierOffset == R.AccessModifierOffset &&
AlignAfterOpenBracket == R.AlignAfterOpenBracket &&
+ AlignArrayOfStructures == R.AlignArrayOfStructures &&
AlignConsecutiveAssignments == R.AlignConsecutiveAssignments &&
AlignConsecutiveBitFields == R.AlignConsecutiveBitFields &&
AlignConsecutiveDeclarations == R.AlignConsecutiveDeclarations &&
diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index 8d16f299d3aef..53cbbf66e85ab 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -143,6 +143,16 @@ template <> struct ScalarEnumerationTraits<FormatStyle::AlignConsecutiveStyle> {
}
};
+template <>
+struct ScalarEnumerationTraits<FormatStyle::ArrayInitializerAlignmentStyle> {
+ static void enumeration(IO &IO,
+ FormatStyle::ArrayInitializerAlignmentStyle &Value) {
+ IO.enumCase(Value, "None", FormatStyle::AIAS_None);
+ IO.enumCase(Value, "Left", FormatStyle::AIAS_Left);
+ IO.enumCase(Value, "Right", FormatStyle::AIAS_Right);
+ }
+};
+
template <> struct ScalarEnumerationTraits<FormatStyle::ShortIfStyle> {
static void enumeration(IO &IO, FormatStyle::ShortIfStyle &Value) {
IO.enumCase(Value, "Never", FormatStyle::SIS_Never);
@@ -507,6 +517,7 @@ template <> struct MappingTraits<FormatStyle> {
IO.mapOptional("AccessModifierOffset", Style.AccessModifierOffset);
IO.mapOptional("AlignAfterOpenBracket", Style.AlignAfterOpenBracket);
+ IO.mapOptional("AlignArrayOfStructures", Style.AlignArrayOfStructures);
IO.mapOptional("AlignConsecutiveMacros", Style.AlignConsecutiveMacros);
IO.mapOptional("AlignConsecutiveAssignments",
Style.AlignConsecutiveAssignments);
@@ -943,6 +954,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
LLVMStyle.AccessModifierOffset = -2;
LLVMStyle.AlignEscapedNewlines = FormatStyle::ENAS_Right;
LLVMStyle.AlignAfterOpenBracket = FormatStyle::BAS_Align;
+ LLVMStyle.AlignArrayOfStructures = FormatStyle::AIAS_None;
LLVMStyle.AlignOperands = FormatStyle::OAS_Align;
LLVMStyle.AlignTrailingComments = true;
LLVMStyle.AlignConsecutiveAssignments = FormatStyle::ACS_None;
diff --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h
index 2a06b6f2f7e6b..57c5eeb5a091f 100644
--- a/clang/lib/Format/FormatToken.h
+++ b/clang/lib/Format/FormatToken.h
@@ -431,6 +431,15 @@ struct FormatToken {
/// The next token in the unwrapped line.
FormatToken *Next = nullptr;
+ /// 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;
+
/// If this token starts a block, this contains all the unwrapped lines
/// in it.
SmallVector<AnnotatedLine *, 1> Children;
diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp
index d08c991daf43f..750839c57c162 100755
--- a/clang/lib/Format/TokenAnnotator.cpp
+++ b/clang/lib/Format/TokenAnnotator.cpp
@@ -729,6 +729,21 @@ class AnnotatingParser {
return false;
}
+ bool couldBeInStructArrayInitializer() const {
+ if (Contexts.size() < 2)
+ return false;
+ // We want to back up no more then 2 context levels i.e.
+ // . { { <-
+ const auto End = std::next(Contexts.rbegin(), 2);
+ auto Last = Contexts.rbegin();
+ unsigned Depth = 0;
+ for (; Last != End; ++Last) {
+ if (Last->ContextKind == tok::l_brace)
+ ++Depth;
+ }
+ return Depth == 2 && Last->ContextKind != tok::l_brace;
+ }
+
bool parseBrace() {
if (CurrentToken) {
FormatToken *Left = CurrentToken->Previous;
@@ -746,10 +761,17 @@ class AnnotatingParser {
Left->Previous->is(TT_JsTypeColon))
Contexts.back().IsExpression = false;
+ unsigned CommaCount = 0;
while (CurrentToken) {
if (CurrentToken->is(tok::r_brace)) {
Left->MatchingParen = CurrentToken;
CurrentToken->MatchingParen = Left;
+ if (Style.AlignArrayOfStructures != FormatStyle::AIAS_None) {
+ if (Left->ParentBracket == tok::l_brace &&
+ couldBeInStructArrayInitializer() && CommaCount > 0) {
+ Contexts.back().InStructArrayInitializer = true;
+ }
+ }
next();
return true;
}
@@ -773,9 +795,11 @@ class AnnotatingParser {
Style.Language == FormatStyle::LK_JavaScript)
Left->setType(TT_DictLiteral);
}
- if (CurrentToken->is(tok::comma) &&
- Style.Language == FormatStyle::LK_JavaScript)
- Left->setType(TT_DictLiteral);
+ if (CurrentToken->is(tok::comma)) {
+ if (Style.Language == FormatStyle::LK_JavaScript)
+ Left->setType(TT_DictLiteral);
+ ++CommaCount;
+ }
if (!consumeToken())
return false;
}
@@ -1339,6 +1363,12 @@ class AnnotatingParser {
return LT_ObjCMethodDecl;
}
+ for (const auto &ctx : Contexts) {
+ if (ctx.InStructArrayInitializer) {
+ return LT_ArrayOfStructInitializer;
+ }
+ }
+
return LT_Other;
}
@@ -1414,6 +1444,7 @@ class AnnotatingParser {
bool IsForEachMacro = false;
bool InCpp11AttributeSpecifier = false;
bool InCSharpAttributeSpecifier = false;
+ bool InStructArrayInitializer = false;
};
/// Puts a new \c Context onto the stack \c Contexts for the lifetime
@@ -1429,7 +1460,16 @@ class AnnotatingParser {
P.Contexts.back().IsExpression));
}
- ~ScopedContextCreator() { P.Contexts.pop_back(); }
+ ~ScopedContextCreator() {
+ if (P.Style.AlignArrayOfStructures != FormatStyle::AIAS_None) {
+ if (P.Contexts.back().InStructArrayInitializer) {
+ P.Contexts.pop_back();
+ P.Contexts.back().InStructArrayInitializer = true;
+ return;
+ }
+ }
+ P.Contexts.pop_back();
+ }
};
void modifyContext(const FormatToken &Current) {
@@ -2473,6 +2513,12 @@ void TokenAnnotator::calculateFormattingInformation(AnnotatedLine &Line) {
: Line.FirstStartColumn + Line.First->ColumnWidth;
FormatToken *Current = Line.First->Next;
bool InFunctionDecl = Line.MightBeFunctionDecl;
+ bool AlignArrayOfStructures =
+ (Style.AlignArrayOfStructures != FormatStyle::AIAS_None &&
+ Line.Type == LT_ArrayOfStructInitializer);
+ if (AlignArrayOfStructures)
+ calculateArrayInitializerColumnList(Line);
+
while (Current) {
if (isFunctionDeclarationName(*Current, Line))
Current->setType(TT_FunctionDeclarationName);
@@ -2592,6 +2638,45 @@ void TokenAnnotator::calculateUnbreakableTailLengths(AnnotatedLine &Line) {
}
}
+void TokenAnnotator::calculateArrayInitializerColumnList(AnnotatedLine &Line) {
+ if (Line.First == Line.Last) {
+ return;
+ }
+ auto *CurrentToken = Line.First;
+ CurrentToken->ArrayInitializerLineStart = true;
+ unsigned Depth = 0;
+ while (CurrentToken != nullptr && CurrentToken != Line.Last) {
+ if (CurrentToken->is(tok::l_brace)) {
+ CurrentToken->IsArrayInitializer = true;
+ if (CurrentToken->Next != nullptr)
+ CurrentToken->Next->MustBreakBefore = true;
+ CurrentToken =
+ calculateInitializerColumnList(Line, CurrentToken->Next, Depth + 1);
+ } else {
+ CurrentToken = CurrentToken->Next;
+ }
+ }
+}
+
+FormatToken *TokenAnnotator::calculateInitializerColumnList(
+ AnnotatedLine &Line, FormatToken *CurrentToken, unsigned Depth) {
+ while (CurrentToken != nullptr && CurrentToken != Line.Last) {
+ if (CurrentToken->is(tok::l_brace))
+ ++Depth;
+ else if (CurrentToken->is(tok::r_brace))
+ --Depth;
+ if (Depth == 2 && CurrentToken->isOneOf(tok::l_brace, tok::comma)) {
+ CurrentToken = CurrentToken->Next;
+ if (CurrentToken == nullptr)
+ break;
+ CurrentToken->StartsColumn = true;
+ CurrentToken = CurrentToken->Previous;
+ }
+ CurrentToken = CurrentToken->Next;
+ }
+ return CurrentToken;
+}
+
unsigned TokenAnnotator::splitPenalty(const AnnotatedLine &Line,
const FormatToken &Tok,
bool InFunctionDecl) {
diff --git a/clang/lib/Format/TokenAnnotator.h b/clang/lib/Format/TokenAnnotator.h
index 537710029b00a..7f2dff561e7b4 100644
--- a/clang/lib/Format/TokenAnnotator.h
+++ b/clang/lib/Format/TokenAnnotator.h
@@ -31,7 +31,8 @@ enum LineType {
LT_ObjCProperty, // An @property line.
LT_Other,
LT_PreprocessorDirective,
- LT_VirtualFunctionDecl
+ LT_VirtualFunctionDecl,
+ LT_ArrayOfStructInitializer,
};
class AnnotatedLine {
@@ -189,6 +190,12 @@ class TokenAnnotator {
void calculateUnbreakableTailLengths(AnnotatedLine &Line);
+ void calculateArrayInitializerColumnList(AnnotatedLine &Line);
+
+ FormatToken *calculateInitializerColumnList(AnnotatedLine &Line,
+ FormatToken *CurrentToken,
+ unsigned Depth);
+
const FormatStyle &Style;
const AdditionalKeywords &Keywords;
diff --git a/clang/lib/Format/WhitespaceManager.cpp b/clang/lib/Format/WhitespaceManager.cpp
index 515cb7941e785..222c4c4e8e29a 100644
--- a/clang/lib/Format/WhitespaceManager.cpp
+++ b/clang/lib/Format/WhitespaceManager.cpp
@@ -13,6 +13,8 @@
#include "WhitespaceManager.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallVector.h"
+#include <algorithm>
namespace clang {
namespace format {
@@ -100,6 +102,7 @@ const tooling::Replacements &WhitespaceManager::generateReplacements() {
alignChainedConditionals();
alignTrailingComments();
alignEscapedNewlines();
+ alignArrayInitializers();
generateChanges();
return Replaces;
@@ -952,6 +955,305 @@ void WhitespaceManager::alignEscapedNewlines(unsigned Start, unsigned End,
}
}
+void WhitespaceManager::alignArrayInitializers() {
+ if (Style.AlignArrayOfStructures == FormatStyle::AIAS_None)
+ return;
+
+ for (unsigned ChangeIndex = 1U, ChangeEnd = Changes.size();
+ ChangeIndex < ChangeEnd; ++ChangeIndex) {
+ auto &C = Changes[ChangeIndex];
+ if (C.Tok->IsArrayInitializer) {
+ bool FoundComplete = false;
+ for (unsigned InsideIndex = ChangeIndex + 1; InsideIndex < ChangeEnd;
+ ++InsideIndex) {
+ if (Changes[InsideIndex].Tok == C.Tok->MatchingParen) {
+ alignArrayInitializers(ChangeIndex, InsideIndex + 1);
+ ChangeIndex = InsideIndex + 1;
+ FoundComplete = true;
+ break;
+ }
+ }
+ if (!FoundComplete)
+ ChangeIndex = ChangeEnd;
+ }
+ }
+}
+
+void WhitespaceManager::alignArrayInitializers(unsigned Start, unsigned End) {
+
+ if (Style.AlignArrayOfStructures == FormatStyle::AIAS_Right)
+ alignArrayInitializersRightJustified(getCells(Start, End));
+ else if (Style.AlignArrayOfStructures == FormatStyle::AIAS_Left)
+ alignArrayInitializersLeftJustified(getCells(Start, End));
+}
+
+void WhitespaceManager::alignArrayInitializersRightJustified(
+ CellDescriptions &&CellDescs) {
+ auto &Cells = CellDescs.Cells;
+
+ // Now go through and fixup the spaces.
+ auto *CellIter = Cells.begin();
+ for (auto i = 0U; i < CellDescs.CellCount; i++, ++CellIter) {
+ unsigned NetWidth = 0U;
+ if (isSplitCell(*CellIter))
+ NetWidth = getNetWidth(Cells.begin(), CellIter, CellDescs.InitialSpaces);
+ auto CellWidth = getMaximumCellWidth(CellIter, NetWidth);
+
+ if (Changes[CellIter->Index].Tok->is(tok::r_brace)) {
+ // So in here we want to see if there is a brace that falls
+ // on a line that was split. If so on that line we make sure that
+ // the spaces in front of the brace are enough.
+ Changes[CellIter->Index].NewlinesBefore = 0;
+ Changes[CellIter->Index].Spaces = 0;
+ for (const auto *Next = CellIter->NextColumnElement; Next != nullptr;
+ Next = Next->NextColumnElement) {
+ Changes[Next->Index].Spaces = 0;
+ Changes[Next->Index].NewlinesBefore = 0;
+ }
+ // Unless the array is empty, we need the position of all the
+ // immediately adjacent cells
+ if (CellIter != Cells.begin()) {
+ auto ThisNetWidth =
+ getNetWidth(Cells.begin(), CellIter, CellDescs.InitialSpaces);
+ auto MaxNetWidth =
+ getMaximumNetWidth(Cells.begin(), CellIter, CellDescs.InitialSpaces,
+ CellDescs.CellCount);
+ if (ThisNetWidth < MaxNetWidth)
+ Changes[CellIter->Index].Spaces = (MaxNetWidth - ThisNetWidth);
+ auto RowCount = 1U;
+ auto Offset = std::distance(Cells.begin(), CellIter);
+ for (const auto *Next = CellIter->NextColumnElement; Next != nullptr;
+ Next = Next->NextColumnElement) {
+ auto *Start = (Cells.begin() + RowCount * CellDescs.CellCount);
+ auto *End = Start + Offset;
+ ThisNetWidth = getNetWidth(Start, End, CellDescs.InitialSpaces);
+ if (ThisNetWidth < MaxNetWidth)
+ Changes[Next->Index].Spaces = (MaxNetWidth - ThisNetWidth);
+ ++RowCount;
+ }
+ }
+ } else {
+ auto ThisWidth =
+ calculateCellWidth(CellIter->Index, CellIter->EndIndex, true) +
+ NetWidth;
+ if (Changes[CellIter->Index].NewlinesBefore == 0) {
+ Changes[CellIter->Index].Spaces = (CellWidth - (ThisWidth + NetWidth));
+ Changes[CellIter->Index].Spaces += (i > 0) ? 1 : 0;
+ }
+ alignToStartOfCell(CellIter->Index, CellIter->EndIndex);
+ for (const auto *Next = CellIter->NextColumnElement; Next != nullptr;
+ Next = Next->NextColumnElement) {
+ ThisWidth =
+ calculateCellWidth(Next->Index, Next->EndIndex, true) + NetWidth;
+ if (Changes[Next->Index].NewlinesBefore == 0) {
+ Changes[Next->Index].Spaces = (CellWidth - ThisWidth);
+ Changes[Next->Index].Spaces += (i > 0) ? 1 : 0;
+ }
+ alignToStartOfCell(Next->Index, Next->EndIndex);
+ }
+ }
+ }
+}
+
+void WhitespaceManager::alignArrayInitializersLeftJustified(
+ CellDescriptions &&CellDescs) {
+ auto &Cells = CellDescs.Cells;
+
+ // Now go through and fixup the spaces.
+ auto *CellIter = Cells.begin();
+ // The first cell needs to be against the left brace.
+ if (Changes[CellIter->Index].NewlinesBefore == 0)
+ Changes[CellIter->Index].Spaces = 0;
+ else
+ Changes[CellIter->Index].Spaces = CellDescs.InitialSpaces;
+ ++CellIter;
+ for (auto i = 1U; i < CellDescs.CellCount; i++, ++CellIter) {
+ unsigned NetWidth = 0U;
+ if (isSplitCell(*CellIter))
+ NetWidth = getNetWidth(Cells.begin(), CellIter, CellDescs.InitialSpaces);
+ auto MaxNetWidth = getMaximumNetWidth(
+ Cells.begin(), CellIter, CellDescs.InitialSpaces, CellDescs.CellCount);
+ auto ThisNetWidth =
+ getNetWidth(Cells.begin(), CellIter, CellDescs.InitialSpaces);
+ if (Changes[CellIter->Index].NewlinesBefore == 0) {
+ Changes[CellIter->Index].Spaces =
+ MaxNetWidth - ThisNetWidth +
+ (Changes[CellIter->Index].Tok->isNot(tok::r_brace) ? 1 : 0);
+ }
+ auto RowCount = 1U;
+ auto Offset = std::distance(Cells.begin(), CellIter);
+ for (const auto *Next = CellIter->NextColumnElement; Next != nullptr;
+ Next = Next->NextColumnElement) {
+ auto *Start = (Cells.begin() + RowCount * CellDescs.CellCount);
+ auto *End = Start + Offset;
+ auto ThisNetWidth = getNetWidth(Start, End, CellDescs.InitialSpaces);
+ if (Changes[Next->Index].NewlinesBefore == 0) {
+ Changes[Next->Index].Spaces =
+ MaxNetWidth - ThisNetWidth +
+ (Changes[Next->Index].Tok->isNot(tok::r_brace) ? 1 : 0);
+ }
+ ++RowCount;
+ }
+ }
+}
+
+bool WhitespaceManager::isSplitCell(const CellDescription &Cell) {
+ if (Cell.HasSplit)
+ return true;
+ for (const auto *Next = Cell.NextColumnElement; Next != nullptr;
+ Next = Next->NextColumnElement) {
+ if (Next->HasSplit)
+ return true;
+ }
+ return false;
+}
+
+WhitespaceManager::CellDescriptions WhitespaceManager::getCells(unsigned Start,
+ unsigned End) {
+
+ unsigned Depth = 0;
+ unsigned Cell = 0;
+ unsigned CellCount = 0;
+ unsigned InitialSpaces = 0;
+ unsigned InitialTokenLength = 0;
+ unsigned EndSpaces = 0;
+ SmallVector<CellDescription> Cells;
+ const FormatToken *MatchingParen = nullptr;
+ for (unsigned i = Start; i < End; ++i) {
+ auto &C = Changes[i];
+ if (C.Tok->is(tok::l_brace))
+ ++Depth;
+ else if (C.Tok->is(tok::r_brace))
+ --Depth;
+ if (Depth == 2) {
+ if (C.Tok->is(tok::l_brace)) {
+ Cell = 0;
+ MatchingParen = C.Tok->MatchingParen;
+ if (InitialSpaces == 0) {
+ InitialSpaces = C.Spaces + C.TokenLength;
+ InitialTokenLength = C.TokenLength;
+ auto j = i - 1;
+ for (; Changes[j].NewlinesBefore == 0 && j > Start; --j) {
+ InitialSpaces += Changes[j].Spaces + Changes[j].TokenLength;
+ InitialTokenLength += Changes[j].TokenLength;
+ }
+ if (C.NewlinesBefore == 0) {
+ InitialSpaces += Changes[j].Spaces + Changes[j].TokenLength;
+ InitialTokenLength += Changes[j].TokenLength;
+ }
+ }
+ } else if (C.Tok->is(tok::comma)) {
+ if (!Cells.empty())
+ Cells.back().EndIndex = i;
+ Cell++;
+ }
+ } else if (Depth == 1) {
+ if (C.Tok == MatchingParen) {
+ if (!Cells.empty())
+ Cells.back().EndIndex = i;
+ Cells.push_back(CellDescription{i, ++Cell, i + 1, false, nullptr});
+ CellCount = Cell + 1;
+ // Go to the next non-comment and ensure there is a break in front
+ const auto *NextNonComment = C.Tok->getNextNonComment();
+ while (NextNonComment->is(tok::comma))
+ NextNonComment = NextNonComment->getNextNonComment();
+ auto j = i;
+ while (Changes[j].Tok != NextNonComment && j < End)
+ j++;
+ if (j < End && Changes[j].NewlinesBefore == 0 &&
+ Changes[j].Tok->isNot(tok::r_brace)) {
+ Changes[j].NewlinesBefore = 1;
+ // Account for the added token lengths
+ Changes[j].Spaces = InitialSpaces - InitialTokenLength;
+ }
+ } else if (C.Tok->is(tok::comment)) {
+ // Trailing comments stay at a space past the last token
+ C.Spaces = Changes[i - 1].Tok->is(tok::comma) ? 1 : 2;
+ } else if (C.Tok->is(tok::l_brace)) {
+ // We need to make sure that the ending braces is aligned to the
+ // start of our initializer
+ auto j = i - 1;
+ for (; j > 0 && !Changes[j].Tok->ArrayInitializerLineStart; --j)
+ ; // Nothing the loop does the work
+ EndSpaces = Changes[j].Spaces;
+ }
+ } else if (Depth == 0 && C.Tok->is(tok::r_brace)) {
+ C.NewlinesBefore = 1;
+ C.Spaces = EndSpaces;
+ }
+ if (C.Tok->StartsColumn) {
+ // This gets us past tokens that have been split over multiple
+ // lines
+ bool HasSplit = false;
+ if (Changes[i].NewlinesBefore > 0) {
+ // So if we split a line previously and the tail line + this token is
+ // less then the column limit we remove the split here and just put
+ // the column start at a space past the comma
+ auto j = i - 1;
+ if ((j - 1) > Start && Changes[j].Tok->is(tok::comma) &&
+ Changes[j - 1].NewlinesBefore > 0) {
+ --j;
+ auto LineLimit = Changes[j].Spaces + Changes[j].TokenLength;
+ if (LineLimit < Style.ColumnLimit) {
+ Changes[i].NewlinesBefore = 0;
+ Changes[i].Spaces = 1;
+ }
+ }
+ }
+ while (Changes[i].NewlinesBefore > 0 && Changes[i].Tok == C.Tok) {
+ Changes[i].Spaces = InitialSpaces;
+ ++i;
+ HasSplit = true;
+ }
+ if (Changes[i].Tok != C.Tok)
+ --i;
+ Cells.push_back(CellDescription{i, Cell, i, HasSplit, nullptr});
+ }
+ }
+
+ return linkCells({Cells, CellCount, InitialSpaces});
+}
+
+unsigned WhitespaceManager::calculateCellWidth(unsigned Start, unsigned End,
+ bool WithSpaces) const {
+ unsigned CellWidth = 0;
+ for (auto i = Start; i < End; i++) {
+ if (Changes[i].NewlinesBefore > 0)
+ CellWidth = 0;
+ CellWidth += Changes[i].TokenLength;
+ CellWidth += (WithSpaces ? Changes[i].Spaces : 0);
+ }
+ return CellWidth;
+}
+
+void WhitespaceManager::alignToStartOfCell(unsigned Start, unsigned End) {
+ if ((End - Start) <= 1)
+ return;
+ // If the line is broken anywhere in there make sure everything
+ // is aligned to the parent
+ for (auto i = Start + 1; i < End; i++) {
+ if (Changes[i].NewlinesBefore > 0)
+ Changes[i].Spaces = Changes[Start].Spaces;
+ }
+}
+
+WhitespaceManager::CellDescriptions
+WhitespaceManager::linkCells(CellDescriptions &&CellDesc) {
+ auto &Cells = CellDesc.Cells;
+ for (auto *CellIter = Cells.begin(); CellIter != Cells.end(); ++CellIter) {
+ if (CellIter->NextColumnElement == nullptr &&
+ ((CellIter + 1) != Cells.end())) {
+ for (auto *NextIter = CellIter + 1; NextIter != Cells.end(); ++NextIter) {
+ if (NextIter->Cell == CellIter->Cell) {
+ CellIter->NextColumnElement = &(*NextIter);
+ break;
+ }
+ }
+ }
+ }
+ return std::move(CellDesc);
+}
+
void WhitespaceManager::generateChanges() {
for (unsigned i = 0, e = Changes.size(); i != e; ++i) {
const Change &C = Changes[i];
diff --git a/clang/lib/Format/WhitespaceManager.h b/clang/lib/Format/WhitespaceManager.h
index 1398a3aee2b89..4f8f95040af6a 100644
--- a/clang/lib/Format/WhitespaceManager.h
+++ b/clang/lib/Format/WhitespaceManager.h
@@ -18,6 +18,8 @@
#include "TokenAnnotator.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Format/Format.h"
+#include "llvm/ADT/SmallVector.h"
+#include <algorithm>
#include <string>
#include <tuple>
@@ -173,6 +175,28 @@ class WhitespaceManager {
};
private:
+ struct CellDescription {
+ unsigned Index = 0;
+ unsigned Cell = 0;
+ unsigned EndIndex = 0;
+ bool HasSplit = false;
+ CellDescription *NextColumnElement = nullptr;
+
+ constexpr bool operator==(const CellDescription &Other) const {
+ return Index == Other.Index && Cell == Other.Cell &&
+ EndIndex == Other.EndIndex;
+ }
+ constexpr bool operator!=(const CellDescription &Other) const {
+ return !(*this == Other);
+ }
+ };
+
+ struct CellDescriptions {
+ SmallVector<CellDescription> Cells;
+ unsigned CellCount = 0;
+ unsigned InitialSpaces = 0;
+ };
+
/// Calculate \c IsTrailingComment, \c TokenLength for the last tokens
/// or token parts in a line and \c PreviousEndOfTokenColumn and
/// \c EscapedNewlineColumn for the first tokens or token parts in a line.
@@ -207,6 +231,89 @@ class WhitespaceManager {
/// the specified \p Column.
void alignEscapedNewlines(unsigned Start, unsigned End, unsigned Column);
+ /// Align Array Initializers over all \c Changes.
+ void alignArrayInitializers();
+
+ /// Align Array Initializers from change \p Start to change \p End at
+ /// the specified \p Column.
+ void alignArrayInitializers(unsigned Start, unsigned End);
+
+ /// Align Array Initializers being careful to right justify the columns
+ /// as described by \p CellDescs.
+ void alignArrayInitializersRightJustified(CellDescriptions &&CellDescs);
+
+ /// Align Array Initializers being careful to leftt justify the columns
+ /// as described by \p CellDescs.
+ void alignArrayInitializersLeftJustified(CellDescriptions &&CellDescs);
+
+ /// Calculate the cell width between two indexes.
+ unsigned calculateCellWidth(unsigned Start, unsigned End,
+ bool WithSpaces = false) 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 preceeding 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.
+ 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 != nullptr;
+ 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) const {
+ auto MaxNetWidth = getNetWidth(CellStart, CellStop, InitialSpaces);
+ auto RowCount = 1U;
+ auto Offset = std::distance(CellStart, CellStop);
+ for (const auto *Next = CellStop->NextColumnElement; Next != nullptr;
+ Next = Next->NextColumnElement) {
+ 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);
+
/// Fill \c Replaces with the replacements for all effective changes.
void generateChanges();
diff --git a/clang/test/Format/struct-array-initializer.cpp b/clang/test/Format/struct-array-initializer.cpp
new file mode 100644
index 0000000000000..2406dbaded6c5
--- /dev/null
+++ b/clang/test/Format/struct-array-initializer.cpp
@@ -0,0 +1,60 @@
+// RUN: grep -Ev "// *[A-Z-]+:" %s \
+// RUN: | clang-format -style="{BasedOnStyle: LLVM, AlignArrayOfStructures: Right}" %s \
+// RUN: | FileCheck -strict-whitespace -check-prefix=CHECK1 %s
+// RUN: grep -Ev "// *[A-Z-]+:" %s \
+// RUN: | clang-format -style="{BasedOnStyle: LLVM, AlignArrayOfStructures: Left}" %s \
+// RUN: | FileCheck -strict-whitespace -check-prefix=CHECK2 %s
+struct test {
+ int a;
+ int b;
+ const char *c;
+};
+
+struct toast {
+ int a;
+ const char *b;
+ int c;
+ float d;
+};
+
+void f() {
+ struct test demo[] = {{56, 23, "hello"}, {-1, 93463, "world"}, {7, 5, "!!"}};
+ // CHECK1: {{^[[:space:]]{2}struct test demo\[\] = \{$}}
+ // CHECK1-NEXT: {{([[:space:]]{4})}}{56, 23, "hello"},
+ // CHECK1-NEXT: {{([[:space:]]{4})}}{-1, 93463, "world"},
+ // CHECK1-NEXT: {{([[:space:]]{4})}}{ 7, 5, "!!"}
+ // CHECK1-NEXT: {{^[[:space:]]{2}\};$}}
+}
+
+void g() {
+ struct toast demo[] = {
+ {56, "hello world I have some things to say", 30, 4.2},
+ {93463, "those things are really comments", 1, 3.1},
+ {7, "about a wide range of topics", 789, .112233}};
+ // CHECK1: {{^[[:space:]]{2}struct toast demo\[\] = \{$}}
+ // CHECK1-NEXT: {{([[:space:]]{4})}}{ 56, "hello world I have some things to say", 30, 4.2},
+ // CHECK1-NEXT: {{([[:space:]]{4})}}{93463, "those things are really comments", 1, 3.1},
+ // CHECK1-NEXT: {{([[:space:]]{4})}}{ 7, "about a wide range of topics", 789, .112233}
+ // CHECK1-NEXT: {{^[[:space:]]{2}\};$}}
+}
+
+void h() {
+ struct test demo[] = {{56, 23, "hello"}, {-1, 93463, "world"}, {7, 5, "!!"}};
+ // CHECK2: {{^[[:space:]]{2}struct test demo\[\] = \{$}}
+ // CHECK2-NEXT: {{([[:space:]]{4})}}{56, 23, "hello"},
+ // CHECK2-NEXT: {{([[:space:]]{4})}}{-1, 93463, "world"},
+ // CHECK2-NEXT: {{([[:space:]]{4})}}{7, 5, "!!" }
+ // CHECK2-NEXT: {{^[[:space:]]{2}\};$}}
+}
+
+void i() {
+ struct toast demo[] = {
+ {56, "hello world I have some things to say", 30, 4.2},
+ {93463, "those things are really comments", 1, 3.1},
+ {7, "about a wide range of topics", 789, .112233}};
+ // CHECK2: {{^[[:space:]]{2}struct toast demo\[\] = \{$}}
+ // CHECK2-NEXT: {{([[:space:]]{4})}}{56, "hello world I have some things to say", 30, 4.2 },
+ // CHECK2-NEXT: {{([[:space:]]{4})}}{93463, "those things are really comments", 1, 3.1 },
+ // CHECK2-NEXT: {{([[:space:]]{4})}}{7, "about a wide range of topics", 789, .112233}
+ // CHECK2-NEXT: {{^[[:space:]]{2}\};$}}
+}
diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index 5ea4fcc9ffe36..279f2b3119fc9 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -16602,6 +16602,407 @@ TEST_F(FormatTest, CatchExceptionReferenceBinding) {
getLLVMStyle());
}
+TEST_F(FormatTest, CatchAlignArrayOfStructuresRightAlignment) {
+ auto Style = getLLVMStyle();
+ Style.AlignArrayOfStructures = FormatStyle::AIAS_Right;
+ Style.AlignConsecutiveAssignments =
+ FormatStyle::AlignConsecutiveStyle::ACS_Consecutive;
+ Style.AlignConsecutiveDeclarations =
+ FormatStyle::AlignConsecutiveStyle::ACS_Consecutive;
+ verifyFormat("struct test demo[] = {\n"
+ " {56, 23, \"hello\"},\n"
+ " {-1, 93463, \"world\"},\n"
+ " { 7, 5, \"!!\"}\n"
+ "};\n",
+ Style);
+
+ verifyFormat("struct test demo[] = {\n"
+ " {56, 23, \"hello\"}, // first line\n"
+ " {-1, 93463, \"world\"}, // second line\n"
+ " { 7, 5, \"!!\"} // third line\n"
+ "};\n",
+ Style);
+
+ verifyFormat("struct test demo[4] = {\n"
+ " { 56, 23, 21, \"oh\"}, // first line\n"
+ " { -1, 93463, 22, \"my\"}, // second line\n"
+ " { 7, 5, 1, \"goodness\"} // third line\n"
+ " {234, 5, 1, \"gracious\"} // fourth line\n"
+ "};\n",
+ Style);
+
+ verifyFormat("struct test demo[3] = {\n"
+ " {56, 23, \"hello\"},\n"
+ " {-1, 93463, \"world\"},\n"
+ " { 7, 5, \"!!\"}\n"
+ "};\n",
+ Style);
+
+ verifyFormat("struct test demo[3] = {\n"
+ " {int{56}, 23, \"hello\"},\n"
+ " {int{-1}, 93463, \"world\"},\n"
+ " { int{7}, 5, \"!!\"}\n"
+ "};\n",
+ Style);
+
+ verifyFormat("struct test demo[] = {\n"
+ " {56, 23, \"hello\"},\n"
+ " {-1, 93463, \"world\"},\n"
+ " { 7, 5, \"!!\"},\n"
+ "};\n",
+ Style);
+
+ verifyFormat("test demo[] = {\n"
+ " {56, 23, \"hello\"},\n"
+ " {-1, 93463, \"world\"},\n"
+ " { 7, 5, \"!!\"},\n"
+ "};\n",
+ Style);
+
+ verifyFormat("demo = std::array<struct test, 3>{\n"
+ " test{56, 23, \"hello\"},\n"
+ " test{-1, 93463, \"world\"},\n"
+ " test{ 7, 5, \"!!\"},\n"
+ "};\n",
+ Style);
+
+ verifyFormat("test demo[] = {\n"
+ " {56, 23, \"hello\"},\n"
+ "#if X\n"
+ " {-1, 93463, \"world\"},\n"
+ "#endif\n"
+ " { 7, 5, \"!!\"}\n"
+ "};\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",
+ Style);
+
+ verifyFormat("return GradForUnaryCwise(g, {\n"
+ " {{\"sign\"}, \"Sign\", "
+ " {\"x\", \"dy\"}},\n"
+ " { {\"dx\"}, \"Mul\", {\"dy\""
+ ", \"sign\"}},\n"
+ "});\n",
+ Style);
+
+ Style.ColumnLimit = 0;
+ EXPECT_EQ(
+ "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"
+ "};",
+ format("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;
+ verifyFormat("test demo[] = {\n"
+ " {56, 23, /* a comment */ \"hello\"},\n"
+ " {-1, 93463, \"world\"},\n"
+ " { 7, 5, \"!!\"}\n"
+ "};\n",
+ Style);
+
+ verifyFormat("test demo[] = {\n"
+ " {56, 23, \"hello\"},\n"
+ " {-1, 93463, \"world\" /* comment here */},\n"
+ " { 7, 5, \"!!\"}\n"
+ "};\n",
+ Style);
+
+ verifyFormat("test demo[] = {\n"
+ " {56, /* a comment */ 23, \"hello\"},\n"
+ " {-1, 93463, \"world\"},\n"
+ " { 7, 5, \"!!\"}\n"
+ "};\n",
+ Style);
+
+ Style.ColumnLimit = 20;
+ EXPECT_EQ(
+ "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"
+ "};",
+ 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 "
+ "to be split over multiple lines\"},test{-1, 93463, \"world\"},"
+ "test{7, 5, \"!!\"},};",
+ Style));
+ // This caused a core dump by enabling Alignment in the LLVMStyle globally
+ Style = getLLVMStyleWithColumns(50);
+ Style.AlignArrayOfStructures = FormatStyle::AIAS_Right;
+ verifyFormat("static A x = {\n"
+ " {{init1, init2, init3, init4},\n"
+ " {init1, init2, init3, init4}}\n"
+ "};",
+ Style);
+ Style.ColumnLimit = 100;
+ EXPECT_EQ(
+ "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"
+ " {-1, 93463, \"world\"},\n"
+ " { 7, 5, \"!!\"},\n"
+ "};",
+ format("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 = getLLVMStyleWithColumns(50);
+ Style.AlignArrayOfStructures = FormatStyle::AIAS_Right;
+ Style.AlignConsecutiveAssignments =
+ FormatStyle::AlignConsecutiveStyle::ACS_Consecutive;
+ Style.AlignConsecutiveDeclarations =
+ FormatStyle::AlignConsecutiveStyle::ACS_Consecutive;
+ verifyFormat("struct test demo[] = {\n"
+ " {56, 23, \"hello\"},\n"
+ " {-1, 93463, \"world\"},\n"
+ " { 7, 5, \"!!\"}\n"
+ "};\n"
+ "static A x = {\n"
+ " {{init1, init2, init3, init4},\n"
+ " {init1, init2, init3, init4}}\n"
+ "};",
+ Style);
+ Style.ColumnLimit = 100;
+ Style.AlignConsecutiveAssignments =
+ FormatStyle::AlignConsecutiveStyle::ACS_AcrossComments;
+ Style.AlignConsecutiveDeclarations =
+ FormatStyle::AlignConsecutiveStyle::ACS_AcrossComments;
+ verifyFormat("struct test demo[] = {\n"
+ " {56, 23, \"hello\"},\n"
+ " {-1, 93463, \"world\"},\n"
+ " { 7, 5, \"!!\"}\n"
+ "};\n"
+ "struct test demo[4] = {\n"
+ " { 56, 23, 21, \"oh\"}, // first line\n"
+ " { -1, 93463, 22, \"my\"}, // second line\n"
+ " { 7, 5, 1, \"goodness\"} // third line\n"
+ " {234, 5, 1, \"gracious\"} // fourth line\n"
+ "};\n",
+ Style);
+ EXPECT_EQ(
+ "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"
+ "};",
+ format("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));
+}
+
+TEST_F(FormatTest, CatchAlignArrayOfStructuresLeftAlignment) {
+ auto Style = getLLVMStyle();
+ Style.AlignArrayOfStructures = FormatStyle::AIAS_Left;
+ verifyFormat("struct test demo[] = {\n"
+ " {56, 23, \"hello\"},\n"
+ " {-1, 93463, \"world\"},\n"
+ " {7, 5, \"!!\" }\n"
+ "};\n",
+ Style);
+
+ verifyFormat("struct test demo[] = {\n"
+ " {56, 23, \"hello\"}, // first line\n"
+ " {-1, 93463, \"world\"}, // second line\n"
+ " {7, 5, \"!!\" } // third line\n"
+ "};\n",
+ Style);
+ verifyFormat("struct test demo[4] = {\n"
+ " {56, 23, 21, \"oh\" }, // first line\n"
+ " {-1, 93463, 22, \"my\" }, // second line\n"
+ " {7, 5, 1, \"goodness\"} // third line\n"
+ " {234, 5, 1, \"gracious\"} // fourth line\n"
+ "};\n",
+ Style);
+ verifyFormat("struct test demo[3] = {\n"
+ " {56, 23, \"hello\"},\n"
+ " {-1, 93463, \"world\"},\n"
+ " {7, 5, \"!!\" }\n"
+ "};\n",
+ Style);
+
+ verifyFormat("struct test demo[3] = {\n"
+ " {int{56}, 23, \"hello\"},\n"
+ " {int{-1}, 93463, \"world\"},\n"
+ " {int{7}, 5, \"!!\" }\n"
+ "};\n",
+ Style);
+ verifyFormat("struct test demo[] = {\n"
+ " {56, 23, \"hello\"},\n"
+ " {-1, 93463, \"world\"},\n"
+ " {7, 5, \"!!\" },\n"
+ "};\n",
+ Style);
+ verifyFormat("test demo[] = {\n"
+ " {56, 23, \"hello\"},\n"
+ " {-1, 93463, \"world\"},\n"
+ " {7, 5, \"!!\" },\n"
+ "};\n",
+ Style);
+ verifyFormat("demo = std::array<struct test, 3>{\n"
+ " test{56, 23, \"hello\"},\n"
+ " test{-1, 93463, \"world\"},\n"
+ " test{7, 5, \"!!\" },\n"
+ "};\n",
+ Style);
+ verifyFormat("test demo[] = {\n"
+ " {56, 23, \"hello\"},\n"
+ "#if X\n"
+ " {-1, 93463, \"world\"},\n"
+ "#endif\n"
+ " {7, 5, \"!!\" }\n"
+ "};\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",
+ Style);
+
+ verifyFormat("return GradForUnaryCwise(g, {\n"
+ " {{\"sign\"}, \"Sign\", {\"x\", "
+ "\"dy\"} },\n"
+ " {{\"dx\"}, \"Mul\", "
+ "{\"dy\", \"sign\"}},\n"
+ "});\n",
+ Style);
+
+ Style.ColumnLimit = 0;
+ EXPECT_EQ(
+ "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"
+ "};",
+ format("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;
+ verifyFormat("test demo[] = {\n"
+ " {56, 23, /* a comment */ \"hello\"},\n"
+ " {-1, 93463, \"world\" },\n"
+ " {7, 5, \"!!\" }\n"
+ "};\n",
+ Style);
+
+ verifyFormat("test demo[] = {\n"
+ " {56, 23, \"hello\" },\n"
+ " {-1, 93463, \"world\" /* comment here */},\n"
+ " {7, 5, \"!!\" }\n"
+ "};\n",
+ Style);
+
+ verifyFormat("test demo[] = {\n"
+ " {56, /* a comment */ 23, \"hello\"},\n"
+ " {-1, 93463, \"world\"},\n"
+ " {7, 5, \"!!\" }\n"
+ "};\n",
+ Style);
+
+ Style.ColumnLimit = 20;
+ EXPECT_EQ(
+ "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"
+ "};",
+ 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 "
+ "to be split over multiple lines\"},test{-1, 93463, \"world\"},"
+ "test{7, 5, \"!!\"},};",
+ Style));
+
+ // This caused a core dump by enabling Alignment in the LLVMStyle globally
+ Style = getLLVMStyleWithColumns(50);
+ Style.AlignArrayOfStructures = FormatStyle::AIAS_Left;
+ verifyFormat("static A x = {\n"
+ " {{init1, init2, init3, init4},\n"
+ " {init1, init2, init3, init4}}\n"
+ "};",
+ Style);
+ Style.ColumnLimit = 100;
+ EXPECT_EQ(
+ "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"
+ " {-1, 93463, \"world\"},\n"
+ " {7, 5, \"!!\" },\n"
+ "};",
+ format("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));
+}
+
TEST_F(FormatTest, UnderstandsPragmas) {
verifyFormat("#pragma omp reduction(| : var)");
verifyFormat("#pragma omp reduction(+ : var)");
More information about the cfe-commits
mailing list