1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
//===--- HeuristicResolver.h - Resolution of dependent names -----*- C++-*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_HEURISTICRESOLVER_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_HEURISTICRESOLVER_H
#include "clang/AST/Decl.h"
#include <vector>
namespace clang {
class ASTContext;
class CallExpr;
class CXXBasePath;
class CXXDependentScopeMemberExpr;
class DeclarationName;
class DependentScopeDeclRefExpr;
class NamedDecl;
class Type;
class UnresolvedUsingValueDecl;
namespace clangd {
// This class heuristic resolution of declarations and types in template code.
//
// As a compiler, clang only needs to perform certain types of processing on
// template code (such as resolving dependent names to declarations, or
// resolving the type of a dependent expression) after instantiation. Indeed,
// C++ language features such as template specialization mean such resolution
// cannot be done accurately before instantiation
//
// However, template code is written and read in uninstantiated form, and clangd
// would like to provide editor features like go-to-definition in template code
// where possible. To this end, clangd attempts to resolve declarations and
// types in uninstantiated code by using heuristics, understanding that the
// results may not be fully accurate but that this is better than nothing.
//
// At this time, the heuristic used is a simple but effective one: assume that
// template instantiations are based on the primary template definition and not
// not a specialization. More advanced heuristics may be added in the future.
class HeuristicResolver {
public:
HeuristicResolver(ASTContext &Ctx) : Ctx(Ctx) {}
// Try to heuristically resolve certain types of expressions, declarations, or
// types to one or more likely-referenced declarations.
std::vector<const NamedDecl *>
resolveMemberExpr(const CXXDependentScopeMemberExpr *ME) const;
std::vector<const NamedDecl *>
resolveDeclRefExpr(const DependentScopeDeclRefExpr *RE) const;
std::vector<const NamedDecl *>
resolveTypeOfCallExpr(const CallExpr *CE) const;
std::vector<const NamedDecl *>
resolveCalleeOfCallExpr(const CallExpr *CE) const;
std::vector<const NamedDecl *>
resolveUsingValueDecl(const UnresolvedUsingValueDecl *UUVD) const;
std::vector<const NamedDecl *>
resolveDependentNameType(const DependentNameType *DNT) const;
std::vector<const NamedDecl *> resolveTemplateSpecializationType(
const DependentTemplateSpecializationType *DTST) const;
// Try to heuristically resolve a dependent nested name specifier
// to the type it likely denotes. Note that *dependent* name specifiers always
// denote types, not namespaces.
const Type *
resolveNestedNameSpecifierToType(const NestedNameSpecifier *NNS) const;
// Given the type T of a dependent expression that appears of the LHS of a
// "->", heuristically find a corresponding pointee type in whose scope we
// could look up the name appearing on the RHS.
const Type *getPointeeType(const Type *T) const;
private:
ASTContext &Ctx;
// Given a tag-decl type and a member name, heuristically resolve the
// name to one or more declarations.
// The current heuristic is simply to look up the name in the primary
// template. This is a heuristic because the template could potentially
// have specializations that declare different members.
// Multiple declarations could be returned if the name is overloaded
// (e.g. an overloaded method in the primary template).
// This heuristic will give the desired answer in many cases, e.g.
// for a call to vector<T>::size().
std::vector<const NamedDecl *> resolveDependentMember(
const Type *T, DeclarationName Name,
llvm::function_ref<bool(const NamedDecl *ND)> Filter) const;
// Try to heuristically resolve the type of a possibly-dependent expression
// `E`.
const Type *resolveExprToType(const Expr *E) const;
std::vector<const NamedDecl *> resolveExprToDecls(const Expr *E) const;
// Helper function for HeuristicResolver::resolveDependentMember()
// which takes a possibly-dependent type `T` and heuristically
// resolves it to a CXXRecordDecl in which we can try name lookup.
CXXRecordDecl *resolveTypeToRecordDecl(const Type *T) const;
// This is a reimplementation of CXXRecordDecl::lookupDependentName()
// so that the implementation can call into other HeuristicResolver helpers.
// FIXME: Once HeuristicResolver is upstreamed to the clang libraries
// (https://github.com/clangd/clangd/discussions/1662),
// CXXRecordDecl::lookupDepenedentName() can be removed, and its call sites
// can be modified to benefit from the more comprehensive heuristics offered
// by HeuristicResolver instead.
std::vector<const NamedDecl *> lookupDependentName(
CXXRecordDecl *RD, DeclarationName Name,
llvm::function_ref<bool(const NamedDecl *ND)> Filter) const;
bool findOrdinaryMemberInDependentClasses(const CXXBaseSpecifier *Specifier,
CXXBasePath &Path,
DeclarationName Name) const;
};
} // namespace clangd
} // namespace clang
#endif
|