[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