sstwcw updated this revision to Diff 408295.
sstwcw edited the summary of this revision.

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D119599/new/

https://reviews.llvm.org/D119599

Files:
  clang/docs/ClangFormatStyleOptions.rst
  clang/include/clang/Format/Format.h
  clang/lib/Format/Format.cpp
  clang/lib/Format/WhitespaceManager.cpp
  clang/unittests/Format/FormatTest.cpp

Index: clang/unittests/Format/FormatTest.cpp
===================================================================
--- clang/unittests/Format/FormatTest.cpp
+++ clang/unittests/Format/FormatTest.cpp
@@ -16392,6 +16392,157 @@
                    Alignment));
 }
 
+TEST_F(FormatTest, AlignCompoundAssignments) {
+  FormatStyle Alignment = getLLVMStyle();
+  Alignment.AlignConsecutiveAssignments = FormatStyle::ACS_Consecutive;
+  Alignment.AlignConsecutiveAssignmentsOptions.AlignCompound = true;
+  Alignment.AlignConsecutiveAssignmentsOptions.PadOperators = false;
+  verifyFormat("aa <= 5;\n"
+               "a               = 5;\n"
+               "bcd             = 5;\n"
+               "ghtyf           = 5;\n"
+               "dvfvdb          = 5;\n"
+               "a               = 5;\n"
+               "vdsvsv          = 5;\n"
+               "sfdbddfbdfbb    = 5;\n"
+               "dvsdsv          = 5;\n"
+               "int dsvvdvsdvvv = 123;",
+               Alignment);
+  verifyFormat("aa <= 5;\n"
+               "a              &= 5;\n"
+               "bcd            *= 5;\n"
+               "ghtyf          += 5;\n"
+               "dvfvdb         -= 5;\n"
+               "a              /= 5;\n"
+               "vdsvsv         %= 5;\n"
+               "sfdbddfbdfbb   ^= 5;\n"
+               "dvsdsv         |= 5;\n"
+               "int dsvvdvsdvvv = 123;",
+               Alignment);
+  verifyFormat("a      &= 5;\n"
+               "bcd    *= 5;\n"
+               "ghtyf >>= 5;\n"
+               "dvfvdb -= 5;\n"
+               "a      /= 5;\n"
+               "aa <= 5;\n"
+               "vdsvsv         %= 5;\n"
+               "sfdbddfbdfbb   ^= 5;\n"
+               "dvsdsv        <<= 5;\n"
+               "int dsvvdvsdvvv = 123;",
+               Alignment);
+  Alignment.AlignConsecutiveAssignmentsOptions.PadOperators = true;
+  verifyFormat("aa <= 5;\n"
+               "a               = 5;\n"
+               "bcd             = 5;\n"
+               "ghtyf           = 5;\n"
+               "dvfvdb          = 5;\n"
+               "a               = 5;\n"
+               "vdsvsv          = 5;\n"
+               "sfdbddfbdfbb    = 5;\n"
+               "dvsdsv          = 5;\n"
+               "int dsvvdvsdvvv = 123;",
+               Alignment);
+  verifyFormat("aa <= 5;\n"
+               "a               &= 5;\n"
+               "bcd             *= 5;\n"
+               "ghtyf           += 5;\n"
+               "dvfvdb          -= 5;\n"
+               "a               /= 5;\n"
+               "vdsvsv          %= 5;\n"
+               "sfdbddfbdfbb    ^= 5;\n"
+               "dvsdsv          |= 5;\n"
+               "int dsvvdvsdvvv  = 123;",
+               Alignment);
+  verifyFormat("a       &= 5;\n"
+               "bcd     *= 5;\n"
+               "ghtyf  >>= 5;\n"
+               "dvfvdb  -= 5;\n"
+               "a       /= 5;\n"
+               "aa <= 5;\n"
+               "vdsvsv           %= 5;\n"
+               "sfdbddfbdfbb     ^= 5;\n"
+               "dvsdsv          <<= 5;\n"
+               "int dsvvdvsdvvv   = 123;",
+               Alignment);
+  Alignment.AlignConsecutiveAssignments = FormatStyle::ACS_Consecutive;
+  EXPECT_EQ("int a   += 5;\n"
+            "int one  = 1;\n"
+            "\n"
+            "int oneTwoThree = 123;\n",
+            format("int a += 5;\n"
+                   "int one = 1;\n"
+                   "\n"
+                   "int oneTwoThree = 123;\n",
+                   Alignment));
+  EXPECT_EQ("int a   += 5;\n"
+            "int one  = 1;\n"
+            "//\n"
+            "int oneTwoThree = 123;\n",
+            format("int a += 5;\n"
+                   "int one = 1;\n"
+                   "//\n"
+                   "int oneTwoThree = 123;\n",
+                   Alignment));
+  Alignment.AlignConsecutiveAssignments = FormatStyle::ACS_AcrossEmptyLines;
+  EXPECT_EQ("int a           += 5;\n"
+            "int one          = 1;\n"
+            "\n"
+            "int oneTwoThree  = 123;\n",
+            format("int a += 5;\n"
+                   "int one = 1;\n"
+                   "\n"
+                   "int oneTwoThree = 123;\n",
+                   Alignment));
+  EXPECT_EQ("int a   += 5;\n"
+            "int one  = 1;\n"
+            "//\n"
+            "int oneTwoThree = 123;\n",
+            format("int a += 5;\n"
+                   "int one = 1;\n"
+                   "//\n"
+                   "int oneTwoThree = 123;\n",
+                   Alignment));
+  Alignment.AlignConsecutiveAssignments = FormatStyle::ACS_AcrossComments;
+  EXPECT_EQ("int a   += 5;\n"
+            "int one  = 1;\n"
+            "\n"
+            "int oneTwoThree = 123;\n",
+            format("int a += 5;\n"
+                   "int one = 1;\n"
+                   "\n"
+                   "int oneTwoThree = 123;\n",
+                   Alignment));
+  EXPECT_EQ("int a           += 5;\n"
+            "int one          = 1;\n"
+            "//\n"
+            "int oneTwoThree  = 123;\n",
+            format("int a += 5;\n"
+                   "int one = 1;\n"
+                   "//\n"
+                   "int oneTwoThree = 123;\n",
+                   Alignment));
+  Alignment.AlignConsecutiveAssignments =
+      FormatStyle::ACS_AcrossEmptyLinesAndComments;
+  EXPECT_EQ("int a            += 5;\n"
+            "int one         >>= 1;\n"
+            "\n"
+            "int oneTwoThree   = 123;\n",
+            format("int a += 5;\n"
+                   "int one >>= 1;\n"
+                   "\n"
+                   "int oneTwoThree = 123;\n",
+                   Alignment));
+  EXPECT_EQ("int a            += 5;\n"
+            "int one           = 1;\n"
+            "//\n"
+            "int oneTwoThree <<= 123;\n",
+            format("int a += 5;\n"
+                   "int one = 1;\n"
+                   "//\n"
+                   "int oneTwoThree <<= 123;\n",
+                   Alignment));
+}
+
 TEST_F(FormatTest, AlignConsecutiveAssignments) {
   FormatStyle Alignment = getLLVMStyle();
   Alignment.AlignConsecutiveMacros = FormatStyle::ACS_Consecutive;
@@ -16410,7 +16561,8 @@
   verifyFormat("int a           = method();\n"
                "int oneTwoThree = 133;",
                Alignment);
-  verifyFormat("a &= 5;\n"
+  verifyFormat("aa <= 5;\n"
+               "a &= 5;\n"
                "bcd *= 5;\n"
                "ghtyf += 5;\n"
                "dvfvdb -= 5;\n"
@@ -19297,6 +19449,8 @@
   CHECK_PARSE_BOOL(SpaceBeforeSquareBrackets);
   CHECK_PARSE_BOOL(UseCRLF);
 
+  CHECK_PARSE_NESTED_BOOL(AlignConsecutiveAssignmentsOptions, AlignCompound);
+  CHECK_PARSE_NESTED_BOOL(AlignConsecutiveAssignmentsOptions, PadOperators);
   CHECK_PARSE_NESTED_BOOL(BraceWrapping, AfterCaseLabel);
   CHECK_PARSE_NESTED_BOOL(BraceWrapping, AfterClass);
   CHECK_PARSE_NESTED_BOOL(BraceWrapping, AfterEnum);
Index: clang/lib/Format/WhitespaceManager.cpp
===================================================================
--- clang/lib/Format/WhitespaceManager.cpp
+++ clang/lib/Format/WhitespaceManager.cpp
@@ -267,10 +267,14 @@
 }
 
 // Align a single sequence of tokens, see AlignTokens below.
+// Column - The token for which Matches returns true is moved to this
+// column.
+// RightJustify - Whether it is the token's right end or left end that
+// gets moved to that column.
 template <typename F>
 static void
 AlignTokenSequence(const FormatStyle &Style, unsigned Start, unsigned End,
-                   unsigned Column, F &&Matches,
+                   unsigned Column, bool RightJustify, F &&Matches,
                    SmallVector<WhitespaceManager::Change, 16> &Changes) {
   bool FoundMatchOnLine = false;
   int Shift = 0;
@@ -329,7 +333,8 @@
     // shifted by the same amount
     if (!FoundMatchOnLine && !SkipMatchCheck && Matches(Changes[i])) {
       FoundMatchOnLine = true;
-      Shift = Column - Changes[i].StartOfTokenColumn;
+      Shift = Column - RightJustify * Changes[i].TokenLength -
+              Changes[i].StartOfTokenColumn;
       Changes[i].Spaces += Shift;
       // FIXME: This is a workaround that should be removed when we fix
       // http://llvm.org/PR53699. An assertion later below verifies this.
@@ -456,13 +461,35 @@
 // However, the special exception is that we do NOT skip function parameters
 // that are split across multiple lines. See the test case in FormatTest.cpp
 // that mentions "split function parameter alignment" for an example of this.
+// When the parameter RightJustify is true, the operator will be
+// right-justified. It is used to align compound assignments like `+=`
+// and `=`.
+// When RightJustify and PadAnchors are true, operators in each block to
+// be aligned will be padded on the left to the same length before
+// aligning.
 template <typename F>
 static unsigned AlignTokens(
     const FormatStyle &Style, F &&Matches,
     SmallVector<WhitespaceManager::Change, 16> &Changes, unsigned StartAt,
-    const FormatStyle::AlignConsecutiveStyle &ACS = FormatStyle::ACS_None) {
-  unsigned MinColumn = 0;
-  unsigned MaxColumn = UINT_MAX;
+    const FormatStyle::AlignConsecutiveStyle &ACS = FormatStyle::ACS_None,
+    bool RightJustify = false, bool PadAnchors = false) {
+  // We arrange each line in 3 parts. The operator to be aligned (the
+  // anchor), and text to its left and right. In the aligned text the
+  // width of each part will be the maximum of that over the block that
+  // has been aligned.
+  // Maximum widths of each part so far.
+  // When RightJustify is true and PadAnchors is false, the part from
+  // start of line to the right end of the anchor. Otherwise, only the
+  // part to the left of the anchor. Including the space that exists on
+  // its left from the start. Not including the padding added on the
+  // left to right-justify the anchor.
+  unsigned WidthLeft = 0;
+  // The operator to be aligned when RightJustify is true and PadAnchors
+  // is false. 0 otherwise.
+  unsigned WidthAnchor = 0;
+  // Width to the right of the anchor. Plus width of the anchor when
+  // RightJustify is false.
+  unsigned WidthRight = 0;
 
   // Line number of the start and the end of the current token sequence.
   unsigned StartOfSequence = 0;
@@ -495,10 +522,12 @@
   // containing any matching token to be aligned and located after such token.
   auto AlignCurrentSequence = [&] {
     if (StartOfSequence > 0 && StartOfSequence < EndOfSequence)
-      AlignTokenSequence(Style, StartOfSequence, EndOfSequence, MinColumn,
-                         Matches, Changes);
-    MinColumn = 0;
-    MaxColumn = UINT_MAX;
+      AlignTokenSequence(Style, StartOfSequence, EndOfSequence,
+                         WidthLeft + WidthAnchor, RightJustify, Matches,
+                         Changes);
+    WidthLeft = 0;
+    WidthAnchor = 0;
+    WidthRight = 0;
     StartOfSequence = 0;
     EndOfSequence = 0;
   };
@@ -563,29 +592,44 @@
     if (StartOfSequence == 0)
       StartOfSequence = i;
 
-    unsigned ChangeMinColumn = Changes[i].StartOfTokenColumn;
-    int LineLengthAfter = Changes[i].TokenLength;
+    unsigned ChangeWidthLeft = Changes[i].StartOfTokenColumn;
+    unsigned ChangeWidthAnchor = 0;
+    unsigned ChangeWidthRight = 0;
+    if (RightJustify) {
+      if (PadAnchors)
+        ChangeWidthAnchor = Changes[i].TokenLength;
+      else
+        ChangeWidthLeft += Changes[i].TokenLength;
+    } else
+      ChangeWidthRight = Changes[i].TokenLength;
     for (unsigned j = i + 1; j != e && Changes[j].NewlinesBefore == 0; ++j) {
-      LineLengthAfter += Changes[j].Spaces;
+      ChangeWidthRight += Changes[j].Spaces;
       // Changes are generally 1:1 with the tokens, but a change could also be
       // inside of a token, in which case it's counted more than once: once for
       // the whitespace surrounding the token (!IsInsideToken) and once for
       // each whitespace change within it (IsInsideToken).
       // Therefore, changes inside of a token should only count the space.
       if (!Changes[j].IsInsideToken)
-        LineLengthAfter += Changes[j].TokenLength;
+        ChangeWidthRight += Changes[j].TokenLength;
     }
-    unsigned ChangeMaxColumn = Style.ColumnLimit - LineLengthAfter;
 
     // If we are restricted by the maximum column width, end the sequence.
-    if (ChangeMinColumn > MaxColumn || ChangeMaxColumn < MinColumn ||
-        CommasBeforeLastMatch != CommasBeforeMatch) {
+    unsigned NewLeft = std::max(ChangeWidthLeft, WidthLeft);
+    unsigned NewAnchor = std::max(ChangeWidthAnchor, WidthAnchor);
+    unsigned NewRight = std::max(ChangeWidthRight, WidthRight);
+    // `ColumnLimit == 0` means there is no column limit.
+    if (Style.ColumnLimit != 0 &&
+        Style.ColumnLimit < NewLeft + NewAnchor + NewRight) {
       AlignCurrentSequence();
       StartOfSequence = i;
+      WidthLeft = ChangeWidthLeft;
+      WidthAnchor = ChangeWidthAnchor;
+      WidthRight = ChangeWidthRight;
+    } else {
+      WidthLeft = NewLeft;
+      WidthAnchor = NewAnchor;
+      WidthRight = NewRight;
     }
-
-    MinColumn = std::max(MinColumn, ChangeMinColumn);
-    MaxColumn = std::min(MaxColumn, ChangeMaxColumn);
   }
 
   EndOfSequence = i;
@@ -760,9 +804,13 @@
         if (Previous && Previous->is(tok::kw_operator))
           return false;
 
-        return C.Tok->is(tok::equal);
+        return Style.AlignConsecutiveAssignmentsOptions.AlignCompound
+                   ? C.Tok->getPrecedence() == prec::Assignment
+                   : C.Tok->is(tok::equal);
       },
-      Changes, /*StartAt=*/0, Style.AlignConsecutiveAssignments);
+      Changes, /*StartAt=*/0, Style.AlignConsecutiveAssignments,
+      /*RightJustify=*/true,
+      /*PadAnchors=*/Style.AlignConsecutiveAssignmentsOptions.PadOperators);
 }
 
 void WhitespaceManager::alignConsecutiveBitFields() {
Index: clang/lib/Format/Format.cpp
===================================================================
--- clang/lib/Format/Format.cpp
+++ clang/lib/Format/Format.cpp
@@ -152,6 +152,15 @@
   }
 };
 
