[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)
.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 @@ void ChangeNamespaceTool::run(
Result.Nodes.getNodeAs<UsingDirectiveDecl>(
"using_namespace")) {
UsingNamespaceDecls.insert(UsingNamespace);
} else if (const auto *NamespaceAlias =
Result.Nodes.getNodeAs<NamespaceAliasDecl>(
"namespace_alias")) {
NamespaceAliasDecls.insert(NamespaceAlias);
} else if (const auto *NsDecl =
Result.Nodes.getNodeAs<NamespaceDecl>("old_ns")) {
moveOldNamespace(Result, NsDecl);
@ -687,6 +696,38 @@ void ChangeNamespaceTool::replaceQualifiedSymbolInDeclContext(
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
// qualified name.
bool Matched = false;

View File

@ -154,6 +154,9 @@ private:
// Records all using namespace declarations, which can be used to shorten
// namespace specifiers.
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
// be fixed.
llvm::SmallVector<TypeLoc, 8> BaseCtorInitializerTypeLocs;

View File

@ -825,6 +825,114 @@ TEST_F(ChangeNamespaceTest, UsingNamespaceInGlobal) {
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) {
std::string Code = "namespace glob {\n"
"class Glob {};\n"