clang-tools 17.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/DeclTemplate.h"
12#include "clang/AST/ExprCXX.h"
13
14namespace clang {
15namespace clangd {
16
17// Convenience lambdas for use as the 'Filter' parameter of
18// HeuristicResolver::resolveDependentMember().
19const auto NoFilter = [](const NamedDecl *D) { return true; };
20const auto NonStaticFilter = [](const NamedDecl *D) {
21 return D->isCXXInstanceMember();
22};
23const auto StaticFilter = [](const NamedDecl *D) {
24 return !D->isCXXInstanceMember();
25};
26const auto ValueFilter = [](const NamedDecl *D) { return isa<ValueDecl>(D); };
27const auto TypeFilter = [](const NamedDecl *D) { return isa<TypeDecl>(D); };
28const auto TemplateFilter = [](const NamedDecl *D) {
29 return isa<TemplateDecl>(D);
30};
31
32// Helper function for HeuristicResolver::resolveDependentMember()
33// which takes a possibly-dependent type `T` and heuristically
34// resolves it to a CXXRecordDecl in which we can try name lookup.
35CXXRecordDecl *resolveTypeToRecordDecl(const Type *T) {
36 assert(T);
37
38 if (const auto *RT = T->getAs<RecordType>())
39 return dyn_cast<CXXRecordDecl>(RT->getDecl());
40
41 if (const auto *ICNT = T->getAs<InjectedClassNameType>())
42 T = ICNT->getInjectedSpecializationType().getTypePtrOrNull();
43 if (!T)
44 return nullptr;
45
46 const auto *TST = T->getAs<TemplateSpecializationType>();
47 if (!TST)
48 return nullptr;
49
50 const ClassTemplateDecl *TD = dyn_cast_or_null<ClassTemplateDecl>(
51 TST->getTemplateName().getAsTemplateDecl());
52 if (!TD)
53 return nullptr;
54
55 return TD->getTemplatedDecl();
56}
57
59 if (!T)
60 return nullptr;
61
62 if (T->isPointerType())
63 return T->castAs<PointerType>()->getPointeeType().getTypePtrOrNull();
64
65 // Try to handle smart pointer types.
66
67 // Look up operator-> in the primary template. If we find one, it's probably a
68 // smart pointer type.
69 auto ArrowOps = resolveDependentMember(
70 T, Ctx.DeclarationNames.getCXXOperatorName(OO_Arrow), NonStaticFilter);
71 if (ArrowOps.empty())
72 return nullptr;
73
74 // Getting the return type of the found operator-> method decl isn't useful,
75 // because we discarded template arguments to perform lookup in the primary
76 // template scope, so the return type would just have the form U* where U is a
77 // template parameter type.
78 // Instead, just handle the common case where the smart pointer type has the
79 // form of SmartPtr<X, ...>, and assume X is the pointee type.
80 auto *TST = T->getAs<TemplateSpecializationType>();
81 if (!TST)
82 return nullptr;
83 if (TST->template_arguments().size() == 0)
84 return nullptr;
85 const TemplateArgument &FirstArg = TST->template_arguments()[0];
86 if (FirstArg.getKind() != TemplateArgument::Type)
87 return nullptr;
88 return FirstArg.getAsType().getTypePtrOrNull();
89}
90
91std::vector<const NamedDecl *> HeuristicResolver::resolveMemberExpr(
92 const CXXDependentScopeMemberExpr *ME) const {
93 // If the expression has a qualifier, first try resolving the member
94 // inside the qualifier's type.
95 // Note that we cannot use a NonStaticFilter in either case, for a couple
96 // of reasons:
97 // 1. It's valid to access a static member using instance member syntax,
98 // e.g. `instance.static_member`.
99 // 2. We can sometimes get a CXXDependentScopeMemberExpr for static
100 // member syntax too, e.g. if `X::static_member` occurs inside
101 // an instance method, it's represented as a CXXDependentScopeMemberExpr
102 // with `this` as the base expression as `X` as the qualifier
103 // (which could be valid if `X` names a base class after instantiation).
104 if (NestedNameSpecifier *NNS = ME->getQualifier()) {
105 if (const Type *QualifierType = resolveNestedNameSpecifierToType(NNS)) {
106 auto Decls =
107 resolveDependentMember(QualifierType, ME->getMember(), NoFilter);
108 if (!Decls.empty())
109 return Decls;
110 }
111 }
112
113 // If that didn't yield any results, try resolving the member inside
114 // the expression's base type.
115 const Type *BaseType = ME->getBaseType().getTypePtrOrNull();
116 if (ME->isArrow()) {
117 BaseType = getPointeeType(BaseType);
118 }
119 if (!BaseType)
120 return {};
121 if (const auto *BT = BaseType->getAs<BuiltinType>()) {
122 // If BaseType is the type of a dependent expression, it's just
123 // represented as BuiltinType::Dependent which gives us no information. We
124 // can get further by analyzing the dependent expression.
125 Expr *Base = ME->isImplicitAccess() ? nullptr : ME->getBase();
126 if (Base && BT->getKind() == BuiltinType::Dependent) {
127 BaseType = resolveExprToType(Base);
128 }
129 }
130 return resolveDependentMember(BaseType, ME->getMember(), NoFilter);
131}
132
133std::vector<const NamedDecl *> HeuristicResolver::resolveDeclRefExpr(
134 const DependentScopeDeclRefExpr *RE) const {
135 return resolveDependentMember(RE->getQualifier()->getAsType(),
136 RE->getDeclName(), StaticFilter);
137}
138
139std::vector<const NamedDecl *>
141 const auto *CalleeType = resolveExprToType(CE->getCallee());
142 if (!CalleeType)
143 return {};
144 if (const auto *FnTypePtr = CalleeType->getAs<PointerType>())
145 CalleeType = FnTypePtr->getPointeeType().getTypePtr();
146 if (const FunctionType *FnType = CalleeType->getAs<FunctionType>()) {
147 if (const auto *D =
148 resolveTypeToRecordDecl(FnType->getReturnType().getTypePtr())) {
149 return {D};
150 }
151 }
152 return {};
153}
154
155std::vector<const NamedDecl *>
157 if (const auto *ND = dyn_cast_or_null<NamedDecl>(CE->getCalleeDecl())) {
158 return {ND};
159 }
160
161 return resolveExprToDecls(CE->getCallee());
162}
163
164std::vector<const NamedDecl *> HeuristicResolver::resolveUsingValueDecl(
165 const UnresolvedUsingValueDecl *UUVD) const {
166 return resolveDependentMember(UUVD->getQualifier()->getAsType(),
167 UUVD->getNameInfo().getName(), ValueFilter);
168}
169
170std::vector<const NamedDecl *> HeuristicResolver::resolveDependentNameType(
171 const DependentNameType *DNT) const {
172 return resolveDependentMember(
173 resolveNestedNameSpecifierToType(DNT->getQualifier()),
174 DNT->getIdentifier(), TypeFilter);
175}
176
177std::vector<const NamedDecl *>
179 const DependentTemplateSpecializationType *DTST) const {
180 return resolveDependentMember(
181 resolveNestedNameSpecifierToType(DTST->getQualifier()),
182 DTST->getIdentifier(), TemplateFilter);
183}
184
185const Type *resolveDeclsToType(const std::vector<const NamedDecl *> &Decls) {
186 if (Decls.size() != 1) // Names an overload set -- just bail.
187 return nullptr;
188 if (const auto *TD = dyn_cast<TypeDecl>(Decls[0])) {
189 return TD->getTypeForDecl();
190 }
191 if (const auto *VD = dyn_cast<ValueDecl>(Decls[0])) {
192 return VD->getType().getTypePtrOrNull();
193 }
194 return nullptr;
195}
196
197std::vector<const NamedDecl *>
198HeuristicResolver::resolveExprToDecls(const Expr *E) const {
199 if (const auto *ME = dyn_cast<CXXDependentScopeMemberExpr>(E)) {
200 return resolveMemberExpr(ME);
201 }
202 if (const auto *RE = dyn_cast<DependentScopeDeclRefExpr>(E)) {
203 return resolveDeclRefExpr(RE);
204 }
205 if (const auto *OE = dyn_cast<OverloadExpr>(E)) {
206 return {OE->decls_begin(), OE->decls_end()};
207 }
208 if (const auto *CE = dyn_cast<CallExpr>(E)) {
210 }
211 if (const auto *ME = dyn_cast<MemberExpr>(E))
212 return {ME->getMemberDecl()};
213
214 return {};
215}
216
217const Type *HeuristicResolver::resolveExprToType(const Expr *E) const {
218 std::vector<const NamedDecl *> Decls = resolveExprToDecls(E);
219 if (!Decls.empty())
220 return resolveDeclsToType(Decls);
221
222 return E->getType().getTypePtr();
223}
224
226 const NestedNameSpecifier *NNS) const {
227 if (!NNS)
228 return nullptr;
229
230 // The purpose of this function is to handle the dependent (Kind ==
231 // Identifier) case, but we need to recurse on the prefix because
232 // that may be dependent as well, so for convenience handle
233 // the TypeSpec cases too.
234 switch (NNS->getKind()) {
235 case NestedNameSpecifier::TypeSpec:
236 case NestedNameSpecifier::TypeSpecWithTemplate:
237 return NNS->getAsType();
238 case NestedNameSpecifier::Identifier: {
239 return resolveDeclsToType(resolveDependentMember(
240 resolveNestedNameSpecifierToType(NNS->getPrefix()),
241 NNS->getAsIdentifier(), TypeFilter));
242 }
243 default:
244 break;
245 }
246 return nullptr;
247}
248
249std::vector<const NamedDecl *> HeuristicResolver::resolveDependentMember(
250 const Type *T, DeclarationName Name,
251 llvm::function_ref<bool(const NamedDecl *ND)> Filter) const {
252 if (!T)
253 return {};
254 if (auto *ET = T->getAs<EnumType>()) {
255 auto Result = ET->getDecl()->lookup(Name);
256 return {Result.begin(), Result.end()};
257 }
258 if (auto *RD = resolveTypeToRecordDecl(T)) {
259 if (!RD->hasDefinition())
260 return {};
261 RD = RD->getDefinition();
262 return RD->lookupDependentName(Name, Filter);
263 }
264 return {};
265}
266
267} // namespace clangd
268} // namespace clang
const Expr * E
CaptureExpr CE
Token Name
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
const Type * resolveDeclsToType(const std::vector< const NamedDecl * > &Decls)
const auto TypeFilter
const auto ValueFilter
@ Type
An inlay hint that for a type annotation.
const auto TemplateFilter
const auto NonStaticFilter
CXXRecordDecl * resolveTypeToRecordDecl(const Type *T)
const auto StaticFilter
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//