Skip to content

Commit

Permalink
[change-namespace] don't fix using shadow decls in classes.
Browse files Browse the repository at this point in the history
Summary:
Using shadow declarations in classes always refers to base class, which does not
need to be fixed/qualified since it can be inferred from inheritance.

Reviewers: bkramer

Subscribers: cfe-commits

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

llvm-svn: 288919
  • Loading branch information
Eric Liu committed Dec 7, 2016
1 parent c3c6463 commit 8685c76
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 10 deletions.
34 changes: 24 additions & 10 deletions clang-tools-extra/change-namespace/ChangeNamespace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,12 @@ void ChangeNamespaceTool::registerMatchers(ast_matchers::MatchFinder *Finder) {
hasAncestor(cxxRecordDecl()),
allOf(IsInMovedNs, unless(cxxRecordDecl(unless(isDefinition())))))));

// Using shadow declarations in classes always refers to base class, which
// does not need to be qualified since it can be inferred from inheritance.
// Note that this does not match using alias declarations.
auto UsingShadowDeclInClass =
usingDecl(hasAnyUsingShadowDecl(decl()), hasParent(cxxRecordDecl()));

// Match TypeLocs on the declaration. Carefully match only the outermost
// TypeLoc and template specialization arguments (which are not outermost)
// that are directly linked to types matching `DeclMatcher`. Nested name
Expand All @@ -337,28 +343,36 @@ void ChangeNamespaceTool::registerMatchers(ast_matchers::MatchFinder *Finder) {
unless(anyOf(hasParent(typeLoc(loc(qualType(
allOf(hasDeclaration(DeclMatcher),
unless(templateSpecializationType())))))),
hasParent(nestedNameSpecifierLoc()))),
hasParent(nestedNameSpecifierLoc()),
hasAncestor(isImplicit()),
hasAncestor(UsingShadowDeclInClass))),
hasAncestor(decl().bind("dc")))
.bind("type"),
this);

// Types in `UsingShadowDecl` is not matched by `typeLoc` above, so we need to
// special case it.
Finder->addMatcher(usingDecl(IsInMovedNs, hasAnyUsingShadowDecl(decl()))
// Since using declarations inside classes must have the base class in the
// nested name specifier, we leave it to the nested name specifier matcher.
Finder->addMatcher(usingDecl(IsInMovedNs, hasAnyUsingShadowDecl(decl()),
unless(UsingShadowDeclInClass))
.bind("using_with_shadow"),
this);

// Handle types in nested name specifier. Specifiers that are in a TypeLoc
// matched above are not matched, e.g. "A::" in "A::A" is not matched since
// "A::A" would have already been fixed.
Finder->addMatcher(nestedNameSpecifierLoc(
hasAncestor(decl(IsInMovedNs).bind("dc")),
loc(nestedNameSpecifier(specifiesType(
hasDeclaration(DeclMatcher.bind("from_decl"))))),
unless(hasAncestor(typeLoc(loc(qualType(hasDeclaration(
decl(equalsBoundNode("from_decl")))))))))
.bind("nested_specifier_loc"),
this);
Finder->addMatcher(
nestedNameSpecifierLoc(
hasAncestor(decl(IsInMovedNs).bind("dc")),
loc(nestedNameSpecifier(
specifiesType(hasDeclaration(DeclMatcher.bind("from_decl"))))),
unless(anyOf(hasAncestor(isImplicit()),
hasAncestor(UsingShadowDeclInClass),
hasAncestor(typeLoc(loc(qualType(hasDeclaration(
decl(equalsBoundNode("from_decl"))))))))))
.bind("nested_specifier_loc"),
this);

// Matches base class initializers in constructors. TypeLocs of base class
// initializers do not need to be fixed. For example,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,36 @@ TEST_F(ChangeNamespaceTest, FixUsingShadowDecl) {
EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
}

TEST_F(ChangeNamespaceTest, DontFixUsingShadowDeclInClasses) {
std::string Code = "namespace na {\n"
"class A {};\n"
"class Base { public: Base() {} void m() {} };\n"
"namespace nb {\n"
"class D : public Base {\n"
"public:\n"
" using AA = A; using B = Base;\n"
" using Base::m; using Base::Base;\n"
"};"
"} // namespace nb\n"
"} // namespace na\n";

std::string Expected = "namespace na {\n"
"class A {};\n"
"class Base { public: Base() {} void m() {} };\n"
"\n"
"} // namespace na\n"
"namespace x {\n"
"namespace y {\n"
"class D : public na::Base {\n"
"public:\n"
" using AA = na::A; using B = na::Base;\n"
" using Base::m; using Base::Base;\n"
"};"
"} // namespace y\n"
"} // namespace x\n";
EXPECT_EQ(format(Expected), runChangeNamespaceOnCode(Code));
}

TEST_F(ChangeNamespaceTest, TypeInNestedNameSpecifier) {
std::string Code =
"namespace na {\n"
Expand Down

0 comments on commit 8685c76

Please sign in to comment.