//===--- 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 "HeuristicResolver.h" #include "clang/AST/ASTContext.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/ExprCXX.h" namespace clang { namespace clangd { // 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(D); }; const auto TypeFilter = [](const NamedDecl *D) { return isa(D); }; const auto TemplateFilter = [](const NamedDecl *D) { return isa(D); }; // 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) { assert(T); if (const auto *RT = T->getAs()) return dyn_cast(RT->getDecl()); if (const auto *ICNT = T->getAs()) T = ICNT->getInjectedSpecializationType().getTypePtrOrNull(); if (!T) return nullptr; const auto *TST = T->getAs(); if (!TST) return nullptr; const ClassTemplateDecl *TD = dyn_cast_or_null( TST->getTemplateName().getAsTemplateDecl()); if (!TD) return nullptr; return TD->getTemplatedDecl(); } const Type *HeuristicResolver::getPointeeType(const Type *T) const { if (!T) return nullptr; if (T->isPointerType()) return T->castAs()->getPointeeType().getTypePtrOrNull(); // 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 nullptr; // 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, and assume X is the pointee type. auto *TST = T->getAs(); if (!TST) return nullptr; if (TST->template_arguments().size() == 0) return nullptr; const TemplateArgument &FirstArg = TST->template_arguments()[0]; if (FirstArg.getKind() != TemplateArgument::Type) return nullptr; return FirstArg.getAsType().getTypePtrOrNull(); } std::vector HeuristicResolver::resolveMemberExpr( const CXXDependentScopeMemberExpr *ME) const { // If the expression has a qualifier, first 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 (const Type *QualifierType = resolveNestedNameSpecifierToType(NNS)) { auto Decls = resolveDependentMember(QualifierType, ME->getMember(), NoFilter); if (!Decls.empty()) return Decls; } } // If that didn't yield any results, try resolving the member inside // the expression's base type. const Type *BaseType = ME->getBaseType().getTypePtrOrNull(); if (ME->isArrow()) { BaseType = getPointeeType(BaseType); } if (!BaseType) return {}; if (const auto *BT = BaseType->getAs()) { // 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. Expr *Base = ME->isImplicitAccess() ? nullptr : ME->getBase(); if (Base && BT->getKind() == BuiltinType::Dependent) { BaseType = resolveExprToType(Base); } } return resolveDependentMember(BaseType, ME->getMember(), NoFilter); } std::vector HeuristicResolver::resolveDeclRefExpr( const DependentScopeDeclRefExpr *RE) const { return resolveDependentMember(RE->getQualifier()->getAsType(), RE->getDeclName(), StaticFilter); } std::vector HeuristicResolver::resolveTypeOfCallExpr(const CallExpr *CE) const { const auto *CalleeType = resolveExprToType(CE->getCallee()); if (!CalleeType) return {}; if (const auto *FnTypePtr = CalleeType->getAs()) CalleeType = FnTypePtr->getPointeeType().getTypePtr(); if (const FunctionType *FnType = CalleeType->getAs()) { if (const auto *D = resolveTypeToRecordDecl(FnType->getReturnType().getTypePtr())) { return {D}; } } return {}; } std::vector HeuristicResolver::resolveCalleeOfCallExpr(const CallExpr *CE) const { if (const auto *ND = dyn_cast_or_null(CE->getCalleeDecl())) { return {ND}; } return resolveExprToDecls(CE->getCallee()); } std::vector HeuristicResolver::resolveUsingValueDecl( const UnresolvedUsingValueDecl *UUVD) const { return resolveDependentMember(UUVD->getQualifier()->getAsType(), UUVD->getNameInfo().getName(), ValueFilter); } std::vector HeuristicResolver::resolveDependentNameType( const DependentNameType *DNT) const { return resolveDependentMember( resolveNestedNameSpecifierToType(DNT->getQualifier()), DNT->getIdentifier(), TypeFilter); } std::vector HeuristicResolver::resolveTemplateSpecializationType( const DependentTemplateSpecializationType *DTST) const { return resolveDependentMember( resolveNestedNameSpecifierToType(DTST->getQualifier()), DTST->getIdentifier(), TemplateFilter); } const Type *resolveDeclsToType(const std::vector &Decls) { if (Decls.size() != 1) // Names an overload set -- just bail. return nullptr; if (const auto *TD = dyn_cast(Decls[0])) { return TD->getTypeForDecl(); } if (const auto *VD = dyn_cast(Decls[0])) { return VD->getType().getTypePtrOrNull(); } return nullptr; } std::vector HeuristicResolver::resolveExprToDecls(const Expr *E) const { if (const auto *ME = dyn_cast(E)) { return resolveMemberExpr(ME); } if (const auto *RE = dyn_cast(E)) { return resolveDeclRefExpr(RE); } if (const auto *OE = dyn_cast(E)) { return {OE->decls_begin(), OE->decls_end()}; } if (const auto *CE = dyn_cast(E)) { return resolveTypeOfCallExpr(CE); } if (const auto *ME = dyn_cast(E)) return {ME->getMemberDecl()}; return {}; } const Type *HeuristicResolver::resolveExprToType(const Expr *E) const { std::vector Decls = resolveExprToDecls(E); if (!Decls.empty()) return resolveDeclsToType(Decls); return E->getType().getTypePtr(); } const Type *HeuristicResolver::resolveNestedNameSpecifierToType( const NestedNameSpecifier *NNS) const { if (!NNS) return nullptr; // 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: case NestedNameSpecifier::TypeSpecWithTemplate: return NNS->getAsType(); case NestedNameSpecifier::Identifier: { return resolveDeclsToType(resolveDependentMember( resolveNestedNameSpecifierToType(NNS->getPrefix()), NNS->getAsIdentifier(), TypeFilter)); } default: break; } return nullptr; } std::vector HeuristicResolver::resolveDependentMember( const Type *T, DeclarationName Name, llvm::function_ref Filter) const { if (!T) return {}; if (auto *ET = T->getAs()) { auto Result = ET->getDecl()->lookup(Name); return {Result.begin(), Result.end()}; } if (auto *RD = resolveTypeToRecordDecl(T)) { if (!RD->hasDefinition()) return {}; RD = RD->getDefinition(); return RD->lookupDependentName(Name, Filter); } return {}; } } // namespace clangd } // namespace clang