| //===--- Ref.h ---------------------------------------------------*- 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_INDEX_REF_H |
| #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_REF_H |
| |
| #include "SymbolID.h" |
| #include "SymbolLocation.h" |
| #include "clang/Index/IndexSymbol.h" |
| #include "llvm/ADT/DenseMap.h" |
| #include "llvm/ADT/Hashing.h" |
| #include "llvm/Support/Allocator.h" |
| #include "llvm/Support/StringSaver.h" |
| #include "llvm/Support/raw_ostream.h" |
| #include <cstdint> |
| #include <set> |
| #include <utility> |
| |
| namespace clang { |
| namespace clangd { |
| |
| /// Describes the kind of a cross-reference. |
| /// |
| /// This is a bitfield which can be combined from different kinds. |
| enum class RefKind : uint8_t { |
| Unknown = 0, |
| // Points to symbol declaration. Example: |
| // |
| // class Foo; |
| // ^ Foo declaration |
| // Foo foo; |
| // ^ this does not reference Foo declaration |
| Declaration = 1 << 0, |
| // Points to symbol definition. Example: |
| // |
| // int foo(); |
| // ^ references foo declaration, but not foo definition |
| // int foo() { return 42; } |
| // ^ references foo definition, but not declaration |
| // bool bar() { return true; } |
| // ^ references both definition and declaration |
| Definition = 1 << 1, |
| // Points to symbol reference. Example: |
| // |
| // int Foo = 42; |
| // int Bar = Foo + 1; |
| // ^ this is a reference to Foo |
| Reference = 1 << 2, |
| // The reference explicitly spells out declaration's name. Such references can |
| // not come from macro expansions or implicit AST nodes. |
| // |
| // class Foo { public: Foo() {} }; |
| // ^ references declaration, definition and explicitly spells out name |
| // #define MACRO Foo |
| // v there is an implicit constructor call here which is not a spelled ref |
| // Foo foo; |
| // ^ this reference explicitly spells out Foo's name |
| // struct Bar { |
| // MACRO Internal; |
| // ^ this references Foo, but does not explicitly spell out its name |
| // }; |
| Spelled = 1 << 3, |
| All = Declaration | Definition | Reference | Spelled, |
| }; |
| |
| inline RefKind operator|(RefKind L, RefKind R) { |
| return static_cast<RefKind>(static_cast<uint8_t>(L) | |
| static_cast<uint8_t>(R)); |
| } |
| inline RefKind &operator|=(RefKind &L, RefKind R) { return L = L | R; } |
| inline RefKind operator&(RefKind A, RefKind B) { |
| return static_cast<RefKind>(static_cast<uint8_t>(A) & |
| static_cast<uint8_t>(B)); |
| } |
| |
| llvm::raw_ostream &operator<<(llvm::raw_ostream &, RefKind); |
| |
| /// Represents a symbol occurrence in the source file. |
| /// Despite the name, it could be a declaration/definition/reference. |
| /// |
| /// WARNING: Location does not own the underlying data - Copies are shallow. |
| struct Ref { |
| /// The source location where the symbol is named. |
| SymbolLocation Location; |
| RefKind Kind = RefKind::Unknown; |
| /// The ID of the symbol whose definition contains this reference. |
| /// For example, for a reference inside a function body, this would |
| /// be that function. For top-level definitions this isNull(). |
| SymbolID Container; |
| }; |
| |
| inline bool operator<(const Ref &L, const Ref &R) { |
| return std::tie(L.Location, L.Kind, L.Container) < |
| std::tie(R.Location, R.Kind, R.Container); |
| } |
| inline bool operator==(const Ref &L, const Ref &R) { |
| return std::tie(L.Location, L.Kind, L.Container) == |
| std::tie(R.Location, R.Kind, R.Container); |
| } |
| |
| llvm::raw_ostream &operator<<(llvm::raw_ostream &, const Ref &); |
| |
| /// An efficient structure of storing large set of symbol references in memory. |
| /// Filenames are deduplicated. |
| class RefSlab { |
| public: |
| // Refs are stored in order. |
| using value_type = std::pair<SymbolID, llvm::ArrayRef<Ref>>; |
| using const_iterator = std::vector<value_type>::const_iterator; |
| using iterator = const_iterator; |
| |
| RefSlab() = default; |
| RefSlab(RefSlab &&Slab) = default; |
| RefSlab &operator=(RefSlab &&RHS) = default; |
| |
| const_iterator begin() const { return Refs.begin(); } |
| const_iterator end() const { return Refs.end(); } |
| /// Gets the number of symbols. |
| size_t size() const { return Refs.size(); } |
| size_t numRefs() const { return NumRefs; } |
| bool empty() const { return Refs.empty(); } |
| |
| size_t bytes() const { |
| return sizeof(*this) + Arena.getTotalMemory() + |
| sizeof(value_type) * Refs.capacity(); |
| } |
| |
| /// RefSlab::Builder is a mutable container that can 'freeze' to RefSlab. |
| class Builder { |
| public: |
| Builder() : UniqueStrings(Arena) {} |
| /// Adds a ref to the slab. Deep copy: Strings will be owned by the slab. |
| void insert(const SymbolID &ID, const Ref &S); |
| /// Consumes the builder to finalize the slab. |
| RefSlab build() &&; |
| |
| private: |
| // A ref we're storing with its symbol to consume with build(). |
| // All strings are interned, so DenseMapInfo can use pointer comparisons. |
| struct Entry { |
| SymbolID Symbol; |
| Ref Reference; |
| }; |
| friend struct llvm::DenseMapInfo<Entry>; |
| |
| llvm::BumpPtrAllocator Arena; |
| llvm::UniqueStringSaver UniqueStrings; // Contents on the arena. |
| llvm::DenseSet<Entry> Entries; |
| }; |
| |
| private: |
| RefSlab(std::vector<value_type> Refs, llvm::BumpPtrAllocator Arena, |
| size_t NumRefs) |
| : Arena(std::move(Arena)), Refs(std::move(Refs)), NumRefs(NumRefs) {} |
| |
| llvm::BumpPtrAllocator Arena; |
| std::vector<value_type> Refs; |
| /// Number of all references. |
| size_t NumRefs = 0; |
| }; |
| |
| } // namespace clangd |
| } // namespace clang |
| |
| namespace llvm { |
| template <> struct DenseMapInfo<clang::clangd::RefSlab::Builder::Entry> { |
| using Entry = clang::clangd::RefSlab::Builder::Entry; |
| static inline Entry getEmptyKey() { |
| static Entry E{clang::clangd::SymbolID(""), {}}; |
| return E; |
| } |
| static inline Entry getTombstoneKey() { |
| static Entry E{clang::clangd::SymbolID("TOMBSTONE"), {}}; |
| return E; |
| } |
| static unsigned getHashValue(const Entry &Val) { |
| return llvm::hash_combine( |
| Val.Symbol, reinterpret_cast<uintptr_t>(Val.Reference.Location.FileURI), |
| Val.Reference.Location.Start.rep(), Val.Reference.Location.End.rep()); |
| } |
| static bool isEqual(const Entry &LHS, const Entry &RHS) { |
| return std::tie(LHS.Symbol, LHS.Reference.Location.FileURI, |
| LHS.Reference.Kind) == |
| std::tie(RHS.Symbol, RHS.Reference.Location.FileURI, |
| RHS.Reference.Kind) && |
| LHS.Reference.Location.Start == RHS.Reference.Location.Start && |
| LHS.Reference.Location.End == RHS.Reference.Location.End; |
| } |
| }; |
| } // namespace llvm |
| |
| #endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_REF_H |