+template <>
+struct MappingTraits<FormatStyle::AlignConsecutiveAssignmentsOptionsT> {
+  static void mapping(IO &IO,
+                      FormatStyle::AlignConsecutiveAssignmentsOptionsT &Value) {
+    IO.mapOptional("AlignCompound", Value.AlignCompound);
+    IO.mapOptional("PadOperators", Value.PadOperators);
+  }
+};
+
 template <> struct ScalarEnumerationTraits<FormatStyle::AlignConsecutiveStyle> {
   static void enumeration(IO &IO, FormatStyle::AlignConsecutiveStyle &Value) {
     IO.enumCase(Value, "None", FormatStyle::ACS_None);
@@ -600,13 +609,15 @@
     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);
+    IO.mapOptional("AlignConsecutiveAssignmentsOptions",
+                   Style.AlignConsecutiveAssignmentsOptions);
     IO.mapOptional("AlignConsecutiveBitFields",
                    Style.AlignConsecutiveBitFields);
     IO.mapOptional("AlignConsecutiveDeclarations",
                    Style.AlignConsecutiveDeclarations);
+    IO.mapOptional("AlignConsecutiveMacros", Style.AlignConsecutiveMacros);
     IO.mapOptional("AlignEscapedNewlines", Style.AlignEscapedNewlines);
     IO.mapOptional("AlignOperands", Style.AlignOperands);
     IO.mapOptional("AlignTrailingComments", Style.AlignTrailingComments);
@@ -1138,6 +1149,8 @@
   LLVMStyle.AlignOperands = FormatStyle::OAS_Align;
   LLVMStyle.AlignTrailingComments = true;
   LLVMStyle.AlignConsecutiveAssignments = FormatStyle::ACS_None;
+  LLVMStyle.AlignConsecutiveAssignmentsOptions = {/*AlignCompound=*/false,
+                                                  /*PadOperators=*/true};
   LLVMStyle.AlignConsecutiveBitFields = FormatStyle::ACS_None;
   LLVMStyle.AlignConsecutiveDeclarations = FormatStyle::ACS_None;
   LLVMStyle.AlignConsecutiveMacros = FormatStyle::ACS_None;
Index: clang/include/clang/Format/Format.h
===================================================================
--- clang/include/clang/Format/Format.h
+++ clang/include/clang/Format/Format.h
@@ -148,6 +148,39 @@
     ACS_AcrossEmptyLinesAndComments
   };
 
