diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -203,6 +203,17 @@ argument1, argument2); +**AlignArrayOfStructures** (``bool``) + If ``true``, when using initialization for an array + of structs aligns the fields into right justified columns + + .. code-block:: c + struct test demo[] = + { + {56, 23, "hello" }, + {-1, 93463, "world" }, + { 7, 5, "!!" } + } **AlignConsecutiveAssignments** (``AlignConsecutiveStyle``) Style of aligning consecutive assignments. diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -259,6 +259,9 @@ - ``git-clang-format`` no longer formats changes to symbolic links. (Fixes https://llvm.org/PR46992.) +- 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 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -90,6 +90,18 @@ /// brackets. BracketAlignmentStyle AlignAfterOpenBracket; + /// if ``true``, when using initialization for an array of structs + /// aligns the fields into columns + /// \code + /// struct test demo[] = + /// { + /// {56, 23, "hello" }, + /// {-1, 93463, "world" }, + /// {7, 5, "!!" } + /// } + /// \endcode + bool AlignArrayOfStructures; + /// Styles for alignment of consecutive tokens. Tokens can be assignment signs /// (see /// ``AlignConsecutiveAssignments``), bitfield member separators (see @@ -3258,6 +3270,7 @@ 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 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -507,6 +507,7 @@ 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); @@ -942,6 +943,7 @@ LLVMStyle.AccessModifierOffset = -2; LLVMStyle.AlignEscapedNewlines = FormatStyle::ENAS_Right; LLVMStyle.AlignAfterOpenBracket = FormatStyle::BAS_Align; + LLVMStyle.AlignArrayOfStructures = false; 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 --- a/clang/lib/Format/FormatToken.h +++ b/clang/lib/Format/FormatToken.h @@ -431,6 +431,12 @@ /// The next token in the unwrapped line. FormatToken *Next = nullptr; + /// The first token in set of column elements + bool StartsColumn = false; + + /// This starts an array initializer + bool IsArrayInitializer = false; + /// If this token starts a block, this contains all the unwrapped lines /// in it. SmallVector Children; diff --git a/clang/lib/Format/TokenAnnotator.h b/clang/lib/Format/TokenAnnotator.h --- a/clang/lib/Format/TokenAnnotator.h +++ b/clang/lib/Format/TokenAnnotator.h @@ -31,7 +31,8 @@ LT_ObjCProperty, // An @property line. LT_Other, LT_PreprocessorDirective, - LT_VirtualFunctionDecl + LT_VirtualFunctionDecl, + LT_ArrayOfStructInitializer }; class AnnotatedLine { @@ -189,6 +190,12 @@ 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/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -729,11 +729,25 @@ 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 = Contexts.rbegin() + 2; + auto Last = Contexts.rbegin(); + unsigned Depth = 0; + for (; Last != End; Last = std::next(Last)) { + if (Last->ContextKind == tok::l_brace) + ++Depth; + } + return Depth == 2 && Last->ContextKind != tok::l_brace; + } + bool parseBrace() { if (CurrentToken) { FormatToken *Left = CurrentToken->Previous; Left->ParentBracket = Contexts.back().ContextKind; - if (Contexts.back().CaretFound) Left->setType(TT_ObjCBlockLBrace); Contexts.back().CaretFound = false; @@ -746,10 +760,17 @@ 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) { + if (Left->ParentBracket == tok::l_brace && + couldBeInStructArrayInitializer() && CommaCount > 0) { + Contexts.back().InStructArrayInitializer = true; + } + } next(); return true; } @@ -773,9 +794,11 @@ 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 +1362,12 @@ return LT_ObjCMethodDecl; } + for (const auto &ctx : Contexts) { + if (ctx.InStructArrayInitializer) { + return LT_ArrayOfStructInitializer; + } + } + return LT_Other; } @@ -1414,6 +1443,7 @@ 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 +1459,16 @@ P.Contexts.back().IsExpression)); } - ~ScopedContextCreator() { P.Contexts.pop_back(); } + ~ScopedContextCreator() { + if (P.Style.AlignArrayOfStructures) { + 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 +2512,11 @@ : Line.FirstStartColumn + Line.First->ColumnWidth; FormatToken *Current = Line.First->Next; bool InFunctionDecl = Line.MightBeFunctionDecl; + bool AlignArrayOfStructures = (Style.AlignArrayOfStructures && + Line.Type == LT_ArrayOfStructInitializer); + if (AlignArrayOfStructures) + calculateArrayInitializerColumnList(Line); + while (Current) { if (isFunctionDeclarationName(*Current, Line)) Current->setType(TT_FunctionDeclarationName); @@ -2592,6 +2636,44 @@ } } +void TokenAnnotator::calculateArrayInitializerColumnList(AnnotatedLine &Line) { + if (Line.First == Line.Last) { + return; + } + auto *CurrentToken = Line.First; + 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/WhitespaceManager.h b/clang/lib/Format/WhitespaceManager.h --- a/clang/lib/Format/WhitespaceManager.h +++ b/clang/lib/Format/WhitespaceManager.h @@ -20,6 +20,7 @@ #include "clang/Format/Format.h" #include #include +#include namespace clang { namespace format { @@ -173,6 +174,27 @@ }; private: + struct CellDescription { + unsigned Index = 0; + unsigned Cell = 0; + unsigned EndIndex = 0; + bool HasSplit = false; + CellDescription *NextColumnElement = nullptr; + + constexpr bool operator==(const CellDescription &oth) const noexcept { + return Index == oth.Index && Cell == oth.Cell && EndIndex == oth.EndIndex; + } + constexpr bool operator!=(const CellDescription &oth) const noexcept { + return !(*this == oth); + } + }; + + struct CellDescriptions { + std::vector 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 +229,42 @@ /// 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); + + /// Calculate the cell width between two indexes + unsigned calculateCellWidth(unsigned Start, unsigned End, + bool WithSpaces = false); + + /// 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 + auto getNetWidth(const I &Start, const I &End, unsigned InitialSpaces) { + 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; + } + + /// align a split cell with a newline to the first element in the cell + void alignToStartOfCell(unsigned Start, unsigned End); + /// Fill \c Replaces with the replacements for all effective changes. void generateChanges(); diff --git a/clang/lib/Format/WhitespaceManager.cpp b/clang/lib/Format/WhitespaceManager.cpp --- a/clang/lib/Format/WhitespaceManager.cpp +++ b/clang/lib/Format/WhitespaceManager.cpp @@ -13,6 +13,7 @@ #include "WhitespaceManager.h" #include "llvm/ADT/STLExtras.h" +#include namespace clang { namespace format { @@ -100,6 +101,7 @@ alignChainedConditionals(); alignTrailingComments(); alignEscapedNewlines(); + alignArrayInitializers(); generateChanges(); return Replaces; @@ -944,6 +946,260 @@ } } +void WhitespaceManager::alignArrayInitializers() { + if (!Style.AlignArrayOfStructures) + 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) { + + auto CellDescs = getCells(Start, End); + auto &Cells = CellDescs.Cells; + auto CellCount = CellDescs.CellCount; + auto InitialSpaces = CellDescs.InitialSpaces; + + // Now go through and fixup the spaces + auto CellIter = Cells.begin(); + for (auto i = 0U; i < CellCount; i++, ++CellIter) { + auto HasSplit = isSplitCell(*CellIter); + unsigned NetWidth = 0U; + if (HasSplit) + NetWidth = getNetWidth(Cells.begin(), CellIter, InitialSpaces); + auto 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); + } + 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; + } + // Except if the array is empty we need the position of all the + // cells immediantly adjacent to this + if (CellIter != Cells.begin()) { + auto ThisNetWidth = getNetWidth(Cells.begin(), CellIter, InitialSpaces); + auto 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 * CellCount); + auto End = Start + Offset; + MaxNetWidth = + std::max(MaxNetWidth, getNetWidth(Start, End, InitialSpaces)); + ++RowCount; + } + if (ThisNetWidth < MaxNetWidth) + Changes[CellIter->Index].Spaces = (MaxNetWidth - ThisNetWidth); + RowCount = 1U; + for (const auto *Next = CellIter->NextColumnElement; Next != nullptr; + Next = Next->NextColumnElement) { + auto Start = (Cells.begin() + RowCount * CellCount); + auto End = Start + Offset; + ThisNetWidth = getNetWidth(Start, End, 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); + } + } + } +} + +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; + std::vector 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 (Depth == 0 && C.Tok->is(tok::r_brace)) { + C.NewlinesBefore = 1; + } + 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}); + } + } + + // Now link the pointers + 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 {Cells, CellCount, InitialSpaces}; +} + +unsigned WhitespaceManager::calculateCellWidth(unsigned Start, unsigned End, + bool WithSpaces) { + 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; + } +} + void WhitespaceManager::generateChanges() { for (unsigned i = 0, e = Changes.size(); i != e; ++i) { const Change &C = Changes[i]; diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -16401,6 +16401,229 @@ getLLVMStyle()); } +TEST_F(FormatTest, CatchAlignArrayOfStructures) { + auto Style = getLLVMStyle(); + Style.AlignArrayOfStructures = true; + 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{\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{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 = true; + 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 = true; + 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, UnderstandsPragmas) { verifyFormat("#pragma omp reduction(| : var)"); verifyFormat("#pragma omp reduction(+ : var)");