forked from OSchip/llvm-project
[clangd] Improve heuristic resolution of dependent types in TargetFinder
* Try to apply heuristic resolution recursively to the base expression of a CXXDependentScopeMemberExpr. * Try to apply heuristic resolution recursively to the callee expression in a call expression. Fixes https://github.com/clangd/clangd/issues/441 Subscribers: ilya-biryukov, jkorous, arphaman, kadircet, usaxena95, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D82739
This commit is contained in:
parent
8953376478
commit
9946dcd3e9
|
@ -59,24 +59,32 @@ nodeToString(const ast_type_traits::DynTypedNode &N) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function for getMembersReferencedViaDependentName()
|
// Helper function for getMembersReferencedViaDependentName()
|
||||||
// which takes a dependent type `T` and heuristically
|
// which takes a possibly-dependent type `T` and heuristically
|
||||||
// resolves it to a CXXRecordDecl in which we can try name lookup.
|
// resolves it to a CXXRecordDecl in which we can try name lookup.
|
||||||
CXXRecordDecl *resolveTypeToRecordDecl(const Type *T) {
|
CXXRecordDecl *resolveTypeToRecordDecl(const Type *T) {
|
||||||
assert(T);
|
assert(T);
|
||||||
if (const auto *ICNT = T->getAs<InjectedClassNameType>()) {
|
|
||||||
|
if (const auto *RT = T->getAs<RecordType>())
|
||||||
|
return dyn_cast<CXXRecordDecl>(RT->getDecl());
|
||||||
|
|
||||||
|
if (const auto *ICNT = T->getAs<InjectedClassNameType>())
|
||||||
T = ICNT->getInjectedSpecializationType().getTypePtrOrNull();
|
T = ICNT->getInjectedSpecializationType().getTypePtrOrNull();
|
||||||
}
|
if (!T)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
const auto *TST = T->getAs<TemplateSpecializationType>();
|
const auto *TST = T->getAs<TemplateSpecializationType>();
|
||||||
if (!TST)
|
if (!TST)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
const ClassTemplateDecl *TD = dyn_cast_or_null<ClassTemplateDecl>(
|
const ClassTemplateDecl *TD = dyn_cast_or_null<ClassTemplateDecl>(
|
||||||
TST->getTemplateName().getAsTemplateDecl());
|
TST->getTemplateName().getAsTemplateDecl());
|
||||||
if (!TD)
|
if (!TD)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
return TD->getTemplatedDecl();
|
return TD->getTemplatedDecl();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Given a dependent type and a member name, heuristically resolve the
|
// Given a tag-decl type and a member name, heuristically resolve the
|
||||||
// name to one or more declarations.
|
// name to one or more declarations.
|
||||||
// The current heuristic is simply to look up the name in the primary
|
// The current heuristic is simply to look up the name in the primary
|
||||||
// template. This is a heuristic because the template could potentially
|
// template. This is a heuristic because the template could potentially
|
||||||
|
@ -154,6 +162,10 @@ const Type *getPointeeType(const Type *T) {
|
||||||
return FirstArg.getAsType().getTypePtrOrNull();
|
return FirstArg.getAsType().getTypePtrOrNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Forward declaration, needed as this function is mutually recursive
|
||||||
|
// with resolveDependentExprToDecls.
|
||||||
|
const Type *resolveDependentExprToType(const Expr *E);
|
||||||
|
|
||||||
// Try to heuristically resolve a dependent expression `E` to one
|
// Try to heuristically resolve a dependent expression `E` to one
|
||||||
// or more declarations that it likely references.
|
// or more declarations that it likely references.
|
||||||
std::vector<const NamedDecl *> resolveDependentExprToDecls(const Expr *E) {
|
std::vector<const NamedDecl *> resolveDependentExprToDecls(const Expr *E) {
|
||||||
|
@ -163,6 +175,15 @@ std::vector<const NamedDecl *> resolveDependentExprToDecls(const Expr *E) {
|
||||||
if (ME->isArrow()) {
|
if (ME->isArrow()) {
|
||||||
BaseType = getPointeeType(BaseType);
|
BaseType = getPointeeType(BaseType);
|
||||||
}
|
}
|
||||||
|
if (const auto *BT = BaseType->getAs<BuiltinType>()) {
|
||||||
|
// If BaseType is the type of a dependent expression, it's just
|
||||||
|
// represented as BultinType::Dependent which gives us no information. We
|
||||||
|
// can get further by analyzing the depedent expression.
|
||||||
|
Expr *Base = ME->isImplicitAccess() ? nullptr : ME->getBase();
|
||||||
|
if (Base && BT->getKind() == BuiltinType::Dependent) {
|
||||||
|
BaseType = resolveDependentExprToType(Base);
|
||||||
|
}
|
||||||
|
}
|
||||||
return getMembersReferencedViaDependentName(
|
return getMembersReferencedViaDependentName(
|
||||||
BaseType, [ME](ASTContext &) { return ME->getMember(); },
|
BaseType, [ME](ASTContext &) { return ME->getMember(); },
|
||||||
/*IsNonstaticMember=*/true);
|
/*IsNonstaticMember=*/true);
|
||||||
|
@ -173,7 +194,36 @@ std::vector<const NamedDecl *> resolveDependentExprToDecls(const Expr *E) {
|
||||||
[RE](ASTContext &) { return RE->getDeclName(); },
|
[RE](ASTContext &) { return RE->getDeclName(); },
|
||||||
/*IsNonstaticMember=*/false);
|
/*IsNonstaticMember=*/false);
|
||||||
}
|
}
|
||||||
|
if (const auto *CE = dyn_cast<CallExpr>(E)) {
|
||||||
|
const auto *CalleeType = resolveDependentExprToType(CE->getCallee());
|
||||||
|
if (!CalleeType)
|
||||||
return {};
|
return {};
|
||||||
|
if (const auto *FnTypePtr = CalleeType->getAs<PointerType>())
|
||||||
|
CalleeType = FnTypePtr->getPointeeType().getTypePtr();
|
||||||
|
if (const FunctionType *FnType = CalleeType->getAs<FunctionType>()) {
|
||||||
|
if (const auto *D =
|
||||||
|
resolveTypeToRecordDecl(FnType->getReturnType().getTypePtr())) {
|
||||||
|
return {D};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (const auto *ME = dyn_cast<MemberExpr>(E)) {
|
||||||
|
return {ME->getMemberDecl()};
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to heuristically resolve the type of a dependent expression `E`.
|
||||||
|
const Type *resolveDependentExprToType(const Expr *E) {
|
||||||
|
std::vector<const NamedDecl *> Decls = resolveDependentExprToDecls(E);
|
||||||
|
if (Decls.size() != 1) // Names an overload set -- just bail.
|
||||||
|
return nullptr;
|
||||||
|
if (const auto *TD = dyn_cast<TypeDecl>(Decls[0])) {
|
||||||
|
return TD->getTypeForDecl();
|
||||||
|
} else if (const auto *VD = dyn_cast<ValueDecl>(Decls[0])) {
|
||||||
|
return VD->getType().getTypePtrOrNull();
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const NamedDecl *getTemplatePattern(const NamedDecl *D) {
|
const NamedDecl *getTemplatePattern(const NamedDecl *D) {
|
||||||
|
@ -235,9 +285,8 @@ const NamedDecl *getTemplatePattern(const NamedDecl *D) {
|
||||||
// and both are lossy. We must know upfront what the caller ultimately wants.
|
// and both are lossy. We must know upfront what the caller ultimately wants.
|
||||||
//
|
//
|
||||||
// FIXME: improve common dependent scope using name lookup in primary templates.
|
// FIXME: improve common dependent scope using name lookup in primary templates.
|
||||||
// e.g. template<typename T> int foo() { return std::vector<T>().size(); }
|
// We currently handle DependentScopeDeclRefExpr and
|
||||||
// formally size() is unresolved, but the primary template is a good guess.
|
// CXXDependentScopeMemberExpr, but some other constructs remain to be handled:
|
||||||
// This affects:
|
|
||||||
// - DependentTemplateSpecializationType,
|
// - DependentTemplateSpecializationType,
|
||||||
// - DependentNameType
|
// - DependentNameType
|
||||||
// - UnresolvedUsingValueDecl
|
// - UnresolvedUsingValueDecl
|
||||||
|
|
|
@ -561,6 +561,74 @@ TEST_F(TargetDeclTest, OverloadExpr) {
|
||||||
EXPECT_DECLS("UnresolvedMemberExpr", "void func(int *)", "void func(char *)");
|
EXPECT_DECLS("UnresolvedMemberExpr", "void func(int *)", "void func(char *)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(TargetDeclTest, DependentExprs) {
|
||||||
|
Flags = {"-fno-delayed-template-parsing"};
|
||||||
|
|
||||||
|
// Heuristic resolution of method of dependent field
|
||||||
|
Code = R"cpp(
|
||||||
|
struct A { void foo() {} };
|
||||||
|
template <typename T>
|
||||||
|
struct B {
|
||||||
|
A a;
|
||||||
|
void bar() {
|
||||||
|
this->a.[[foo]]();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
)cpp";
|
||||||
|
EXPECT_DECLS("CXXDependentScopeMemberExpr", "void foo()");
|
||||||
|
|
||||||
|
// Similar to above but base expression involves a function call.
|
||||||
|
Code = R"cpp(
|
||||||
|
struct A {
|
||||||
|
void foo() {}
|
||||||
|
};
|
||||||
|
struct B {
|
||||||
|
A getA();
|
||||||
|
};
|
||||||
|
template <typename T>
|
||||||
|
struct C {
|
||||||
|
B c;
|
||||||
|
void bar() {
|
||||||
|
this->c.getA().[[foo]]();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
)cpp";
|
||||||
|
EXPECT_DECLS("CXXDependentScopeMemberExpr", "void foo()");
|
||||||
|
|
||||||
|
// Similar to above but uses a function pointer.
|
||||||
|
Code = R"cpp(
|
||||||
|
struct A {
|
||||||
|
void foo() {}
|
||||||
|
};
|
||||||
|
struct B {
|
||||||
|
using FPtr = A(*)();
|
||||||
|
FPtr fptr;
|
||||||
|
};
|
||||||
|
template <typename T>
|
||||||
|
struct C {
|
||||||
|
B c;
|
||||||
|
void bar() {
|
||||||
|
this->c.fptr().[[foo]]();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
)cpp";
|
||||||
|
EXPECT_DECLS("CXXDependentScopeMemberExpr", "void foo()");
|
||||||
|
|
||||||
|
// Base expression involves a member access into this.
|
||||||
|
Code = R"cpp(
|
||||||
|
struct Bar {
|
||||||
|
int aaaa;
|
||||||
|
};
|
||||||
|
template <typename T> struct Foo {
|
||||||
|
Bar func(int);
|
||||||
|
void test() {
|
||||||
|
func(1).[[aaaa]];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
)cpp";
|
||||||
|
EXPECT_DECLS("CXXDependentScopeMemberExpr", "int aaaa");
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(TargetDeclTest, ObjC) {
|
TEST_F(TargetDeclTest, ObjC) {
|
||||||
Flags = {"-xobjective-c"};
|
Flags = {"-xobjective-c"};
|
||||||
Code = R"cpp(
|
Code = R"cpp(
|
||||||
|
@ -718,7 +786,8 @@ protected:
|
||||||
|
|
||||||
TEST_F(FindExplicitReferencesTest, All) {
|
TEST_F(FindExplicitReferencesTest, All) {
|
||||||
std::pair</*Code*/ llvm::StringRef, /*References*/ llvm::StringRef> Cases[] =
|
std::pair</*Code*/ llvm::StringRef, /*References*/ llvm::StringRef> Cases[] =
|
||||||
{// Simple expressions.
|
{
|
||||||
|
// Simple expressions.
|
||||||
{R"cpp(
|
{R"cpp(
|
||||||
int global;
|
int global;
|
||||||
int func();
|
int func();
|
||||||
|
|
Loading…
Reference in New Issue