clang-tools 20.0.0git
HeuristicResolver.cpp
Go to the documentation of this file.
1//===--- HeuristicResolver.cpp ---------------------------*- C++-*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "HeuristicResolver.h"
10#include "clang/AST/ASTContext.h"
11#include "clang/AST/CXXInheritance.h"
12#include "clang/AST/DeclTemplate.h"
13#include "clang/AST/ExprCXX.h"
14#include "clang/AST/Type.h"
15
16namespace clang {
17namespace clangd {
18
19namespace {
20
21// Helper class for implementing HeuristicResolver.
22// Unlike HeuristicResolver which is a long-lived class,
23// a new instance of this class is created for every external
24// call into a HeuristicResolver operation. That allows this
25// class to store state that's local to such a top-level call,
26// particularly "recursion protection sets" that keep track of
27// nodes that have already been seen to avoid infinite recursion.
28class HeuristicResolverImpl {
29public:
30 HeuristicResolverImpl(ASTContext &Ctx) : Ctx(Ctx) {}
31
32 // These functions match the public interface of HeuristicResolver
33 // (but aren't const since they may modify the recursion protection sets).
34 std::vector<const NamedDecl *>
35 resolveMemberExpr(const CXXDependentScopeMemberExpr *ME);
36 std::vector<const NamedDecl *>
37 resolveDeclRefExpr(const DependentScopeDeclRefExpr *RE);
38 std::vector<const NamedDecl *> resolveTypeOfCallExpr(const CallExpr *CE);
39 std::vector<const NamedDecl *> resolveCalleeOfCallExpr(const CallExpr *CE);
40 std::vector<const NamedDecl *>
41 resolveUsingValueDecl(const UnresolvedUsingValueDecl *UUVD);
42 std::vector<const NamedDecl *>
43 resolveDependentNameType(const DependentNameType *DNT);
44 std::vector<const NamedDecl *> resolveTemplateSpecializationType(
45 const DependentTemplateSpecializationType *DTST);
46 const Type *resolveNestedNameSpecifierToType(const NestedNameSpecifier *NNS);
47 const Type *getPointeeType(const Type *T);
48
49private:
50 ASTContext &Ctx;
51
52 // Recursion protection sets
53 llvm::SmallSet<const DependentNameType *, 4> SeenDependentNameTypes;
54
55 // Given a tag-decl type and a member name, heuristically resolve the
56 // name to one or more declarations.
57 // The current heuristic is simply to look up the name in the primary
58 // template. This is a heuristic because the template could potentially
59 // have specializations that declare different members.
60 // Multiple declarations could be returned if the name is overloaded
61 // (e.g. an overloaded method in the primary template).
62 // This heuristic will give the desired answer in many cases, e.g.
63 // for a call to vector<T>::size().
64 std::vector<const NamedDecl *>
65 resolveDependentMember(const Type *T, DeclarationName Name,
66 llvm::function_ref<bool(const NamedDecl *ND)> Filter);
67
68 // Try to heuristically resolve the type of a possibly-dependent expression
69 // `E`.
70 const Type *resolveExprToType(const Expr *E);
71 std::vector<const NamedDecl *> resolveExprToDecls(const Expr *E);
72
73 // Helper function for HeuristicResolver::resolveDependentMember()
74 // which takes a possibly-dependent type `T` and heuristically
75 // resolves it to a CXXRecordDecl in which we can try name lookup.
76 CXXRecordDecl *resolveTypeToRecordDecl(const Type *T);
77
78 // This is a reimplementation of CXXRecordDecl::lookupDependentName()
79 // so that the implementation can call into other HeuristicResolver helpers.
80 // FIXME: Once HeuristicResolver is upstreamed to the clang libraries
81 // (https://github.com/clangd/clangd/discussions/1662),
82 // CXXRecordDecl::lookupDepenedentName() can be removed, and its call sites
83 // can be modified to benefit from the more comprehensive heuristics offered
84 // by HeuristicResolver instead.
85 std::vector<const NamedDecl *>
86 lookupDependentName(CXXRecordDecl *RD, DeclarationName Name,
87 llvm::function_ref<bool(const NamedDecl *ND)> Filter);
88 bool findOrdinaryMemberInDependentClasses(const CXXBaseSpecifier *Specifier,
89 CXXBasePath &Path,
90 DeclarationName Name);
91};
92
93// Convenience lambdas for use as the 'Filter' parameter of
94// HeuristicResolver::resolveDependentMember().
95const auto NoFilter = [](const NamedDecl *D) { return true; };
96const auto NonStaticFilter = [](const NamedDecl *D) {
97 return D->isCXXInstanceMember();
98};
99const auto StaticFilter = [](const NamedDecl *D) {
100 return !D->isCXXInstanceMember();
101};
102const auto ValueFilter = [](const NamedDecl *D) { return isa<ValueDecl>(D); };
103const auto TypeFilter = [](const NamedDecl *D) { return isa<TypeDecl>(D); };
104const auto TemplateFilter = [](const NamedDecl *D) {
105 return isa<TemplateDecl>(D);
106};
107
108const Type *resolveDeclsToType(const std::vector<const NamedDecl *> &Decls,
109 ASTContext &Ctx) {
110 if (Decls.size() != 1) // Names an overload set -- just bail.
111 return nullptr;
112 if (const auto *TD = dyn_cast<TypeDecl>(Decls[0])) {
113 return Ctx.getTypeDeclType(TD).getTypePtr();
114 }
115 if (const auto *VD = dyn_cast<ValueDecl>(Decls[0])) {
116 return VD->getType().getTypePtrOrNull();
117 }
118 return nullptr;
119}
120
121TemplateName getReferencedTemplateName(const Type *T) {
122 if (const auto *TST = T->getAs<TemplateSpecializationType>()) {
123 return TST->getTemplateName();
124 }
125 if (const auto *DTST = T->getAs<DeducedTemplateSpecializationType>()) {
126 return DTST->getTemplateName();
127 }
128 return TemplateName();
129}
130
131// Helper function for HeuristicResolver::resolveDependentMember()
132// which takes a possibly-dependent type `T` and heuristically
133// resolves it to a CXXRecordDecl in which we can try name lookup.
134CXXRecordDecl *HeuristicResolverImpl::resolveTypeToRecordDecl(const Type *T) {
135 assert(T);
136
137 // Unwrap type sugar such as type aliases.
138 T = T->getCanonicalTypeInternal().getTypePtr();
139
140 if (const auto *DNT = T->getAs<DependentNameType>()) {
141 T = resolveDeclsToType(resolveDependentNameType(DNT), Ctx);
142 if (!T)
143 return nullptr;
144 T = T->getCanonicalTypeInternal().getTypePtr();
145 }
146
147 if (const auto *RT = T->getAs<RecordType>())
148 return dyn_cast<CXXRecordDecl>(RT->getDecl());
149
150 if (const auto *ICNT = T->getAs<InjectedClassNameType>())
151 T = ICNT->getInjectedSpecializationType().getTypePtrOrNull();
152 if (!T)
153 return nullptr;
154
155 TemplateName TN = getReferencedTemplateName(T);
156 if (TN.isNull())
157 return nullptr;
158
159 const ClassTemplateDecl *TD =
160 dyn_cast_or_null<ClassTemplateDecl>(TN.getAsTemplateDecl());
161 if (!TD)
162 return nullptr;
163
164 return TD->getTemplatedDecl();
165}
166
167const Type *HeuristicResolverImpl::getPointeeType(const Type *T) {
168 if (!T)
169 return nullptr;
170
171 if (T->isPointerType())
172 return T->castAs<PointerType>()->getPointeeType().getTypePtrOrNull();
173
174 // Try to handle smart pointer types.
175
176 // Look up operator-> in the primary template. If we find one, it's probably a
177 // smart pointer type.
178 auto ArrowOps = resolveDependentMember(
179 T, Ctx.DeclarationNames.getCXXOperatorName(OO_Arrow), NonStaticFilter);
180 if (ArrowOps.empty())
181 return nullptr;
182
183 // Getting the return type of the found operator-> method decl isn't useful,
184 // because we discarded template arguments to perform lookup in the primary
185 // template scope, so the return type would just have the form U* where U is a
186 // template parameter type.
187 // Instead, just handle the common case where the smart pointer type has the
188 // form of SmartPtr<X, ...>, and assume X is the pointee type.
189 auto *TST = T->getAs<TemplateSpecializationType>();
190 if (!TST)
191 return nullptr;
192 if (TST->template_arguments().size() == 0)
193 return nullptr;
194 const TemplateArgument &FirstArg = TST->template_arguments()[0];
195 if (FirstArg.getKind() != TemplateArgument::Type)
196 return nullptr;
197 return FirstArg.getAsType().getTypePtrOrNull();
198}
199
200std::vector<const NamedDecl *> HeuristicResolverImpl::resolveMemberExpr(
201 const CXXDependentScopeMemberExpr *ME) {
202 // If the expression has a qualifier, try resolving the member inside the
203 // qualifier's type.
204 // Note that we cannot use a NonStaticFilter in either case, for a couple
205 // of reasons:
206 // 1. It's valid to access a static member using instance member syntax,
207 // e.g. `instance.static_member`.
208 // 2. We can sometimes get a CXXDependentScopeMemberExpr for static
209 // member syntax too, e.g. if `X::static_member` occurs inside
210 // an instance method, it's represented as a CXXDependentScopeMemberExpr
211 // with `this` as the base expression as `X` as the qualifier
212 // (which could be valid if `X` names a base class after instantiation).
213 if (NestedNameSpecifier *NNS = ME->getQualifier()) {
214 if (const Type *QualifierType = resolveNestedNameSpecifierToType(NNS)) {
215 auto Decls =
216 resolveDependentMember(QualifierType, ME->getMember(), NoFilter);
217 if (!Decls.empty())
218 return Decls;
219 }
220
221 // Do not proceed to try resolving the member in the expression's base type
222 // without regard to the qualifier, as that could produce incorrect results.
223 // For example, `void foo() { this->Base::foo(); }` shouldn't resolve to
224 // foo() itself!
225 return {};
226 }
227
228 // Try resolving the member inside the expression's base type.
229 const Type *BaseType = ME->getBaseType().getTypePtrOrNull();
230 if (ME->isArrow()) {
231 BaseType = getPointeeType(BaseType);
232 }
233 if (!BaseType)
234 return {};
235 if (const auto *BT = BaseType->getAs<BuiltinType>()) {
236 // If BaseType is the type of a dependent expression, it's just
237 // represented as BuiltinType::Dependent which gives us no information. We
238 // can get further by analyzing the dependent expression.
239 Expr *Base = ME->isImplicitAccess() ? nullptr : ME->getBase();
240 if (Base && BT->getKind() == BuiltinType::Dependent) {
241 BaseType = resolveExprToType(Base);
242 }
243 }
244 return resolveDependentMember(BaseType, ME->getMember(), NoFilter);
245}
246
247std::vector<const NamedDecl *>
248HeuristicResolverImpl::resolveDeclRefExpr(const DependentScopeDeclRefExpr *RE) {
249 return resolveDependentMember(RE->getQualifier()->getAsType(),
250 RE->getDeclName(), StaticFilter);
251}
252
253std::vector<const NamedDecl *>
254HeuristicResolverImpl::resolveTypeOfCallExpr(const CallExpr *CE) {
255 const auto *CalleeType = resolveExprToType(CE->getCallee());
256 if (!CalleeType)
257 return {};
258 if (const auto *FnTypePtr = CalleeType->getAs<PointerType>())
259 CalleeType = FnTypePtr->getPointeeType().getTypePtr();
260 if (const FunctionType *FnType = CalleeType->getAs<FunctionType>()) {
261 if (const auto *D =
262 resolveTypeToRecordDecl(FnType->getReturnType().getTypePtr())) {
263 return {D};
264 }
265 }
266 return {};
267}
268
269std::vector<const NamedDecl *>
270HeuristicResolverImpl::resolveCalleeOfCallExpr(const CallExpr *CE) {
271 if (const auto *ND = dyn_cast_or_null<NamedDecl>(CE->getCalleeDecl())) {
272 return {ND};
273 }
274
275 return resolveExprToDecls(CE->getCallee());
276}
277
278std::vector<const NamedDecl *> HeuristicResolverImpl::resolveUsingValueDecl(
279 const UnresolvedUsingValueDecl *UUVD) {
280 return resolveDependentMember(UUVD->getQualifier()->getAsType(),
281 UUVD->getNameInfo().getName(), ValueFilter);
282}
283
284std::vector<const NamedDecl *>
285HeuristicResolverImpl::resolveDependentNameType(const DependentNameType *DNT) {
286 if (auto [_, inserted] = SeenDependentNameTypes.insert(DNT); !inserted)
287 return {};
288 return resolveDependentMember(
289 resolveNestedNameSpecifierToType(DNT->getQualifier()),
290 DNT->getIdentifier(), TypeFilter);
291}
292
293std::vector<const NamedDecl *>
294HeuristicResolverImpl::resolveTemplateSpecializationType(
295 const DependentTemplateSpecializationType *DTST) {
296 return resolveDependentMember(
297 resolveNestedNameSpecifierToType(DTST->getQualifier()),
298 DTST->getIdentifier(), TemplateFilter);
299}
300
301std::vector<const NamedDecl *>
302HeuristicResolverImpl::resolveExprToDecls(const Expr *E) {
303 if (const auto *ME = dyn_cast<CXXDependentScopeMemberExpr>(E)) {
304 return resolveMemberExpr(ME);
305 }
306 if (const auto *RE = dyn_cast<DependentScopeDeclRefExpr>(E)) {
307 return resolveDeclRefExpr(RE);
308 }
309 if (const auto *OE = dyn_cast<OverloadExpr>(E)) {
310 return {OE->decls_begin(), OE->decls_end()};
311 }
312 if (const auto *CE = dyn_cast<CallExpr>(E)) {
313 return resolveTypeOfCallExpr(CE);
314 }
315 if (const auto *ME = dyn_cast<MemberExpr>(E))
316 return {ME->getMemberDecl()};
317
318 return {};
319}
320
321const Type *HeuristicResolverImpl::resolveExprToType(const Expr *E) {
322 std::vector<const NamedDecl *> Decls = resolveExprToDecls(E);
323 if (!Decls.empty())
324 return resolveDeclsToType(Decls, Ctx);
325
326 return E->getType().getTypePtr();
327}
328
329const Type *HeuristicResolverImpl::resolveNestedNameSpecifierToType(
330 const NestedNameSpecifier *NNS) {
331 if (!NNS)
332 return nullptr;
333
334 // The purpose of this function is to handle the dependent (Kind ==
335 // Identifier) case, but we need to recurse on the prefix because
336 // that may be dependent as well, so for convenience handle
337 // the TypeSpec cases too.
338 switch (NNS->getKind()) {
339 case NestedNameSpecifier::TypeSpec:
340 case NestedNameSpecifier::TypeSpecWithTemplate:
341 return NNS->getAsType();
342 case NestedNameSpecifier::Identifier: {
343 return resolveDeclsToType(
344 resolveDependentMember(
345 resolveNestedNameSpecifierToType(NNS->getPrefix()),
346 NNS->getAsIdentifier(), TypeFilter),
347 Ctx);
348 }
349 default:
350 break;
351 }
352 return nullptr;
353}
354
355bool isOrdinaryMember(const NamedDecl *ND) {
356 return ND->isInIdentifierNamespace(Decl::IDNS_Ordinary | Decl::IDNS_Tag |
357 Decl::IDNS_Member);
358}
359
360bool findOrdinaryMember(const CXXRecordDecl *RD, CXXBasePath &Path,
361 DeclarationName Name) {
362 Path.Decls = RD->lookup(Name).begin();
363 for (DeclContext::lookup_iterator I = Path.Decls, E = I.end(); I != E; ++I)
364 if (isOrdinaryMember(*I))
365 return true;
366
367 return false;
368}
369
370bool HeuristicResolverImpl::findOrdinaryMemberInDependentClasses(
371 const CXXBaseSpecifier *Specifier, CXXBasePath &Path,
372 DeclarationName Name) {
373 CXXRecordDecl *RD =
374 resolveTypeToRecordDecl(Specifier->getType().getTypePtr());
375 if (!RD)
376 return false;
377 return findOrdinaryMember(RD, Path, Name);
378}
379
380std::vector<const NamedDecl *> HeuristicResolverImpl::lookupDependentName(
381 CXXRecordDecl *RD, DeclarationName Name,
382 llvm::function_ref<bool(const NamedDecl *ND)> Filter) {
383 std::vector<const NamedDecl *> Results;
384
385 // Lookup in the class.
386 bool AnyOrdinaryMembers = false;
387 for (const NamedDecl *ND : RD->lookup(Name)) {
388 if (isOrdinaryMember(ND))
389 AnyOrdinaryMembers = true;
390 if (Filter(ND))
391 Results.push_back(ND);
392 }
393 if (AnyOrdinaryMembers)
394 return Results;
395
396 // Perform lookup into our base classes.
397 CXXBasePaths Paths;
398 Paths.setOrigin(RD);
399 if (!RD->lookupInBases(
400 [&](const CXXBaseSpecifier *Specifier, CXXBasePath &Path) {
401 return findOrdinaryMemberInDependentClasses(Specifier, Path, Name);
402 },
403 Paths, /*LookupInDependent=*/true))
404 return Results;
405 for (DeclContext::lookup_iterator I = Paths.front().Decls, E = I.end();
406 I != E; ++I) {
407 if (isOrdinaryMember(*I) && Filter(*I))
408 Results.push_back(*I);
409 }
410 return Results;
411}
412
413std::vector<const NamedDecl *> HeuristicResolverImpl::resolveDependentMember(
414 const Type *T, DeclarationName Name,
415 llvm::function_ref<bool(const NamedDecl *ND)> Filter) {
416 if (!T)
417 return {};
418 if (auto *ET = T->getAs<EnumType>()) {
419 auto Result = ET->getDecl()->lookup(Name);
420 return {Result.begin(), Result.end()};
421 }
422 if (auto *RD = resolveTypeToRecordDecl(T)) {
423 if (!RD->hasDefinition())
424 return {};
425 RD = RD->getDefinition();
426 return lookupDependentName(RD, Name, Filter);
427 }
428 return {};
429}
430} // namespace
431
432std::vector<const NamedDecl *> HeuristicResolver::resolveMemberExpr(
433 const CXXDependentScopeMemberExpr *ME) const {
434 return HeuristicResolverImpl(Ctx).resolveMemberExpr(ME);
435}
436std::vector<const NamedDecl *> HeuristicResolver::resolveDeclRefExpr(
437 const DependentScopeDeclRefExpr *RE) const {
438 return HeuristicResolverImpl(Ctx).resolveDeclRefExpr(RE);
439}
440std::vector<const NamedDecl *>
442 return HeuristicResolverImpl(Ctx).resolveTypeOfCallExpr(CE);
443}
444std::vector<const NamedDecl *>
446 return HeuristicResolverImpl(Ctx).resolveCalleeOfCallExpr(CE);
447}
448std::vector<const NamedDecl *> HeuristicResolver::resolveUsingValueDecl(
449 const UnresolvedUsingValueDecl *UUVD) const {
450 return HeuristicResolverImpl(Ctx).resolveUsingValueDecl(UUVD);
451}
452std::vector<const NamedDecl *> HeuristicResolver::resolveDependentNameType(
453 const DependentNameType *DNT) const {
454 return HeuristicResolverImpl(Ctx).resolveDependentNameType(DNT);
455}
456std::vector<const NamedDecl *>
458 const DependentTemplateSpecializationType *DTST) const {
459 return HeuristicResolverImpl(Ctx).resolveTemplateSpecializationType(DTST);
460}
462 const NestedNameSpecifier *NNS) const {
463 return HeuristicResolverImpl(Ctx).resolveNestedNameSpecifierToType(NNS);
464}
466 return HeuristicResolverImpl(Ctx).getPointeeType(T);
467}
468
469} // namespace clangd
470} // namespace clang
const Expr * E
CaptureExpr CE
llvm::SmallString< 256U > Name
std::vector< CodeCompletionResult > Results
std::vector< const NamedDecl * > resolveMemberExpr(const CXXDependentScopeMemberExpr *ME) const
std::vector< const NamedDecl * > resolveDeclRefExpr(const DependentScopeDeclRefExpr *RE) const
const Type * getPointeeType(const Type *T) const
std::vector< const NamedDecl * > resolveDependentNameType(const DependentNameType *DNT) const
std::vector< const NamedDecl * > resolveUsingValueDecl(const UnresolvedUsingValueDecl *UUVD) const
std::vector< const NamedDecl * > resolveCalleeOfCallExpr(const CallExpr *CE) const
const Type * resolveNestedNameSpecifierToType(const NestedNameSpecifier *NNS) const
std::vector< const NamedDecl * > resolveTemplateSpecializationType(const DependentTemplateSpecializationType *DTST) const
std::vector< const NamedDecl * > resolveTypeOfCallExpr(const CallExpr *CE) const
std::string Path
A typedef to represent a file path.
Definition: Path.h:26
@ Type
An inlay hint that for a type annotation.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//