Skip to content

Commit c0703d7

Browse files
committed
[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 clangd/clangd#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.
1 parent 99eb843 commit c0703d7

File tree

2 files changed

+149
-0
lines changed

2 files changed

+149
-0
lines changed

clang-tools-extra/clangd/HeuristicResolver.cpp

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,14 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "HeuristicResolver.h"
10+
#include "AST.h"
1011
#include "clang/AST/ASTContext.h"
1112
#include "clang/AST/CXXInheritance.h"
13+
#include "clang/AST/Decl.h"
14+
#include "clang/AST/DeclCXX.h"
1215
#include "clang/AST/DeclTemplate.h"
1316
#include "clang/AST/ExprCXX.h"
17+
#include "clang/AST/RecursiveASTVisitor.h"
1418
#include "clang/AST/Type.h"
1519

1620
namespace clang {
@@ -46,6 +50,98 @@ const Type *resolveDeclsToType(const std::vector<const NamedDecl *> &Decls,
4650
return nullptr;
4751
}
4852

53+
// Visitor that helps to extract deduced type from instantiated entities.
54+
// This merely performs the source location comparison against each Decl
55+
// until it finds a Decl with the same location as the
56+
// dependent one. Its associated type will then be extracted.
57+
struct InstantiatedDeclVisitor : RecursiveASTVisitor<InstantiatedDeclVisitor> {
58+
59+
InstantiatedDeclVisitor(NamedDecl *DependentDecl) : DependentDecl(DependentDecl) {}
60+
61+
bool shouldVisitTemplateInstantiations() const { return true; }
62+
63+
bool shouldVisitLambdaBody() const { return true; }
64+
65+
bool shouldVisitImplicitCode() const { return true; }
66+
67+
template <typename D>
68+
bool onDeclVisited(D *MaybeInstantiated) {
69+
if (MaybeInstantiated->getDeclContext()->isDependentContext())
70+
return true;
71+
auto *Dependent = dyn_cast<D>(DependentDecl);
72+
if (!Dependent)
73+
return true;
74+
auto LHS = MaybeInstantiated->getTypeSourceInfo(),
75+
RHS = Dependent->getTypeSourceInfo();
76+
if (!LHS || !RHS)
77+
return true;
78+
if (LHS->getTypeLoc().getSourceRange() !=
79+
RHS->getTypeLoc().getSourceRange())
80+
return true;
81+
DeducedType = MaybeInstantiated->getType();
82+
return false;
83+
}
84+
85+
bool VisitFieldDecl(FieldDecl *FD) {
86+
return onDeclVisited(FD);
87+
}
88+
89+
bool VisitVarDecl(VarDecl *VD) {
90+
return onDeclVisited(VD);
91+
}
92+
93+
NamedDecl *DependentDecl;
94+
QualType DeducedType;
95+
};
96+
97+
/// Attempt to resolve the dependent type from the surrounding context for which
98+
/// a single instantiation is available.
99+
const Type *
100+
resolveTypeFromInstantiatedTemplate(const CXXDependentScopeMemberExpr *Expr) {
101+
if (Expr->isImplicitAccess())
102+
return nullptr;
103+
104+
auto *Base = Expr->getBase();
105+
NamedDecl *ND = nullptr;
106+
if (auto *CXXMember = dyn_cast<MemberExpr>(Base))
107+
ND = CXXMember->getMemberDecl();
108+
109+
if (auto *DRExpr = dyn_cast<DeclRefExpr>(Base))
110+
ND = DRExpr->getFoundDecl();
111+
112+
// FIXME: Handle CXXUnresolvedConstructExpr. This kind of type doesn't have
113+
// available Decls to be matched against. Which inhibits the current heuristic
114+
// from resolving expressions such as `T().fo^o()`, where T is a
115+
// single-instantiated template parameter.
116+
if (!ND)
117+
return nullptr;
118+
119+
NamedDecl *Instantiation = nullptr;
120+
121+
// Find out a single instantiation that we can start with. The enclosing
122+
// context for the current Decl might not be a templated entity (e.g. a member
123+
// function inside a class template), hence we shall walk up the decl
124+
// contexts first.
125+
for (auto *EnclosingContext = ND->getDeclContext(); EnclosingContext;
126+
EnclosingContext = EnclosingContext->getParent()) {
127+
if (auto *ND = dyn_cast<NamedDecl>(EnclosingContext)) {
128+
Instantiation = getOnlyInstantiation(ND);
129+
if (Instantiation)
130+
break;
131+
}
132+
}
133+
134+
if (!Instantiation)
135+
return nullptr;
136+
137+
// This will traverse down the instantiation entity, visit each Decl, and
138+
// extract the deduced type for the undetermined Decl `ND`.
139+
InstantiatedDeclVisitor Visitor(ND);
140+
Visitor.TraverseDecl(Instantiation);
141+
142+
return Visitor.DeducedType.getTypePtrOrNull();
143+
}
144+
49145
} // namespace
50146

51147
// Helper function for HeuristicResolver::resolveDependentMember()
@@ -150,6 +246,11 @@ std::vector<const NamedDecl *> HeuristicResolver::resolveMemberExpr(
150246
if (ME->isArrow()) {
151247
BaseType = getPointeeType(BaseType);
152248
}
249+
250+
if (BaseType->isDependentType())
251+
if (auto *MaybeResolved = resolveTypeFromInstantiatedTemplate(ME))
252+
BaseType = MaybeResolved;
253+
153254
if (!BaseType)
154255
return {};
155256
if (const auto *BT = BaseType->getAs<BuiltinType>()) {

clang-tools-extra/clangd/unittests/XRefsTests.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1222,6 +1222,54 @@ TEST(LocateSymbol, TextualSmoke) {
12221222
hasID(getSymbolID(&findDecl(AST, "MyClass"))))));
12231223
}
12241224

