https://github.com/zyn0217 updated https://github.com/llvm/llvm-project/pull/71279
>From d73a8e2ee683e6812c21cb1de7363b14565a96d1 Mon Sep 17 00:00:00 2001 From: Younan Zhang <zyn7...@gmail.com> Date: Sat, 4 Nov 2023 18:43:58 +0800 Subject: [PATCH 1/2] [clangd] Resolve the dependent type from its single instantiation. Take 1 This is an enhancement to the HeuristicResolver, trying to extract the deduced type from the single instantiation for a template. This partially addresses the point #1 from https://github.com/clangd/clangd/issues/1768. This patch doesn't tackle CXXUnresolvedConstructExpr or similarities since I feel that is more arduous and would prefer to leave it for my future work. --- .../clangd/HeuristicResolver.cpp | 101 ++++++++++++++++++ .../clangd/unittests/XRefsTests.cpp | 48 +++++++++ 2 files changed, 149 insertions(+) diff --git a/clang-tools-extra/clangd/HeuristicResolver.cpp b/clang-tools-extra/clangd/HeuristicResolver.cpp index 3c147b6b582bf0b..d3dced9b325367a 100644 --- a/clang-tools-extra/clangd/HeuristicResolver.cpp +++ b/clang-tools-extra/clangd/HeuristicResolver.cpp @@ -7,10 +7,14 @@ //===----------------------------------------------------------------------===// #include "HeuristicResolver.h" +#include "AST.h" #include "clang/AST/ASTContext.h" #include "clang/AST/CXXInheritance.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/ExprCXX.h" +#include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/Type.h" namespace clang { @@ -46,6 +50,98 @@ const Type *resolveDeclsToType(const std::vector<const NamedDecl *> &Decls, return nullptr; } +// Visitor that helps to extract deduced type from instantiated entities. +// This merely performs the source location comparison against each Decl +// until it finds a Decl with the same location as the +// dependent one. Its associated type will then be extracted. +struct InstantiatedDeclVisitor : RecursiveASTVisitor<InstantiatedDeclVisitor> { + + InstantiatedDeclVisitor(NamedDecl *DependentDecl) : DependentDecl(DependentDecl) {} + + bool shouldVisitTemplateInstantiations() const { return true; } + + bool shouldVisitLambdaBody() const { return true; } + + bool shouldVisitImplicitCode() const { return true; } + + template <typename D> + bool onDeclVisited(D *MaybeInstantiated) { + if (MaybeInstantiated->getDeclContext()->isDependentContext()) + return true; + auto *Dependent = dyn_cast<D>(DependentDecl); + if (!Dependent) + return true; + auto LHS = MaybeInstantiated->getTypeSourceInfo(), + RHS = Dependent->getTypeSourceInfo(); + if (!LHS || !RHS) + return true; + if (LHS->getTypeLoc().getSourceRange() != + RHS->getTypeLoc().getSourceRange()) + return true; + DeducedType = MaybeInstantiated->getType(); + return false; + } + + bool VisitFieldDecl(FieldDecl *FD) { + return onDeclVisited(FD); + } + + bool VisitVarDecl(VarDecl *VD) { + return onDeclVisited(VD); + } + + NamedDecl *DependentDecl; + QualType DeducedType; +}; + +/// Attempt to resolve the dependent type from the surrounding context for which +/// a single instantiation is available. +const Type * +resolveTypeFromInstantiatedTemplate(const CXXDependentScopeMemberExpr *Expr) { + if (Expr->isImplicitAccess()) + return nullptr; + + auto *Base = Expr->getBase(); + NamedDecl *ND = nullptr; + if (auto *CXXMember = dyn_cast<MemberExpr>(Base)) + ND = CXXMember->getMemberDecl(); + + if (auto *DRExpr = dyn_cast<DeclRefExpr>(Base)) + ND = DRExpr->getFoundDecl(); + + // FIXME: Handle CXXUnresolvedConstructExpr. This kind of type doesn't have + // available Decls to be matched against. Which inhibits the current heuristic + // from resolving expressions such as `T().fo^o()`, where T is a + // single-instantiated template parameter. + if (!ND) + return nullptr; + + NamedDecl *Instantiation = nullptr; + + // Find out a single instantiation that we can start with. The enclosing + // context for the current Decl might not be a templated entity (e.g. a member + // function inside a class template), hence we shall walk up the decl + // contexts first. + for (auto *EnclosingContext = ND->getDeclContext(); EnclosingContext; + EnclosingContext = EnclosingContext->getParent()) { + if (auto *ND = dyn_cast<NamedDecl>(EnclosingContext)) { + Instantiation = getOnlyInstantiation(ND); + if (Instantiation) + break; + } + } + + if (!Instantiation) + return nullptr; + + // This will traverse down the instantiation entity, visit each Decl, and + // extract the deduced type for the undetermined Decl `ND`. + InstantiatedDeclVisitor Visitor(ND); + Visitor.TraverseDecl(Instantiation); + + return Visitor.DeducedType.getTypePtrOrNull(); +} + } // namespace // Helper function for HeuristicResolver::resolveDependentMember() @@ -150,6 +246,11 @@ std::vector<const NamedDecl *> HeuristicResolver::resolveMemberExpr( if (ME->isArrow()) { BaseType = getPointeeType(BaseType); } + + if (BaseType->isDependentType()) + if (auto *MaybeResolved = resolveTypeFromInstantiatedTemplate(ME)) + BaseType = MaybeResolved; + if (!BaseType) return {}; if (const auto *BT = BaseType->getAs<BuiltinType>()) { diff --git a/clang-tools-extra/clangd/unittests/XRefsTests.cpp b/clang-tools-extra/clangd/unittests/XRefsTests.cpp index f53cbf01b7992c8..ead24dec575de0d 100644 --- a/clang-tools-extra/clangd/unittests/XRefsTests.cpp +++ b/clang-tools-extra/clangd/unittests/XRefsTests.cpp @@ -1222,6 +1222,54 @@ TEST(LocateSymbol, TextualSmoke) { hasID(getSymbolID(&findDecl(AST, "MyClass")))))); } +TEST(LocateSymbol, DeduceDependentTypeFromSingleInstantiation) { + Annotations T(R"cpp( + struct WildCat { + void $wild_meow[[meow]](); + }; + + struct DomesticCat { + void $domestic_meow[[meow]](); + }; + + template <typename Ours> + struct Human { + template <typename Others> + void feed(Others O) { + O.me$1^ow(); + Others Child; + Child.me$2^ow(); + // FIXME: Others().me^ow(); + Ours Baby; + Baby.me$3^ow(); + // struct Inner { + // Ours Pet; + // }; + // Inner().Pet.me^ow(); + auto Lambda = [](auto C) { + C.me$4^ow(); + }; + Lambda(Others()); + } + }; + + void foo() { + Human<DomesticCat>().feed(WildCat()); + } + )cpp"); + + auto TU = TestTU::withCode(T.code()); + auto AST = TU.build(); + EXPECT_THAT(locateSymbolAt(AST, T.point("1")), + ElementsAre(sym("meow", T.range("wild_meow"), std::nullopt))); + EXPECT_THAT(locateSymbolAt(AST, T.point("2")), + ElementsAre(sym("meow", T.range("wild_meow"), std::nullopt))); + EXPECT_THAT(locateSymbolAt(AST, T.point("3")), + ElementsAre(sym("meow", T.range("domestic_meow"), std::nullopt))); + EXPECT_THAT(locateSymbolAt(AST, T.point("4")), + ElementsAre(sym("meow", T.range("wild_meow"), std::nullopt))); +} + TEST(LocateSymbol, Textual) { const char *Tests[] = { R"cpp(// Comment >From 41728d1c1e1245d9069c663290a2f8a14e766e01 Mon Sep 17 00:00:00 2001 From: Younan Zhang <zyn7...@gmail.com> Date: Sat, 11 Nov 2023 11:46:58 +0800 Subject: [PATCH 2/2] Don't resolve for null base type --- clang-tools-extra/clangd/HeuristicResolver.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/clang-tools-extra/clangd/HeuristicResolver.cpp b/clang-tools-extra/clangd/HeuristicResolver.cpp index d3dced9b325367a..a3a9bcd60ad2566 100644 --- a/clang-tools-extra/clangd/HeuristicResolver.cpp +++ b/clang-tools-extra/clangd/HeuristicResolver.cpp @@ -247,12 +247,13 @@ std::vector<const NamedDecl *> HeuristicResolver::resolveMemberExpr( BaseType = getPointeeType(BaseType); } + if (!BaseType) + return {}; + if (BaseType->isDependentType()) if (auto *MaybeResolved = resolveTypeFromInstantiatedTemplate(ME)) BaseType = MaybeResolved; - if (!BaseType) - return {}; if (const auto *BT = BaseType->getAs<BuiltinType>()) { // If BaseType is the type of a dependent expression, it's just // represented as BuiltinType::Dependent which gives us no information. We _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits