From: Nathan Ridge Date: Tue, 29 Sep 2020 07:19:59 +0000 (-0400) Subject: [clangd] Heuristic resolution for dependent type and template names X-Git-Tag: llvmorg-13-init~9518 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=1b962fdd5f365a10684d9f70d703ae101c20d37a;p=platform%2Fupstream%2Fllvm.git [clangd] Heuristic resolution for dependent type and template names Fixes https://github.com/clangd/clangd/issues/543 Differential Revision: https://reviews.llvm.org/D88469 --- diff --git a/clang-tools-extra/clangd/FindTarget.cpp b/clang-tools-extra/clangd/FindTarget.cpp index 4cf62d3..19ffdbb 100644 --- a/clang-tools-extra/clangd/FindTarget.cpp +++ b/clang-tools-extra/clangd/FindTarget.cpp @@ -125,6 +125,10 @@ const auto StaticFilter = [](const NamedDecl *D) { return !D->isCXXInstanceMember(); }; const auto ValueFilter = [](const NamedDecl *D) { return isa(D); }; +const auto TypeFilter = [](const NamedDecl *D) { return isa(D); }; +const auto TemplateFilter = [](const NamedDecl *D) { + return isa(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 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 Decls = resolveExprToDecls(E); +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(); - } else if (const auto *VD = dyn_cast(Decls[0])) { + } + if (const auto *VD = dyn_cast(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(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! diff --git a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp index e4f584b..1ea2aa3 100644 --- a/clang-tools-extra/clangd/unittests/FindTargetTests.cpp +++ b/clang-tools-extra/clangd/unittests/FindTargetTests.cpp @@ -728,6 +728,54 @@ TEST_F(TargetDeclTest, DependentExprs) { "template T convert() const"); } +TEST_F(TargetDeclTest, DependentTypes) { + Flags = {"-fno-delayed-template-parsing"}; + + // Heuristic resolution of dependent type name + Code = R"cpp( + template + struct A { struct B {}; }; + + template + void foo(typename A::[[B]]); + )cpp"; + EXPECT_DECLS("DependentNameTypeLoc", "struct B"); + + // Heuristic resolution of dependent type name which doesn't get a TypeLoc + Code = R"cpp( + template + struct A { struct B { struct C {}; }; }; + + template + void foo(typename A::[[B]]::C); + )cpp"; + EXPECT_DECLS("NestedNameSpecifierLoc", "struct B"); + + // Heuristic resolution of dependent type name whose qualifier is also + // dependent + Code = R"cpp( + template + struct A { struct B { struct C {}; }; }; + + template + void foo(typename A::B::[[C]]); + )cpp"; + EXPECT_DECLS("DependentNameTypeLoc", "struct C"); + + // Heuristic resolution of dependent template name + Code = R"cpp( + template + struct A { + template struct B {}; + }; + + template + void foo(typename A::template [[B]]); + )cpp"; + EXPECT_DECLS("DependentTemplateSpecializationTypeLoc", + "template struct B"); +} + TEST_F(TargetDeclTest, ObjC) { Flags = {"-xobjective-c"}; Code = R"cpp(