[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
Fri Nov 10 20:08:32 PST 2023
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 <zyn7109 at gmail.com>
Date: Sat, 4 Nov 2023 18:43:58 +0800
Subject: [PATCH 1/3] [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 <zyn7109 at gmail.com>
Date: Sat, 11 Nov 2023 11:46:58 +0800
Subject: [PATCH 2/3] 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
>From bd05ba6a472c5a03dc70fcb92dfcf671d0cdb09d Mon Sep 17 00:00:00 2001
From: Younan Zhang <zyn7109 at gmail.com>
Date: Sat, 11 Nov 2023 12:07:58 +0800
Subject: [PATCH 3/3] Format
---
clang-tools-extra/clangd/HeuristicResolver.cpp | 14 +++++---------
1 file changed, 5 insertions(+), 9 deletions(-)
diff --git a/clang-tools-extra/clangd/HeuristicResolver.cpp b/clang-tools-extra/clangd/HeuristicResolver.cpp
index a3a9bcd60ad2566..90de811b10330d9 100644
--- a/clang-tools-extra/clangd/HeuristicResolver.cpp
+++ b/clang-tools-extra/clangd/HeuristicResolver.cpp
@@ -56,7 +56,8 @@ const Type *resolveDeclsToType(const std::vector<const NamedDecl *> &Decls,
// dependent one. Its associated type will then be extracted.
struct InstantiatedDeclVisitor : RecursiveASTVisitor<InstantiatedDeclVisitor> {
- InstantiatedDeclVisitor(NamedDecl *DependentDecl) : DependentDecl(DependentDecl) {}
+ InstantiatedDeclVisitor(NamedDecl *DependentDecl)
+ : DependentDecl(DependentDecl) {}
bool shouldVisitTemplateInstantiations() const { return true; }
@@ -64,8 +65,7 @@ struct InstantiatedDeclVisitor : RecursiveASTVisitor<InstantiatedDeclVisitor> {
bool shouldVisitImplicitCode() const { return true; }
- template <typename D>
- bool onDeclVisited(D *MaybeInstantiated) {
+ template <typename D> bool onDeclVisited(D *MaybeInstantiated) {
if (MaybeInstantiated->getDeclContext()->isDependentContext())
return true;
auto *Dependent = dyn_cast<D>(DependentDecl);
@@ -82,13 +82,9 @@ struct InstantiatedDeclVisitor : RecursiveASTVisitor<InstantiatedDeclVisitor> {
return false;
}
- bool VisitFieldDecl(FieldDecl *FD) {
- return onDeclVisited(FD);
- }
+ bool VisitFieldDecl(FieldDecl *FD) { return onDeclVisited(FD); }
- bool VisitVarDecl(VarDecl *VD) {
- return onDeclVisited(VD);
- }
+ bool VisitVarDecl(VarDecl *VD) { return onDeclVisited(VD); }
NamedDecl *DependentDecl;
QualType DeducedType;
More information about the cfe-commits
mailing list