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 @@ -251,6 +251,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 @@ -3249,6 +3261,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/ContinuationIndenter.h b/clang/lib/Format/ContinuationIndenter.h --- a/clang/lib/Format/ContinuationIndenter.h +++ b/clang/lib/Format/ContinuationIndenter.h @@ -188,6 +188,9 @@ /// by clang-format and string literals with escaped newlines. bool nextIsMultilineString(const LineState &State); + /// make sure row positions is properly initialized and return the max width + unsigned setRowPositions(const FormatToken &Current, LineState &State); + FormatStyle Style; const AdditionalKeywords &Keywords; const SourceManager &SourceMgr; @@ -453,6 +456,13 @@ /// Does not need to be considered for memoization because it doesn't change. const AnnotatedLine *Line; + /// The column position of the end of each of the row elements + /// for array initializers + std::vector> RowPositions; + + /// Keep track of the current column index in array initializers + unsigned CurrentColumnIndex = 0U; + /// Comparison operator to be able to used \c LineState in \c map. bool operator<(const LineState &Other) const { if (NextToken != Other.NextToken) @@ -474,6 +484,8 @@ return false; return Stack < Other.Stack; } + + unsigned getLBraceDepth() const; }; } // end namespace format diff --git a/clang/lib/Format/ContinuationIndenter.cpp b/clang/lib/Format/ContinuationIndenter.cpp --- a/clang/lib/Format/ContinuationIndenter.cpp +++ b/clang/lib/Format/ContinuationIndenter.cpp @@ -597,10 +597,37 @@ PPColumnCorrection = -1; } - if (!DryRun) - Whitespaces.replaceWhitespace(Current, /*Newlines=*/0, Spaces, - State.Column + Spaces + PPColumnCorrection); - + if (!DryRun) { + if (Style.AlignArrayOfStructures && + State.Line->Type == LT_ArrayOfStructInitializer) { + auto ColumnPos = State.Column; + if (Current.PreviousColumn != nullptr) { + auto MaxWidth = setRowPositions(Current, State); + Spaces = MaxWidth - Current.getThisColumnWidth(); + Spaces += ((Current.ColumnIndex != 0) ? 1 : 0U); + if ((Current.hasPreviousSplitMax() || + MaxWidth < Current.getMaximumColumnWidth()) && + Current.ColumnIndex > 0) { + // We had a line get broken so net the previous + // column + auto LeftPosition = + State.RowPositions[Current.ColumnIndex - 1].first + + State.RowPositions[Current.ColumnIndex - 1].second - 1; + if (Spaces > LeftPosition) + Spaces -= LeftPosition; + else if (Spaces < LeftPosition) + Spaces = 1; + } + ColumnPos = State.RowPositions[Current.ColumnIndex].first; + } else if (Current.IsRowClosing) { + Spaces += 1; + } + Whitespaces.replaceWhitespace(Current, /*Newlines=*/0, Spaces, ColumnPos); + } else { + Whitespaces.replaceWhitespace(Current, /*Newlines=*/0, Spaces, + State.Column + Spaces + PPColumnCorrection); + } + } // If "BreakBeforeInheritanceComma" mode, don't break within the inheritance // declaration unless there is multiple inheritance. if (Style.BreakInheritanceList == FormatStyle::BILS_BeforeComma && @@ -885,9 +912,45 @@ std::max(1u, std::min(Current.NewlinesBefore, MaxEmptyLinesToKeep)); bool ContinuePPDirective = State.Line->InPPDirective && State.Line->Type != LT_ImportStatement; - Whitespaces.replaceWhitespace(Current, Newlines, State.Column, State.Column, - State.Stack.back().IsAligned, - ContinuePPDirective); + bool AlignArrayOfStructures = + (Style.AlignArrayOfStructures && + State.Line->Type == LT_ArrayOfStructInitializer); + if (AlignArrayOfStructures) { + unsigned Depth = State.getLBraceDepth(); + if (Current.PreviousColumn != nullptr) { + auto MaxWidth = setRowPositions(Current, State); + auto ColumnPos = State.RowPositions[Current.ColumnIndex].first; + auto Spaces = MaxWidth - Current.getThisColumnWidth(); + // FIXME really we shouldn't have this test here + if (Current.ColumnIndex == 0 && + (ColumnPos + Current.getThisColumnWidth()) < Style.ColumnLimit) { + Newlines = 0; + State.RowPositions[Current.ColumnIndex] = + std::make_pair(State.Column + 1, MaxWidth); + } else if (Current.ColumnIndex > 0 && Newlines > 0) { + // We are linebreaking at a column so set the position to the first + State.RowPositions[Current.ColumnIndex] = + std::make_pair(State.RowPositions[0].first, + State.RowPositions[Current.ColumnIndex].second); + ColumnPos = State.RowPositions[Current.ColumnIndex].first; + Spaces += ColumnPos - 1; + } + Whitespaces.replaceWhitespace(Current, Newlines, Spaces, ColumnPos); + } else if (!Current.isOneOf(tok::r_brace, tok::comma, tok::l_brace) && + State.RowPositions.size() > State.CurrentColumnIndex && + Depth == 2) { + auto ColumnPos = State.RowPositions[State.CurrentColumnIndex].first; + Whitespaces.replaceWhitespace(Current, Newlines, ColumnPos - 1, + ColumnPos); + } else { + Whitespaces.replaceWhitespace(Current, Newlines, State.Column, + State.Column); + } + } else { + Whitespaces.replaceWhitespace(Current, Newlines, State.Column, + State.Column, State.Stack.back().IsAligned, + ContinuePPDirective); + } } if (!Current.isTrailingComment()) @@ -1951,6 +2014,12 @@ unsigned UnbreakableTailLength = (State.NextToken && canBreak(State)) ? 0 : Current.UnbreakableTailLength; + if (Style.AlignArrayOfStructures && + State.Line->Type == LT_ArrayOfStructInitializer) { + if (!State.RowPositions.empty()) { + StartColumn = State.RowPositions[0].first - 1; + } + } return std::make_unique( Current, StartColumn, Prefix, Postfix, UnbreakableTailLength, State.Line->InPPDirective, Encoding, Style); @@ -2188,10 +2257,24 @@ LLVM_DEBUG(llvm::dbgs() << " Breaking at: " << TailOffset + Split.first << ", " << Split.second << "\n"); - if (!DryRun) + if (!DryRun) { + if (Style.AlignArrayOfStructures && + State.Line->Type == LT_ArrayOfStructInitializer) { + // So at this point we have a initializer. We need to make sure the + // related column tokens are updated as well as any changes already + // inflight linked to those tokens + auto *CurrentWritableToken = State.NextToken->Previous; + for (; CurrentWritableToken != &Current; + CurrentWritableToken = CurrentWritableToken->Previous) { + } + assert(CurrentWritableToken != nullptr); + CurrentWritableToken->SplitMax = + CurrentWritableToken->TokenText.substr(TailOffset + Split.first) + .size(); + } Token->insertBreak(LineIndex, TailOffset, Split, ContentIndent, Whitespaces); - + } Penalty += NewBreakPenalty; TailOffset += Split.first + Split.second; RemainingTokenColumns = NewRemainingTokenColumns; @@ -2384,5 +2467,27 @@ return false; } +unsigned ContinuationIndenter::setRowPositions(const FormatToken &Current, + LineState &State) { + auto MaxWidth = Current.getMaximumColumnWidth(Style.ColumnLimit); + State.CurrentColumnIndex = Current.ColumnIndex; + if (State.RowPositions.size() <= Current.ColumnIndex) + State.RowPositions.push_back(std::make_pair(State.Column, MaxWidth)); + else if (State.RowPositions[Current.ColumnIndex].second != MaxWidth) { + State.RowPositions[Current.ColumnIndex] = + std::make_pair(State.Column, MaxWidth); + } + return MaxWidth; +} + +unsigned LineState::getLBraceDepth() const { + unsigned Depth = 0U; + for (const auto &PState : Stack) { + if (PState.Tok != nullptr) + Depth += (PState.Tok->is(tok::l_brace)) ? 1 : 0; + } + return Depth; +} + } // namespace format } // namespace clang 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 @@ -506,6 +506,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); @@ -941,6 +942,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,20 @@ /// The next token in the unwrapped line. FormatToken *Next = nullptr; + /// The previous vertical column element + FormatToken *PreviousColumn = nullptr; + + /// Indicates that this is the closing bracket for a row + bool IsRowClosing = false; + + /// We we split a token into multiples in the array initializer + /// case we need to track the size of the split for other columns + unsigned SplitMax = 0U; + + /// For a token in a array initializer we need to track where in the + /// row it is so we can set offsets appropriately + unsigned ColumnIndex = 0U; + /// If this token starts a block, this contains all the unwrapped lines /// in it. SmallVector Children; @@ -695,6 +709,17 @@ void copyFrom(const FormatToken &Tok) { *this = Tok; } + /// Returns the maximum width of all the columns above and below + /// this one + unsigned getMaximumColumnWidth(unsigned Limit = 0) const noexcept; + + /// Returns the column width of multiple tokens in a array initializer + unsigned getThisColumnWidth(unsigned Limit = 0) const noexcept; + + /// Returns a boolean indicated that a previous column element + /// was split + bool hasPreviousSplitMax() const noexcept; + private: // Only allow copying via the explicit copyFrom method. FormatToken(const FormatToken &) = delete; diff --git a/clang/lib/Format/FormatToken.cpp b/clang/lib/Format/FormatToken.cpp --- a/clang/lib/Format/FormatToken.cpp +++ b/clang/lib/Format/FormatToken.cpp @@ -16,6 +16,7 @@ #include "ContinuationIndenter.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/Debug.h" +#include #include namespace clang { @@ -69,6 +70,49 @@ } } +unsigned FormatToken::getMaximumColumnWidth(unsigned Limit) const noexcept { + unsigned MaxWidth = getThisColumnWidth(); + auto *PColumn = PreviousColumn; + while (PColumn != nullptr && PColumn != this) { + MaxWidth = std::max(MaxWidth, PColumn->getThisColumnWidth(Limit)); + PColumn = PColumn->PreviousColumn; + } + return MaxWidth; +} + +unsigned FormatToken::getThisColumnWidth(unsigned Limit) const noexcept { + auto ColWidth = + (SplitMax > 0 && SplitMax < ColumnWidth) ? SplitMax : ColumnWidth; + const auto *NextColEntry = Next; + while (NextColEntry != nullptr && + !NextColEntry->isOneOf(tok::r_brace, tok::comma)) { + if (NextColEntry->SplitMax > 0 && + NextColEntry->SplitMax < NextColEntry->ColumnWidth) { + ColWidth += NextColEntry->SplitMax; + } else if (Limit > 0 && ColWidth + NextColEntry->ColumnWidth >= Limit) { + ColWidth = NextColEntry->ColumnWidth; + } else { + ColWidth += NextColEntry->ColumnWidth; + } + NextColEntry = NextColEntry->Next; + } + return ColWidth; +} + +bool FormatToken::hasPreviousSplitMax() const noexcept { + if (SplitMax > 0) + return true; + // We can't recurse here because there is no stopping + // condition in the case in which there is no SplitMaximum + const auto *PColumn = PreviousColumn; + while (PColumn != nullptr && PColumn != this) { + if (PColumn->SplitMax > 0) + return true; + PColumn = PColumn->PreviousColumn; + } + return false; +} + TokenRole::~TokenRole() {} void TokenRole::precomputeFormattingInfos(const FormatToken *Token) {} 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 @@ -18,6 +18,9 @@ #include "UnwrappedLineParser.h" #include "clang/Format/Format.h" +#include +#include + namespace clang { class SourceManager; @@ -31,7 +34,8 @@ LT_ObjCProperty, // An @property line. LT_Other, LT_PreprocessorDirective, - LT_VirtualFunctionDecl + LT_VirtualFunctionDecl, + LT_ArrayOfStructInitializer }; class AnnotatedLine { @@ -189,6 +193,13 @@ void calculateUnbreakableTailLengths(AnnotatedLine &Line); + void calculateArrayInitializerColumnList(AnnotatedLine &Line); + + FormatToken * + calculateInitializerColumnList(AnnotatedLine &Line, + std::deque &&ColumnEntries, + 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,21 @@ return false; } + bool couldBeInStructArrayInitializer() const { + const auto end = 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; Left->ParentBracket = Contexts.back().ContextKind; - if (Contexts.back().CaretFound) Left->setType(TT_ObjCBlockLBrace); Contexts.back().CaretFound = false; @@ -746,10 +756,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 +790,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 +1358,12 @@ return LT_ObjCMethodDecl; } + for (const auto &ctx : Contexts) { + if (ctx.InStructArrayInitializer) { + return LT_ArrayOfStructInitializer; + } + } + return LT_Other; } @@ -1414,6 +1439,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 +1455,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 +2508,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 +2632,86 @@ } } +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 = calculateInitializerColumnList( + Line, std::deque{}, CurrentToken->Next, Depth + 1); + else + CurrentToken = CurrentToken->Next; + } +} + +// NOLINTNEXTLINE(misc-no-recursion) +FormatToken *TokenAnnotator::calculateInitializerColumnList( + AnnotatedLine &Line, std::deque &&ColumnEntries, + FormatToken *CurrentToken, unsigned Depth) { + if (Depth == 1) { + CurrentToken->MustBreakBefore = true; + return calculateInitializerColumnList(Line, std::move(ColumnEntries), + CurrentToken, Depth + 1); + } + + std::deque NextRow; + while (CurrentToken != nullptr && CurrentToken != Line.Last) { + if (CurrentToken->is(tok::l_brace)) + ++Depth; + else if (CurrentToken->is(tok::r_brace)) + --Depth; + + if (CurrentToken->Next != nullptr) { + if (CurrentToken->is(tok::r_brace)) { + if (Depth == 2) { + if (CurrentToken->Next->is(tok::comma)) { + auto *NextLBrace = CurrentToken->Next->Next; + const auto *StopValue = CurrentToken->Next->getNextNonComment(); + while (NextLBrace != nullptr && NextLBrace != StopValue) + NextLBrace = NextLBrace->Next; + if (NextLBrace != nullptr) + NextLBrace->MustBreakBefore = true; + } + CurrentToken->IsRowClosing = true; + return calculateInitializerColumnList(Line, std::move(NextRow), + CurrentToken->Next, Depth); + } + + if (Depth == 1) { + // We are dropping out of the loop. So in this case take all of the + // entries in the last row and point the tails at the heads so we can + // loop around the columns ending where we began + while (!ColumnEntries.empty()) { + auto *LastToken = ColumnEntries.front(); + if (LastToken->PreviousColumn != nullptr) { + auto *StartToken = LastToken->PreviousColumn; + while (StartToken->PreviousColumn != nullptr) { + StartToken = StartToken->PreviousColumn; + } + StartToken->PreviousColumn = LastToken; + } + ColumnEntries.pop_front(); + } + CurrentToken->MustBreakBefore = true; + } + } else if (Depth == 3 && + CurrentToken->isOneOf(tok::comma, tok::l_brace)) { + CurrentToken->Next->ColumnIndex = NextRow.size(); + NextRow.push_back(CurrentToken->Next); + if (!ColumnEntries.empty()) { + CurrentToken->Next->PreviousColumn = ColumnEntries.front(); + ColumnEntries.pop_front(); + } + } + } + CurrentToken = CurrentToken->Next; + } + return CurrentToken; +} + unsigned TokenAnnotator::splitPenalty(const AnnotatedLine &Line, const FormatToken &Tok, bool InFunctionDecl) { 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 @@ -16367,6 +16367,162 @@ 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 = 20; + EXPECT_EQ( + "demo = std::array<\n" + " struct test, 3>{\n" + " test{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); + + // FIXME This is a case where the lines are obviously malformed + /* + 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)");