hokein updated this revision to Diff 77820.
hokein marked an inline comment as done.
hokein added a comment.

Fix a corner case, and add a test.


https://reviews.llvm.org/D26493

Files:
  clang-move/ClangMove.cpp
  unittests/clang-move/ClangMoveTests.cpp

Index: unittests/clang-move/ClangMoveTests.cpp
===================================================================
--- unittests/clang-move/ClangMoveTests.cpp
+++ unittests/clang-move/ClangMoveTests.cpp
@@ -127,8 +127,10 @@
 
 const char ExpectedNewHeader[] = "#ifndef NEW_FOO_H\n"
                                  "#define NEW_FOO_H\n"
+                                 "\n"
                                  "namespace a {\n"
                                  "class C1; // test\n"
+                                 "\n"
                                  "template <typename T> class C2;\n"
                                  "namespace b {\n"
                                  "// This is a Foo class\n"
@@ -144,6 +146,7 @@
                                  "}; // abc\n"
                                  "} // namespace b\n"
                                  "} // namespace a\n"
+                                 "\n"
                                  "#endif // NEW_FOO_H\n";
 
 const char ExpectedNewCC[] = "namespace a {\n"
@@ -154,17 +157,21 @@
                              "/// comment2.\n"
                              "int kConstInt1 = 0;\n"
                              "} // namespace\n"
+                             "\n"
                              "/* comment 3*/\n"
                              "static int kConstInt2 = 1;\n"
+                             "\n"
                              "/** comment4\n"
                              "*/\n"
                              "static int help() {\n"
                              "  int a = 0;\n"
                              "  return a;\n"
                              "}\n"
+                             "\n"
                              "// comment5\n"
                              "// comment5\n"
                              "void Foo::f() { f1(); }\n"
+                             "\n"
                              "/////////////\n"
                              "// comment //\n"
                              "/////////////\n"
@@ -312,15 +319,17 @@
 TEST(ClangMove, DontMoveAll) {
   const char ExpectedHeader[] = "#ifndef NEW_FOO_H\n"
                                 "#define NEW_FOO_H\n"
+                                "\n"
                                 "class A {\npublic:\n  int f();\n};\n"
+                                "\n"
                                 "#endif // NEW_FOO_H\n";
   const char Code[] = "#include \"foo.h\"\nint A::f() { return 0; }";
   std::vector<std::string> TestHeaders = {
-    "typedef int Int;\nclass A {\npublic:\n  int f();\n};",
-    "using Int=int;\nclass A {\npublic:\n  int f();\n};",
-    "class B {};\nclass A {\npublic:\n  int f();\n};",
-    "void f() {};\nclass A {\npublic:\n  int f();\n};",
-    "enum Color { RED };\nclass A {\npublic:\n  int f();\n};",
+    "typedef int Int;\nclass A {\npublic:\n  int f();\n};\n",
+    "using Int=int;\nclass A {\npublic:\n  int f();\n};\n",
+    "class B {};\nclass A {\npublic:\n  int f();\n};\n",
+    "void f() {};\nclass A {\npublic:\n  int f();\n};\n",
+    "enum Color { RED };\nclass A {\npublic:\n  int f();\n};\n",
   };
   move::ClangMoveTool::MoveDefinitionSpec Spec;
   Spec.Names.push_back("A");
@@ -332,7 +341,7 @@
     auto Results = runClangMoveOnCode(Spec, Header.c_str(), Code);
     EXPECT_EQ(ExpectedHeader, Results[Spec.NewHeader]);
     // The expected old header should not contain class A definition.
-    std::string ExpectedOldHeader = Header.substr(0, Header.size() - 31);
+    std::string ExpectedOldHeader = Header.substr(0, Header.size() - 32);
     EXPECT_EQ(ExpectedOldHeader, Results[Spec.OldHeader]);
   }
 }
@@ -355,6 +364,62 @@
   EXPECT_EQ(ExpectedNewCode, Results[Spec.NewCC]);
 }
 
