Handle adding new nested namespace in old namespace.
authorEric Liu <ioeric@google.com>
Thu, 10 Nov 2016 18:29:01 +0000 (18:29 +0000)
committerEric Liu <ioeric@google.com>
Thu, 10 Nov 2016 18:29:01 +0000 (18:29 +0000)
Reviewers: hokein

Subscribers: cfe-commits

Differential Revision: https://reviews.llvm.org/D26456

llvm-svn: 286486

clang-tools-extra/change-namespace/ChangeNamespace.cpp
clang-tools-extra/unittests/change-namespace/ChangeNamespaceTests.cpp

index 81fa607..a400a37 100644 (file)
@@ -57,16 +57,19 @@ SourceLocation EndLocationForType(TypeLoc TLoc) {
 }
 
 // Returns the containing namespace of `InnerNs` by skipping `PartialNsName`.
-// If the `InnerNs` does not have `PartialNsName` as suffix, nullptr is
-// returned.
+// If the `InnerNs` does not have `PartialNsName` as suffix, or `PartialNsName`
+// is empty, nullptr is returned.
 // For example, if `InnerNs` is "a::b::c" and `PartialNsName` is "b::c", then
 // the NamespaceDecl of namespace "a" will be returned.
 const NamespaceDecl *getOuterNamespace(const NamespaceDecl *InnerNs,
                                        llvm::StringRef PartialNsName) {
+  if (!InnerNs || PartialNsName.empty())
+    return nullptr;
   const auto *CurrentContext = llvm::cast<DeclContext>(InnerNs);
   const auto *CurrentNs = InnerNs;
   llvm::SmallVector<llvm::StringRef, 4> PartialNsNameSplitted;
-  PartialNsName.split(PartialNsNameSplitted, "::");
+  PartialNsName.split(PartialNsNameSplitted, "::", /*MaxSplit=*/-1,
+                      /*KeepEmpty=*/false);
   while (!PartialNsNameSplitted.empty()) {
     // Get the inner-most namespace in CurrentContext.
     while (CurrentContext && !llvm::isa<NamespaceDecl>(CurrentContext))
@@ -468,16 +471,20 @@ void ChangeNamespaceTool::moveOldNamespace(
   // "x::y" will be inserted inside the existing namespace "a" and after "a::b".
   // `OuterNs` is the first namespace in `DiffOldNamespace`, e.g. "namespace b"
   // in the above example.
-  // FIXME: consider the case where DiffOldNamespace is empty.
+  // If there is no outer namespace (i.e. DiffOldNamespace is empty), the new
+  // namespace will be a nested namespace in the old namespace.
   const NamespaceDecl *OuterNs = getOuterNamespace(NsDecl, DiffOldNamespace);
-  SourceLocation LocAfterNs =
-      getStartOfNextLine(OuterNs->getRBraceLoc(), *Result.SourceManager,
-                         Result.Context->getLangOpts());
-  assert(LocAfterNs.isValid() &&
-         "Failed to get location after DiffOldNamespace");
+  SourceLocation InsertionLoc = Start;
+  if (OuterNs) {
+    SourceLocation LocAfterNs =
+        getStartOfNextLine(OuterNs->getRBraceLoc(), *Result.SourceManager,
+                           Result.Context->getLangOpts());
+    assert(LocAfterNs.isValid() &&
+           "Failed to get location after DiffOldNamespace");
+    InsertionLoc = LocAfterNs;
+  }
   MoveNs.InsertionOffset = Result.SourceManager->getFileOffset(
-      Result.SourceManager->getSpellingLoc(LocAfterNs));
-
+      Result.SourceManager->getSpellingLoc(InsertionLoc));
   MoveNs.FID = Result.SourceManager->getFileID(Start);
   MoveNs.SourceMgr = Result.SourceManager;
   MoveNamespaces[R.getFilePath()].push_back(MoveNs);
index 8d10208..edd5e13 100644 (file)
@@ -97,6 +97,48 @@ TEST_F(ChangeNamespaceTest, SimpleMoveWithoutTypeRefs) {
   EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
 }
 
+TEST_F(ChangeNamespaceTest, NewNsNestedInOldNs) {
+  NewNamespace = "na::nb::nc";
+  std::string Code = "namespace na {\n"
+                     "namespace nb {\n"
+                     "class A {};\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+  std::string Expected = "namespace na {\n"
+                         "namespace nb {\n"
+                         "namespace nc {\n"
+                         "class A {};\n"
+                         "} // namespace nc\n"
+                         "\n"
+                         "} // namespace nb\n"
+                         "} // namespace na\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
+TEST_F(ChangeNamespaceTest, NewNsNestedInOldNsWithRefs) {
+  NewNamespace = "na::nb::nc";
+  std::string Code = "namespace na {\n"
+                     "class A {};\n"
+                     "namespace nb {\n"
+                     "class B {};\n"
+                     "class C {};\n"
+                     "void f() { A a; B b; }\n"
+                     "} // namespace nb\n"
+                     "} // namespace na\n";
+  std::string Expected = "namespace na {\n"
+                         "class A {};\n"
+                         "namespace nb {\n"
+                         "namespace nc {\n"
+                         "class B {};\n"
+                         "class C {};\n"
+                         "void f() { A a; B b; }\n"
+                         "} // namespace nc\n"
+                         "\n"
+                         "} // namespace nb\n"
+                         "} // namespace na\n";
+  EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
+}
+
 TEST_F(ChangeNamespaceTest, SimpleMoveIntoAnotherNestedNamespace) {
   NewNamespace = "na::nc";
   std::string Code = "namespace na {\n"