From 6aa9416d0666e21640d37171688e8607e8f71307 Mon Sep 17 00:00:00 2001 From: Eric Liu Date: Thu, 10 Nov 2016 18:29:01 +0000 Subject: [PATCH] Handle adding new nested namespace in old namespace. Reviewers: hokein Subscribers: cfe-commits Differential Revision: https://reviews.llvm.org/D26456 llvm-svn: 286486 --- .../change-namespace/ChangeNamespace.cpp | 29 +++++++++------ .../change-namespace/ChangeNamespaceTests.cpp | 42 ++++++++++++++++++++++ 2 files changed, 60 insertions(+), 11 deletions(-) diff --git a/clang-tools-extra/change-namespace/ChangeNamespace.cpp b/clang-tools-extra/change-namespace/ChangeNamespace.cpp index 81fa607..a400a37 100644 --- a/clang-tools-extra/change-namespace/ChangeNamespace.cpp +++ b/clang-tools-extra/change-namespace/ChangeNamespace.cpp @@ -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(InnerNs); const auto *CurrentNs = InnerNs; llvm::SmallVector 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(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); diff --git a/clang-tools-extra/unittests/change-namespace/ChangeNamespaceTests.cpp b/clang-tools-extra/unittests/change-namespace/ChangeNamespaceTests.cpp index 8d10208..edd5e13 100644 --- a/clang-tools-extra/unittests/change-namespace/ChangeNamespaceTests.cpp +++ b/clang-tools-extra/unittests/change-namespace/ChangeNamespaceTests.cpp @@ -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" -- 2.7.4