| 1 | //===--- Index.h -------------------------------------------------*- C++-*-===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_INDEX_H |
| 10 | #define |
| 11 | |
| 12 | #include "index/Ref.h" |
| 13 | #include "index/Relation.h" |
| 14 | #include "index/Symbol.h" |
| 15 | #include "index/SymbolID.h" |
| 16 | #include "llvm/ADT/DenseSet.h" |
| 17 | #include "llvm/ADT/FunctionExtras.h" |
| 18 | #include "llvm/Support/JSON.h" |
| 19 | #include <mutex> |
| 20 | #include <optional> |
| 21 | #include <string> |
| 22 | |
| 23 | namespace clang { |
| 24 | namespace clangd { |
| 25 | |
| 26 | struct FuzzyFindRequest { |
| 27 | /// A query string for the fuzzy find. This is matched against symbols' |
| 28 | /// un-qualified identifiers and should not contain qualifiers like "::". |
| 29 | std::string Query; |
| 30 | /// If this is non-empty, symbols must be in at least one of the scopes |
| 31 | /// (e.g. namespaces) excluding nested scopes. For example, if a scope "xyz::" |
| 32 | /// is provided, the matched symbols must be defined in namespace xyz but not |
| 33 | /// namespace xyz::abc. |
| 34 | /// |
| 35 | /// The global scope is "", a top level scope is "foo::", etc. |
| 36 | std::vector<std::string> Scopes; |
| 37 | /// If set to true, allow symbols from any scope. Scopes explicitly listed |
| 38 | /// above will be ranked higher. |
| 39 | bool AnyScope = false; |
| 40 | /// The number of top candidates to return. The index may choose to |
| 41 | /// return more than this, e.g. if it doesn't know which candidates are best. |
| 42 | std::optional<uint32_t> Limit; |
| 43 | /// If set to true, only symbols for completion support will be considered. |
| 44 | bool RestrictForCodeCompletion = false; |
| 45 | /// Contextually relevant files (e.g. the file we're code-completing in). |
| 46 | /// Paths should be absolute. |
| 47 | std::vector<std::string> ProximityPaths; |
| 48 | /// Preferred types of symbols. These are raw representation of `OpaqueType`. |
| 49 | std::vector<std::string> PreferredTypes; |
| 50 | |
| 51 | bool operator==(const FuzzyFindRequest &Req) const { |
| 52 | return std::tie(args: Query, args: Scopes, args: Limit, args: RestrictForCodeCompletion, |
| 53 | args: ProximityPaths, args: PreferredTypes) == |
| 54 | std::tie(args: Req.Query, args: Req.Scopes, args: Req.Limit, |
| 55 | args: Req.RestrictForCodeCompletion, args: Req.ProximityPaths, |
| 56 | args: Req.PreferredTypes); |
| 57 | } |
| 58 | bool operator!=(const FuzzyFindRequest &Req) const { return !(*this == Req); } |
| 59 | }; |
| 60 | bool fromJSON(const llvm::json::Value &Value, FuzzyFindRequest &Request, |
| 61 | llvm::json::Path); |
| 62 | llvm::json::Value toJSON(const FuzzyFindRequest &Request); |
| 63 | |
| 64 | struct LookupRequest { |
| 65 | llvm::DenseSet<SymbolID> IDs; |
| 66 | }; |
| 67 | |
| 68 | struct RefsRequest { |
| 69 | llvm::DenseSet<SymbolID> IDs; |
| 70 | RefKind Filter = RefKind::All; |
| 71 | /// If set, limit the number of refers returned from the index. The index may |
| 72 | /// choose to return less than this, e.g. it tries to avoid returning stale |
| 73 | /// results. |
| 74 | std::optional<uint32_t> Limit; |
| 75 | /// If set, populates the container of the reference. |
| 76 | /// Index implementations may chose to populate containers no matter what. |
| 77 | bool WantContainer = false; |
| 78 | }; |
| 79 | |
| 80 | struct ContainedRefsRequest { |
| 81 | /// Note that RefKind::Call just restricts the matched SymbolKind to |
| 82 | /// functions, not the form of the reference (e.g. address-of-function, |
| 83 | /// which can indicate an indirect call, should still be caught). |
| 84 | static const RefKind SupportedRefKinds = RefKind::Call; |
| 85 | |
| 86 | SymbolID ID; |
| 87 | /// If set, limit the number of refers returned from the index. The index may |
| 88 | /// choose to return less than this, e.g. it tries to avoid returning stale |
| 89 | /// results. |
| 90 | std::optional<uint32_t> Limit; |
| 91 | }; |
| 92 | |
| 93 | struct RelationsRequest { |
| 94 | llvm::DenseSet<SymbolID> Subjects; |
| 95 | RelationKind Predicate; |
| 96 | /// If set, limit the number of relations returned from the index. |
| 97 | std::optional<uint32_t> Limit; |
| 98 | }; |
| 99 | |
| 100 | struct ContainedRefsResult { |
| 101 | /// The source location where the symbol is named. |
| 102 | SymbolLocation Location; |
| 103 | RefKind Kind = RefKind::Unknown; |
| 104 | /// The ID of the symbol which is referred to |
| 105 | SymbolID Symbol; |
| 106 | }; |
| 107 | |
| 108 | /// Describes what data is covered by an index. |
| 109 | /// |
| 110 | /// Indexes may contain symbols but not references from a file, etc. |
| 111 | /// This affects merging: if a staler index contains a reference but a fresher |
| 112 | /// one does not, we want to trust the fresher index *only* if it actually |
| 113 | /// includes references in general. |
| 114 | enum class IndexContents : uint8_t { |
| 115 | None = 0, |
| 116 | Symbols = 1 << 1, |
| 117 | References = 1 << 2, |
| 118 | Relations = 1 << 3, |
| 119 | All = Symbols | References | Relations |
| 120 | }; |
| 121 | |
| 122 | inline constexpr IndexContents operator&(IndexContents L, IndexContents R) { |
| 123 | return static_cast<IndexContents>(static_cast<uint8_t>(L) & |
| 124 | static_cast<uint8_t>(R)); |
| 125 | } |
| 126 | |
| 127 | inline constexpr IndexContents operator|(IndexContents L, IndexContents R) { |
| 128 | return static_cast<IndexContents>(static_cast<uint8_t>(L) | |
| 129 | static_cast<uint8_t>(R)); |
| 130 | } |
| 131 | |
| 132 | /// Interface for symbol indexes that can be used for searching or |
| 133 | /// matching symbols among a set of symbols based on names or unique IDs. |
| 134 | class SymbolIndex { |
| 135 | public: |
| 136 | virtual ~SymbolIndex() = default; |
| 137 | |
| 138 | /// Matches symbols in the index fuzzily and applies \p Callback on |
| 139 | /// each matched symbol before returning. |
| 140 | /// If returned Symbols are used outside Callback, they must be deep-copied! |
| 141 | /// |
| 142 | /// Returns true if there may be more results (limited by Req.Limit). |
| 143 | virtual bool |
| 144 | fuzzyFind(const FuzzyFindRequest &Req, |
| 145 | llvm::function_ref<void(const Symbol &)> Callback) const = 0; |
| 146 | |
| 147 | /// Looks up symbols with any of the given symbol IDs and applies \p Callback |
| 148 | /// on each matched symbol. |
| 149 | /// The returned symbol must be deep-copied if it's used outside Callback. |
| 150 | virtual void |
| 151 | lookup(const LookupRequest &Req, |
| 152 | llvm::function_ref<void(const Symbol &)> Callback) const = 0; |
| 153 | |
| 154 | /// Finds all occurrences (e.g. references, declarations, definitions) of |
| 155 | /// symbols and applies \p Callback on each result. |
| 156 | /// |
| 157 | /// Results should be returned in arbitrary order. |
| 158 | /// The returned result must be deep-copied if it's used outside Callback. |
| 159 | /// FIXME: there's no indication which result references which symbol. |
| 160 | /// |
| 161 | /// Returns true if there will be more results (limited by Req.Limit); |
| 162 | virtual bool refs(const RefsRequest &Req, |
| 163 | llvm::function_ref<void(const Ref &)> Callback) const = 0; |
| 164 | |
| 165 | /// Find all symbols that are referenced by a symbol and apply |
| 166 | /// \p Callback on each result. |
| 167 | /// |
| 168 | /// Results should be returned in arbitrary order. |
| 169 | /// The returned result must be deep-copied if it's used outside Callback. |
| 170 | /// |
| 171 | /// Returns true if there will be more results (limited by Req.Limit); |
| 172 | virtual bool containedRefs( |
| 173 | const ContainedRefsRequest &Req, |
| 174 | llvm::function_ref<void(const ContainedRefsResult &)> Callback) const = 0; |
| 175 | |
| 176 | /// Finds all relations (S, P, O) stored in the index such that S is among |
| 177 | /// Req.Subjects and P is Req.Predicate, and invokes \p Callback for (S, O) in |
| 178 | /// each. |
| 179 | virtual void relations( |
| 180 | const RelationsRequest &Req, |
| 181 | llvm::function_ref<void(const SymbolID &Subject, const Symbol &Object)> |
| 182 | Callback) const = 0; |
| 183 | |
| 184 | /// Returns function which checks if the specified file was used to build this |
| 185 | /// index or not. The function must only be called while the index is alive. |
| 186 | using IndexedFiles = |
| 187 | llvm::unique_function<IndexContents(llvm::StringRef) const>; |
| 188 | virtual IndexedFiles indexedFiles() const = 0; |
| 189 | |
| 190 | /// Returns estimated size of index (in bytes). |
| 191 | virtual size_t estimateMemoryUsage() const = 0; |
| 192 | }; |
| 193 | |
| 194 | // Delegating implementation of SymbolIndex whose delegate can be swapped out. |
| 195 | class SwapIndex : public SymbolIndex { |
| 196 | public: |
| 197 | // If an index is not provided, reset() must be called. |
| 198 | SwapIndex(std::unique_ptr<SymbolIndex> Index = nullptr) |
| 199 | : Index(std::move(Index)) {} |
| 200 | void reset(std::unique_ptr<SymbolIndex>); |
| 201 | |
| 202 | // SymbolIndex methods delegate to the current index, which is kept alive |
| 203 | // until the call returns (even if reset() is called). |
| 204 | bool fuzzyFind(const FuzzyFindRequest &, |
| 205 | llvm::function_ref<void(const Symbol &)>) const override; |
| 206 | void lookup(const LookupRequest &, |
| 207 | llvm::function_ref<void(const Symbol &)>) const override; |
| 208 | bool refs(const RefsRequest &, |
| 209 | llvm::function_ref<void(const Ref &)>) const override; |
| 210 | bool containedRefs( |
| 211 | const ContainedRefsRequest &, |
| 212 | llvm::function_ref<void(const ContainedRefsResult &)>) const override; |
| 213 | void relations(const RelationsRequest &, |
| 214 | llvm::function_ref<void(const SymbolID &, const Symbol &)>) |
| 215 | const override; |
| 216 | |
| 217 | llvm::unique_function<IndexContents(llvm::StringRef) const> |
| 218 | indexedFiles() const override; |
| 219 | |
| 220 | size_t estimateMemoryUsage() const override; |
| 221 | |
| 222 | private: |
| 223 | std::shared_ptr<SymbolIndex> snapshot() const; |
| 224 | mutable std::mutex Mutex; |
| 225 | std::shared_ptr<SymbolIndex> Index; |
| 226 | }; |
| 227 | |
| 228 | } // namespace clangd |
| 229 | } // namespace clang |
| 230 | |
| 231 | #endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_INDEX_H |
| 232 | |