ioeric updated this revision to Diff 56696.
ioeric added a comment.

- Extended redundant comma cleanup to general lists, and change the way 
constructor initializer list is handled. Removed comments cleanup, leave it for 
a future patch.


http://reviews.llvm.org/D19804

Files:
  lib/Format/Format.cpp
  unittests/Format/CleanupTest.cpp

Index: unittests/Format/CleanupTest.cpp
===================================================================
--- unittests/Format/CleanupTest.cpp
+++ unittests/Format/CleanupTest.cpp
@@ -113,6 +113,133 @@
   EXPECT_EQ(Expected, Result);
 }
 
+TEST_F(CleanupTest, CtorInitializationSimpleRedundantComma) {
+  std::string Code = "class A {\nA() : , {} };";
+  std::string Expected = "class A {\nA()  {} };";
+  std::vector<tooling::Range> Ranges;
+  Ranges.push_back(tooling::Range(17, 0));
+  Ranges.push_back(tooling::Range(19, 0));
+  std::string Result = cleanup(Code, Ranges);
+  EXPECT_EQ(Expected, Result);
+
+  Code = "class A {\nA() : x(1), {} };";
+  Expected = "class A {\nA() : x(1) {} };";
+  Ranges.clear();
+  Ranges.push_back(tooling::Range(23, 0));
+  Result = cleanup(Code, Ranges);
+  EXPECT_EQ(Expected, Result);
+
+  Code = "class A {\nA() :,,,,{} };";
+  Expected = "class A {\nA() {} };";
+  Ranges.clear();
+  Ranges.push_back(tooling::Range(15, 0));
+  Result = cleanup(Code, Ranges);
+  EXPECT_EQ(Expected, Result);
+}
+
+TEST_F(CleanupTest, ListSimpleRedundantComma) {
+  std::string Code = "void f() { std::vector<int> v = {1,2,,,3}; }";
+  std::string Expected = "void f() { std::vector<int> v = {1,2,3}; }";
+  std::vector<tooling::Range> Ranges;
+  Ranges.push_back(tooling::Range(40, 0));
+  std::string Result = cleanup(Code, Ranges);
+  EXPECT_EQ(Expected, Result);
+
+  Code = "int main() { f(1,,2,3,,4); }";
+  Expected = "int main() { f(1,2,3,4); }";
+  Ranges.clear();
+  Ranges.push_back(tooling::Range(17, 0));
+  Ranges.push_back(tooling::Range(22, 0));
+  Result = cleanup(Code, Ranges);
+  EXPECT_EQ(Expected, Result);
+}
+
+TEST_F(CleanupTest, CtorInitializationBracesInParens) {
+  std::string Code = "class A {\nA() : x({1}),, {} };";
+  std::string Expected = "class A {\nA() : x({1}) {} };";
+  std::vector<tooling::Range> Ranges;
+  Ranges.push_back(tooling::Range(24, 0));
+  Ranges.push_back(tooling::Range(26, 0));
+  std::string Result = cleanup(Code, Ranges);
+  EXPECT_EQ(Expected, Result);
+}
+
+TEST_F(CleanupTest, RedundantCommaNotInAffectedRanges) {
+  std::string Code =
+      "class A {\nA() : x({1}), /* comment */, { int x = 0; } };";
+  std::string Expected =
+      "class A {\nA() : x({1}), /* comment */, { int x = 0; } };";
+  // Set the affected range to be "int x = 0", which does not intercept the
+  // constructor initialization list.
+  std::vector<tooling::Range> Ranges(1, tooling::Range(42, 9));
+  std::string Result = cleanup(Code, Ranges);
+  EXPECT_EQ(Expected, Result);
+
+  Code = "class A {\nA() : x(1), {} };";
+  Expected = "class A {\nA() : x(1), {} };";
+  // No range. Fixer should do nothing.
+  Ranges.clear();
+  Result = cleanup(Code, Ranges);
+  EXPECT_EQ(Expected, Result);
+}
+
+// FIXME: delete comments too.
+TEST_F(CleanupTest, CtorInitializationCommentAroundCommas) {
+  // Remove redundant commas around comment.
+  std::string Code = "class A {\nA() : x({1}), /* comment */, {} };";
+  std::string Expected = "class A {\nA() : x({1}) /* comment */ {} };";
+  std::vector<tooling::Range> Ranges;
+  Ranges.push_back(tooling::Range(25, 0));
+  Ranges.push_back(tooling::Range(40, 0));
+  std::string Result = cleanup(Code, Ranges);
+  EXPECT_EQ(Expected, Result);
+
+  // Remove trailing comma and ignore comment.
+  Code = "class A {\nA() : x({1}), // comment\n{} };";
+  Expected = "class A {\nA() : x({1}) // comment\n{} };";
+  Ranges = std::vector<tooling::Range>(1, tooling::Range(25, 0));
+  Result = cleanup(Code, Ranges);
+  EXPECT_EQ(Expected, Result);
+
+  // Remove trailing comma and ignore comment.
+  Code = "class A {\nA() : x({1}), // comment\n , y(1),{} };";
+  Expected = "class A {\nA() : x({1}), // comment\n  y(1){} };";
+  Ranges = std::vector<tooling::Range>(1, tooling::Range(38, 0));
+  Result = cleanup(Code, Ranges);
+  EXPECT_EQ(Expected, Result);
+
+  // Remove trailing comma and ignore comment.
+  Code = "class A {\nA() : x({1}), \n/* comment */, y(1),{} };";
+  Expected = "class A {\nA() : x({1}), \n/* comment */ y(1){} };";
+  Ranges = std::vector<tooling::Range>(1, tooling::Range(40, 0));
+  Result = cleanup(Code, Ranges);
+  EXPECT_EQ(Expected, Result);
+
+  // Remove trailing comma and ignore comment.
+  Code = "class A {\nA() : , // comment\n y(1),{} };";
+  Expected = "class A {\nA() :  // comment\n y(1){} };";
+  Ranges = std::vector<tooling::Range>(1, tooling::Range(17, 0));
+  Result = cleanup(Code, Ranges);
+  EXPECT_EQ(Expected, Result);
+}
+
+TEST_F(CleanupTest, CtorInitializerInNamespace) {
+  std::string Code = "namespace A {\n"
+                     "namespace B {\n" // missing r_brace
+                     "} // namespace A\n\n"
+                     "namespace C {\n"
+                     "class A { A() : x(0),, {} };\n"
+                     "inline namespace E { namespace { } }\n"
+                     "}";
+  std::string Expected = "namespace A {\n"
+                         "\n\n\nnamespace C {\n"
+                         "class A { A() : x(0) {} };\n   \n"
+                         "}";
+  std::vector<tooling::Range> Ranges(1, tooling::Range(0, Code.size()));
+  std::string Result = cleanup(Code, Ranges);
+  EXPECT_EQ(Expected, Result);
+}
+
 } // end namespace
 } // end namespace format
 } // end namespace clang
