[clang-tools-extra] [clangd] Resolve the dependent type from its single instantiation. Take 1 (PR #71279)

Younan Zhang via cfe-commits cfe-commits at lists.llvm.org
Sat Nov 4 04:15:44 PDT 2023


https://github.com/zyn0217 created https://github.com/llvm/llvm-project/pull/71279

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.

>From d73a8e2ee683e6812c21cb1de7363b14565a96d1 Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Sat, 4 Nov 2023 18:43:58 +0800
Subject: [PATCH] [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



More information about the cfe-commits mailing list