diff --git a/clang-tools-extra/clangd/HeuristicResolver.h b/clang-tools-extra/clangd/HeuristicResolver.h --- a/clang-tools-extra/clangd/HeuristicResolver.h +++ b/clang-tools-extra/clangd/HeuristicResolver.h @@ -94,6 +94,11 @@ // `E`. const Type *resolveExprToType(const Expr *E) const; std::vector resolveExprToDecls(const Expr *E) const; + + // Helper function for HeuristicResolver::resolveDependentMember() + // which takes a possibly-dependent type `T` and heuristically + // resolves it to a CXXRecordDecl in which we can try name lookup. + CXXRecordDecl *resolveTypeToRecordDecl(const Type *T) const; }; } // namespace clangd diff --git a/clang-tools-extra/clangd/HeuristicResolver.cpp b/clang-tools-extra/clangd/HeuristicResolver.cpp --- a/clang-tools-extra/clangd/HeuristicResolver.cpp +++ b/clang-tools-extra/clangd/HeuristicResolver.cpp @@ -10,6 +10,7 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/ExprCXX.h" +#include "clang/AST/Type.h" namespace clang { namespace clangd { @@ -29,15 +30,39 @@ return isa(D); }; +namespace { + +const Type *resolveDeclsToType(const std::vector &Decls, + ASTContext &Ctx) { + if (Decls.size() != 1) // Names an overload set -- just bail. + return nullptr; + if (const auto *TD = dyn_cast(Decls[0])) { + return Ctx.getTypeDeclType(TD).getTypePtr(); + } + if (const auto *VD = dyn_cast(Decls[0])) { + return VD->getType().getTypePtrOrNull(); + } + return nullptr; +} + +} // namespace + // Helper function for HeuristicResolver::resolveDependentMember() // which takes a possibly-dependent type `T` and heuristically // resolves it to a CXXRecordDecl in which we can try name lookup. -CXXRecordDecl *resolveTypeToRecordDecl(const Type *T) { +CXXRecordDecl *HeuristicResolver::resolveTypeToRecordDecl(const Type *T) const { assert(T); // Unwrap type sugar such as type aliases. T = T->getCanonicalTypeInternal().getTypePtr(); + if (const auto *DNT = T->getAs()) { + T = resolveDeclsToType(resolveDependentNameType(DNT), Ctx); + if (!T) + return nullptr; + T = T->getCanonicalTypeInternal().getTypePtr(); + } + if (const auto *RT = T->getAs()) return dyn_cast(RT->getDecl()); @@ -185,18 +210,6 @@ DTST->getIdentifier(), TemplateFilter); } -const Type *resolveDeclsToType(const std::vector &Decls) { - if (Decls.size() != 1) // Names an overload set -- just bail. - return nullptr; - if (const auto *TD = dyn_cast(Decls[0])) { - return TD->getTypeForDecl(); - } - if (const auto *VD = dyn_cast(Decls[0])) { - return VD->getType().getTypePtrOrNull(); - } - return nullptr; -} - std::vector HeuristicResolver::resolveExprToDecls(const Expr *E) const { if (const auto *ME = dyn_cast(E)) { @@ -220,7 +233,7 @@ const Type *HeuristicResolver::resolveExprToType(const Expr *E) const { std::vector Decls = resolveExprToDecls(E); if (!Decls.empty()) - return resolveDeclsToType(Decls); + return resolveDeclsToType(Decls, Ctx); return E->getType().getTypePtr(); } @@ -239,9 +252,11 @@ case NestedNameSpecifier::TypeSpecWithTemplate: return NNS->getAsType(); case NestedNameSpecifier::Identifier: { - return resolveDeclsToType(resolveDependentMember( - resolveNestedNameSpecifierToType(NNS->getPrefix()), - NNS->getAsIdentifier(), TypeFilter)); + return resolveDeclsToType( + resolveDependentMember( + resolveNestedNameSpecifierToType(NNS->getPrefix()), + NNS->getAsIdentifier(), TypeFilter), + Ctx); } default: break; diff --git a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp --- a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp +++ b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp @@ -877,6 +877,22 @@ } )cpp"; EXPECT_DECLS("CXXDependentScopeMemberExpr", "void find()"); + + Code = R"cpp( + template + struct Waldo { + void find(); + }; + template + struct MetaWaldo { + using Type = Waldo; + }; + template + void foo(typename MetaWaldo::Type w) { + w.[[find]](); + } + )cpp"; + EXPECT_DECLS("CXXDependentScopeMemberExpr", "void find()"); } TEST_F(TargetDeclTest, DependentTypes) {