+  ///
+  struct AlignConsecutiveAssignmentsOptionsT {
+    /// Whether compound assignments like ``+=``'s are aligned along
+    /// with ``=``'s.
+    /// \code
+    ///   true:
+    ///   a   &= 2;
+    ///   bbb  = 2;
+    ///
+    ///   false:
+    ///   a &= 2;
+    ///   bbb = 2;
+    /// \endcode
+    bool AlignCompound;
+    /// Whether short assignment operators are left-padded to the same
+    /// length as long ones in order to put all assignment operators to
+    /// the right of the left hand side.
+    /// \code
+    ///   true:
+    ///   a   >>= 2;
+    ///   bbb   = 2;
+    ///
+    ///   false:
+    ///   a >>= 2;
+    ///   bbb = 2;
+    /// \endcode
+    bool PadOperators;
+  };
+  /// Options for aligning consecutive assignments. They only take effect
+  /// when ``AlignConsecutiveAssignments`` is not ``None``.
+  /// \version 15
+  AlignConsecutiveAssignmentsOptionsT AlignConsecutiveAssignmentsOptions;
+
   /// Style of aligning consecutive macro definitions.
   ///
   /// ``Consecutive`` will result in formattings like:
@@ -3920,6 +3953,10 @@
            AlignAfterOpenBracket == R.AlignAfterOpenBracket &&
            AlignArrayOfStructures == R.AlignArrayOfStructures &&
            AlignConsecutiveAssignments == R.AlignConsecutiveAssignments &&
