1 #include "query.h"
2
3 #include "indexer.h"
4 #include "serializer.h"
5 #include "serializers/json.h"
6
7 #include <doctest/doctest.h>
8 #include <optional.h>
9 #include <loguru.hpp>
10
11 #include <cassert>
12 #include <cstdint>
13 #include <functional>
14 #include <string>
15 #include <unordered_map>
16 #include <unordered_set>
17
18 // TODO: Make all copy constructors explicit.
19
20 namespace {
21
22 template <typename T>
VerifyUnique(const std::vector<T> & values0)23 void VerifyUnique(const std::vector<T>& values0) {
24 // FIXME: Run on a big code-base for a while and verify no assertions are
25 // triggered.
26 #if false
27 auto values = values0;
28 std::sort(values.begin(), values.end());
29 assert(std::unique(values.begin(), values.end()) == values.end());
30 #endif
31 }
32
33 template <typename T>
RemoveRange(std::vector<T> * dest,const std::vector<T> & to_remove)34 void RemoveRange(std::vector<T>* dest, const std::vector<T>& to_remove) {
35 std::unordered_set<T> to_remove_set(to_remove.begin(), to_remove.end());
36 dest->erase(
37 std::remove_if(dest->begin(), dest->end(),
38 [&](const T& t) { return to_remove_set.count(t) > 0; }),
39 dest->end());
40 }
41
ToQuery(const IdMap & id_map,const IndexType::Def & type)42 optional<QueryType::Def> ToQuery(const IdMap& id_map,
43 const IndexType::Def& type) {
44 if (type.detailed_name.empty())
45 return nullopt;
46
47 QueryType::Def result;
48 result.detailed_name = type.detailed_name;
49 result.short_name_offset = type.short_name_offset;
50 result.short_name_size = type.short_name_size;
51 result.kind = type.kind;
52 if (!type.hover.empty())
53 result.hover = type.hover;
54 if (!type.comments.empty())
55 result.comments = type.comments;
56 result.file = id_map.primary_file;
57 result.spell = id_map.ToQuery(type.spell);
58 result.extent = id_map.ToQuery(type.extent);
59 result.alias_of = id_map.ToQuery(type.alias_of);
60 result.bases = id_map.ToQuery(type.bases);
61 result.types = id_map.ToQuery(type.types);
62 result.funcs = id_map.ToQuery(type.funcs);
63 result.vars = id_map.ToQuery(type.vars);
64 return result;
65 }
66
ToQuery(const IdMap & id_map,const IndexFunc::Def & func)67 optional<QueryFunc::Def> ToQuery(const IdMap& id_map,
68 const IndexFunc::Def& func) {
69 if (func.detailed_name.empty())
70 return nullopt;
71
72 QueryFunc::Def result;
73 result.detailed_name = func.detailed_name;
74 result.short_name_offset = func.short_name_offset;
75 result.short_name_size = func.short_name_size;
76 result.kind = func.kind;
77 result.storage = func.storage;
78 if (!func.hover.empty())
79 result.hover = func.hover;
80 if (!func.comments.empty())
81 result.comments = func.comments;
82 result.file = id_map.primary_file;
83 result.spell = id_map.ToQuery(func.spell);
84 result.extent = id_map.ToQuery(func.extent);
85 result.declaring_type = id_map.ToQuery(func.declaring_type);
86 result.bases = id_map.ToQuery(func.bases);
87 result.vars = id_map.ToQuery(func.vars);
88 result.callees = id_map.ToQuery(func.callees);
89 return result;
90 }
91
ToQuery(const IdMap & id_map,const IndexVar::Def & var)92 optional<QueryVar::Def> ToQuery(const IdMap& id_map, const IndexVar::Def& var) {
93 if (var.detailed_name.empty())
94 return nullopt;
95
96 QueryVar::Def result;
97 result.detailed_name = var.detailed_name;
98 result.short_name_offset = var.short_name_offset;
99 result.short_name_size = var.short_name_size;
100 if (!var.hover.empty())
101 result.hover = var.hover;
102 if (!var.comments.empty())
103 result.comments = var.comments;
104 result.file = id_map.primary_file;
105 result.spell = id_map.ToQuery(var.spell);
106 result.extent = id_map.ToQuery(var.extent);
107 result.type = id_map.ToQuery(var.type);
108 result.kind = var.kind;
109 result.storage = var.storage;
110 return result;
111 }
112
113 // Adds the mergeable updates in |source| to |dest|. If a mergeable update for
114 // the destination type already exists, it will be combined. This makes merging
115 // updates take longer but reduces import time on the querydb thread.
116 template <typename TId, typename TValue>
AddMergeableRange(std::vector<MergeableUpdate<TId,TValue>> * dest,std::vector<MergeableUpdate<TId,TValue>> && source)117 void AddMergeableRange(std::vector<MergeableUpdate<TId, TValue>>* dest,
118 std::vector<MergeableUpdate<TId, TValue>>&& source) {
119 // TODO: Consider caching the lookup table. It can probably save even more
120 // time at the cost of some additional memory.
121
122 // Build lookup table.
123 spp::sparse_hash_map<TId, size_t> id_to_index;
124 id_to_index.resize(dest->size());
125 for (size_t i = 0; i < dest->size(); ++i)
126 id_to_index[(*dest)[i].id] = i;
127
128 // Add entries. Try to add them to an existing entry.
129 for (auto& entry : source) {
130 auto it = id_to_index.find(entry.id);
131 if (it != id_to_index.end()) {
132 AddRange(&(*dest)[it->second].to_add, std::move(entry.to_add));
133 AddRange(&(*dest)[it->second].to_remove, std::move(entry.to_remove));
134 } else {
135 dest->push_back(std::move(entry));
136 }
137 }
138 }
139
140 // Compares |previous| and |current|, adding all elements that are in |previous|
141 // but not |current| to |removed|, and all elements that are in |current| but
142 // not |previous| to |added|.
143 //
144 // Returns true iff |removed| or |added| are non-empty.
145 template <typename T>
ComputeDifferenceForUpdate(std::vector<T> && previous,std::vector<T> && current,std::vector<T> * removed,std::vector<T> * added)146 bool ComputeDifferenceForUpdate(std::vector<T>&& previous,
147 std::vector<T>&& current,
148 std::vector<T>* removed,
149 std::vector<T>* added) {
150 // We need to sort to use std::set_difference.
151 std::sort(previous.begin(), previous.end());
152 std::sort(current.begin(), current.end());
153
154 auto it0 = previous.begin(), it1 = current.begin();
155 while (it0 != previous.end() && it1 != current.end()) {
156 // Elements in |previous| that are not in |current|.
157 if (*it0 < *it1)
158 removed->push_back(std::move(*it0++));
159 // Elements in |current| that are not in |previous|.
160 else if (*it1 < *it0)
161 added->push_back(std::move(*it1++));
162 else
163 ++it0, ++it1;
164 }
165 while (it0 != previous.end())
166 removed->push_back(std::move(*it0++));
167 while (it1 != current.end())
168 added->push_back(std::move(*it1++));
169
170 return !removed->empty() || !added->empty();
171 }
172
173 template <typename T>
CompareGroups(std::vector<T> & previous_data,std::vector<T> & current_data,std::function<void (T *)> on_removed,std::function<void (T *)> on_added,std::function<void (T *,T *)> on_found)174 void CompareGroups(std::vector<T>& previous_data,
175 std::vector<T>& current_data,
176 std::function<void(T*)> on_removed,
177 std::function<void(T*)> on_added,
178 std::function<void(T*, T*)> on_found) {
179 std::sort(previous_data.begin(), previous_data.end());
180 std::sort(current_data.begin(), current_data.end());
181
182 auto prev_it = previous_data.begin();
183 auto curr_it = current_data.begin();
184 while (prev_it != previous_data.end() && curr_it != current_data.end()) {
185 // same id
186 if (prev_it->usr == curr_it->usr) {
187 on_found(&*prev_it, &*curr_it);
188 ++prev_it;
189 ++curr_it;
190 }
191
192 // prev_id is smaller - prev_it has data curr_it does not have.
193 else if (prev_it->usr < curr_it->usr) {
194 on_removed(&*prev_it);
195 ++prev_it;
196 }
197
198 // prev_id is bigger - curr_it has data prev_it does not have.
199 else {
200 on_added(&*curr_it);
201 ++curr_it;
202 }
203 }
204
205 // if prev_it still has data, that means it is not in curr_it and was removed.
206 while (prev_it != previous_data.end()) {
207 on_removed(&*prev_it);
208 ++prev_it;
209 }
210
211 // if curr_it still has data, that means it is not in prev_it and was added.
212 while (curr_it != current_data.end()) {
213 on_added(&*curr_it);
214 ++curr_it;
215 }
216 }
217
BuildFileDefUpdate(const IdMap & id_map,const IndexFile & indexed)218 QueryFile::DefUpdate BuildFileDefUpdate(const IdMap& id_map,
219 const IndexFile& indexed) {
220 QueryFile::Def def;
221 def.file = id_map.primary_file;
222 def.path = indexed.path;
223 def.args = indexed.args;
224 def.includes = indexed.includes;
225 def.inactive_regions = indexed.skipped_by_preprocessor;
226 def.dependencies = indexed.dependencies;
227
228 // Convert enum to markdown compatible strings
229 def.language = [&indexed]() {
230 switch (indexed.language) {
231 case LanguageId::C:
232 return "c";
233 case LanguageId::Cpp:
234 return "cpp";
235 case LanguageId::ObjC:
236 return "objective-c";
237 case LanguageId::ObjCpp:
238 return "objective-cpp";
239 default:
240 return "";
241 }
242 }();
243
244 auto add_all_symbols = [&](Reference ref, AnyId id, SymbolKind kind) {
245 def.all_symbols.push_back(
246 QueryId::SymbolRef(ref.range, id, kind, ref.role));
247 };
248 auto add_outline = [&](Reference ref, AnyId id, SymbolKind kind) {
249 def.outline.push_back(QueryId::SymbolRef(ref.range, id, kind, ref.role));
250 };
251
252 for (const IndexType& type : indexed.types) {
253 QueryId::Type id = id_map.ToQuery(type.id);
254 if (type.def.spell)
255 add_all_symbols(*type.def.spell, id, SymbolKind::Type);
256 if (type.def.extent)
257 add_outline(*type.def.extent, id, SymbolKind::Type);
258 for (auto decl : type.declarations) {
259 add_all_symbols(decl, id, SymbolKind::Type);
260 // Constructor positions have references to the class,
261 // which we do not want to show in textDocument/documentSymbol
262 if (!(decl.role & Role::Reference))
263 add_outline(decl, id, SymbolKind::Type);
264 }
265 for (Reference use : type.uses)
266 add_all_symbols(use, id, SymbolKind::Type);
267 }
268 for (const IndexFunc& func : indexed.funcs) {
269 QueryId::Func id = id_map.ToQuery(func.id);
270 if (func.def.spell)
271 add_all_symbols(*func.def.spell, id, SymbolKind::Func);
272 if (func.def.extent)
273 add_outline(*func.def.extent, id, SymbolKind::Func);
274 for (const IndexFunc::Declaration& decl : func.declarations) {
275 add_all_symbols(decl.spell, id, SymbolKind::Func);
276 add_outline(decl.spell, id, SymbolKind::Func);
277 }
278 for (auto use : func.uses) {
279 // Make ranges of implicit function calls larger (spanning one more column
280 // to the left/right). This is hacky but useful. e.g.
281 // textDocument/definition on the space/semicolon in `A a;` or `return
282 // 42;` will take you to the constructor.
283 if (use.role & Role::Implicit) {
284 if (use.range.start.column > 0)
285 use.range.start.column--;
286 use.range.end.column++;
287 }
288 add_all_symbols(use, id, SymbolKind::Func);
289 }
290 }
291 for (const IndexVar& var : indexed.vars) {
292 QueryId::Var id = id_map.ToQuery(var.id);
293 if (var.def.spell)
294 add_all_symbols(*var.def.spell, id, SymbolKind::Var);
295 if (var.def.extent)
296 add_outline(*var.def.extent, id, SymbolKind::Var);
297 for (auto decl : var.declarations) {
298 add_all_symbols(decl, id, SymbolKind::Var);
299 add_outline(decl, id, SymbolKind::Var);
300 }
301 for (auto use : var.uses)
302 add_all_symbols(use, id, SymbolKind::Var);
303 }
304
305 std::sort(def.outline.begin(), def.outline.end(),
306 [](const QueryId::SymbolRef& a, const QueryId::SymbolRef& b) {
307 return a.range.start < b.range.start;
308 });
309 std::sort(def.all_symbols.begin(), def.all_symbols.end(),
310 [](const QueryId::SymbolRef& a, const QueryId::SymbolRef& b) {
311 return a.range.start < b.range.start;
312 });
313
314 return QueryFile::DefUpdate(def, indexed.file_contents);
315 }
316
GetQueryFileIdFromPath(QueryDatabase * query_db,const AbsolutePath & path,bool create_if_missing)317 Maybe<QueryId::File> GetQueryFileIdFromPath(QueryDatabase* query_db,
318 const AbsolutePath& path,
319 bool create_if_missing) {
320 auto it = query_db->usr_to_file.find(path);
321 if (it != query_db->usr_to_file.end())
322 return QueryId::File(it->second.id);
323 if (!create_if_missing)
324 return {};
325
326 RawId idx = query_db->files.size();
327 query_db->usr_to_file[path] = QueryId::File(idx);
328 query_db->files.push_back(QueryFile(path));
329 return QueryId::File(idx);
330 }
331
GetQueryTypeIdFromUsr(QueryDatabase * query_db,Usr usr,bool create_if_missing)332 Maybe<QueryId::Type> GetQueryTypeIdFromUsr(QueryDatabase* query_db,
333 Usr usr,
334 bool create_if_missing) {
335 auto it = query_db->usr_to_type.find(usr);
336 if (it != query_db->usr_to_type.end())
337 return QueryId::Type(it->second.id);
338 if (!create_if_missing)
339 return {};
340
341 RawId idx = query_db->types.size();
342 query_db->usr_to_type[usr] = QueryId::Type(idx);
343 query_db->types.push_back(QueryType(usr));
344 return QueryId::Type(idx);
345 }
346
GetQueryFuncIdFromUsr(QueryDatabase * query_db,Usr usr,bool create_if_missing)347 Maybe<QueryId::Func> GetQueryFuncIdFromUsr(QueryDatabase* query_db,
348 Usr usr,
349 bool create_if_missing) {
350 auto it = query_db->usr_to_func.find(usr);
351 if (it != query_db->usr_to_func.end())
352 return QueryId::Func(it->second.id);
353 if (!create_if_missing)
354 return {};
355
356 RawId idx = query_db->funcs.size();
357 query_db->usr_to_func[usr] = QueryId::Func(idx);
358 query_db->funcs.push_back(QueryFunc(usr));
359 return QueryId::Func(idx);
360 }
361
GetQueryVarIdFromUsr(QueryDatabase * query_db,Usr usr,bool create_if_missing)362 Maybe<QueryId::Var> GetQueryVarIdFromUsr(QueryDatabase* query_db,
363 Usr usr,
364 bool create_if_missing) {
365 auto it = query_db->usr_to_var.find(usr);
366 if (it != query_db->usr_to_var.end())
367 return QueryId::Var(it->second.id);
368 if (!create_if_missing)
369 return {};
370
371 RawId idx = query_db->vars.size();
372 query_db->usr_to_var[usr] = QueryId::Var(idx);
373 query_db->vars.push_back(QueryVar(usr));
374 return QueryId::Var(idx);
375 }
376
377 // Returns true if an element with the same file is found.
378 template <typename Q>
TryReplaceDef(std::forward_list<Q> & def_list,Q && def)379 bool TryReplaceDef(std::forward_list<Q>& def_list, Q&& def) {
380 for (auto& def1 : def_list)
381 if (def1.file == def.file) {
382 if (!def1.spell || def.spell)
383 def1 = std::move(def);
384 return true;
385 }
386 return false;
387 }
388
389 } // namespace
390
GetQueryFileIdFromPath(const std::string & path)391 Maybe<QueryId::File> QueryDatabase::GetQueryFileIdFromPath(
392 const std::string& path) {
393 return ::GetQueryFileIdFromPath(this, path, false);
394 }
395
GetQueryTypeIdFromUsr(Usr usr)396 Maybe<QueryId::Type> QueryDatabase::GetQueryTypeIdFromUsr(Usr usr) {
397 return ::GetQueryTypeIdFromUsr(this, usr, false);
398 }
399
GetQueryFuncIdFromUsr(Usr usr)400 Maybe<QueryId::Func> QueryDatabase::GetQueryFuncIdFromUsr(Usr usr) {
401 return ::GetQueryFuncIdFromUsr(this, usr, false);
402 }
403
GetQueryVarIdFromUsr(Usr usr)404 Maybe<QueryId::Var> QueryDatabase::GetQueryVarIdFromUsr(Usr usr) {
405 return ::GetQueryVarIdFromUsr(this, usr, false);
406 }
407
IdMap(QueryDatabase * query_db,const IdCache & local_ids)408 IdMap::IdMap(QueryDatabase* query_db, const IdCache& local_ids)
409 : local_ids(local_ids) {
410 // LOG_S(INFO) << "Creating IdMap for " << local_ids.primary_file;
411 primary_file =
412 *GetQueryFileIdFromPath(query_db, local_ids.primary_file, true);
413
414 cached_type_ids_.resize(local_ids.type_id_to_usr.size());
415 for (const auto& entry : local_ids.type_id_to_usr)
416 cached_type_ids_[entry.first] =
417 *GetQueryTypeIdFromUsr(query_db, entry.second, true);
418
419 cached_func_ids_.resize(local_ids.func_id_to_usr.size());
420 for (const auto& entry : local_ids.func_id_to_usr)
421 cached_func_ids_[entry.first] =
422 *GetQueryFuncIdFromUsr(query_db, entry.second, true);
423
424 cached_var_ids_.resize(local_ids.var_id_to_usr.size());
425 for (const auto& entry : local_ids.var_id_to_usr)
426 cached_var_ids_[entry.first] =
427 *GetQueryVarIdFromUsr(query_db, entry.second, true);
428 }
429
ToQuery(SymbolKind kind,Id<void> id) const430 Id<void> IdMap::ToQuery(SymbolKind kind, Id<void> id) const {
431 switch (kind) {
432 case SymbolKind::File:
433 return primary_file;
434 case SymbolKind::Type:
435 return ToQuery(IndexId::Type(id.id));
436 case SymbolKind::Func:
437 return ToQuery(IndexId::Func(id.id));
438 case SymbolKind::Var:
439 return ToQuery(IndexId::Var(id.id));
440 case SymbolKind::Invalid:
441 break;
442 }
443 assert(false);
444 return Id<void>(-1);
445 }
446
ToQuery(IndexId::Type id) const447 QueryId::Type IdMap::ToQuery(IndexId::Type id) const {
448 assert(cached_type_ids_.find(id) != cached_type_ids_.end());
449 return QueryId::Type(cached_type_ids_.find(id)->second);
450 }
ToQuery(IndexId::Func id) const451 QueryId::Func IdMap::ToQuery(IndexId::Func id) const {
452 assert(cached_func_ids_.find(id) != cached_func_ids_.end());
453 return QueryId::Func(cached_func_ids_.find(id)->second);
454 }
ToQuery(IndexId::Var id) const455 QueryId::Var IdMap::ToQuery(IndexId::Var id) const {
456 assert(cached_var_ids_.find(id) != cached_var_ids_.end());
457 return QueryId::Var(cached_var_ids_.find(id)->second);
458 }
459
ToQuery(IndexId::SymbolRef ref) const460 QueryId::SymbolRef IdMap::ToQuery(IndexId::SymbolRef ref) const {
461 QueryId::SymbolRef result;
462 result.range = ref.range;
463 result.id = ToQuery(ref.kind, ref.id);
464 result.kind = ref.kind;
465 result.role = ref.role;
466 return result;
467 }
ToQuery(IndexId::LexicalRef ref) const468 QueryId::LexicalRef IdMap::ToQuery(IndexId::LexicalRef ref) const {
469 QueryId::LexicalRef result;
470 result.file = primary_file;
471 result.range = ref.range;
472 result.id = ToQuery(ref.kind, ref.id);
473 result.kind = ref.kind;
474 result.role = ref.role;
475 return result;
476 }
ToQuery(IndexFunc::Declaration decl) const477 QueryId::LexicalRef IdMap::ToQuery(IndexFunc::Declaration decl) const {
478 return ToQuery(decl.spell);
479 }
480
481 // ----------------------
482 // INDEX THREAD FUNCTIONS
483 // ----------------------
484
485 // static
CreateDelta(const IdMap * previous_id_map,const IdMap * current_id_map,IndexFile * previous,IndexFile * current)486 IndexUpdate IndexUpdate::CreateDelta(const IdMap* previous_id_map,
487 const IdMap* current_id_map,
488 IndexFile* previous,
489 IndexFile* current) {
490 // This function runs on an indexer thread.
491
492 if (!previous_id_map) {
493 assert(!previous);
494 IndexFile empty(current->path, "<empty>");
495 return IndexUpdate(*current_id_map, *current_id_map, empty, *current);
496 }
497 return IndexUpdate(*previous_id_map, *current_id_map, *previous, *current);
498 }
499
IndexUpdate(const IdMap & previous_id_map,const IdMap & current_id_map,IndexFile & previous_file,IndexFile & current_file)500 IndexUpdate::IndexUpdate(const IdMap& previous_id_map,
501 const IdMap& current_id_map,
502 IndexFile& previous_file,
503 IndexFile& current_file) {
504 // This function runs on an indexer thread.
505
506 // |query_name| is the name of the variable on the query type.
507 // |index_name| is the name of the variable on the index type.
508 // |type| is the type of the variable.
509 #define PROCESS_UPDATE_DIFF(type_id, query_name, index_name, type) \
510 { \
511 /* Check for changes. */ \
512 std::vector<type> removed, added; \
513 auto query_previous = previous_id_map.ToQuery(previous->index_name); \
514 auto query_current = current_id_map.ToQuery(current->index_name); \
515 bool did_add = ComputeDifferenceForUpdate(std::move(query_previous), \
516 std::move(query_current), \
517 &removed, &added); \
518 if (did_add) { \
519 query_name.push_back(MergeableUpdate<type_id, type>( \
520 current_id_map.ToQuery(current->id), std::move(added), \
521 std::move(removed))); \
522 } \
523 }
524 // File
525 files_def_update.push_back(BuildFileDefUpdate(current_id_map, current_file));
526
527 // **NOTE** We only remove entries if they were defined in the previous index.
528 // For example, if a type is included from another file it will be defined
529 // simply so we can attribute the usage/reference to it. If the reference goes
530 // away we don't want to remove the type/func/var usage.
531
532 // Types
533 CompareGroups<IndexType>(
534 previous_file.types, current_file.types,
535 /*onRemoved:*/
536 [this, &previous_id_map](IndexType* type) {
537 if (type->def.spell)
538 types_removed.push_back(type->usr);
539 if (!type->declarations.empty())
540 types_declarations.push_back(QueryType::DeclarationsUpdate(
541 previous_id_map.ToQuery(type->id), {},
542 previous_id_map.ToQuery(type->declarations)));
543 if (!type->derived.empty())
544 types_derived.push_back(
545 QueryType::DerivedUpdate(previous_id_map.ToQuery(type->id), {},
546 previous_id_map.ToQuery(type->derived)));
547 if (!type->instances.empty())
548 types_instances.push_back(QueryType::InstancesUpdate(
549 previous_id_map.ToQuery(type->id), {},
550 previous_id_map.ToQuery(type->instances)));
551 if (!type->uses.empty())
552 types_uses.push_back(
553 QueryType::UsesUpdate(previous_id_map.ToQuery(type->id), {},
554 previous_id_map.ToQuery(type->uses)));
555 },
556 /*onAdded:*/
557 [this, ¤t_id_map](IndexType* type) {
558 optional<QueryType::Def> def_update =
559 ToQuery(current_id_map, type->def);
560 if (def_update)
561 types_def_update.push_back(
562 QueryType::DefUpdate(type->usr, std::move(*def_update)));
563 if (!type->declarations.empty())
564 types_declarations.push_back(QueryType::DeclarationsUpdate(
565 current_id_map.ToQuery(type->id),
566 current_id_map.ToQuery(type->declarations)));
567 if (!type->derived.empty())
568 types_derived.push_back(
569 QueryType::DerivedUpdate(current_id_map.ToQuery(type->id),
570 current_id_map.ToQuery(type->derived)));
571 if (!type->instances.empty())
572 types_instances.push_back(QueryType::InstancesUpdate(
573 current_id_map.ToQuery(type->id),
574 current_id_map.ToQuery(type->instances)));
575 if (!type->uses.empty())
576 types_uses.push_back(
577 QueryType::UsesUpdate(current_id_map.ToQuery(type->id),
578 current_id_map.ToQuery(type->uses)));
579 },
580 /*onFound:*/
581 [this, &previous_id_map, ¤t_id_map](IndexType* previous,
582 IndexType* current) {
583 optional<QueryType::Def> previous_remapped_def =
584 ToQuery(previous_id_map, previous->def);
585 optional<QueryType::Def> current_remapped_def =
586 ToQuery(current_id_map, current->def);
587 if (current_remapped_def &&
588 previous_remapped_def != current_remapped_def &&
589 !current_remapped_def->detailed_name.empty()) {
590 types_def_update.push_back(QueryType::DefUpdate(
591 current->usr, std::move(*current_remapped_def)));
592 }
593
594 PROCESS_UPDATE_DIFF(QueryId::Type, types_declarations, declarations,
595 QueryId::LexicalRef);
596 PROCESS_UPDATE_DIFF(QueryId::Type, types_derived, derived,
597 QueryId::Type);
598 PROCESS_UPDATE_DIFF(QueryId::Type, types_instances, instances,
599 QueryId::Var);
600 PROCESS_UPDATE_DIFF(QueryId::Type, types_uses, uses,
601 QueryId::LexicalRef);
602 });
603
604 // Functions
605 CompareGroups<IndexFunc>(
606 previous_file.funcs, current_file.funcs,
607 /*onRemoved:*/
608 [this, &previous_id_map](IndexFunc* func) {
609 if (func->def.spell)
610 funcs_removed.emplace_back(func->usr, previous_id_map.primary_file);
611 if (!func->declarations.empty())
612 funcs_declarations.push_back(QueryFunc::DeclarationsUpdate(
613 previous_id_map.ToQuery(func->id), {},
614 previous_id_map.ToQuery(func->declarations)));
615 if (!func->derived.empty())
616 funcs_derived.push_back(
617 QueryFunc::DerivedUpdate(previous_id_map.ToQuery(func->id), {},
618 previous_id_map.ToQuery(func->derived)));
619 if (!func->uses.empty())
620 funcs_uses.push_back(
621 QueryFunc::UsesUpdate(previous_id_map.ToQuery(func->id), {},
622 previous_id_map.ToQuery(func->uses)));
623 },
624 /*onAdded:*/
625 [this, ¤t_id_map](IndexFunc* func) {
626 optional<QueryFunc::Def> def_update =
627 ToQuery(current_id_map, func->def);
628 if (def_update)
629 funcs_def_update.push_back(
630 QueryFunc::DefUpdate(func->usr, std::move(*def_update)));
631 if (!func->declarations.empty())
632 funcs_declarations.push_back(QueryFunc::DeclarationsUpdate(
633 current_id_map.ToQuery(func->id),
634 current_id_map.ToQuery(func->declarations)));
635 if (!func->derived.empty())
636 funcs_derived.push_back(
637 QueryFunc::DerivedUpdate(current_id_map.ToQuery(func->id),
638 current_id_map.ToQuery(func->derived)));
639 if (!func->uses.empty())
640 funcs_uses.push_back(
641 QueryFunc::UsesUpdate(current_id_map.ToQuery(func->id),
642 current_id_map.ToQuery(func->uses)));
643 },
644 /*onFound:*/
645 [this, &previous_id_map, ¤t_id_map](IndexFunc* previous,
646 IndexFunc* current) {
647 optional<QueryFunc::Def> previous_remapped_def =
648 ToQuery(previous_id_map, previous->def);
649 optional<QueryFunc::Def> current_remapped_def =
650 ToQuery(current_id_map, current->def);
651 if (current_remapped_def &&
652 previous_remapped_def != current_remapped_def &&
653 !current_remapped_def->detailed_name.empty()) {
654 funcs_def_update.push_back(QueryFunc::DefUpdate(
655 current->usr, std::move(*current_remapped_def)));
656 }
657
658 PROCESS_UPDATE_DIFF(QueryId::Func, funcs_declarations, declarations,
659 QueryId::LexicalRef);
660 PROCESS_UPDATE_DIFF(QueryId::Func, funcs_derived, derived,
661 QueryId::Func);
662 PROCESS_UPDATE_DIFF(QueryId::Func, funcs_uses, uses,
663 QueryId::LexicalRef);
664 });
665
666 // Variables
667 CompareGroups<IndexVar>(
668 previous_file.vars, current_file.vars,
669 /*onRemoved:*/
670 [this, &previous_id_map](IndexVar* var) {
671 if (var->def.spell)
672 vars_removed.emplace_back(var->usr, previous_id_map.primary_file);
673 if (!var->declarations.empty())
674 vars_declarations.push_back(QueryVar::DeclarationsUpdate(
675 previous_id_map.ToQuery(var->id), {},
676 previous_id_map.ToQuery(var->declarations)));
677 if (!var->uses.empty())
678 vars_uses.push_back(
679 QueryVar::UsesUpdate(previous_id_map.ToQuery(var->id), {},
680 previous_id_map.ToQuery(var->uses)));
681 },
682 /*onAdded:*/
683 [this, ¤t_id_map](IndexVar* var) {
684 optional<QueryVar::Def> def_update = ToQuery(current_id_map, var->def);
685 if (def_update)
686 vars_def_update.push_back(
687 QueryVar::DefUpdate(var->usr, std::move(*def_update)));
688 if (!var->declarations.empty())
689 vars_declarations.push_back(QueryVar::DeclarationsUpdate(
690 current_id_map.ToQuery(var->id),
691 current_id_map.ToQuery(var->declarations)));
692 if (!var->uses.empty())
693 vars_uses.push_back(
694 QueryVar::UsesUpdate(current_id_map.ToQuery(var->id),
695 current_id_map.ToQuery(var->uses)));
696 },
697 /*onFound:*/
698 [this, &previous_id_map, ¤t_id_map](IndexVar* previous,
699 IndexVar* current) {
700 optional<QueryVar::Def> previous_remapped_def =
701 ToQuery(previous_id_map, previous->def);
702 optional<QueryVar::Def> current_remapped_def =
703 ToQuery(current_id_map, current->def);
704 if (current_remapped_def &&
705 previous_remapped_def != current_remapped_def &&
706 !current_remapped_def->detailed_name.empty())
707 vars_def_update.push_back(QueryVar::DefUpdate(
708 current->usr, std::move(*current_remapped_def)));
709
710 PROCESS_UPDATE_DIFF(QueryId::Var, vars_declarations, declarations,
711 QueryId::LexicalRef);
712 PROCESS_UPDATE_DIFF(QueryId::Var, vars_uses, uses, QueryId::LexicalRef);
713 });
714
715 #undef PROCESS_UPDATE_DIFF
716 }
717
718 // This function runs on an indexer thread.
Merge(IndexUpdate && update)719 void IndexUpdate::Merge(IndexUpdate&& update) {
720 #define INDEX_UPDATE_APPEND(name) AddRange(&name, std::move(update.name));
721 #define INDEX_UPDATE_MERGE(name) \
722 AddMergeableRange(&name, std::move(update.name));
723
724 INDEX_UPDATE_APPEND(files_removed);
725 INDEX_UPDATE_APPEND(files_def_update);
726
727 INDEX_UPDATE_APPEND(types_removed);
728 INDEX_UPDATE_APPEND(types_def_update);
729 INDEX_UPDATE_MERGE(types_derived);
730 INDEX_UPDATE_MERGE(types_instances);
731 INDEX_UPDATE_MERGE(types_uses);
732
733 INDEX_UPDATE_APPEND(funcs_removed);
734 INDEX_UPDATE_APPEND(funcs_def_update);
735 INDEX_UPDATE_MERGE(funcs_declarations);
736 INDEX_UPDATE_MERGE(funcs_derived);
737 INDEX_UPDATE_MERGE(funcs_uses);
738
739 INDEX_UPDATE_APPEND(vars_removed);
740 INDEX_UPDATE_APPEND(vars_def_update);
741 INDEX_UPDATE_MERGE(vars_declarations);
742 INDEX_UPDATE_MERGE(vars_uses);
743
744 #undef INDEX_UPDATE_APPEND
745 #undef INDEX_UPDATE_MERGE
746 }
747
ToString()748 std::string IndexUpdate::ToString() {
749 rapidjson::StringBuffer output;
750 rapidjson::Writer<rapidjson::StringBuffer> writer(output);
751 JsonWriter json_writer(&writer);
752 IndexUpdate& update = *this;
753 Reflect(json_writer, update);
754 return output.GetString();
755 }
756
757 // ------------------------
758 // QUERYDB THREAD FUNCTIONS
759 // ------------------------
760
RemoveUsrs(SymbolKind usr_kind,const std::vector<Usr> & to_remove)761 void QueryDatabase::RemoveUsrs(SymbolKind usr_kind,
762 const std::vector<Usr>& to_remove) {
763 // This function runs on the querydb thread.
764
765 // When we remove an element, we just erase the state from the storage. We do
766 // not update array indices because that would take a huge amount of time for
767 // a very large index.
768 //
769 // There means that there is some memory growth that will never be reclaimed,
770 // but it should be pretty minimal and is solved by simply restarting the
771 // indexer and loading from cache, which is a fast operation.
772 //
773 // TODO: Add "cquery: Reload Index" command which unloads all querydb state
774 // and fully reloads from cache. This will address the memory leak above.
775
776 switch (usr_kind) {
777 case SymbolKind::Type: {
778 for (const Usr& usr : to_remove) {
779 QueryType& type = types[usr_to_type[usr].id];
780 if (type.symbol_idx)
781 symbols[type.symbol_idx->id].kind = SymbolKind::Invalid;
782 type.def.clear();
783 }
784 break;
785 }
786 default:
787 break;
788 }
789 }
790
RemoveUsrs(SymbolKind usr_kind,const std::vector<WithUsr<QueryId::File>> & to_remove)791 void QueryDatabase::RemoveUsrs(
792 SymbolKind usr_kind,
793 const std::vector<WithUsr<QueryId::File>>& to_remove) {
794 switch (usr_kind) {
795 case SymbolKind::Func: {
796 for (const auto& usr_file : to_remove) {
797 QueryFunc& func = funcs[usr_to_func[usr_file.usr].id];
798 func.def.remove_if([&](const QueryFunc::Def& def) {
799 return def.file == usr_file.value;
800 });
801 }
802 break;
803 }
804 case SymbolKind::Var: {
805 for (const auto& usr_file : to_remove) {
806 QueryVar& var = vars[usr_to_var[usr_file.usr].id];
807 var.def.remove_if([&](const QueryVar::Def& def) {
808 return def.file == usr_file.value;
809 });
810 }
811 break;
812 }
813 default:
814 assert(false);
815 break;
816 }
817 }
818
ApplyIndexUpdate(IndexUpdate * update)819 void QueryDatabase::ApplyIndexUpdate(IndexUpdate* update) {
820 // This function runs on the querydb thread.
821
822 // Example types:
823 // storage_name => std::vector<optional<QueryType>>
824 // merge_update => QueryType::DerivedUpdate =>
825 // MergeableUpdate<QueryId::Type, QueryId::Type> def =>
826 // QueryType def->def_var_name => std::vector<QueryId::Type>
827 #define HANDLE_MERGEABLE(update_var_name, def_var_name, storage_name) \
828 for (auto merge_update : update->update_var_name) { \
829 auto& def = storage_name[merge_update.id.id]; \
830 AddRange(&def.def_var_name, merge_update.to_add); \
831 RemoveRange(&def.def_var_name, merge_update.to_remove); \
832 VerifyUnique(def.def_var_name); \
833 }
834
835 for (const AbsolutePath& filename : update->files_removed)
836 files[usr_to_file[filename].id].def = nullopt;
837 ImportOrUpdate(update->files_def_update);
838
839 RemoveUsrs(SymbolKind::Type, update->types_removed);
840 ImportOrUpdate(std::move(update->types_def_update));
841 HANDLE_MERGEABLE(types_declarations, declarations, types);
842 HANDLE_MERGEABLE(types_derived, derived, types);
843 HANDLE_MERGEABLE(types_instances, instances, types);
844 HANDLE_MERGEABLE(types_uses, uses, types);
845
846 RemoveUsrs(SymbolKind::Func, update->funcs_removed);
847 ImportOrUpdate(std::move(update->funcs_def_update));
848 HANDLE_MERGEABLE(funcs_declarations, declarations, funcs);
849 HANDLE_MERGEABLE(funcs_derived, derived, funcs);
850 HANDLE_MERGEABLE(funcs_uses, uses, funcs);
851
852 RemoveUsrs(SymbolKind::Var, update->vars_removed);
853 ImportOrUpdate(std::move(update->vars_def_update));
854 HANDLE_MERGEABLE(vars_declarations, declarations, vars);
855 HANDLE_MERGEABLE(vars_uses, uses, vars);
856
857 #undef HANDLE_MERGEABLE
858 }
859
ImportOrUpdate(const std::vector<QueryFile::DefUpdate> & updates)860 void QueryDatabase::ImportOrUpdate(
861 const std::vector<QueryFile::DefUpdate>& updates) {
862 // This function runs on the querydb thread.
863
864 for (auto& def : updates) {
865 auto it = usr_to_file.find(def.value.path);
866 assert(it != usr_to_file.end());
867
868 QueryFile& existing = files[it->second.id];
869
870 existing.def = def.value;
871 UpdateSymbols(&existing.symbol_idx, SymbolKind::File, it->second);
872 }
873 }
874
ImportOrUpdate(std::vector<QueryType::DefUpdate> && updates)875 void QueryDatabase::ImportOrUpdate(
876 std::vector<QueryType::DefUpdate>&& updates) {
877 // This function runs on the querydb thread.
878
879 for (auto& def : updates) {
880 assert(!def.value.detailed_name.empty());
881
882 auto it = usr_to_type.find(def.usr);
883 assert(it != usr_to_type.end());
884
885 assert(it->second.id >= 0 && it->second.id < types.size());
886 QueryType& existing = types[it->second.id];
887 if (!TryReplaceDef(existing.def, std::move(def.value))) {
888 existing.def.push_front(std::move(def.value));
889 UpdateSymbols(&existing.symbol_idx, SymbolKind::Type, it->second);
890 }
891 }
892 }
893
ImportOrUpdate(std::vector<QueryFunc::DefUpdate> && updates)894 void QueryDatabase::ImportOrUpdate(
895 std::vector<QueryFunc::DefUpdate>&& updates) {
896 // This function runs on the querydb thread.
897
898 for (auto& def : updates) {
899 assert(!def.value.detailed_name.empty());
900
901 auto it = usr_to_func.find(def.usr);
902 assert(it != usr_to_func.end());
903
904 assert(it->second.id >= 0 && it->second.id < funcs.size());
905 QueryFunc& existing = funcs[it->second.id];
906 if (!TryReplaceDef(existing.def, std::move(def.value))) {
907 existing.def.push_front(std::move(def.value));
908 UpdateSymbols(&existing.symbol_idx, SymbolKind::Func, it->second);
909 }
910 }
911 }
912
ImportOrUpdate(std::vector<QueryVar::DefUpdate> && updates)913 void QueryDatabase::ImportOrUpdate(std::vector<QueryVar::DefUpdate>&& updates) {
914 // This function runs on the querydb thread.
915
916 for (auto& def : updates) {
917 assert(!def.value.detailed_name.empty());
918
919 auto it = usr_to_var.find(def.usr);
920 assert(it != usr_to_var.end());
921
922 assert(it->second.id >= 0 && it->second.id < vars.size());
923 QueryVar& existing = vars[it->second.id];
924 if (!TryReplaceDef(existing.def, std::move(def.value))) {
925 existing.def.push_front(std::move(def.value));
926 if (!existing.def.front().is_local())
927 UpdateSymbols(&existing.symbol_idx, SymbolKind::Var, it->second);
928 }
929 }
930 }
931
UpdateSymbols(Maybe<AnyId> * symbol_idx,SymbolKind kind,AnyId idx)932 void QueryDatabase::UpdateSymbols(Maybe<AnyId>* symbol_idx,
933 SymbolKind kind,
934 AnyId idx) {
935 if (!symbol_idx->HasValue()) {
936 *symbol_idx = AnyId(symbols.size());
937 symbols.push_back(SymbolIdx{idx, kind});
938 }
939 }
940
941 // For Func, the returned name does not include parameters.
GetSymbolDetailedName(RawId symbol_idx) const942 std::string_view QueryDatabase::GetSymbolDetailedName(RawId symbol_idx) const {
943 RawId idx = symbols[symbol_idx].id.id;
944 switch (symbols[symbol_idx].kind) {
945 default:
946 break;
947 case SymbolKind::File:
948 if (files[idx].def)
949 return files[idx].def->path.path;
950 break;
951 case SymbolKind::Func:
952 if (const auto* def = funcs[idx].AnyDef())
953 return def->DetailedName(false);
954 break;
955 case SymbolKind::Type:
956 if (const auto* def = types[idx].AnyDef())
957 return def->detailed_name;
958 break;
959 case SymbolKind::Var:
960 if (const auto* def = vars[idx].AnyDef())
961 return def->detailed_name;
962 break;
963 }
964 return "";
965 }
966
GetSymbolShortName(RawId symbol_idx) const967 std::string_view QueryDatabase::GetSymbolShortName(RawId symbol_idx) const {
968 RawId idx = symbols[symbol_idx].id.id;
969 switch (symbols[symbol_idx].kind) {
970 default:
971 break;
972 case SymbolKind::File:
973 if (files[idx].def)
974 return files[idx].def->path.path;
975 break;
976 case SymbolKind::Func:
977 if (const auto* def = funcs[idx].AnyDef())
978 return def->ShortName();
979 break;
980 case SymbolKind::Type:
981 if (const auto* def = types[idx].AnyDef())
982 return def->ShortName();
983 break;
984 case SymbolKind::Var:
985 if (const auto* def = vars[idx].AnyDef())
986 return def->ShortName();
987 break;
988 }
989 return "";
990 }
991
992 TEST_SUITE("query") {
GetDelta(IndexFile previous,IndexFile current)993 IndexUpdate GetDelta(IndexFile previous, IndexFile current) {
994 QueryDatabase db;
995 IdMap previous_map(&db, previous.id_cache);
996 IdMap current_map(&db, current.id_cache);
997 return IndexUpdate::CreateDelta(&previous_map, ¤t_map, &previous,
998 ¤t);
999 }
1000
1001 TEST_CASE("remove defs") {
1002 IndexFile previous(AbsolutePath("foo.cc"), "<empty>");
1003 IndexFile current(AbsolutePath("foo.cc"), "<empty>");
1004
1005 previous.Resolve(previous.ToTypeId(HashUsr("usr1")))->def.spell =
1006 IndexId::LexicalRef(Range(Position(1, 0)), {}, {}, {});
1007 previous.Resolve(previous.ToFuncId(HashUsr("usr2")))->def.spell =
1008 IndexId::LexicalRef(Range(Position(2, 0)), {}, {}, {});
1009 previous.Resolve(previous.ToVarId(HashUsr("usr3")))->def.spell =
1010 IndexId::LexicalRef(Range(Position(3, 0)), {}, {}, {});
1011
1012 IndexUpdate update = GetDelta(previous, current);
1013
1014 REQUIRE(update.types_removed == std::vector<Usr>{HashUsr("usr1")});
1015 REQUIRE(update.funcs_removed.size() == 1);
1016 REQUIRE(update.funcs_removed[0].usr == HashUsr("usr2"));
1017 REQUIRE(update.vars_removed.size() == 1);
1018 REQUIRE(update.vars_removed[0].usr == HashUsr("usr3"));
1019 }
1020
1021 TEST_CASE("do not remove ref-only defs") {
1022 IndexFile previous(AbsolutePath("foo.cc"), "<empty>");
1023 IndexFile current(AbsolutePath("foo.cc"), "<empty>");
1024
1025 previous.Resolve(previous.ToTypeId(HashUsr("usr1")))
1026 ->uses.push_back(
1027 IndexId::LexicalRef(Range(Position(1, 0)), AnyId(0), SymbolKind::Func, {}));
1028 previous.Resolve(previous.ToFuncId(HashUsr("usr2")))
1029 ->uses.push_back(
1030 IndexId::LexicalRef(Range(Position(2, 0)), AnyId(0), SymbolKind::Func, {}));
1031 previous.Resolve(previous.ToVarId(HashUsr("usr3")))
1032 ->uses.push_back(
1033 IndexId::LexicalRef(Range(Position(3, 0)), AnyId(0), SymbolKind::Func, {}));
1034
1035 IndexUpdate update = GetDelta(previous, current);
1036
1037 REQUIRE(update.types_removed == std::vector<Usr>{});
1038 REQUIRE(update.funcs_removed.empty());
1039 REQUIRE(update.vars_removed.empty());
1040 }
1041
1042 TEST_CASE("func callers") {
1043 IndexFile previous(AbsolutePath("foo.cc"), "<empty>");
1044 IndexFile current(AbsolutePath("foo.cc"), "<empty>");
1045
1046 IndexFunc* pf = previous.Resolve(previous.ToFuncId(HashUsr("usr")));
1047 IndexFunc* cf = current.Resolve(current.ToFuncId(HashUsr("usr")));
1048
1049 pf->uses.push_back(IndexId::LexicalRef(Range(Position(1, 0)), AnyId(0), SymbolKind::Func, {}));
1050 cf->uses.push_back(IndexId::LexicalRef(Range(Position(2, 0)), AnyId(0), SymbolKind::Func, {}));
1051
1052 IndexUpdate update = GetDelta(previous, current);
1053
1054 REQUIRE(update.funcs_removed.empty());
1055 REQUIRE(update.funcs_uses.size() == 1);
1056 REQUIRE(update.funcs_uses[0].id == QueryId::Func(0));
1057 REQUIRE(update.funcs_uses[0].to_remove.size() == 1);
1058 REQUIRE(update.funcs_uses[0].to_remove[0].range == Range(Position(1, 0)));
1059 REQUIRE(update.funcs_uses[0].to_add.size() == 1);
1060 REQUIRE(update.funcs_uses[0].to_add[0].range == Range(Position(2, 0)));
1061 }
1062
1063 TEST_CASE("type usages") {
1064 IndexFile previous(AbsolutePath("foo.cc"), "<empty>");
1065 IndexFile current(AbsolutePath("foo.cc"), "<empty>");
1066
1067 IndexType* pt = previous.Resolve(previous.ToTypeId(HashUsr("usr")));
1068 IndexType* ct = current.Resolve(current.ToTypeId(HashUsr("usr")));
1069
1070 pt->uses.push_back(IndexId::LexicalRef(Range(Position(1, 0)), AnyId(0), SymbolKind::Type, {}));
1071 ct->uses.push_back(IndexId::LexicalRef(Range(Position(2, 0)), AnyId(0), SymbolKind::Type, {}));
1072
1073 IndexUpdate update = GetDelta(previous, current);
1074
1075 REQUIRE(update.types_removed == std::vector<Usr>{});
1076 REQUIRE(update.types_def_update.empty());
1077 REQUIRE(update.types_uses.size() == 1);
1078 REQUIRE(update.types_uses[0].to_remove.size() == 1);
1079 REQUIRE(update.types_uses[0].to_remove[0].range == Range(Position(1, 0)));
1080 REQUIRE(update.types_uses[0].to_add.size() == 1);
1081 REQUIRE(update.types_uses[0].to_add[0].range == Range(Position(2, 0)));
1082 }
1083
1084 TEST_CASE("apply delta") {
1085 IndexFile previous(AbsolutePath("foo.cc"), "<empty>");
1086 IndexFile current(AbsolutePath("foo.cc"), "<empty>");
1087
1088 IndexFunc* pf = previous.Resolve(previous.ToFuncId(HashUsr("usr")));
1089 IndexFunc* cf = current.Resolve(current.ToFuncId(HashUsr("usr")));
1090 pf->uses.push_back(IndexId::LexicalRef(Range(Position(1, 0)), AnyId(0), SymbolKind::Func, {}));
1091 pf->uses.push_back(IndexId::LexicalRef(Range(Position(2, 0)), AnyId(0), SymbolKind::Func, {}));
1092 cf->uses.push_back(IndexId::LexicalRef(Range(Position(4, 0)), AnyId(0), SymbolKind::Func, {}));
1093 cf->uses.push_back(IndexId::LexicalRef(Range(Position(5, 0)), AnyId(0), SymbolKind::Func, {}));
1094
1095 QueryDatabase db;
1096 IdMap previous_map(&db, previous.id_cache);
1097 IdMap current_map(&db, current.id_cache);
1098 REQUIRE(db.funcs.size() == 1);
1099
1100 IndexUpdate import_update =
1101 IndexUpdate::CreateDelta(nullptr, &previous_map, nullptr, &previous);
1102 IndexUpdate delta_update = IndexUpdate::CreateDelta(
1103 &previous_map, ¤t_map, &previous, ¤t);
1104
1105 db.ApplyIndexUpdate(&import_update);
1106 REQUIRE(db.funcs[0].uses.size() == 2);
1107 REQUIRE(db.funcs[0].uses[0].range == Range(Position(1, 0)));
1108 REQUIRE(db.funcs[0].uses[1].range == Range(Position(2, 0)));
1109
1110 db.ApplyIndexUpdate(&delta_update);
1111 REQUIRE(db.funcs[0].uses.size() == 2);
1112 REQUIRE(db.funcs[0].uses[0].range == Range(Position(4, 0)));
1113 REQUIRE(db.funcs[0].uses[1].range == Range(Position(5, 0)));
1114 }
1115
1116 TEST_CASE("Remove variable with usage") {
__anon6772d7021302(const char* json) 1117 auto load_index_from_json = [](const char* json) {
1118 return Deserialize(SerializeFormat::Json,
1119 AbsolutePath::BuildDoNotUse("foo.cc"), json, "<empty>",
1120 nullopt);
1121 };
1122
1123 auto previous = load_index_from_json(R"RAW(
1124 {
1125 "types": [
1126 {
1127 "id": 0,
1128 "usr": 17,
1129 "detailed_name": "",
1130 "short_name_offset": 0,
1131 "short_name_size": 0,
1132 "kind": 0,
1133 "hover": "",
1134 "comments": "",
1135 "parents": [],
1136 "derived": [],
1137 "types": [],
1138 "funcs": [],
1139 "vars": [],
1140 "instances": [
1141 0
1142 ],
1143 "uses": []
1144 }
1145 ],
1146 "funcs": [
1147 {
1148 "id": 0,
1149 "usr": 4259594751088586730,
1150 "detailed_name": "void foo()",
1151 "short_name_offset": 5,
1152 "short_name_size": 3,
1153 "kind": 12,
1154 "storage": 1,
1155 "hover": "",
1156 "comments": "",
1157 "declarations": [],
1158 "spell": "1:6-1:9|-1|1|2",
1159 "extent": "1:1-4:2|-1|1|0",
1160 "base": [],
1161 "derived": [],
1162 "locals": [],
1163 "uses": [],
1164 "callees": []
1165 }
1166 ],
1167 "vars": [
1168 {
1169 "id": 0,
1170 "usr": 16837348799350457167,
1171 "detailed_name": "int a",
1172 "short_name_offset": 4,
1173 "short_name_size": 1,
1174 "hover": "",
1175 "comments": "",
1176 "declarations": [],
1177 "spell": "2:7-2:8|0|3|2",
1178 "extent": "2:3-2:8|0|3|2",
1179 "type": 0,
1180 "uses": [
1181 "3:3-3:4|0|3|4"
1182 ],
1183 "kind": 13,
1184 "storage": 1
1185 }
1186 ]
1187 }
1188 )RAW");
1189
1190 auto current = load_index_from_json(R"RAW(
1191 {
1192 "types": [],
1193 "funcs": [
1194 {
1195 "id": 0,
1196 "usr": 4259594751088586730,
1197 "detailed_name": "void foo()",
1198 "short_name_offset": 5,
1199 "short_name_size": 3,
1200 "kind": 12,
1201 "storage": 1,
1202 "hover": "",
1203 "comments": "",
1204 "declarations": [],
1205 "spell": "1:6-1:9|-1|1|2",
1206 "extent": "1:1-5:2|-1|1|0",
1207 "base": [],
1208 "derived": [],
1209 "locals": [],
1210 "uses": [],
1211 "callees": []
1212 }
1213 ],
1214 "vars": []
1215 }
1216 )RAW");
1217
1218 // Validate previous/current were parsed.
1219 REQUIRE(previous->vars.size() == 1);
1220 REQUIRE(current->vars.size() == 0);
1221
1222 QueryDatabase db;
1223
1224 // Apply initial file.
1225 {
1226 IdMap previous_map(&db, previous->id_cache);
1227 IndexUpdate import_update = IndexUpdate::CreateDelta(
1228 nullptr, &previous_map, nullptr, previous.get());
1229 db.ApplyIndexUpdate(&import_update);
1230 }
1231
1232 REQUIRE(db.vars.size() == 1);
1233 REQUIRE(db.vars[0].uses.size() == 1);
1234
1235 // Apply change.
1236 {
1237 IdMap previous_map(&db, previous->id_cache);
1238 IdMap current_map(&db, current->id_cache);
1239 IndexUpdate delta_update = IndexUpdate::CreateDelta(
1240 &previous_map, ¤t_map, previous.get(), current.get());
1241 db.ApplyIndexUpdate(&delta_update);
1242 }
1243 REQUIRE(db.vars.size() == 1);
1244 REQUIRE(db.vars[0].uses.size() == 0);
1245 }
1246 }
1247