1 //===--- ProjectAware.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 #include "ProjectAware.h"
10 #include "Config.h"
11 #include "index/Index.h"
12 #include "index/MemIndex.h"
13 #include "index/Merge.h"
14 #include "index/Ref.h"
15 #include "index/Serialization.h"
16 #include "index/Symbol.h"
17 #include "index/SymbolID.h"
18 #include "support/Logger.h"
19 #include "support/Threading.h"
20 #include "support/Trace.h"
21 #include "llvm/ADT/ArrayRef.h"
22 #include "llvm/ADT/DenseMap.h"
23 #include "llvm/ADT/STLExtras.h"
24 #include "llvm/ADT/StringRef.h"
25 #include "llvm/Support/ErrorHandling.h"
26 #include <map>
27 #include <memory>
28 #include <mutex>
29 #include <tuple>
30 
31 namespace clang {
32 namespace clangd {
33 namespace {
34 class ProjectAwareIndex : public SymbolIndex {
35 public:
36   size_t estimateMemoryUsage() const override;
37 
38   /// Only queries the associated index with the current context.
39   void lookup(const LookupRequest &Req,
40               llvm::function_ref<void(const Symbol &)> Callback) const override;
41 
42   /// Query all indexes while prioritizing the associated one (if any).
43   bool refs(const RefsRequest &Req,
44             llvm::function_ref<void(const Ref &)> Callback) const override;
45 
46   /// Queries only the associates index when Req.RestrictForCodeCompletion is
47   /// set, otherwise queries all.
48   bool
49   fuzzyFind(const FuzzyFindRequest &Req,
50             llvm::function_ref<void(const Symbol &)> Callback) const override;
51 
52   /// Query all indexes while prioritizing the associated one (if any).
53   void relations(const RelationsRequest &Req,
54                  llvm::function_ref<void(const SymbolID &, const Symbol &)>
55                      Callback) const override;
56 
57   llvm::unique_function<IndexContents(llvm::StringRef) const>
58   indexedFiles() const override;
59 
60   ProjectAwareIndex(IndexFactory Gen, bool Sync) : Gen(std::move(Gen)) {
61     if (!Sync)
62       Tasks = std::make_unique<AsyncTaskRunner>();
63   }
64 
65 private:
66   // Returns the index associated with current context, if any.
67   SymbolIndex *getIndex() const;
68 
69   // Storage for all the external indexes.
70   mutable std::mutex Mu;
71   mutable llvm::DenseMap<Config::ExternalIndexSpec,
72                          std::unique_ptr<SymbolIndex>>
73       IndexForSpec;
74   mutable std::unique_ptr<AsyncTaskRunner> Tasks;
75 
76   const IndexFactory Gen;
77 };
78 
79 size_t ProjectAwareIndex::estimateMemoryUsage() const {
80   size_t Total = 0;
81   std::lock_guard<std::mutex> Lock(Mu);
82   for (auto &Entry : IndexForSpec)
83     Total += Entry.second->estimateMemoryUsage();
84   return Total;
85 }
86 
87 void ProjectAwareIndex::lookup(
88     const LookupRequest &Req,
89     llvm::function_ref<void(const Symbol &)> Callback) const {
90   trace::Span Tracer("ProjectAwareIndex::lookup");
91   if (auto *Idx = getIndex())
92     Idx->lookup(Req, Callback);
93 }
94 
95 bool ProjectAwareIndex::refs(
96     const RefsRequest &Req,
97     llvm::function_ref<void(const Ref &)> Callback) const {
98   trace::Span Tracer("ProjectAwareIndex::refs");
99   if (auto *Idx = getIndex())
100     return Idx->refs(Req, Callback);
101   return false;
102 }
103 
104 bool ProjectAwareIndex::fuzzyFind(
105     const FuzzyFindRequest &Req,
106     llvm::function_ref<void(const Symbol &)> Callback) const {
107   trace::Span Tracer("ProjectAwareIndex::fuzzyFind");
108   if (auto *Idx = getIndex())
109     return Idx->fuzzyFind(Req, Callback);
110   return false;
111 }
112 
113 void ProjectAwareIndex::relations(
114     const RelationsRequest &Req,
115     llvm::function_ref<void(const SymbolID &, const Symbol &)> Callback) const {
116   trace::Span Tracer("ProjectAwareIndex::relations");
117   if (auto *Idx = getIndex())
118     return Idx->relations(Req, Callback);
119 }
120 
121 llvm::unique_function<IndexContents(llvm::StringRef) const>
122 ProjectAwareIndex::indexedFiles() const {
123   trace::Span Tracer("ProjectAwareIndex::indexedFiles");
124   if (auto *Idx = getIndex())
125     return Idx->indexedFiles();
126   return [](llvm::StringRef) { return IndexContents::None; };
127 }
128 
129 SymbolIndex *ProjectAwareIndex::getIndex() const {
130   const auto &C = Config::current();
131   if (C.Index.External.Kind == Config::ExternalIndexSpec::None)
132     return nullptr;
133   const auto &External = C.Index.External;
134   std::lock_guard<std::mutex> Lock(Mu);
135   auto Entry = IndexForSpec.try_emplace(External, nullptr);
136   if (Entry.second)
137     Entry.first->getSecond() = Gen(External, Tasks.get());
138   return Entry.first->second.get();
139 }
140 } // namespace
141 
142 std::unique_ptr<SymbolIndex> createProjectAwareIndex(IndexFactory Gen,
143                                                      bool Sync) {
144   assert(Gen);
145   return std::make_unique<ProjectAwareIndex>(std::move(Gen), Sync);
146 }
147 } // namespace clangd
148 } // namespace clang
149