[clangd] Heuristic resolution for dependent type and template names

Fixes https://github.com/clangd/clangd/issues/543

Differential Revision: https://reviews.llvm.org/D88469
This commit is contained in:
Nathan Ridge 2020-09-29 03:19:59 -04:00
parent 039126c97d
commit 1b962fdd5f
2 changed files with 103 additions and 11 deletions

View File

@ -125,6 +125,10 @@ const auto StaticFilter = [](const NamedDecl *D) {
return !D->isCXXInstanceMember();
};
const auto ValueFilter = [](const NamedDecl *D) { return isa<ValueDecl>(D); };
const auto TypeFilter = [](const NamedDecl *D) { return isa<TypeDecl>(D); };
const auto TemplateFilter = [](const NamedDecl *D) {
return isa<TemplateDecl>(D);
};
// Given the type T of a dependent expression that appears of the LHS of a
// "->", heuristically find a corresponding pointee type in whose scope we
@ -219,19 +223,45 @@ std::vector<const NamedDecl *> resolveExprToDecls(const Expr *E) {
return {};
}
// Try to heuristically resolve the type of a possibly-dependent expression `E`.
const Type *resolveExprToType(const Expr *E) {
std::vector<const NamedDecl *> Decls = resolveExprToDecls(E);
const Type *resolveDeclsToType(const std::vector<const NamedDecl *> &Decls) {
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])) {
}
if (const auto *VD = dyn_cast<ValueDecl>(Decls[0])) {
return VD->getType().getTypePtrOrNull();
}
return nullptr;
}
// Try to heuristically resolve the type of a possibly-dependent expression `E`.
const Type *resolveExprToType(const Expr *E) {
return resolveDeclsToType(resolveExprToDecls(E));
}
// Try to heuristically resolve the type of a possibly-dependent nested name
// specifier.
const Type *resolveNestedNameSpecifierToType(const NestedNameSpecifier *NNS) {
if (!NNS)
return nullptr;
switch (NNS->getKind()) {
case NestedNameSpecifier::TypeSpec:
case NestedNameSpecifier::TypeSpecWithTemplate:
return NNS->getAsType();
case NestedNameSpecifier::Identifier: {
return resolveDeclsToType(getMembersReferencedViaDependentName(
resolveNestedNameSpecifierToType(NNS->getPrefix()),
[&](const ASTContext &) { return NNS->getAsIdentifier(); },
TypeFilter));
}
default:
break;
}
return nullptr;
}
const NamedDecl *getTemplatePattern(const NamedDecl *D) {
if (const CXXRecordDecl *CRD = dyn_cast<CXXRecordDecl>(D)) {
if (const auto *Result = CRD->getTemplateInstantiationPattern())
@ -291,10 +321,8 @@ const NamedDecl *getTemplatePattern(const NamedDecl *D) {
// and both are lossy. We must know upfront what the caller ultimately wants.
//
// FIXME: improve common dependent scope using name lookup in primary templates.
// We currently handle DependentScopeDeclRefExpr and
// CXXDependentScopeMemberExpr, but some other constructs remain to be handled:
// - DependentTemplateSpecializationType,
// - DependentNameType
// We currently handle several dependent constructs, but some others remain to
// be handled:
// - UnresolvedUsingTypenameDecl
struct TargetFinder {
using RelSet = DeclRelationSet;
@ -536,6 +564,23 @@ public:
if (auto *TD = DTST->getTemplateName().getAsTemplateDecl())
Outer.add(TD->getTemplatedDecl(), Flags | Rel::TemplatePattern);
}
void VisitDependentNameType(const DependentNameType *DNT) {
for (const NamedDecl *ND : getMembersReferencedViaDependentName(
resolveNestedNameSpecifierToType(DNT->getQualifier()),
[DNT](ASTContext &) { return DNT->getIdentifier(); },
TypeFilter)) {
Outer.add(ND, Flags);
}
}
void VisitDependentTemplateSpecializationType(
const DependentTemplateSpecializationType *DTST) {
for (const NamedDecl *ND : getMembersReferencedViaDependentName(
resolveNestedNameSpecifierToType(DTST->getQualifier()),
[DTST](ASTContext &) { return DTST->getIdentifier(); },
TemplateFilter)) {
Outer.add(ND, Flags);
}
}
void VisitTypedefType(const TypedefType *TT) {
Outer.add(TT->getDecl(), Flags);
}
@ -591,17 +636,16 @@ public:
return;
debug(*NNS, Flags);
switch (NNS->getKind()) {
case NestedNameSpecifier::Identifier:
return;
case NestedNameSpecifier::Namespace:
add(NNS->getAsNamespace(), Flags);
return;
case NestedNameSpecifier::NamespaceAlias:
add(NNS->getAsNamespaceAlias(), Flags);
return;
case NestedNameSpecifier::Identifier:
case NestedNameSpecifier::TypeSpec:
case NestedNameSpecifier::TypeSpecWithTemplate:
add(QualType(NNS->getAsType(), 0), Flags);
add(QualType(resolveNestedNameSpecifierToType(NNS), 0), Flags);
return;
case NestedNameSpecifier::Global:
// This should be TUDecl, but we can't get a pointer to it!

View File

@ -728,6 +728,54 @@ TEST_F(TargetDeclTest, DependentExprs) {
"template <typename T> T convert() const");
}
TEST_F(TargetDeclTest, DependentTypes) {
Flags = {"-fno-delayed-template-parsing"};
// Heuristic resolution of dependent type name
Code = R"cpp(
template <typename>
struct A { struct B {}; };
template <typename T>
void foo(typename A<T>::[[B]]);
)cpp";
EXPECT_DECLS("DependentNameTypeLoc", "struct B");
// Heuristic resolution of dependent type name which doesn't get a TypeLoc
Code = R"cpp(
template <typename>
struct A { struct B { struct C {}; }; };
template <typename T>
void foo(typename A<T>::[[B]]::C);
)cpp";
EXPECT_DECLS("NestedNameSpecifierLoc", "struct B");
// Heuristic resolution of dependent type name whose qualifier is also
// dependent
Code = R"cpp(
template <typename>
struct A { struct B { struct C {}; }; };
template <typename T>
void foo(typename A<T>::B::[[C]]);
)cpp";
EXPECT_DECLS("DependentNameTypeLoc", "struct C");
// Heuristic resolution of dependent template name
Code = R"cpp(
template <typename>
struct A {
template <typename> struct B {};
};
template <typename T>
void foo(typename A<T>::template [[B]]<int>);
)cpp";
EXPECT_DECLS("DependentTemplateSpecializationTypeLoc",
"template <typename> struct B");
}
TEST_F(TargetDeclTest, ObjC) {
Flags = {"-xobjective-c"};
Code = R"cpp(