+TEST(ClangMove, WellFormattedCode) {
+  const std::string CommonHeader =
+      "namespace a {\n"
+      "namespace b {\n"
+      "namespace c {\n"
+      "class C;\n"
+      "\n"
+      "class A {\npublic:\n  void f();\n  void f2();\n};\n"
+      "} // namespace c\n"
+      "} // namespace b\n"
+      "\n"
+      "namespace d {\n"
+      "namespace e {\n"
+      "class B {\npublic:\n  void f();\n};\n"
+      "} // namespace e\n"
+      "} // namespace d\n"
+      "} // namespace a\n";
+  const std::string CommonCode = "\n"
+                                 "namespace a {\n"
+                                 "namespace b {\n"
+                                 "namespace c {\n"
+                                 "void A::f() {}\n"
+                                 "\n"
+                                 "void A::f2() {}\n"
+                                 "} // namespace c\n"
+                                 "} // namespace b\n"
+                                 "\n"
+                                 "namespace d {\n"
+                                 "namespace e {\n"
+                                 "void B::f() {}\n"
+                                 "} // namespace e\n"
+                                 "} // namespace d\n"
+                                 "} // namespace a\n";
+  // Add dummy class to prevent behavior of moving all declarations from header.
+  const std::string TestHeader = CommonHeader + "class D {};\n";
+  const std::string TestCode = "#include \"foo.h\"\n" + CommonCode;
+  const std::string ExpectedNewHeader = "#ifndef NEW_FOO_H\n"
+                                        "#define NEW_FOO_H\n"
+                                        "\n" +
+                                        CommonHeader +
+                                        "\n"
+                                        "#endif // NEW_FOO_H\n";
+  const std::string ExpectedNewCC = "#include \"new_foo.h\"\n" + CommonCode;
+  move::ClangMoveTool::MoveDefinitionSpec Spec;
+  Spec.Names.push_back("a::b::c::A");
+  Spec.Names.push_back("a::d::e::B");
+  Spec.OldHeader = "foo.h";
+  Spec.OldCC = "foo.cc";
+  Spec.NewHeader = "new_foo.h";
+  Spec.NewCC = "new_foo.cc";
+  auto Results = runClangMoveOnCode(Spec, TestHeader.c_str(), TestCode.c_str());
+  EXPECT_EQ(ExpectedNewCC, Results[Spec.NewCC]);
+  EXPECT_EQ(ExpectedNewHeader, Results[Spec.NewHeader]);
+}
+
+
 } // namespace
 } // namespce move
 } // namespace clang
Index: clang-move/ClangMove.cpp
===================================================================
--- clang-move/ClangMove.cpp
+++ clang-move/ClangMove.cpp
@@ -294,7 +294,7 @@
     }
     GuardName = StringRef(GuardName).upper();
     NewCode += "#ifndef " + GuardName + "\n";
-    NewCode += "#define " + GuardName + "\n";
+    NewCode += "#define " + GuardName + "\n\n";
   }
 
   // Add #Includes.
@@ -306,40 +306,63 @@
 
   // Add moved class definition and its related declarations. All declarations
   // in same namespace are grouped together.
+  //
+  // Record namespaces where the current position is in.
   std::vector<std::string> CurrentNamespaces;
   for (const auto &MovedDecl : Decls) {
+    // The namespaces of the declaration being moved.
     std::vector<std::string> DeclNamespaces = GetNamespaces(MovedDecl.Decl);
     auto CurrentIt = CurrentNamespaces.begin();
     auto DeclIt = DeclNamespaces.begin();
+    // Skip the common prefix.
     while (CurrentIt != CurrentNamespaces.end() &&
            DeclIt != DeclNamespaces.end()) {
       if (*CurrentIt != *DeclIt)
         break;
       ++CurrentIt;
       ++DeclIt;
     }
+    // Calculate the new namespaces after adding MovedDecl in CurrentNamespace,
+    // which is used for next iteration of this loop.
     std::vector<std::string> NextNamespaces(CurrentNamespaces.begin(),
                                             CurrentIt);
     NextNamespaces.insert(NextNamespaces.end(), DeclIt, DeclNamespaces.end());
+
+
+    // End with CurrentNamespace.
+    bool HasEndCurrentNamespace = false;
     auto RemainingSize = CurrentNamespaces.end() - CurrentIt;
     for (auto It = CurrentNamespaces.rbegin(); RemainingSize > 0;
          --RemainingSize, ++It) {
       assert(It < CurrentNamespaces.rend());
       NewCode += "} // namespace " + *It + "\n";
+      HasEndCurrentNamespace = true;
     }
+    // Add trailing '\n' after the nested namespace definition.
+    if (HasEndCurrentNamespace)
+      NewCode += "\n";
+
+    // If the moved declaration is not in CurrentNamespace, add extra namespace
+    // definitions.
+    bool IsInNewNamespace = false;
     while (DeclIt != DeclNamespaces.end()) {
       NewCode += "namespace " + *DeclIt + " {\n";
+      IsInNewNamespace = true;
       ++DeclIt;
     }
+    // If the moved declaration is in same namespace CurrentNamespace, add
+    // a preceeding `\n' before the moved declaration.
+    if (!IsInNewNamespace)
+      NewCode += "\n";
     NewCode += getDeclarationSourceText(MovedDecl.Decl, MovedDecl.SM);
     CurrentNamespaces = std::move(NextNamespaces);
   }
   std::reverse(CurrentNamespaces.begin(), CurrentNamespaces.end());
   for (const auto &NS : CurrentNamespaces)
     NewCode += "} // namespace " + NS + "\n";
 
   if (IsHeader)
-    NewCode += "#endif // " + GuardName + "\n";
+    NewCode += "\n#endif // " + GuardName + "\n";
   return clang::tooling::Replacements(
       clang::tooling::Replacement(FileName, 0, 0, NewCode));
 }
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to