Nathan Ridge 9510b09402 [clangd] Factor out the heuristic resolver code into its own class
The patch also does some cleanup on the interface of the entry
points from TargetFinder into the heuristic resolution code.

Since the heuristic resolver is created in a place where the
ASTContext is available, it can store the ASTContext and the
NameFactory hack can be removed.

Differential revision: https://reviews.llvm.org/D92290
2021-02-16 04:10:52 -05:00

224 lines
9.4 KiB
C++

//===--- FindTarget.h - What does an AST node refer to? ---------*- 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
//
//===----------------------------------------------------------------------===//
//
// Many clangd features are concerned with references in the AST:
// - xrefs, go-to-definition, explicitly talk about references
// - hover and code actions relate to things you "target" in the editor
// - refactoring actions need to know about entities that are referenced
// to determine whether/how the edit can be applied.
//
// Historically, we have used libIndex (IndexDataConsumer) to tie source
// locations to referenced declarations. This file defines a more decoupled
// approach based around AST nodes (DynTypedNode), and can be combined with
// SelectionTree or other traversals.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANGD_FINDTARGET_H
#define LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANGD_FINDTARGET_H
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTTypeTraits.h"
#include "clang/AST/NestedNameSpecifier.h"
#include "clang/AST/Stmt.h"
#include "clang/Basic/SourceLocation.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/raw_ostream.h"
#include <bitset>
namespace clang {
namespace clangd {
class HeuristicResolver;
/// Describes the link between an AST node and a Decl it refers to.
enum class DeclRelation : unsigned;
/// A bitfield of DeclRelations.
class DeclRelationSet;
/// targetDecl() finds the declaration referred to by an AST node.
/// For example a RecordTypeLoc refers to the RecordDecl for the type.
///
/// In some cases there are multiple results, e.g. a dependent unresolved
/// OverloadExpr may have several candidates. All will be returned:
///
/// void foo(int); <-- candidate
/// void foo(double); <-- candidate
/// template <typename T> callFoo() { foo(T()); }
/// ^ OverloadExpr
///
/// In other cases, there may be choices about what "referred to" means.
/// e.g. does naming a typedef refer to the underlying type?
/// The results are marked with a set of DeclRelations, and can be filtered.
///
/// struct S{}; <-- candidate (underlying)
/// using T = S{}; <-- candidate (alias)
/// T x;
/// ^ TypedefTypeLoc
///
/// Formally, we walk a graph starting at the provided node, and return the
/// decls that were found. Certain edges in the graph have labels, and for each
/// decl we return the set of labels seen on a path to the decl.
/// For the previous example:
///
/// TypedefTypeLoc T
/// |
/// TypedefType T
/// / \
/// [underlying] [alias]
/// / \
/// RecordDecl S TypeAliasDecl T
///
/// Note that this function only returns NamedDecls. Generally other decls
/// don't have references in this sense, just the node itself.
/// If callers want to support such decls, they should cast the node directly.
///
/// FIXME: some AST nodes cannot be DynTypedNodes, these cannot be specified.
llvm::SmallVector<const NamedDecl *, 1>
targetDecl(const DynTypedNode &, DeclRelationSet Mask,
const HeuristicResolver *Resolver);
/// Similar to targetDecl(), however instead of applying a filter, all possible
/// decls are returned along with their DeclRelationSets.
/// This is suitable for indexing, where everything is recorded and filtering
/// is applied later.
llvm::SmallVector<std::pair<const NamedDecl *, DeclRelationSet>, 1>
allTargetDecls(const DynTypedNode &, const HeuristicResolver *);
enum class DeclRelation : unsigned {
// Template options apply when the declaration is an instantiated template.
// e.g. [[vector<int>]] vec;
/// This is the template instantiation that was referred to.
/// e.g. template<> class vector<int> (the implicit specialization)
TemplateInstantiation,
/// This is the pattern the template specialization was instantiated from.
/// e.g. class vector<T> (the pattern within the primary template)
TemplatePattern,
// Alias options apply when the declaration is an alias.
// e.g. namespace client { [[X]] x; }
/// This declaration is an alias that was referred to.
/// e.g. using ns::X (the UsingDecl directly referenced),
/// using Z = ns::Y (the TypeAliasDecl directly referenced)
Alias,
/// This is the underlying declaration for a renaming-alias, decltype etc.
/// e.g. class ns::Y (the underlying declaration referenced).
///
/// Note that we don't treat `using ns::X` as a first-class declaration like
/// `using Z = ns::Y`. Therefore reference to X that goes through this
/// using-decl is considered a direct reference (without the Underlying bit).
/// Nevertheless, we report `using ns::X` as an Alias, so that some features
/// like go-to-definition can still target it.
Underlying,
};
llvm::raw_ostream &operator<<(llvm::raw_ostream &, DeclRelation);
/// Information about a reference written in the source code, independent of the
/// actual AST node that this reference lives in.
/// Useful for tools that are source-aware, e.g. refactorings.
struct ReferenceLoc {
/// Contains qualifier written in the code, if any, e.g. 'ns::' for 'ns::foo'.
NestedNameSpecifierLoc Qualifier;
/// Start location of the last name part, i.e. 'foo' in 'ns::foo<int>'.
SourceLocation NameLoc;
/// True if the reference is a declaration or definition;
bool IsDecl = false;
// FIXME: add info about template arguments.
/// A list of targets referenced by this name. Normally this has a single
/// element, but multiple is also possible, e.g. in case of using declarations
/// or unresolved overloaded functions.
/// For dependent and unresolved references, Targets can also be empty.
llvm::SmallVector<const NamedDecl *, 1> Targets;
};
llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, ReferenceLoc R);
/// Recursively traverse \p S and report all references explicitly written in
/// the code. The main use-case is refactorings that need to process all
/// references in some subrange of the file and apply simple edits, e.g. add
/// qualifiers.
/// FIXME: currently this does not report references to overloaded operators.
/// FIXME: extend to report location information about declaration names too.
void findExplicitReferences(const Stmt *S,
llvm::function_ref<void(ReferenceLoc)> Out,
const HeuristicResolver *Resolver);
void findExplicitReferences(const Decl *D,
llvm::function_ref<void(ReferenceLoc)> Out,
const HeuristicResolver *Resolver);
void findExplicitReferences(const ASTContext &AST,
llvm::function_ref<void(ReferenceLoc)> Out,
const HeuristicResolver *Resolver);
/// Find declarations explicitly referenced in the source code defined by \p N.
/// For templates, will prefer to return a template instantiation whenever
/// possible. However, can also return a template pattern if the specialization
/// cannot be picked, e.g. in dependent code or when there is no corresponding
/// Decl for a template instantiation, e.g. for templated using decls:
/// template <class T> using Ptr = T*;
/// Ptr<int> x;
/// ^~~ there is no Decl for 'Ptr<int>', so we return the template pattern.
/// \p Mask should not contain TemplatePattern or TemplateInstantiation.
llvm::SmallVector<const NamedDecl *, 1>
explicitReferenceTargets(DynTypedNode N, DeclRelationSet Mask,
const HeuristicResolver *Resolver);
// Boring implementation details of bitfield.
class DeclRelationSet {
using Set = std::bitset<static_cast<unsigned>(DeclRelation::Underlying) + 1>;
Set S;
DeclRelationSet(Set S) : S(S) {}
public:
DeclRelationSet() = default;
DeclRelationSet(DeclRelation R) { S.set(static_cast<unsigned>(R)); }
explicit operator bool() const { return S.any(); }
friend DeclRelationSet operator&(DeclRelationSet L, DeclRelationSet R) {
return L.S & R.S;
}
friend DeclRelationSet operator|(DeclRelationSet L, DeclRelationSet R) {
return L.S | R.S;
}
friend bool operator==(DeclRelationSet L, DeclRelationSet R) {
return L.S == R.S;
}
friend DeclRelationSet operator~(DeclRelationSet R) { return ~R.S; }
DeclRelationSet &operator|=(DeclRelationSet Other) {
S |= Other.S;
return *this;
}
DeclRelationSet &operator&=(DeclRelationSet Other) {
S &= Other.S;
return *this;
}
bool contains(DeclRelationSet Other) const {
return (S & Other.S) == Other.S;
}
friend llvm::raw_ostream &operator<<(llvm::raw_ostream &, DeclRelationSet);
};
// The above operators can't be looked up if both sides are enums.
// over.match.oper.html#3.2
inline DeclRelationSet operator|(DeclRelation L, DeclRelation R) {
return DeclRelationSet(L) | DeclRelationSet(R);
}
inline DeclRelationSet operator&(DeclRelation L, DeclRelation R) {
return DeclRelationSet(L) & DeclRelationSet(R);
}
inline DeclRelationSet operator~(DeclRelation R) { return ~DeclRelationSet(R); }
llvm::raw_ostream &operator<<(llvm::raw_ostream &, DeclRelationSet);
} // namespace clangd
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANGD_FINDTARGET_H