[change-namespace] consider namespace aliases to shorten qualified names.

Reviewers: hokein

Subscribers: cfe-commits

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

llvm-svn: 290421
This commit is contained in:
Eric Liu 2016-12-23 10:47:09 +00:00
parent 0ff941620c
commit 180dac6396
3 changed files with 152 additions and 0 deletions

View File

@ -306,6 +306,11 @@ void ChangeNamespaceTool::registerMatchers(ast_matchers::MatchFinder *Finder) {
IsVisibleInNewNs) IsVisibleInNewNs)
.bind("using_namespace"), .bind("using_namespace"),
this); this);
// Match namespace alias declarations.
Finder->addMatcher(namespaceAliasDecl(isExpansionInFileMatching(FilePattern),
IsVisibleInNewNs)
.bind("namespace_alias"),
this);
// Match old namespace blocks. // Match old namespace blocks.
Finder->addMatcher( Finder->addMatcher(
@ -429,6 +434,10 @@ void ChangeNamespaceTool::run(
Result.Nodes.getNodeAs<UsingDirectiveDecl>( Result.Nodes.getNodeAs<UsingDirectiveDecl>(
"using_namespace")) { "using_namespace")) {
UsingNamespaceDecls.insert(UsingNamespace); UsingNamespaceDecls.insert(UsingNamespace);
} else if (const auto *NamespaceAlias =
Result.Nodes.getNodeAs<NamespaceAliasDecl>(
"namespace_alias")) {
NamespaceAliasDecls.insert(NamespaceAlias);
} else if (const auto *NsDecl = } else if (const auto *NsDecl =
Result.Nodes.getNodeAs<NamespaceDecl>("old_ns")) { Result.Nodes.getNodeAs<NamespaceDecl>("old_ns")) {
moveOldNamespace(Result, NsDecl); moveOldNamespace(Result, NsDecl);
@ -687,6 +696,38 @@ void ChangeNamespaceTool::replaceQualifiedSymbolInDeclContext(
ReplaceName = FromDeclNameRef; 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() +
"::")) {
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(StringRef(AliasQualifiedName).endswith("::" + AliasName));
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 // Checks if there is any using shadow declarations that can shorten the
// qualified name. // qualified name.
bool Matched = false; bool Matched = false;

View File

@ -154,6 +154,9 @@ private:
// Records all using namespace declarations, which can be used to shorten // Records all using namespace declarations, which can be used to shorten
// namespace specifiers. // namespace specifiers.
llvm::SmallPtrSet<const UsingDirectiveDecl *, 8> UsingNamespaceDecls; llvm::SmallPtrSet<const UsingDirectiveDecl *, 8> UsingNamespaceDecls;
// Records all namespace alias declarations, which can be used to shorten
// namespace specifiers.
llvm::SmallPtrSet<const NamespaceAliasDecl *, 8> NamespaceAliasDecls;
// TypeLocs of CXXCtorInitializer. Types of CXXCtorInitializers do not need to // TypeLocs of CXXCtorInitializer. Types of CXXCtorInitializers do not need to
// be fixed. // be fixed.
llvm::SmallVector<TypeLoc, 8> BaseCtorInitializerTypeLocs; llvm::SmallVector<TypeLoc, 8> BaseCtorInitializerTypeLocs;

View File

@ -825,6 +825,114 @@ TEST_F(ChangeNamespaceTest, UsingNamespaceInGlobal) {
EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code)); EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
} }
TEST_F(ChangeNamespaceTest, NamespaceAliasInGlobal) {
std::string Code = "namespace glob {\n"
"class Glob {};\n"
"}\n"
"namespace glob2 { class Glob2 {}; }\n"
"namespace gl = glob;\n"
"namespace gl2 = ::glob2;\n"
"namespace na {\n"
"namespace nb {\n"
"void f() { gl::Glob g; gl2::Glob2 g2; }\n"
"} // namespace nb\n"
"} // namespace na\n";
std::string Expected =
"namespace glob {\n"
"class Glob {};\n"
"}\n"
"namespace glob2 { class Glob2 {}; }\n"
"namespace gl = glob;\n"
"namespace gl2 = ::glob2;\n"
"\n"
"namespace x {\n"
"namespace y {\n"
"void f() { gl::Glob g; gl2::Glob2 g2; }\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::Glob g; }\n"
"} // namespace y\n"
"} // namespace x\n";
EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
}
TEST_F(ChangeNamespaceTest, UsingDeclAfterReference) { TEST_F(ChangeNamespaceTest, UsingDeclAfterReference) {
std::string Code = "namespace glob {\n" std::string Code = "namespace glob {\n"
"class Glob {};\n" "class Glob {};\n"