forked from OSchip/llvm-project
Do not do raw name replacement when FromDecl is a class forward-declaration.
Summary: If the `FromDecl` is a class forward declaration, the reference is still considered as referring to the original definition given the nature of forward-declarations, so we can't do a raw name replacement in this case. Reviewers: bkramer Subscribers: cfe-commits, klimek Differential Revision: https://reviews.llvm.org/D27132 llvm-svn: 287929
This commit is contained in:
parent
84b6f26eca
commit
6135581cdf
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include "clang/Tooling/Core/Lookup.h"
|
||||
#include "clang/AST/Decl.h"
|
||||
#include "clang/AST/DeclCXX.h"
|
||||
using namespace clang;
|
||||
using namespace clang::tooling;
|
||||
|
||||
|
@ -121,14 +122,20 @@ std::string tooling::replaceNestedName(const NestedNameSpecifier *Use,
|
|||
"Expected fully-qualified name!");
|
||||
|
||||
// We can do a raw name replacement when we are not inside the namespace for
|
||||
// the original function and it is not in the global namespace. The
|
||||
// the original class/function and it is not in the global namespace. The
|
||||
// assumption is that outside the original namespace we must have a using
|
||||
// statement that makes this work out and that other parts of this refactor
|
||||
// will automatically fix using statements to point to the new function
|
||||
// will automatically fix using statements to point to the new class/function.
|
||||
// However, if the `FromDecl` is a class forward declaration, the reference is
|
||||
// still considered as referring to the original definition, so we can't do a
|
||||
// raw name replacement in this case.
|
||||
const bool class_name_only = !Use;
|
||||
const bool in_global_namespace =
|
||||
isa<TranslationUnitDecl>(FromDecl->getDeclContext());
|
||||
if (class_name_only && !in_global_namespace &&
|
||||
const bool is_class_forward_decl =
|
||||
isa<CXXRecordDecl>(FromDecl) &&
|
||||
!cast<CXXRecordDecl>(FromDecl)->isCompleteDefinition();
|
||||
if (class_name_only && !in_global_namespace && !is_class_forward_decl &&
|
||||
!usingFromDifferentCanonicalNamespace(FromDecl->getDeclContext(),
|
||||
UseContext)) {
|
||||
auto Pos = ReplacementString.rfind("::");
|
||||
|
|
|
@ -13,7 +13,9 @@ using namespace clang;
|
|||
|
||||
namespace {
|
||||
struct GetDeclsVisitor : TestVisitor<GetDeclsVisitor> {
|
||||
std::function<void(CallExpr *)> OnCall;
|
||||
std::function<void(CallExpr *)> OnCall = [&](CallExpr *Expr) {};
|
||||
std::function<void(RecordTypeLoc)> OnRecordTypeLoc = [&](RecordTypeLoc Type) {
|
||||
};
|
||||
SmallVector<Decl *, 4> DeclStack;
|
||||
|
||||
bool VisitCallExpr(CallExpr *Expr) {
|
||||
|
@ -21,6 +23,11 @@ struct GetDeclsVisitor : TestVisitor<GetDeclsVisitor> {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool VisitRecordTypeLoc(RecordTypeLoc Loc) {
|
||||
OnRecordTypeLoc(Loc);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TraverseDecl(Decl *D) {
|
||||
DeclStack.push_back(D);
|
||||
bool Ret = TestVisitor::TraverseDecl(D);
|
||||
|
@ -29,7 +36,7 @@ struct GetDeclsVisitor : TestVisitor<GetDeclsVisitor> {
|
|||
}
|
||||
};
|
||||
|
||||
TEST(LookupTest, replaceNestedName) {
|
||||
TEST(LookupTest, replaceNestedFunctionName) {
|
||||
GetDeclsVisitor Visitor;
|
||||
|
||||
auto replaceCallExpr = [&](const CallExpr *Expr,
|
||||
|
@ -121,4 +128,37 @@ TEST(LookupTest, replaceNestedName) {
|
|||
"} } }\n");
|
||||
}
|
||||
|
||||
TEST(LookupTest, replaceNestedClassName) {
|
||||
GetDeclsVisitor Visitor;
|
||||
|
||||
auto replaceRecordTypeLoc = [&](RecordTypeLoc Loc,
|
||||
StringRef ReplacementString) {
|
||||
const auto *FD = cast<CXXRecordDecl>(Loc.getDecl());
|
||||
return tooling::replaceNestedName(
|
||||
nullptr, Visitor.DeclStack.back()->getDeclContext(), FD,
|
||||
ReplacementString);
|
||||
};
|
||||
|
||||
Visitor.OnRecordTypeLoc = [&](RecordTypeLoc Type) {
|
||||
// Filter Types by name since there are other `RecordTypeLoc` in the test
|
||||
// file.
|
||||
if (Type.getDecl()->getQualifiedNameAsString() == "a::b::Foo")
|
||||
EXPECT_EQ("x::Bar", replaceRecordTypeLoc(Type, "::a::x::Bar"));
|
||||
};
|
||||
Visitor.runOver("namespace a { namespace b {\n"
|
||||
"class Foo;\n"
|
||||
"namespace c { Foo f();; }\n"
|
||||
"} }\n");
|
||||
|
||||
Visitor.OnRecordTypeLoc = [&](RecordTypeLoc Type) {
|
||||
// Filter Types by name since there are other `RecordTypeLoc` in the test
|
||||
// file.
|
||||
// `a::b::Foo` in using shadow decl is not `TypeLoc`.
|
||||
if (Type.getDecl()->getQualifiedNameAsString() == "a::b::Foo")
|
||||
EXPECT_EQ("Bar", replaceRecordTypeLoc(Type, "::a::x::Bar"));
|
||||
};
|
||||
Visitor.runOver("namespace a { namespace b { class Foo {}; } }\n"
|
||||
"namespace c { using a::b::Foo; Foo f();; }\n");
|
||||
}
|
||||
|
||||
} // end anonymous namespace
|
||||
|
|
Loading…
Reference in New Issue