Index: lib/Format/Format.cpp
===================================================================
--- lib/Format/Format.cpp
+++ lib/Format/Format.cpp
@@ -1816,6 +1816,13 @@
 
     checkEmptyNamespace(AnnotatedLines);
 
+    for (auto &Line : AnnotatedLines) {
+      if (Line->Affected) {
+        cleanupRight(Line->First, Line->Last, tok::comma, tok::comma);
+        checkConstructorInitList(*Line);
+      }
+    }
+
     return generateFixes();
   }
 
@@ -1904,6 +1911,99 @@
     return true;
   }
 
+  FormatToken *getNextTokenNotDeletedUntilEnd(const FormatToken *Tok,
+                                              const FormatToken *End,
+                                              bool SkipComment) {
+    assert(Tok);
+    auto *Res = Tok->Next;
+    while (Res && ((SkipComment && Res->is(tok::comment)) ||
+                   DeletedTokens.find(Res) != DeletedTokens.end())) {
+      if (Res == End)
+        return nullptr;
+      Res = Res->Next;
+    }
+    return Res;
+  }
+
+  // Checks pairs {start, start->next},..., {end->previous, end} and deletes one
+  // of the token in the pair if the left token has \p LK token kind and the
+  // right token has \p RK token kind. If \p DeleteLeft is true, the left token
+  // is deleted on match; otherwise, the right token is deleted.
+  void cleanupTokenInMatchedPair(FormatToken *Start, FormatToken *End,
+                                 tok::TokenKind LK, tok::TokenKind RK,
+                                 bool DeleteLeft) {
+    for (auto *Left = Start; Left != nullptr && Left != End;) {
+      auto *Right =
+          getNextTokenNotDeletedUntilEnd(Left, End, /*SkipComment=*/true);
+      if (!Right)
+        break;
+      if (Left->is(LK) && Right->is(RK)) {
+        deleteToken(DeleteLeft ? Left : Right);
+        // If the right token is deleted, we should keep the left token
+        // unchanged and pair it with the new right token.
+        if (!DeleteLeft)
+          continue;
+      }
+      Left = getNextTokenNotDeletedUntilEnd(Left, End, /*SkipComment=*/true);
+    }
+  }
+
+  void cleanupLeft(FormatToken *Start, FormatToken *End, tok::TokenKind LK,
+                   tok::TokenKind RK) {
+    cleanupTokenInMatchedPair(Start, End, LK, RK, /*DeleteLeft=*/true);
+  }
+
+  void cleanupRight(FormatToken *Start, FormatToken *End, tok::TokenKind LK,
+                    tok::TokenKind RK) {
+    cleanupTokenInMatchedPair(Start, End, LK, RK, /*DeleteLeft=*/false);
+  }
+
+  // Looks for and removes redundant colon/commas in the constructor initializer
+  // list.
+  void checkConstructorInitList(AnnotatedLine &Line) {
+    FormatToken *Tok = Line.First;
+    while (Tok && Tok->Type != TT_CtorInitializerColon) {
+      Tok = Tok->Next;
+    }
+    if (!Tok)
+      return;
+
+    assert(Tok->is(tok::colon) && Tok->Type == TT_CtorInitializerColon);
+
+    // Clean up redundant comma after the constructor initializer colon.
+    cleanupRight(Tok, getNextTokenNotDeletedUntilEnd(Tok, Line.Last,
+                                                     /*SkipComment=*/true),
+                 tok::colon, tok::comma);
+
+    // Find the left brace at the end of the initializer list.
+    Tok = Line.Last;
+    while (Tok && Tok->is(tok::comment)) {
+      Tok = Tok->Previous;
+    }
+    if (!Tok || Tok->isNot(tok::l_brace))
+      return; // FIXME: error handling. Expecting tok::l_brace at the end.
+
+    // Clean up redundant commas/redundant colon right before the left brace.
+    Tok = Tok->Previous;
+    while (Tok && (Tok->is(tok::comment) ||
+                   DeletedTokens.find(Tok) != DeletedTokens.end())) {
+      Tok = Tok->Previous;
+    }
+    // We only need to check one token before the left brace.
+    //
+    // If the token before the left brace is a comma, then the token before the
+    // comma must not be the initializer colon; otherwise, the comma should have
+    // been deleted in the previous steps. It must not be a comma either since a
+    // comma right before another comma should be deleted in
+    // `cleanupLeft(tok::comma, tok::comma)`.
+    //
+    // If the token before the left brace is the intiailizer colon, then we can
+    // simply delete the redundant colon since the initializer list is empty.
+    if (Tok && (Tok->Type == TT_CtorInitializerColon || Tok->is(tok::comma))) {
+      deleteToken(Tok);
+    }
+  }
+
   // Delete the given token.
   inline void deleteToken(FormatToken *Tok) {
     if (Tok)
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to