//===--- 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 #include #include 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(static_cast(L) | static_cast(R)); } inline RefKind &operator|=(RefKind &L, RefKind R) { return L = L | R; } inline RefKind operator&(RefKind A, RefKind B) { return static_cast(static_cast(A) & static_cast(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>; using const_iterator = std::vector::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; llvm::BumpPtrAllocator Arena; llvm::UniqueStringSaver UniqueStrings; // Contents on the arena. llvm::DenseSet Entries; }; private: RefSlab(std::vector Refs, llvm::BumpPtrAllocator Arena, size_t NumRefs) : Arena(std::move(Arena)), Refs(std::move(Refs)), NumRefs(NumRefs) {} llvm::BumpPtrAllocator Arena; std::vector Refs; /// Number of all references. size_t NumRefs = 0; }; } // namespace clangd } // namespace clang namespace llvm { template <> struct DenseMapInfo { 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(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