xref: /llvm-project/clang-tools-extra/clangd/index/Ref.h (revision 61fe67a4017375fd675f75652e857e837f77fa51)
1 //===--- Ref.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_REF_H
10 #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_REF_H
11 
12 #include "index/SymbolID.h"
13 #include "index/SymbolLocation.h"
14 #include "llvm/ADT/Hashing.h"
15 #include "llvm/Support/Allocator.h"
16 #include "llvm/Support/StringSaver.h"
17 #include "llvm/Support/raw_ostream.h"
18 #include <cstdint>
19 #include <set>
20 #include <utility>
21 
22 namespace clang {
23 namespace clangd {
24 
25 /// Describes the kind of a cross-reference.
26 ///
27 /// This is a bitfield which can be combined from different kinds.
28 enum class RefKind : uint8_t {
29   Unknown = 0,
30   // Points to symbol declaration. Example:
31   //
32   // class Foo;
33   //       ^ Foo declaration
34   // Foo foo;
35   // ^ this does not reference Foo declaration
36   Declaration = 1 << 0,
37   // Points to symbol definition. Example:
38   //
39   // int foo();
40   //     ^ references foo declaration, but not foo definition
41   // int foo() { return 42; }
42   //     ^ references foo definition, but not declaration
43   // bool bar() { return true; }
44   //      ^ references both definition and declaration
45   Definition = 1 << 1,
46   // Points to symbol reference. Example:
47   //
48   // int Foo = 42;
49   // int Bar = Foo + 1;
50   //           ^ this is a reference to Foo
51   Reference = 1 << 2,
52   // The reference explicitly spells out declaration's name. Such references can
53   // not come from macro expansions or implicit AST nodes.
54   //
55   // class Foo { public: Foo() {} };
56   //       ^ references declaration, definition and explicitly spells out name
57   // #define MACRO Foo
58   //     v there is an implicit constructor call here which is not a spelled ref
59   // Foo foo;
60   // ^ this reference explicitly spells out Foo's name
61   // struct Bar {
62   //   MACRO Internal;
63   //   ^ this references Foo, but does not explicitly spell out its name
64   // };
65   Spelled = 1 << 3,
66   // A reference which is a call. Used as a filter for which references
67   // to store in data structures used for computing outgoing calls.
68   Call = 1 << 4,
69   All = Declaration | Definition | Reference | Spelled,
70 };
71 
72 inline RefKind operator|(RefKind L, RefKind R) {
73   return static_cast<RefKind>(static_cast<uint8_t>(L) |
74                               static_cast<uint8_t>(R));
75 }
76 inline RefKind &operator|=(RefKind &L, RefKind R) { return L = L | R; }
77 inline RefKind operator&(RefKind A, RefKind B) {
78   return static_cast<RefKind>(static_cast<uint8_t>(A) &
79                               static_cast<uint8_t>(B));
80 }
81 
82 llvm::raw_ostream &operator<<(llvm::raw_ostream &, RefKind);
83 
84 /// Represents a symbol occurrence in the source file.
85 /// Despite the name, it could be a declaration/definition/reference.
86 ///
87 /// WARNING: Location does not own the underlying data - Copies are shallow.
88 struct Ref {
89   /// The source location where the symbol is named.
90   SymbolLocation Location;
91   RefKind Kind = RefKind::Unknown;
92   /// The ID of the symbol whose definition contains this reference.
93   /// For example, for a reference inside a function body, this would
94   /// be that function. For top-level definitions this isNull().
95   SymbolID Container;
96 };
97 
98 inline bool operator<(const Ref &L, const Ref &R) {
99   return std::tie(L.Location, L.Kind, L.Container) <
100          std::tie(R.Location, R.Kind, R.Container);
101 }
102 inline bool operator==(const Ref &L, const Ref &R) {
103   return std::tie(L.Location, L.Kind, L.Container) ==
104          std::tie(R.Location, R.Kind, R.Container);
105 }
106 
107 llvm::raw_ostream &operator<<(llvm::raw_ostream &, const Ref &);
108 
109 /// An efficient structure of storing large set of symbol references in memory.
110 /// Filenames are deduplicated.
111 class RefSlab {
112 public:
113   // Refs are stored in order.
114   using value_type = std::pair<SymbolID, llvm::ArrayRef<Ref>>;
115   using const_iterator = std::vector<value_type>::const_iterator;
116   using iterator = const_iterator;
117 
118   RefSlab() = default;
119   RefSlab(RefSlab &&Slab) = default;
120   RefSlab &operator=(RefSlab &&RHS) = default;
121 
122   const_iterator begin() const { return Refs.begin(); }
123   const_iterator end() const { return Refs.end(); }
124   /// Gets the number of symbols.
125   size_t size() const { return Refs.size(); }
126   size_t numRefs() const { return NumRefs; }
127   bool empty() const { return Refs.empty(); }
128 
129   size_t bytes() const {
130     return sizeof(*this) + Arena.getTotalMemory() +
131            sizeof(value_type) * Refs.capacity();
132   }
133 
134   /// RefSlab::Builder is a mutable container that can 'freeze' to RefSlab.
135   class Builder {
136   public:
137     Builder() : UniqueStrings(Arena) {}
138     /// Adds a ref to the slab. Deep copy: Strings will be owned by the slab.
139     void insert(const SymbolID &ID, const Ref &S);
140     /// Consumes the builder to finalize the slab.
141     RefSlab build() &&;
142 
143   private:
144     // A ref we're storing with its symbol to consume with build().
145     // All strings are interned, so DenseMapInfo can use pointer comparisons.
146     struct Entry {
147       SymbolID Symbol;
148       Ref Reference;
149     };
150     friend struct llvm::DenseMapInfo<Entry>;
151 
152     llvm::BumpPtrAllocator Arena;
153     llvm::UniqueStringSaver UniqueStrings; // Contents on the arena.
154     llvm::DenseSet<Entry> Entries;
155   };
156 
157 private:
158   RefSlab(std::vector<value_type> Refs, llvm::BumpPtrAllocator Arena,
159           size_t NumRefs)
160       : Arena(std::move(Arena)), Refs(std::move(Refs)), NumRefs(NumRefs) {}
161 
162   llvm::BumpPtrAllocator Arena;
163   std::vector<value_type> Refs;
164   /// Number of all references.
165   size_t NumRefs = 0;
166 };
167 
168 } // namespace clangd
169 } // namespace clang
170 
171 namespace llvm {
172 template <> struct DenseMapInfo<clang::clangd::RefSlab::Builder::Entry> {
173   using Entry = clang::clangd::RefSlab::Builder::Entry;
174   static inline Entry getEmptyKey() {
175     static Entry E{clang::clangd::SymbolID(""), {}};
176     return E;
177   }
178   static inline Entry getTombstoneKey() {
179     static Entry E{clang::clangd::SymbolID("TOMBSTONE"), {}};
180     return E;
181   }
182   static unsigned getHashValue(const Entry &Val) {
183     return llvm::hash_combine(
184         Val.Symbol, reinterpret_cast<uintptr_t>(Val.Reference.Location.FileURI),
185         Val.Reference.Location.Start.rep(), Val.Reference.Location.End.rep());
186   }
187   static bool isEqual(const Entry &LHS, const Entry &RHS) {
188     return std::tie(LHS.Symbol, LHS.Reference.Location.FileURI,
189                     LHS.Reference.Kind) ==
190                std::tie(RHS.Symbol, RHS.Reference.Location.FileURI,
191                         RHS.Reference.Kind) &&
192            LHS.Reference.Location.Start == RHS.Reference.Location.Start &&
193            LHS.Reference.Location.End == RHS.Reference.Location.End;
194   }
195 };
196 } // namespace llvm
197 
198 #endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_REF_H
199