mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-26 07:46:07 +00:00
560 lines
21 KiB
C++
560 lines
21 KiB
C++
//===--- HeuristicResolver.cpp ---------------------------*- 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "clang/Sema/HeuristicResolver.h"
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/AST/CXXInheritance.h"
|
|
#include "clang/AST/DeclTemplate.h"
|
|
#include "clang/AST/ExprCXX.h"
|
|
#include "clang/AST/TemplateBase.h"
|
|
#include "clang/AST/Type.h"
|
|
|
|
namespace clang {
|
|
|
|
namespace {
|
|
|
|
// Helper class for implementing HeuristicResolver.
|
|
// Unlike HeuristicResolver which is a long-lived class,
|
|
// a new instance of this class is created for every external
|
|
// call into a HeuristicResolver operation. That allows this
|
|
// class to store state that's local to such a top-level call,
|
|
// particularly "recursion protection sets" that keep track of
|
|
// nodes that have already been seen to avoid infinite recursion.
|
|
class HeuristicResolverImpl {
|
|
public:
|
|
HeuristicResolverImpl(ASTContext &Ctx) : Ctx(Ctx) {}
|
|
|
|
// These functions match the public interface of HeuristicResolver
|
|
// (but aren't const since they may modify the recursion protection sets).
|
|
std::vector<const NamedDecl *>
|
|
resolveMemberExpr(const CXXDependentScopeMemberExpr *ME);
|
|
std::vector<const NamedDecl *>
|
|
resolveDeclRefExpr(const DependentScopeDeclRefExpr *RE);
|
|
std::vector<const NamedDecl *> resolveTypeOfCallExpr(const CallExpr *CE);
|
|
std::vector<const NamedDecl *> resolveCalleeOfCallExpr(const CallExpr *CE);
|
|
std::vector<const NamedDecl *>
|
|
resolveUsingValueDecl(const UnresolvedUsingValueDecl *UUVD);
|
|
std::vector<const NamedDecl *>
|
|
resolveDependentNameType(const DependentNameType *DNT);
|
|
std::vector<const NamedDecl *> resolveTemplateSpecializationType(
|
|
const DependentTemplateSpecializationType *DTST);
|
|
QualType resolveNestedNameSpecifierToType(const NestedNameSpecifier *NNS);
|
|
QualType getPointeeType(QualType T);
|
|
std::vector<const NamedDecl *>
|
|
lookupDependentName(CXXRecordDecl *RD, DeclarationName Name,
|
|
llvm::function_ref<bool(const NamedDecl *ND)> Filter);
|
|
|
|
private:
|
|
ASTContext &Ctx;
|
|
|
|
// Recursion protection sets
|
|
llvm::SmallSet<const DependentNameType *, 4> SeenDependentNameTypes;
|
|
|
|
// 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(QualType T, DeclarationName Name,
|
|
llvm::function_ref<bool(const NamedDecl *ND)> Filter);
|
|
|
|
// Try to heuristically resolve the type of a possibly-dependent expression
|
|
// `E`.
|
|
QualType resolveExprToType(const Expr *E);
|
|
std::vector<const NamedDecl *> resolveExprToDecls(const Expr *E);
|
|
|
|
// Helper function for HeuristicResolver::resolveDependentMember()
|
|
// which takes a possibly-dependent type `T` and heuristically
|
|
// resolves it to a TagDecl in which we can try name lookup.
|
|
TagDecl *resolveTypeToTagDecl(const Type *T);
|
|
|
|
// Helper function for simplifying a type.
|
|
// `Type` is the type to simplify.
|
|
// `E` is the expression whose type `Type` is, if known. This sometimes
|
|
// contains information relevant to the type that's not stored in `Type`
|
|
// itself.
|
|
// If `UnwrapPointer` is true, exactly only pointer type will be unwrapped
|
|
// during simplification, and the operation fails if no pointer type is found.
|
|
QualType simplifyType(QualType Type, const Expr *E, bool UnwrapPointer);
|
|
|
|
bool findOrdinaryMemberInDependentClasses(const CXXBaseSpecifier *Specifier,
|
|
CXXBasePath &Path,
|
|
DeclarationName Name);
|
|
};
|
|
|
|
// Convenience lambdas for use as the 'Filter' parameter of
|
|
// HeuristicResolver::resolveDependentMember().
|
|
const auto NoFilter = [](const NamedDecl *D) { return true; };
|
|
const auto NonStaticFilter = [](const NamedDecl *D) {
|
|
return D->isCXXInstanceMember();
|
|
};
|
|
const auto StaticFilter = [](const NamedDecl *D) {
|
|
return !D->isCXXInstanceMember();
|
|
};
|
|
const auto ValueFilter = [](const NamedDecl *D) { return isa<ValueDecl>(D); };
|
|
const auto TypeFilter = [](const NamedDecl *D) { return isa<TypeDecl>(D); };
|
|
const auto TemplateFilter = [](const NamedDecl *D) {
|
|
return isa<TemplateDecl>(D);
|
|
};
|
|
|
|
QualType resolveDeclsToType(const std::vector<const NamedDecl *> &Decls,
|
|
ASTContext &Ctx) {
|
|
if (Decls.size() != 1) // Names an overload set -- just bail.
|
|
return QualType();
|
|
if (const auto *TD = dyn_cast<TypeDecl>(Decls[0])) {
|
|
return Ctx.getTypeDeclType(TD);
|
|
}
|
|
if (const auto *VD = dyn_cast<ValueDecl>(Decls[0])) {
|
|
return VD->getType();
|
|
}
|
|
return QualType();
|
|
}
|
|
|
|
TemplateName getReferencedTemplateName(const Type *T) {
|
|
if (const auto *TST = T->getAs<TemplateSpecializationType>()) {
|
|
return TST->getTemplateName();
|
|
}
|
|
if (const auto *DTST = T->getAs<DeducedTemplateSpecializationType>()) {
|
|
return DTST->getTemplateName();
|
|
}
|
|
return TemplateName();
|
|
}
|
|
|
|
// 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.
|
|
TagDecl *HeuristicResolverImpl::resolveTypeToTagDecl(const Type *T) {
|
|
assert(T);
|
|
|
|
// Unwrap type sugar such as type aliases.
|
|
T = T->getCanonicalTypeInternal().getTypePtr();
|
|
|
|
if (const auto *DNT = T->getAs<DependentNameType>()) {
|
|
T = resolveDeclsToType(resolveDependentNameType(DNT), Ctx)
|
|
.getTypePtrOrNull();
|
|
if (!T)
|
|
return nullptr;
|
|
T = T->getCanonicalTypeInternal().getTypePtr();
|
|
}
|
|
|
|
if (auto *TT = T->getAs<TagType>()) {
|
|
return TT->getDecl();
|
|
}
|
|
|
|
if (const auto *ICNT = T->getAs<InjectedClassNameType>())
|
|
T = ICNT->getInjectedSpecializationType().getTypePtrOrNull();
|
|
if (!T)
|
|
return nullptr;
|
|
|
|
TemplateName TN = getReferencedTemplateName(T);
|
|
if (TN.isNull())
|
|
return nullptr;
|
|
|
|
const ClassTemplateDecl *TD =
|
|
dyn_cast_or_null<ClassTemplateDecl>(TN.getAsTemplateDecl());
|
|
if (!TD)
|
|
return nullptr;
|
|
|
|
return TD->getTemplatedDecl();
|
|
}
|
|
|
|
QualType HeuristicResolverImpl::getPointeeType(QualType T) {
|
|
if (T.isNull())
|
|
return QualType();
|
|
|
|
if (T->isPointerType())
|
|
return T->castAs<PointerType>()->getPointeeType();
|
|
|
|
// Try to handle smart pointer types.
|
|
|
|
// Look up operator-> in the primary template. If we find one, it's probably a
|
|
// smart pointer type.
|
|
auto ArrowOps = resolveDependentMember(
|
|
T, Ctx.DeclarationNames.getCXXOperatorName(OO_Arrow), NonStaticFilter);
|
|
if (ArrowOps.empty())
|
|
return QualType();
|
|
|
|
// Getting the return type of the found operator-> method decl isn't useful,
|
|
// because we discarded template arguments to perform lookup in the primary
|
|
// template scope, so the return type would just have the form U* where U is a
|
|
// template parameter type.
|
|
// Instead, just handle the common case where the smart pointer type has the
|
|
// form of SmartPtr<X, ...>, and assume X is the pointee type.
|
|
auto *TST = T->getAs<TemplateSpecializationType>();
|
|
if (!TST)
|
|
return QualType();
|
|
if (TST->template_arguments().size() == 0)
|
|
return QualType();
|
|
const TemplateArgument &FirstArg = TST->template_arguments()[0];
|
|
if (FirstArg.getKind() != TemplateArgument::Type)
|
|
return QualType();
|
|
return FirstArg.getAsType();
|
|
}
|
|
|
|
QualType HeuristicResolverImpl::simplifyType(QualType Type, const Expr *E,
|
|
bool UnwrapPointer) {
|
|
bool DidUnwrapPointer = false;
|
|
// A type, together with an optional expression whose type it represents
|
|
// which may have additional information about the expression's type
|
|
// not stored in the QualType itself.
|
|
struct TypeExprPair {
|
|
QualType Type;
|
|
const Expr *E = nullptr;
|
|
};
|
|
TypeExprPair Current{Type, E};
|
|
auto SimplifyOneStep = [UnwrapPointer, &DidUnwrapPointer,
|
|
this](TypeExprPair T) -> TypeExprPair {
|
|
if (UnwrapPointer) {
|
|
if (QualType Pointee = getPointeeType(T.Type); !Pointee.isNull()) {
|
|
DidUnwrapPointer = true;
|
|
return {Pointee};
|
|
}
|
|
}
|
|
if (const auto *RT = T.Type->getAs<ReferenceType>()) {
|
|
// Does not count as "unwrap pointer".
|
|
return {RT->getPointeeType()};
|
|
}
|
|
if (const auto *BT = T.Type->getAs<BuiltinType>()) {
|
|
// If BaseType is the type of a dependent expression, it's just
|
|
// represented as BuiltinType::Dependent which gives us no information. We
|
|
// can get further by analyzing the dependent expression.
|
|
if (T.E && BT->getKind() == BuiltinType::Dependent) {
|
|
return {resolveExprToType(T.E), T.E};
|
|
}
|
|
}
|
|
if (const auto *AT = T.Type->getContainedAutoType()) {
|
|
// If T contains a dependent `auto` type, deduction will not have
|
|
// been performed on it yet. In simple cases (e.g. `auto` variable with
|
|
// initializer), get the approximate type that would result from
|
|
// deduction.
|
|
// FIXME: A more accurate implementation would propagate things like the
|
|
// `const` in `const auto`.
|
|
if (T.E && AT->isUndeducedAutoType()) {
|
|
if (const auto *DRE = dyn_cast<DeclRefExpr>(T.E)) {
|
|
if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
|
|
if (auto *Init = VD->getInit())
|
|
return {resolveExprToType(Init), Init};
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (const auto *TTPT = dyn_cast_if_present<TemplateTypeParmType>(T.Type)) {
|
|
// We can't do much useful with a template parameter (e.g. we cannot look
|
|
// up member names inside it). However, if the template parameter has a
|
|
// default argument, as a heuristic we can replace T with the default
|
|
// argument type.
|
|
if (const auto *TTPD = TTPT->getDecl()) {
|
|
if (TTPD->hasDefaultArgument()) {
|
|
const auto &DefaultArg = TTPD->getDefaultArgument().getArgument();
|
|
if (DefaultArg.getKind() == TemplateArgument::Type) {
|
|
return {DefaultArg.getAsType()};
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return T;
|
|
};
|
|
// As an additional protection against infinite loops, bound the number of
|
|
// simplification steps.
|
|
size_t StepCount = 0;
|
|
const size_t MaxSteps = 64;
|
|
while (!Current.Type.isNull() && StepCount++ < MaxSteps) {
|
|
TypeExprPair New = SimplifyOneStep(Current);
|
|
if (New.Type == Current.Type)
|
|
break;
|
|
Current = New;
|
|
}
|
|
if (UnwrapPointer && !DidUnwrapPointer)
|
|
return QualType();
|
|
return Current.Type;
|
|
}
|
|
|
|
std::vector<const NamedDecl *> HeuristicResolverImpl::resolveMemberExpr(
|
|
const CXXDependentScopeMemberExpr *ME) {
|
|
// If the expression has a qualifier, try resolving the member inside the
|
|
// qualifier's type.
|
|
// Note that we cannot use a NonStaticFilter in either case, for a couple
|
|
// of reasons:
|
|
// 1. It's valid to access a static member using instance member syntax,
|
|
// e.g. `instance.static_member`.
|
|
// 2. We can sometimes get a CXXDependentScopeMemberExpr for static
|
|
// member syntax too, e.g. if `X::static_member` occurs inside
|
|
// an instance method, it's represented as a CXXDependentScopeMemberExpr
|
|
// with `this` as the base expression as `X` as the qualifier
|
|
// (which could be valid if `X` names a base class after instantiation).
|
|
if (NestedNameSpecifier *NNS = ME->getQualifier()) {
|
|
if (QualType QualifierType = resolveNestedNameSpecifierToType(NNS);
|
|
!QualifierType.isNull()) {
|
|
auto Decls =
|
|
resolveDependentMember(QualifierType, ME->getMember(), NoFilter);
|
|
if (!Decls.empty())
|
|
return Decls;
|
|
}
|
|
|
|
// Do not proceed to try resolving the member in the expression's base type
|
|
// without regard to the qualifier, as that could produce incorrect results.
|
|
// For example, `void foo() { this->Base::foo(); }` shouldn't resolve to
|
|
// foo() itself!
|
|
return {};
|
|
}
|
|
|
|
// Try resolving the member inside the expression's base type.
|
|
Expr *Base = ME->isImplicitAccess() ? nullptr : ME->getBase();
|
|
QualType BaseType = ME->getBaseType();
|
|
BaseType = simplifyType(BaseType, Base, ME->isArrow());
|
|
return resolveDependentMember(BaseType, ME->getMember(), NoFilter);
|
|
}
|
|
|
|
std::vector<const NamedDecl *>
|
|
HeuristicResolverImpl::resolveDeclRefExpr(const DependentScopeDeclRefExpr *RE) {
|
|
return resolveDependentMember(
|
|
resolveNestedNameSpecifierToType(RE->getQualifier()), RE->getDeclName(),
|
|
StaticFilter);
|
|
}
|
|
|
|
std::vector<const NamedDecl *>
|
|
HeuristicResolverImpl::resolveTypeOfCallExpr(const CallExpr *CE) {
|
|
QualType CalleeType = resolveExprToType(CE->getCallee());
|
|
if (CalleeType.isNull())
|
|
return {};
|
|
if (const auto *FnTypePtr = CalleeType->getAs<PointerType>())
|
|
CalleeType = FnTypePtr->getPointeeType();
|
|
if (const FunctionType *FnType = CalleeType->getAs<FunctionType>()) {
|
|
if (const auto *D =
|
|
resolveTypeToTagDecl(FnType->getReturnType().getTypePtr())) {
|
|
return {D};
|
|
}
|
|
}
|
|
return {};
|
|
}
|
|
|
|
std::vector<const NamedDecl *>
|
|
HeuristicResolverImpl::resolveCalleeOfCallExpr(const CallExpr *CE) {
|
|
if (const auto *ND = dyn_cast_or_null<NamedDecl>(CE->getCalleeDecl())) {
|
|
return {ND};
|
|
}
|
|
|
|
return resolveExprToDecls(CE->getCallee());
|
|
}
|
|
|
|
std::vector<const NamedDecl *> HeuristicResolverImpl::resolveUsingValueDecl(
|
|
const UnresolvedUsingValueDecl *UUVD) {
|
|
return resolveDependentMember(QualType(UUVD->getQualifier()->getAsType(), 0),
|
|
UUVD->getNameInfo().getName(), ValueFilter);
|
|
}
|
|
|
|
std::vector<const NamedDecl *>
|
|
HeuristicResolverImpl::resolveDependentNameType(const DependentNameType *DNT) {
|
|
if (auto [_, inserted] = SeenDependentNameTypes.insert(DNT); !inserted)
|
|
return {};
|
|
return resolveDependentMember(
|
|
resolveNestedNameSpecifierToType(DNT->getQualifier()),
|
|
DNT->getIdentifier(), TypeFilter);
|
|
}
|
|
|
|
std::vector<const NamedDecl *>
|
|
HeuristicResolverImpl::resolveTemplateSpecializationType(
|
|
const DependentTemplateSpecializationType *DTST) {
|
|
const DependentTemplateStorage &DTN = DTST->getDependentTemplateName();
|
|
return resolveDependentMember(
|
|
resolveNestedNameSpecifierToType(DTN.getQualifier()),
|
|
DTN.getName().getIdentifier(), TemplateFilter);
|
|
}
|
|
|
|
std::vector<const NamedDecl *>
|
|
HeuristicResolverImpl::resolveExprToDecls(const Expr *E) {
|
|
if (const auto *ME = dyn_cast<CXXDependentScopeMemberExpr>(E)) {
|
|
return resolveMemberExpr(ME);
|
|
}
|
|
if (const auto *RE = dyn_cast<DependentScopeDeclRefExpr>(E)) {
|
|
return resolveDeclRefExpr(RE);
|
|
}
|
|
if (const auto *OE = dyn_cast<OverloadExpr>(E)) {
|
|
return {OE->decls_begin(), OE->decls_end()};
|
|
}
|
|
if (const auto *CE = dyn_cast<CallExpr>(E)) {
|
|
return resolveTypeOfCallExpr(CE);
|
|
}
|
|
if (const auto *ME = dyn_cast<MemberExpr>(E))
|
|
return {ME->getMemberDecl()};
|
|
|
|
return {};
|
|
}
|
|
|
|
QualType HeuristicResolverImpl::resolveExprToType(const Expr *E) {
|
|
std::vector<const NamedDecl *> Decls = resolveExprToDecls(E);
|
|
if (!Decls.empty())
|
|
return resolveDeclsToType(Decls, Ctx);
|
|
|
|
return E->getType();
|
|
}
|
|
|
|
QualType HeuristicResolverImpl::resolveNestedNameSpecifierToType(
|
|
const NestedNameSpecifier *NNS) {
|
|
if (!NNS)
|
|
return QualType();
|
|
|
|
// The purpose of this function is to handle the dependent (Kind ==
|
|
// Identifier) case, but we need to recurse on the prefix because
|
|
// that may be dependent as well, so for convenience handle
|
|
// the TypeSpec cases too.
|
|
switch (NNS->getKind()) {
|
|
case NestedNameSpecifier::TypeSpec:
|
|
return QualType(NNS->getAsType(), 0);
|
|
case NestedNameSpecifier::Identifier: {
|
|
return resolveDeclsToType(
|
|
resolveDependentMember(
|
|
resolveNestedNameSpecifierToType(NNS->getPrefix()),
|
|
NNS->getAsIdentifier(), TypeFilter),
|
|
Ctx);
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
return QualType();
|
|
}
|
|
|
|
bool isOrdinaryMember(const NamedDecl *ND) {
|
|
return ND->isInIdentifierNamespace(Decl::IDNS_Ordinary | Decl::IDNS_Tag |
|
|
Decl::IDNS_Member);
|
|
}
|
|
|
|
bool findOrdinaryMember(const CXXRecordDecl *RD, CXXBasePath &Path,
|
|
DeclarationName Name) {
|
|
Path.Decls = RD->lookup(Name).begin();
|
|
for (DeclContext::lookup_iterator I = Path.Decls, E = I.end(); I != E; ++I)
|
|
if (isOrdinaryMember(*I))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool HeuristicResolverImpl::findOrdinaryMemberInDependentClasses(
|
|
const CXXBaseSpecifier *Specifier, CXXBasePath &Path,
|
|
DeclarationName Name) {
|
|
TagDecl *TD = resolveTypeToTagDecl(Specifier->getType().getTypePtr());
|
|
if (const auto *RD = dyn_cast_if_present<CXXRecordDecl>(TD)) {
|
|
return findOrdinaryMember(RD, Path, Name);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
std::vector<const NamedDecl *> HeuristicResolverImpl::lookupDependentName(
|
|
CXXRecordDecl *RD, DeclarationName Name,
|
|
llvm::function_ref<bool(const NamedDecl *ND)> Filter) {
|
|
std::vector<const NamedDecl *> Results;
|
|
|
|
// Lookup in the class.
|
|
bool AnyOrdinaryMembers = false;
|
|
for (const NamedDecl *ND : RD->lookup(Name)) {
|
|
if (isOrdinaryMember(ND))
|
|
AnyOrdinaryMembers = true;
|
|
if (Filter(ND))
|
|
Results.push_back(ND);
|
|
}
|
|
if (AnyOrdinaryMembers)
|
|
return Results;
|
|
|
|
// Perform lookup into our base classes.
|
|
CXXBasePaths Paths;
|
|
Paths.setOrigin(RD);
|
|
if (!RD->lookupInBases(
|
|
[&](const CXXBaseSpecifier *Specifier, CXXBasePath &Path) {
|
|
return findOrdinaryMemberInDependentClasses(Specifier, Path, Name);
|
|
},
|
|
Paths, /*LookupInDependent=*/true))
|
|
return Results;
|
|
for (DeclContext::lookup_iterator I = Paths.front().Decls, E = I.end();
|
|
I != E; ++I) {
|
|
if (isOrdinaryMember(*I) && Filter(*I))
|
|
Results.push_back(*I);
|
|
}
|
|
return Results;
|
|
}
|
|
|
|
std::vector<const NamedDecl *> HeuristicResolverImpl::resolveDependentMember(
|
|
QualType QT, DeclarationName Name,
|
|
llvm::function_ref<bool(const NamedDecl *ND)> Filter) {
|
|
const Type *T = QT.getTypePtrOrNull();
|
|
if (!T)
|
|
return {};
|
|
TagDecl *TD = resolveTypeToTagDecl(T);
|
|
if (!TD)
|
|
return {};
|
|
if (auto *ED = dyn_cast<EnumDecl>(TD)) {
|
|
auto Result = ED->lookup(Name);
|
|
return {Result.begin(), Result.end()};
|
|
}
|
|
if (auto *RD = dyn_cast<CXXRecordDecl>(TD)) {
|
|
if (!RD->hasDefinition())
|
|
return {};
|
|
RD = RD->getDefinition();
|
|
return lookupDependentName(RD, Name, [&](const NamedDecl *ND) {
|
|
if (!Filter(ND))
|
|
return false;
|
|
if (const auto *MD = dyn_cast<CXXMethodDecl>(ND)) {
|
|
return !MD->isInstance() ||
|
|
MD->getMethodQualifiers().compatiblyIncludes(QT.getQualifiers(),
|
|
Ctx);
|
|
}
|
|
return true;
|
|
});
|
|
}
|
|
return {};
|
|
}
|
|
} // namespace
|
|
|
|
std::vector<const NamedDecl *> HeuristicResolver::resolveMemberExpr(
|
|
const CXXDependentScopeMemberExpr *ME) const {
|
|
return HeuristicResolverImpl(Ctx).resolveMemberExpr(ME);
|
|
}
|
|
std::vector<const NamedDecl *> HeuristicResolver::resolveDeclRefExpr(
|
|
const DependentScopeDeclRefExpr *RE) const {
|
|
return HeuristicResolverImpl(Ctx).resolveDeclRefExpr(RE);
|
|
}
|
|
std::vector<const NamedDecl *>
|
|
HeuristicResolver::resolveTypeOfCallExpr(const CallExpr *CE) const {
|
|
return HeuristicResolverImpl(Ctx).resolveTypeOfCallExpr(CE);
|
|
}
|
|
std::vector<const NamedDecl *>
|
|
HeuristicResolver::resolveCalleeOfCallExpr(const CallExpr *CE) const {
|
|
return HeuristicResolverImpl(Ctx).resolveCalleeOfCallExpr(CE);
|
|
}
|
|
std::vector<const NamedDecl *> HeuristicResolver::resolveUsingValueDecl(
|
|
const UnresolvedUsingValueDecl *UUVD) const {
|
|
return HeuristicResolverImpl(Ctx).resolveUsingValueDecl(UUVD);
|
|
}
|
|
std::vector<const NamedDecl *> HeuristicResolver::resolveDependentNameType(
|
|
const DependentNameType *DNT) const {
|
|
return HeuristicResolverImpl(Ctx).resolveDependentNameType(DNT);
|
|
}
|
|
std::vector<const NamedDecl *>
|
|
HeuristicResolver::resolveTemplateSpecializationType(
|
|
const DependentTemplateSpecializationType *DTST) const {
|
|
return HeuristicResolverImpl(Ctx).resolveTemplateSpecializationType(DTST);
|
|
}
|
|
QualType HeuristicResolver::resolveNestedNameSpecifierToType(
|
|
const NestedNameSpecifier *NNS) const {
|
|
return HeuristicResolverImpl(Ctx).resolveNestedNameSpecifierToType(NNS);
|
|
}
|
|
std::vector<const NamedDecl *> HeuristicResolver::lookupDependentName(
|
|
CXXRecordDecl *RD, DeclarationName Name,
|
|
llvm::function_ref<bool(const NamedDecl *ND)> Filter) {
|
|
return HeuristicResolverImpl(Ctx).lookupDependentName(RD, Name, Filter);
|
|
}
|
|
const QualType HeuristicResolver::getPointeeType(QualType T) const {
|
|
return HeuristicResolverImpl(Ctx).getPointeeType(T);
|
|
}
|
|
|
|
} // namespace clang
|