+           AlignConsecutiveAssignmentsOptions.AlignCompound ==
+               R.AlignConsecutiveAssignmentsOptions.AlignCompound &&
+           AlignConsecutiveAssignmentsOptions.PadOperators ==
+               R.AlignConsecutiveAssignmentsOptions.PadOperators &&
            AlignConsecutiveBitFields == R.AlignConsecutiveBitFields &&
            AlignConsecutiveDeclarations == R.AlignConsecutiveDeclarations &&
            AlignConsecutiveMacros == R.AlignConsecutiveMacros &&
Index: clang/docs/ClangFormatStyleOptions.rst
===================================================================
--- clang/docs/ClangFormatStyleOptions.rst
+++ clang/docs/ClangFormatStyleOptions.rst
@@ -340,6 +340,41 @@
        /* A comment. */
        double e         = 4;
 
+**AlignConsecutiveAssignmentsOptions** (``AlignConsecutiveAssignmentsOptionsT``) :versionbadge:`clang-format 15`
+  Options for aligning consecutive assignments. They only take effect
+  when ``AlignConsecutiveAssignments`` is not ``None``.
+
+  Nested configuration flags:
+
+
+  * ``bool AlignCompound`` Whether compound assignments like ``+=``'s are aligned along
+    with ``=``'s.
+
+    .. code-block:: c++
+
+      true:
+      a   &= 2;
+      bbb  = 2;
+
+      false:
+      a &= 2;
+      bbb = 2;
+
+  * ``bool PadOperators`` Whether short assignment operators are left-padded to the same
+    length as long ones in order to put all assignment operators to
+    the right of the left hand side.
+
+    .. code-block:: c++
+
+      true:
+      a   >>= 2;
+      bbb   = 2;
+
+      false:
+      a >>= 2;
+      bbb = 2;
+
+
 **AlignConsecutiveBitFields** (``AlignConsecutiveStyle``) :versionbadge:`clang-format 11`
   Style of aligning consecutive bit field.
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to