1225+
TEST(LocateSymbol, DeduceDependentTypeFromSingleInstantiation) {
1226+
Annotations T(R"cpp(
1227+
struct WildCat {
1228+
void $wild_meow[[meow]]();
1229+
};
1230+
1231+
struct DomesticCat {
1232+
void $domestic_meow[[meow]]();
1233+
};
1234+
1235+
template <typename Ours>
1236+
struct Human {
1237+
template <typename Others>
1238+
void feed(Others O) {
1239+
O.me$1^ow();
1240+
Others Child;
1241+
Child.me$2^ow();
1242+
// FIXME: Others().me^ow();
1243+
Ours Baby;
1244+
Baby.me$3^ow();
1245+
// struct Inner {
1246+
// Ours Pet;
1247+
// };
1248+
// Inner().Pet.me^ow();
1249+
auto Lambda = [](auto C) {
1250+
C.me$4^ow();
1251+
};
1252+
Lambda(Others());
1253+
}
1254+
};
1255+
1256+
void foo() {
1257+
Human<DomesticCat>().feed(WildCat());
1258+
}
1259+
)cpp");
1260+
1261+
auto TU = TestTU::withCode(T.code());
1262+
auto AST = TU.build();
1263+
EXPECT_THAT(locateSymbolAt(AST, T.point("1")),
1264+
ElementsAre(sym("meow", T.range("wild_meow"), std::nullopt)));
1265+
EXPECT_THAT(locateSymbolAt(AST, T.point("2")),
1266+
ElementsAre(sym("meow", T.range("wild_meow"), std::nullopt)));
1267+
EXPECT_THAT(locateSymbolAt(AST, T.point("3")),
1268+
ElementsAre(sym("meow", T.range("domestic_meow"), std::nullopt)));
1269+
EXPECT_THAT(locateSymbolAt(AST, T.point("4")),
1270+
ElementsAre(sym("meow", T.range("wild_meow"), std::nullopt)));
1271+
}
1272+
12251273
TEST(LocateSymbol, Textual) {
12261274
const char *Tests[] = {
12271275
R"cpp(// Comment

0 commit comments

Comments
 (0)