summaryrefslogtreecommitdiffstats
path: root/build/clang-plugin/mozsearch-plugin/from-clangd
diff options
context:
space:
mode:
Diffstat (limited to 'build/clang-plugin/mozsearch-plugin/from-clangd')
-rw-r--r--build/clang-plugin/mozsearch-plugin/from-clangd/HeuristicResolver.cpp349
-rw-r--r--build/clang-plugin/mozsearch-plugin/from-clangd/HeuristicResolver.h122
-rw-r--r--build/clang-plugin/mozsearch-plugin/from-clangd/README.md10
3 files changed, 481 insertions, 0 deletions
diff --git a/build/clang-plugin/mozsearch-plugin/from-clangd/HeuristicResolver.cpp b/build/clang-plugin/mozsearch-plugin/from-clangd/HeuristicResolver.cpp
new file mode 100644
index 0000000000..719094dbf2
--- /dev/null
+++ b/build/clang-plugin/mozsearch-plugin/from-clangd/HeuristicResolver.cpp
@@ -0,0 +1,349 @@
+//===--- 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/CXXInheritance.h"
+#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/Type.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<ValueDecl>(D); };
+const auto TypeFilter = [](const NamedDecl *D) { return isa<TypeDecl>(D); };
+const auto TemplateFilter = [](const NamedDecl *D) {
+ return isa<TemplateDecl>(D);
+};
+
+namespace {
+
+const Type *resolveDeclsToType(const std::vector<const NamedDecl *> &Decls,
+ ASTContext &Ctx) {
+ if (Decls.size() != 1) // Names an overload set -- just bail.
+ return nullptr;
+ if (const auto *TD = dyn_cast<TypeDecl>(Decls[0])) {
+ return Ctx.getTypeDeclType(TD).getTypePtr();
+ }
+ if (const auto *VD = dyn_cast<ValueDecl>(Decls[0])) {
+ return VD->getType().getTypePtrOrNull();
+ }
+ return nullptr;
+}
+
+} // namespace
+
+// 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 *HeuristicResolver::resolveTypeToRecordDecl(const Type *T) const {
+ 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);
+ if (!T)
+ return nullptr;
+ T = T->getCanonicalTypeInternal().getTypePtr();
+ }
+
+ if (const auto *RT = T->getAs<RecordType>())
+ return dyn_cast<CXXRecordDecl>(RT->getDecl());
+
+ if (const auto *ICNT = T->getAs<InjectedClassNameType>())
+ T = ICNT->getInjectedSpecializationType().getTypePtrOrNull();
+ if (!T)
+ return nullptr;
+
+ const auto *TST = T->getAs<TemplateSpecializationType>();
+ if (!TST)
+ return nullptr;
+
+ const ClassTemplateDecl *TD = dyn_cast_or_null<ClassTemplateDecl>(
+ 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<PointerType>()->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<X, ...>, and assume X is the pointee type.
+ auto *TST = T->getAs<TemplateSpecializationType>();
+ 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<const NamedDecl *> 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<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.
+ Expr *Base = ME->isImplicitAccess() ? nullptr : ME->getBase();
+ if (Base && BT->getKind() == BuiltinType::Dependent) {
+ BaseType = resolveExprToType(Base);
+ }
+ }
+ return resolveDependentMember(BaseType, ME->getMember(), NoFilter);
+}
+
+std::vector<const NamedDecl *> HeuristicResolver::resolveDeclRefExpr(
+ const DependentScopeDeclRefExpr *RE) const {
+ return resolveDependentMember(RE->getQualifier()->getAsType(),
+ RE->getDeclName(), StaticFilter);
+}
+
+std::vector<const NamedDecl *>
+HeuristicResolver::resolveTypeOfCallExpr(const CallExpr *CE) const {
+ const auto *CalleeType = resolveExprToType(CE->getCallee());
+ if (!CalleeType)
+ return {};
+ if (const auto *FnTypePtr = CalleeType->getAs<PointerType>())
+ CalleeType = FnTypePtr->getPointeeType().getTypePtr();
+ if (const FunctionType *FnType = CalleeType->getAs<FunctionType>()) {
+ if (const auto *D =
+ resolveTypeToRecordDecl(FnType->getReturnType().getTypePtr())) {
+ return {D};
+ }
+ }
+ return {};
+}
+
+std::vector<const NamedDecl *>
+HeuristicResolver::resolveCalleeOfCallExpr(const CallExpr *CE) const {
+ if (const auto *ND = dyn_cast_or_null<NamedDecl>(CE->getCalleeDecl())) {
+ return {ND};
+ }
+
+ return resolveExprToDecls(CE->getCallee());
+}
+
+std::vector<const NamedDecl *> HeuristicResolver::resolveUsingValueDecl(
+ const UnresolvedUsingValueDecl *UUVD) const {
+ return resolveDependentMember(UUVD->getQualifier()->getAsType(),
+ UUVD->getNameInfo().getName(), ValueFilter);
+}
+
+std::vector<const NamedDecl *> HeuristicResolver::resolveDependentNameType(
+ const DependentNameType *DNT) const {
+ return resolveDependentMember(
+ resolveNestedNameSpecifierToType(DNT->getQualifier()),
+ DNT->getIdentifier(), TypeFilter);
+}
+
+std::vector<const NamedDecl *>
+HeuristicResolver::resolveTemplateSpecializationType(
+ const DependentTemplateSpecializationType *DTST) const {
+ return resolveDependentMember(
+ resolveNestedNameSpecifierToType(DTST->getQualifier()),
+ DTST->getIdentifier(), TemplateFilter);
+}
+
+std::vector<const NamedDecl *>
+HeuristicResolver::resolveExprToDecls(const Expr *E) const {
+ 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 {};
+}
+
+const Type *HeuristicResolver::resolveExprToType(const Expr *E) const {
+ std::vector<const NamedDecl *> Decls = resolveExprToDecls(E);
+ if (!Decls.empty())
+ return resolveDeclsToType(Decls, Ctx);
+
+ 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),
+ Ctx);
+ }
+ default:
+ break;
+ }
+ return nullptr;
+}
+
+namespace {
+
+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;
+}
+
+} // namespace
+
+bool HeuristicResolver::findOrdinaryMemberInDependentClasses(
+ const CXXBaseSpecifier *Specifier, CXXBasePath &Path,
+ DeclarationName Name) const {
+ CXXRecordDecl *RD =
+ resolveTypeToRecordDecl(Specifier->getType().getTypePtr());
+ if (!RD)
+ return false;
+ return findOrdinaryMember(RD, Path, Name);
+}
+
+std::vector<const NamedDecl *> HeuristicResolver::lookupDependentName(
+ CXXRecordDecl *RD, DeclarationName Name,
+ llvm::function_ref<bool(const NamedDecl *ND)> Filter) const {
+ 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 *> HeuristicResolver::resolveDependentMember(
+ const Type *T, DeclarationName Name,
+ llvm::function_ref<bool(const NamedDecl *ND)> Filter) const {
+ if (!T)
+ return {};
+ if (auto *ET = T->getAs<EnumType>()) {
+ auto Result = ET->getDecl()->lookup(Name);
+ return {Result.begin(), Result.end()};
+ }
+ if (auto *RD = resolveTypeToRecordDecl(T)) {
+ if (!RD->hasDefinition())
+ return {};
+ RD = RD->getDefinition();
+ return lookupDependentName(RD, Name, Filter);
+ }
+ return {};
+}
+
+} // namespace clangd
+} // namespace clang
diff --git a/build/clang-plugin/mozsearch-plugin/from-clangd/HeuristicResolver.h b/build/clang-plugin/mozsearch-plugin/from-clangd/HeuristicResolver.h
new file mode 100644
index 0000000000..dc04123d37
--- /dev/null
+++ b/build/clang-plugin/mozsearch-plugin/from-clangd/HeuristicResolver.h
@@ -0,0 +1,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
diff --git a/build/clang-plugin/mozsearch-plugin/from-clangd/README.md b/build/clang-plugin/mozsearch-plugin/from-clangd/README.md
new file mode 100644
index 0000000000..9f39dab231
--- /dev/null
+++ b/build/clang-plugin/mozsearch-plugin/from-clangd/README.md
@@ -0,0 +1,10 @@
+The facilities in this subdirectory are copied over from clangd
+(https://clangd.llvm.org/).
+
+The files here are currently copies of the following upstream files:
+https://github.com/llvm/llvm-project/blob/2bcbcbefcd0f7432f99cc07bb47d1e1ecb579a3f/clang-tools-extra/clangd/HeuristicResolver.h
+https://github.com/llvm/llvm-project/blob/2bcbcbefcd0f7432f99cc07bb47d1e1ecb579a3f/clang-tools-extra/clangd/HeuristicResolver.cpp
+
+If, in the future, these facilities are moved from clangd to
+to libclangTooling and exposed in the clang API headers, we can
+switch to consuming them from there directly.