Index: change-namespace/ChangeNamespace.h =================================================================== --- change-namespace/ChangeNamespace.h +++ change-namespace/ChangeNamespace.h @@ -154,6 +154,9 @@ // Records all using namespace declarations, which can be used to shorten // namespace specifiers. llvm::SmallPtrSet UsingNamespaceDecls; + // Records all namespace alias declarations, which can be used to shorten + // namespace specifiers. + llvm::SmallPtrSet NamespaceAliasDecls; // TypeLocs of CXXCtorInitializer. Types of CXXCtorInitializers do not need to // be fixed. llvm::SmallVector BaseCtorInitializerTypeLocs; Index: change-namespace/ChangeNamespace.cpp =================================================================== --- change-namespace/ChangeNamespace.cpp +++ change-namespace/ChangeNamespace.cpp @@ -306,6 +306,11 @@ IsVisibleInNewNs) .bind("using_namespace"), this); + // Match namespace alias declarations. + Finder->addMatcher(namespaceAliasDecl(isExpansionInFileMatching(FilePattern), + IsVisibleInNewNs) + .bind("namespace_alias"), + this); // Match old namespace blocks. Finder->addMatcher( @@ -429,6 +434,10 @@ Result.Nodes.getNodeAs( "using_namespace")) { UsingNamespaceDecls.insert(UsingNamespace); + } else if (const auto *NamespaceAlias = + Result.Nodes.getNodeAs( + "namespace_alias")) { + NamespaceAliasDecls.insert(NamespaceAlias); } else if (const auto *NsDecl = Result.Nodes.getNodeAs("old_ns")) { moveOldNamespace(Result, NsDecl); @@ -687,6 +696,38 @@ ReplaceName = FromDeclNameRef; } } + // Checks if there is any namespace alias declarations that can shorten the + // qualified name. + for (const auto *NamespaceAlias : NamespaceAliasDecls) { + if (!isDeclVisibleAtLocation(*Result.SourceManager, NamespaceAlias, DeclCtx, + Start)) + continue; + StringRef FromDeclNameRef = FromDeclName; + if (FromDeclNameRef.consume_front( + NamespaceAlias->getNamespace()->getQualifiedNameAsString())) { + FromDeclNameRef = FromDeclNameRef.drop_front(2); // Drop the leading "::". + std::string AliasName = NamespaceAlias->getNameAsString(); + std::string AliasQualifiedName = + NamespaceAlias->getQualifiedNameAsString(); + // We only consider namespace aliases define in the global namepspace or + // in namespaces that are directly visible from the reference, i.e. + // ancestor of the `OldNs`. Note that declarations in ancestor namespaces + // but not visible in the new namespace is filtered out by + // "IsVisibleInNewNs" matcher. + if (AliasQualifiedName != AliasName) { + // The alias is defined in some namespace. + assert(AliasQualifiedName.size() >= AliasName.size() + 2); + llvm::StringRef AliasNs = + StringRef(AliasQualifiedName).drop_back(AliasName.size() + 2); + if (!llvm::StringRef(OldNs).startswith(AliasNs)) + continue; + } + std::string NameWithAliasNamespace = + (AliasName + "::" + FromDeclNameRef).str(); + if (NameWithAliasNamespace.size() < ReplaceName.size()) + ReplaceName = NameWithAliasNamespace; + } + } // Checks if there is any using shadow declarations that can shorten the // qualified name. bool Matched = false; Index: unittests/change-namespace/ChangeNamespaceTests.cpp =================================================================== --- unittests/change-namespace/ChangeNamespaceTests.cpp +++ unittests/change-namespace/ChangeNamespaceTests.cpp @@ -825,6 +825,109 @@ EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); } +TEST_F(ChangeNamespaceTest, NamespaceAliasInGlobal) { + std::string Code = "namespace glob {\n" + "class Glob {};\n" + "}\n" + "namespace gl = glob;\n" + "namespace na {\n" + "namespace nb {\n" + "void f() { gl::Glob g; }\n" + "} // namespace nb\n" + "} // namespace na\n"; + + std::string Expected = "namespace glob {\n" + "class Glob {};\n" + "}\n" + "namespace gl = glob;\n" + "\n" + "namespace x {\n" + "namespace y {\n" + "void f() { gl::Glob g; }\n" + "} // namespace y\n" + "} // namespace x\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, NamespaceAliasInNamespace) { + std::string Code = "namespace glob {\n" + "class Glob {};\n" + "}\n" + "namespace na {\n" + "namespace nb {\n" + "namespace gl = glob;\n" + "void f() { gl::Glob g; }\n" + "} // namespace nb\n" + "} // namespace na\n"; + + std::string Expected = "namespace glob {\n" + "class Glob {};\n" + "}\n" + "\n" + "namespace x {\n" + "namespace y {\n" + "namespace gl = glob;\n" + "void f() { gl::Glob g; }\n" + "} // namespace y\n" + "} // namespace x\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, NamespaceAliasInAncestorNamespace) { + NewNamespace = "na::nx"; + std::string Code = "namespace glob {\n" + "class Glob {};\n" + "}\n" + "namespace other { namespace gl = glob; }\n" + "namespace na {\n" + "namespace ga = glob;\n" + "namespace nb {\n" + "void f() { ga::Glob g; }\n" + "} // namespace nb\n" + "} // namespace na\n"; + + std::string Expected = "namespace glob {\n" + "class Glob {};\n" + "}\n" + "namespace other { namespace gl = glob; }\n" + "namespace na {\n" + "namespace ga = glob;\n" + "\n" + "namespace nx {\n" + "void f() { ga::Glob g; }\n" + "} // namespace nx\n" + "} // namespace na\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + +TEST_F(ChangeNamespaceTest, NamespaceAliasInOtherNamespace) { + std::string Code = "namespace glob {\n" + "class Glob {};\n" + "}\n" + "namespace other { namespace gl = glob; }\n" + "namespace na {\n" + "namespace ga = glob;\n" + "namespace nb {\n" + "void f() { glob::Glob g; }\n" + "} // namespace nb\n" + "} // namespace na\n"; + + std::string Expected = "namespace glob {\n" + "class Glob {};\n" + "}\n" + "namespace other { namespace gl = glob; }\n" + "namespace na {\n" + "namespace ga = glob;\n" + "\n" + "} // namespace na\n" + "namespace x {\n" + "namespace y {\n" + "void f() { glob::Glo g; }\n" + "} // namespace y\n" + "} // namespace x\n"; + EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); +} + TEST_F(ChangeNamespaceTest, UsingDeclAfterReference) { std::string Code = "namespace glob {\n" "class Glob {};\n"