2017-02-23 09:23:23 +00:00
|
|
|
#include "query.h"
|
|
|
|
|
2017-03-25 20:32:44 +00:00
|
|
|
#include "indexer.h"
|
|
|
|
|
|
|
|
#include <optional.h>
|
2017-05-15 03:51:53 +00:00
|
|
|
#include <doctest/doctest.h>
|
2017-03-25 20:32:44 +00:00
|
|
|
|
2017-04-19 00:05:14 +00:00
|
|
|
#include <cassert>
|
2017-02-20 19:08:27 +00:00
|
|
|
#include <cstdint>
|
2017-02-25 23:59:09 +00:00
|
|
|
#include <functional>
|
|
|
|
#include <unordered_set>
|
2017-02-20 19:08:27 +00:00
|
|
|
#include <unordered_map>
|
2017-02-21 09:08:52 +00:00
|
|
|
#include <string>
|
|
|
|
#include <iostream>
|
|
|
|
|
2017-02-25 23:59:09 +00:00
|
|
|
// TODO: Make all copy constructors explicit.
|
|
|
|
|
2017-02-26 19:45:59 +00:00
|
|
|
|
2017-04-08 08:17:29 +00:00
|
|
|
namespace {
|
2017-04-07 06:57:26 +00:00
|
|
|
|
2017-05-22 06:09:09 +00:00
|
|
|
optional<QueryType::DefUpdate> ToQuery(const IdMap& id_map, const IndexType::Def& type) {
|
|
|
|
if (type.detailed_name.empty())
|
|
|
|
return nullopt;
|
|
|
|
|
2017-04-15 05:41:35 +00:00
|
|
|
QueryType::DefUpdate result(type.usr);
|
2017-04-14 02:17:15 +00:00
|
|
|
result.short_name = type.short_name;
|
2017-04-15 04:58:07 +00:00
|
|
|
result.detailed_name = type.detailed_name;
|
2017-04-14 02:17:15 +00:00
|
|
|
result.definition_spelling = id_map.ToQuery(type.definition_spelling);
|
|
|
|
result.definition_extent = id_map.ToQuery(type.definition_extent);
|
|
|
|
result.alias_of = id_map.ToQuery(type.alias_of);
|
|
|
|
result.parents = id_map.ToQuery(type.parents);
|
|
|
|
result.types = id_map.ToQuery(type.types);
|
|
|
|
result.funcs = id_map.ToQuery(type.funcs);
|
|
|
|
result.vars = id_map.ToQuery(type.vars);
|
2017-02-26 19:45:59 +00:00
|
|
|
return result;
|
|
|
|
}
|
2017-04-08 08:17:29 +00:00
|
|
|
|
2017-05-22 06:09:09 +00:00
|
|
|
optional<QueryFunc::DefUpdate> ToQuery(const IdMap& id_map, const IndexFunc::Def& func) {
|
|
|
|
if (func.detailed_name.empty())
|
|
|
|
return nullopt;
|
|
|
|
|
2017-04-15 05:41:35 +00:00
|
|
|
QueryFunc::DefUpdate result(func.usr);
|
2017-04-14 02:17:15 +00:00
|
|
|
result.short_name = func.short_name;
|
2017-04-15 04:58:07 +00:00
|
|
|
result.detailed_name = func.detailed_name;
|
2017-04-14 02:17:15 +00:00
|
|
|
result.definition_spelling = id_map.ToQuery(func.definition_spelling);
|
|
|
|
result.definition_extent = id_map.ToQuery(func.definition_extent);
|
|
|
|
result.declaring_type = id_map.ToQuery(func.declaring_type);
|
|
|
|
result.base = id_map.ToQuery(func.base);
|
|
|
|
result.locals = id_map.ToQuery(func.locals);
|
|
|
|
result.callees = id_map.ToQuery(func.callees);
|
2017-02-26 19:45:59 +00:00
|
|
|
return result;
|
|
|
|
}
|
2017-04-08 08:17:29 +00:00
|
|
|
|
2017-05-22 06:09:09 +00:00
|
|
|
optional<QueryVar::DefUpdate> ToQuery(const IdMap& id_map, const IndexVar::Def& var) {
|
|
|
|
if (var.detailed_name.empty())
|
|
|
|
return nullopt;
|
|
|
|
|
2017-04-15 05:41:35 +00:00
|
|
|
QueryVar::DefUpdate result(var.usr);
|
2017-04-14 02:17:15 +00:00
|
|
|
result.short_name = var.short_name;
|
2017-04-15 04:58:07 +00:00
|
|
|
result.detailed_name = var.detailed_name;
|
2017-04-14 02:17:15 +00:00
|
|
|
result.declaration = id_map.ToQuery(var.declaration);
|
|
|
|
result.definition_spelling = id_map.ToQuery(var.definition_spelling);
|
|
|
|
result.definition_extent = id_map.ToQuery(var.definition_extent);
|
|
|
|
result.variable_type = id_map.ToQuery(var.variable_type);
|
|
|
|
result.declaring_type = id_map.ToQuery(var.declaring_type);
|
2017-05-21 01:26:50 +00:00
|
|
|
result.is_local = var.is_local;
|
2017-02-26 19:45:59 +00:00
|
|
|
return result;
|
2017-02-26 08:11:47 +00:00
|
|
|
}
|
2017-02-25 23:59:09 +00:00
|
|
|
|
2017-04-16 08:31:28 +00:00
|
|
|
|
|
|
|
// Adds the mergeable updates in |source| to |dest|. If a mergeable update for
|
|
|
|
// the destination type already exists, it will be combined. This makes merging
|
|
|
|
// updates take longer but reduces import time on the querydb thread.
|
|
|
|
template <typename TId, typename TValue>
|
|
|
|
void AddMergeableRange(
|
2017-04-19 06:56:37 +00:00
|
|
|
std::vector<MergeableUpdate<TId, TValue>>* dest,
|
|
|
|
const std::vector<MergeableUpdate<TId, TValue>>& source) {
|
2017-04-16 08:31:28 +00:00
|
|
|
|
|
|
|
// TODO: Consider caching the lookup table. It can probably save even more
|
|
|
|
// time at the cost of some additional memory.
|
|
|
|
|
|
|
|
// Build lookup table.
|
2017-04-19 05:45:37 +00:00
|
|
|
spp::sparse_hash_map<TId, size_t> id_to_index;
|
2017-04-16 08:31:28 +00:00
|
|
|
id_to_index.resize(dest->size());
|
|
|
|
for (size_t i = 0; i < dest->size(); ++i)
|
|
|
|
id_to_index[(*dest)[i].id] = i;
|
|
|
|
|
|
|
|
// Add entries. Try to add them to an existing entry.
|
|
|
|
for (const auto& entry : source) {
|
|
|
|
auto it = id_to_index.find(entry.id);
|
|
|
|
if (it != id_to_index.end()) {
|
|
|
|
AddRange(&(*dest)[it->second].to_add, entry.to_add);
|
|
|
|
AddRange(&(*dest)[it->second].to_remove, entry.to_remove);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
dest->push_back(entry);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-19 06:56:37 +00:00
|
|
|
// Compares |previous| and |current|, adding all elements that are
|
|
|
|
// in |previous| but not |current| to |removed|, and all elements
|
|
|
|
// that are in |current| but not |previous| to |added|.
|
|
|
|
//
|
|
|
|
// Returns true iff |removed| or |added| are non-empty.
|
|
|
|
template<typename T>
|
|
|
|
bool ComputeDifferenceForUpdate(
|
|
|
|
std::vector<T>& previous, std::vector<T>& current,
|
|
|
|
std::vector<T>* removed, std::vector<T>* added) {
|
2017-04-07 07:12:53 +00:00
|
|
|
|
2017-04-19 06:56:37 +00:00
|
|
|
// We need to sort to use std::set_difference.
|
|
|
|
std::sort(previous.begin(), previous.end());
|
|
|
|
std::sort(current.begin(), current.end());
|
2017-04-07 07:12:53 +00:00
|
|
|
|
2017-04-19 06:56:37 +00:00
|
|
|
// Returns the elements in |previous| that are not in |current|.
|
|
|
|
std::set_difference(
|
|
|
|
previous.begin(), previous.end(),
|
|
|
|
current.begin(), current.end(),
|
|
|
|
std::back_inserter(*removed));
|
|
|
|
// Returns the elements in |current| that are not in |previous|.
|
|
|
|
std::set_difference(
|
|
|
|
current.begin(), current.end(),
|
|
|
|
previous.begin(), previous.end(),
|
|
|
|
std::back_inserter(*added));
|
2017-04-07 07:12:53 +00:00
|
|
|
|
2017-04-19 06:56:37 +00:00
|
|
|
return !removed->empty() || !added->empty();
|
|
|
|
}
|
2017-04-07 07:12:53 +00:00
|
|
|
|
2017-04-19 06:56:37 +00:00
|
|
|
template<typename T>
|
|
|
|
void 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) {
|
2017-04-07 07:12:53 +00:00
|
|
|
|
2017-04-19 06:56:37 +00:00
|
|
|
std::sort(previous_data.begin(), previous_data.end());
|
|
|
|
std::sort(current_data.begin(), current_data.end());
|
2017-04-07 07:12:53 +00:00
|
|
|
|
2017-04-19 06:56:37 +00:00
|
|
|
auto prev_it = previous_data.begin();
|
|
|
|
auto curr_it = current_data.begin();
|
|
|
|
while (prev_it != previous_data.end() && curr_it != current_data.end()) {
|
|
|
|
// same id
|
|
|
|
if (prev_it->def.usr == curr_it->def.usr) {
|
|
|
|
on_found(&*prev_it, &*curr_it);
|
|
|
|
++prev_it;
|
|
|
|
++curr_it;
|
|
|
|
}
|
2017-04-07 07:12:53 +00:00
|
|
|
|
2017-04-19 06:56:37 +00:00
|
|
|
// prev_id is smaller - prev_it has data curr_it does not have.
|
|
|
|
else if (prev_it->def.usr < curr_it->def.usr) {
|
|
|
|
on_removed(&*prev_it);
|
|
|
|
++prev_it;
|
|
|
|
}
|
2017-04-07 07:12:53 +00:00
|
|
|
|
2017-04-19 06:56:37 +00:00
|
|
|
// prev_id is bigger - curr_it has data prev_it does not have.
|
|
|
|
else {
|
|
|
|
on_added(&*curr_it);
|
|
|
|
++curr_it;
|
|
|
|
}
|
|
|
|
}
|
2017-04-07 07:12:53 +00:00
|
|
|
|
2017-04-19 06:56:37 +00:00
|
|
|
// if prev_it still has data, that means it is not in curr_it and was removed.
|
|
|
|
while (prev_it != previous_data.end()) {
|
|
|
|
on_removed(&*prev_it);
|
|
|
|
++prev_it;
|
|
|
|
}
|
2017-04-07 07:12:53 +00:00
|
|
|
|
2017-04-19 06:56:37 +00:00
|
|
|
// if curr_it still has data, that means it is not in prev_it and was added.
|
|
|
|
while (curr_it != current_data.end()) {
|
|
|
|
on_added(&*curr_it);
|
|
|
|
++curr_it;
|
|
|
|
}
|
|
|
|
}
|
2017-04-07 07:12:53 +00:00
|
|
|
|
2017-05-12 06:08:15 +00:00
|
|
|
QueryFile::Def BuildFileDef(const IdMap& id_map, const IndexFile& indexed) {
|
2017-04-15 05:41:35 +00:00
|
|
|
QueryFile::Def def;
|
|
|
|
def.path = indexed.path;
|
2017-05-21 03:46:15 +00:00
|
|
|
def.includes = indexed.includes;
|
2017-04-07 05:42:57 +00:00
|
|
|
|
2017-04-08 07:11:57 +00:00
|
|
|
auto add_outline = [&def, &id_map](SymbolIdx idx, Range range) {
|
2017-04-08 08:17:29 +00:00
|
|
|
def.outline.push_back(SymbolRef(idx, id_map.ToQuery(range)));
|
2017-02-27 07:23:43 +00:00
|
|
|
};
|
2017-04-08 07:11:57 +00:00
|
|
|
auto add_all_symbols = [&def, &id_map](SymbolIdx idx, Range range) {
|
2017-04-08 08:17:29 +00:00
|
|
|
def.all_symbols.push_back(SymbolRef(idx, id_map.ToQuery(range)));
|
2017-04-03 02:21:21 +00:00
|
|
|
};
|
2017-02-27 07:23:43 +00:00
|
|
|
|
2017-05-12 06:08:15 +00:00
|
|
|
for (const IndexType& type : indexed.types) {
|
|
|
|
if (type.def.definition_spelling.has_value())
|
|
|
|
add_all_symbols(id_map.ToSymbol(type.id), type.def.definition_spelling.value());
|
|
|
|
if (type.def.definition_extent.has_value())
|
|
|
|
add_outline(id_map.ToSymbol(type.id), type.def.definition_extent.value());
|
|
|
|
for (const Range& use : type.uses)
|
|
|
|
add_all_symbols(id_map.ToSymbol(type.id), use);
|
2017-02-27 07:23:43 +00:00
|
|
|
}
|
2017-05-12 06:08:15 +00:00
|
|
|
for (const IndexFunc& func : indexed.funcs) {
|
|
|
|
if (func.def.definition_spelling.has_value())
|
|
|
|
add_all_symbols(id_map.ToSymbol(func.id), func.def.definition_spelling.value());
|
|
|
|
if (func.def.definition_extent.has_value())
|
|
|
|
add_outline(id_map.ToSymbol(func.id), func.def.definition_extent.value());
|
2017-05-27 21:09:20 +00:00
|
|
|
for (const IndexFunc::Declaration& decl : func.declarations) {
|
|
|
|
// TODO: add more outline info?
|
|
|
|
add_all_symbols(id_map.ToSymbol(func.id), decl.spelling);
|
|
|
|
add_outline(id_map.ToSymbol(func.id), decl.spelling);
|
2017-04-03 02:21:21 +00:00
|
|
|
}
|
2017-05-23 06:57:46 +00:00
|
|
|
for (const IndexFuncRef& caller : func.callers) {
|
|
|
|
if (caller.is_implicit) continue;
|
2017-05-12 06:08:15 +00:00
|
|
|
add_all_symbols(id_map.ToSymbol(func.id), caller.loc);
|
2017-05-23 06:57:46 +00:00
|
|
|
}
|
2017-02-27 07:23:43 +00:00
|
|
|
}
|
2017-05-12 06:08:15 +00:00
|
|
|
for (const IndexVar& var : indexed.vars) {
|
|
|
|
if (var.def.definition_spelling.has_value())
|
|
|
|
add_all_symbols(id_map.ToSymbol(var.id), var.def.definition_spelling.value());
|
|
|
|
if (var.def.definition_extent.has_value())
|
|
|
|
add_outline(id_map.ToSymbol(var.id), var.def.definition_extent.value());
|
|
|
|
for (const Range& use : var.uses)
|
|
|
|
add_all_symbols(id_map.ToSymbol(var.id), use);
|
2017-02-27 07:23:43 +00:00
|
|
|
}
|
|
|
|
|
2017-04-08 07:11:57 +00:00
|
|
|
std::sort(def.outline.begin(), def.outline.end(), [](const SymbolRef& a, const SymbolRef& b) {
|
2017-04-07 06:20:30 +00:00
|
|
|
return a.loc.range.start < b.loc.range.start;
|
2017-02-27 07:23:43 +00:00
|
|
|
});
|
2017-04-08 07:11:57 +00:00
|
|
|
std::sort(def.all_symbols.begin(), def.all_symbols.end(), [](const SymbolRef& a, const SymbolRef& b) {
|
2017-04-07 06:20:30 +00:00
|
|
|
return a.loc.range.start < b.loc.range.start;
|
2017-04-03 02:21:21 +00:00
|
|
|
});
|
2017-04-07 05:42:57 +00:00
|
|
|
|
|
|
|
return def;
|
2017-02-27 07:23:43 +00:00
|
|
|
}
|
|
|
|
|
2017-04-19 06:56:37 +00:00
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-02-20 19:08:27 +00:00
|
|
|
|
2017-02-21 09:08:52 +00:00
|
|
|
|
2017-02-25 23:59:09 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-02-26 08:11:47 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
2017-02-23 08:18:54 +00:00
|
|
|
|
2017-02-26 08:11:47 +00:00
|
|
|
|
2017-02-23 08:18:54 +00:00
|
|
|
|
2017-02-22 08:52:00 +00:00
|
|
|
|
2017-02-27 07:23:43 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-04-15 05:41:35 +00:00
|
|
|
QueryFileId GetQueryFileIdFromPath(QueryDatabase* query_db, const std::string& path) {
|
2017-05-22 07:14:11 +00:00
|
|
|
auto it = query_db->usr_to_file.find(LowerPathIfCaseInsensitive(path));
|
2017-05-09 04:20:28 +00:00
|
|
|
if (it != query_db->usr_to_file.end())
|
|
|
|
return QueryFileId(it->second.id);
|
2017-04-07 08:01:58 +00:00
|
|
|
|
2017-04-08 07:11:57 +00:00
|
|
|
size_t idx = query_db->files.size();
|
2017-05-22 07:14:11 +00:00
|
|
|
query_db->usr_to_file[LowerPathIfCaseInsensitive(path)] = QueryFileId(idx);
|
2017-04-15 05:41:35 +00:00
|
|
|
query_db->files.push_back(QueryFile(path));
|
2017-04-08 07:11:57 +00:00
|
|
|
return QueryFileId(idx);
|
2017-04-07 08:01:58 +00:00
|
|
|
}
|
|
|
|
|
2017-04-15 05:41:35 +00:00
|
|
|
QueryTypeId GetQueryTypeIdFromUsr(QueryDatabase* query_db, const Usr& usr) {
|
2017-05-09 04:20:28 +00:00
|
|
|
auto it = query_db->usr_to_type.find(usr);
|
|
|
|
if (it != query_db->usr_to_type.end())
|
|
|
|
return QueryTypeId(it->second.id);
|
2017-04-07 08:01:58 +00:00
|
|
|
|
2017-04-08 07:11:57 +00:00
|
|
|
size_t idx = query_db->types.size();
|
2017-05-09 04:20:28 +00:00
|
|
|
query_db->usr_to_type[usr] = QueryTypeId(idx);
|
2017-04-15 05:41:35 +00:00
|
|
|
query_db->types.push_back(QueryType(usr));
|
2017-04-08 07:11:57 +00:00
|
|
|
return QueryTypeId(idx);
|
2017-04-07 08:01:58 +00:00
|
|
|
}
|
|
|
|
|
2017-04-15 05:41:35 +00:00
|
|
|
QueryFuncId GetQueryFuncIdFromUsr(QueryDatabase* query_db, const Usr& usr) {
|
2017-05-09 04:20:28 +00:00
|
|
|
auto it = query_db->usr_to_func.find(usr);
|
|
|
|
if (it != query_db->usr_to_func.end())
|
|
|
|
return QueryFuncId(it->second.id);
|
2017-04-07 08:01:58 +00:00
|
|
|
|
2017-04-08 07:11:57 +00:00
|
|
|
size_t idx = query_db->funcs.size();
|
2017-05-09 04:20:28 +00:00
|
|
|
query_db->usr_to_func[usr] = QueryFuncId(idx);
|
2017-04-15 05:41:35 +00:00
|
|
|
query_db->funcs.push_back(QueryFunc(usr));
|
2017-04-08 07:11:57 +00:00
|
|
|
return QueryFuncId(idx);
|
2017-04-07 08:01:58 +00:00
|
|
|
}
|
|
|
|
|
2017-04-15 05:41:35 +00:00
|
|
|
QueryVarId GetQueryVarIdFromUsr(QueryDatabase* query_db, const Usr& usr) {
|
2017-05-09 04:20:28 +00:00
|
|
|
auto it = query_db->usr_to_var.find(usr);
|
|
|
|
if (it != query_db->usr_to_var.end())
|
|
|
|
return QueryVarId(it->second.id);
|
2017-04-07 08:01:58 +00:00
|
|
|
|
2017-04-08 07:11:57 +00:00
|
|
|
size_t idx = query_db->vars.size();
|
2017-05-09 04:20:28 +00:00
|
|
|
query_db->usr_to_var[usr] = QueryVarId(idx);
|
2017-04-15 05:41:35 +00:00
|
|
|
query_db->vars.push_back(QueryVar(usr));
|
2017-04-08 07:11:57 +00:00
|
|
|
return QueryVarId(idx);
|
2017-04-07 08:01:58 +00:00
|
|
|
}
|
|
|
|
|
2017-04-15 05:41:35 +00:00
|
|
|
IdMap::IdMap(QueryDatabase* query_db, const IdCache& local_ids)
|
2017-04-07 08:01:58 +00:00
|
|
|
: local_ids(local_ids) {
|
2017-04-15 05:41:35 +00:00
|
|
|
primary_file = GetQueryFileIdFromPath(query_db, local_ids.primary_file);
|
2017-04-08 06:45:28 +00:00
|
|
|
|
2017-04-08 07:11:57 +00:00
|
|
|
cached_type_ids_.resize(local_ids.type_id_to_usr.size());
|
2017-04-07 08:01:58 +00:00
|
|
|
for (const auto& entry : local_ids.type_id_to_usr)
|
2017-04-15 05:55:01 +00:00
|
|
|
cached_type_ids_[entry.first] = GetQueryTypeIdFromUsr(query_db, entry.second);
|
2017-04-07 08:01:58 +00:00
|
|
|
|
2017-04-08 07:11:57 +00:00
|
|
|
cached_func_ids_.resize(local_ids.func_id_to_usr.size());
|
2017-04-07 08:01:58 +00:00
|
|
|
for (const auto& entry : local_ids.func_id_to_usr)
|
2017-04-15 05:55:01 +00:00
|
|
|
cached_func_ids_[entry.first] = GetQueryFuncIdFromUsr(query_db, entry.second);
|
2017-04-07 08:01:58 +00:00
|
|
|
|
2017-04-08 07:11:57 +00:00
|
|
|
cached_var_ids_.resize(local_ids.var_id_to_usr.size());
|
2017-04-07 08:01:58 +00:00
|
|
|
for (const auto& entry : local_ids.var_id_to_usr)
|
2017-04-15 05:55:01 +00:00
|
|
|
cached_var_ids_[entry.first] = GetQueryVarIdFromUsr(query_db, entry.second);
|
2017-04-08 07:11:57 +00:00
|
|
|
}
|
|
|
|
|
2017-04-15 05:41:35 +00:00
|
|
|
QueryLocation IdMap::ToQuery(Range range) const {
|
|
|
|
return QueryLocation(primary_file, range);
|
2017-04-08 07:52:57 +00:00
|
|
|
}
|
2017-04-08 07:11:57 +00:00
|
|
|
QueryTypeId IdMap::ToQuery(IndexTypeId id) const {
|
2017-04-15 05:55:01 +00:00
|
|
|
assert(cached_type_ids_.find(id) != cached_type_ids_.end());
|
|
|
|
return QueryTypeId(cached_type_ids_.find(id)->second);
|
2017-04-07 08:01:58 +00:00
|
|
|
}
|
2017-04-08 07:11:57 +00:00
|
|
|
QueryFuncId IdMap::ToQuery(IndexFuncId id) const {
|
2017-05-21 23:22:00 +00:00
|
|
|
if (id.id == -1) return QueryFuncId((size_t)-1);
|
2017-04-15 05:55:01 +00:00
|
|
|
assert(cached_func_ids_.find(id) != cached_func_ids_.end());
|
|
|
|
return QueryFuncId(cached_func_ids_.find(id)->second);
|
2017-04-08 07:11:57 +00:00
|
|
|
}
|
|
|
|
QueryVarId IdMap::ToQuery(IndexVarId id) const {
|
2017-04-15 05:55:01 +00:00
|
|
|
assert(cached_var_ids_.find(id) != cached_var_ids_.end());
|
|
|
|
return QueryVarId(cached_var_ids_.find(id)->second);
|
2017-04-08 07:11:57 +00:00
|
|
|
}
|
2017-04-08 08:04:38 +00:00
|
|
|
QueryFuncRef IdMap::ToQuery(IndexFuncRef ref) const {
|
2017-05-23 06:57:46 +00:00
|
|
|
return QueryFuncRef(ToQuery(ref.id), ToQuery(ref.loc), ref.is_implicit);
|
2017-04-08 08:04:38 +00:00
|
|
|
}
|
2017-05-27 21:09:20 +00:00
|
|
|
QueryLocation IdMap::ToQuery(IndexFunc::Declaration decl) const {
|
|
|
|
// TODO: expose more than just QueryLocation.
|
|
|
|
return QueryLocation(primary_file, decl.spelling);
|
|
|
|
}
|
2017-04-07 08:01:58 +00:00
|
|
|
|
2017-04-15 05:41:35 +00:00
|
|
|
optional<QueryLocation> IdMap::ToQuery(optional<Range> range) const {
|
2017-04-08 07:52:57 +00:00
|
|
|
if (!range)
|
|
|
|
return nullopt;
|
|
|
|
return ToQuery(range.value());
|
|
|
|
}
|
|
|
|
optional<QueryTypeId> IdMap::ToQuery(optional<IndexTypeId> id) const {
|
|
|
|
if (!id)
|
|
|
|
return nullopt;
|
|
|
|
return ToQuery(id.value());
|
|
|
|
}
|
|
|
|
optional<QueryFuncId> IdMap::ToQuery(optional<IndexFuncId> id) const {
|
|
|
|
if (!id)
|
|
|
|
return nullopt;
|
|
|
|
return ToQuery(id.value());
|
|
|
|
}
|
|
|
|
optional<QueryVarId> IdMap::ToQuery(optional<IndexVarId> id) const {
|
|
|
|
if (!id)
|
|
|
|
return nullopt;
|
|
|
|
return ToQuery(id.value());
|
|
|
|
}
|
2017-04-08 08:04:38 +00:00
|
|
|
optional<QueryFuncRef> IdMap::ToQuery(optional<IndexFuncRef> ref) const {
|
|
|
|
if (!ref)
|
|
|
|
return nullopt;
|
|
|
|
return ToQuery(ref.value());
|
|
|
|
}
|
2017-05-27 21:09:20 +00:00
|
|
|
optional<QueryLocation> IdMap::ToQuery(optional<IndexFunc::Declaration> decl) const {
|
|
|
|
if (!decl)
|
|
|
|
return nullopt;
|
|
|
|
return ToQuery(decl.value());
|
|
|
|
}
|
2017-04-08 07:52:57 +00:00
|
|
|
|
|
|
|
template<typename In, typename Out>
|
|
|
|
std::vector<Out> ToQueryTransform(const IdMap& id_map, const std::vector<In>& input) {
|
|
|
|
std::vector<Out> result;
|
|
|
|
result.reserve(input.size());
|
|
|
|
for (const In& in : input)
|
|
|
|
result.push_back(id_map.ToQuery(in));
|
|
|
|
return result;
|
|
|
|
}
|
2017-04-15 05:41:35 +00:00
|
|
|
std::vector<QueryLocation> IdMap::ToQuery(std::vector<Range> ranges) const {
|
|
|
|
return ToQueryTransform<Range, QueryLocation>(*this, ranges);
|
2017-04-08 07:52:57 +00:00
|
|
|
}
|
|
|
|
std::vector<QueryTypeId> IdMap::ToQuery(std::vector<IndexTypeId> ids) const {
|
|
|
|
return ToQueryTransform<IndexTypeId, QueryTypeId>(*this, ids);
|
|
|
|
}
|
|
|
|
std::vector<QueryFuncId> IdMap::ToQuery(std::vector<IndexFuncId> ids) const {
|
|
|
|
return ToQueryTransform<IndexFuncId, QueryFuncId>(*this, ids);
|
|
|
|
}
|
|
|
|
std::vector<QueryVarId> IdMap::ToQuery(std::vector<IndexVarId> ids) const {
|
|
|
|
return ToQueryTransform<IndexVarId, QueryVarId>(*this, ids);
|
|
|
|
}
|
2017-04-08 08:04:38 +00:00
|
|
|
std::vector<QueryFuncRef> IdMap::ToQuery(std::vector<IndexFuncRef> refs) const {
|
|
|
|
return ToQueryTransform<IndexFuncRef, QueryFuncRef>(*this, refs);
|
|
|
|
}
|
2017-05-27 21:09:20 +00:00
|
|
|
std::vector<QueryLocation> IdMap::ToQuery(std::vector<IndexFunc::Declaration> decls) const {
|
|
|
|
return ToQueryTransform<IndexFunc::Declaration, QueryLocation>(*this, decls);
|
|
|
|
}
|
2017-04-08 07:52:57 +00:00
|
|
|
|
2017-04-08 07:11:57 +00:00
|
|
|
SymbolIdx IdMap::ToSymbol(IndexTypeId id) const {
|
|
|
|
return SymbolIdx(SymbolKind::Type, ToQuery(id).id);
|
|
|
|
}
|
|
|
|
SymbolIdx IdMap::ToSymbol(IndexFuncId id) const {
|
|
|
|
return SymbolIdx(SymbolKind::Func, ToQuery(id).id);
|
|
|
|
}
|
|
|
|
SymbolIdx IdMap::ToSymbol(IndexVarId id) const {
|
|
|
|
return SymbolIdx(SymbolKind::Var, ToQuery(id).id);
|
|
|
|
}
|
2017-04-07 08:01:58 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-02-27 07:23:43 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-04-19 06:56:37 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ----------------------
|
|
|
|
// INDEX THREAD FUNCTIONS
|
|
|
|
// ----------------------
|
|
|
|
|
2017-04-07 05:42:57 +00:00
|
|
|
// static
|
2017-05-12 06:08:15 +00:00
|
|
|
IndexUpdate IndexUpdate::CreateDelta(const IdMap* previous_id_map, const IdMap* current_id_map, IndexFile* previous, IndexFile* current) {
|
2017-04-19 06:56:37 +00:00
|
|
|
// This function runs on an indexer thread.
|
|
|
|
|
2017-04-08 06:45:28 +00:00
|
|
|
if (!previous_id_map) {
|
|
|
|
assert(!previous);
|
2017-05-22 06:09:09 +00:00
|
|
|
IndexFile empty(current->path);
|
|
|
|
return IndexUpdate(*current_id_map, *current_id_map, empty, *current);
|
2017-04-08 06:45:28 +00:00
|
|
|
}
|
|
|
|
return IndexUpdate(*previous_id_map, *current_id_map, *previous, *current);
|
2017-04-07 05:42:57 +00:00
|
|
|
}
|
2017-02-27 07:23:43 +00:00
|
|
|
|
2017-05-12 06:08:15 +00:00
|
|
|
IndexUpdate::IndexUpdate(const IdMap& previous_id_map, const IdMap& current_id_map, IndexFile& previous_file, IndexFile& current_file) {
|
2017-04-19 06:56:37 +00:00
|
|
|
// This function runs on an indexer thread.
|
|
|
|
|
2017-02-25 23:59:09 +00:00
|
|
|
// |query_name| is the name of the variable on the query type.
|
|
|
|
// |index_name| is the name of the variable on the index type.
|
|
|
|
// |type| is the type of the variable.
|
2017-04-15 05:24:49 +00:00
|
|
|
#define PROCESS_UPDATE_DIFF(type_id, query_name, index_name, type) \
|
2017-04-08 07:52:57 +00:00
|
|
|
{ \
|
|
|
|
/* Check for changes. */ \
|
|
|
|
std::vector<type> removed, added; \
|
|
|
|
auto previous = previous_id_map.ToQuery(previous_def->index_name); \
|
|
|
|
auto current = current_id_map.ToQuery(current_def->index_name); \
|
|
|
|
bool did_add = ComputeDifferenceForUpdate( \
|
|
|
|
previous, current, \
|
|
|
|
&removed, &added); \
|
|
|
|
if (did_add) {\
|
2017-04-10 00:08:54 +00:00
|
|
|
/*std::cerr << "Adding mergeable update on " << current_def->def.short_name << " (" << current_def->def.usr << ") for field " << #index_name << std::endl;*/ \
|
2017-04-15 05:24:49 +00:00
|
|
|
query_name.push_back(MergeableUpdate<type_id, type>(current_id_map.ToQuery(current_def->id), added, removed)); \
|
2017-04-08 07:52:57 +00:00
|
|
|
} \
|
|
|
|
}
|
2017-02-27 07:23:43 +00:00
|
|
|
// File
|
2017-04-07 07:30:08 +00:00
|
|
|
files_def_update.push_back(BuildFileDef(current_id_map, current_file));
|
2017-02-25 23:59:09 +00:00
|
|
|
|
2017-05-25 00:18:04 +00:00
|
|
|
// **NOTE** We only remove entries if they were defined in the previous index.
|
|
|
|
// For example, if a type is included from another file it will be defined
|
|
|
|
// simply so we can attribute the usage/reference to it. If the reference goes
|
|
|
|
// away we don't want to remove the type/func/var usage.
|
|
|
|
|
2017-02-25 23:59:09 +00:00
|
|
|
// Types
|
2017-05-12 06:08:15 +00:00
|
|
|
CompareGroups<IndexType>(previous_file.types, current_file.types,
|
2017-05-25 00:18:04 +00:00
|
|
|
/*onRemoved:*/[this, &previous_id_map](IndexType* type) {
|
|
|
|
if (type->def.definition_spelling)
|
|
|
|
types_removed.push_back(type->def.usr);
|
|
|
|
else {
|
|
|
|
if (!type->derived.empty())
|
|
|
|
types_derived.push_back(QueryType::DerivedUpdate(previous_id_map.ToQuery(type->id), {}, previous_id_map.ToQuery(type->derived)));
|
|
|
|
if (!type->instances.empty())
|
|
|
|
types_instances.push_back(QueryType::InstancesUpdate(previous_id_map.ToQuery(type->id), {}, previous_id_map.ToQuery(type->instances)));
|
|
|
|
if (!type->uses.empty())
|
|
|
|
types_uses.push_back(QueryType::UsesUpdate(previous_id_map.ToQuery(type->id), {}, previous_id_map.ToQuery(type->uses)));
|
|
|
|
}
|
2017-02-25 23:59:09 +00:00
|
|
|
},
|
2017-05-12 06:08:15 +00:00
|
|
|
/*onAdded:*/[this, ¤t_id_map](IndexType* type) {
|
2017-05-22 06:09:09 +00:00
|
|
|
optional<QueryType::DefUpdate> def_update = ToQuery(current_id_map, type->def);
|
|
|
|
if (def_update)
|
|
|
|
types_def_update.push_back(*def_update);
|
2017-04-14 02:17:15 +00:00
|
|
|
if (!type->derived.empty())
|
2017-04-15 05:41:35 +00:00
|
|
|
types_derived.push_back(QueryType::DerivedUpdate(current_id_map.ToQuery(type->id), current_id_map.ToQuery(type->derived)));
|
2017-04-21 07:03:33 +00:00
|
|
|
if (!type->instances.empty())
|
|
|
|
types_instances.push_back(QueryType::InstancesUpdate(current_id_map.ToQuery(type->id), current_id_map.ToQuery(type->instances)));
|
2017-04-14 02:17:15 +00:00
|
|
|
if (!type->uses.empty())
|
2017-04-15 05:41:35 +00:00
|
|
|
types_uses.push_back(QueryType::UsesUpdate(current_id_map.ToQuery(type->id), current_id_map.ToQuery(type->uses)));
|
2017-02-25 23:59:09 +00:00
|
|
|
},
|
2017-05-12 06:08:15 +00:00
|
|
|
/*onFound:*/[this, &previous_id_map, ¤t_id_map](IndexType* previous_def, IndexType* current_def) {
|
2017-04-15 05:41:35 +00:00
|
|
|
optional<QueryType::DefUpdate> previous_remapped_def = ToQuery(previous_id_map, previous_def->def);
|
|
|
|
optional<QueryType::DefUpdate> current_remapped_def = ToQuery(current_id_map, current_def->def);
|
2017-04-19 17:55:54 +00:00
|
|
|
if (current_remapped_def && previous_remapped_def != current_remapped_def && !current_remapped_def->detailed_name.empty())
|
2017-04-14 02:17:15 +00:00
|
|
|
types_def_update.push_back(*current_remapped_def);
|
2017-02-27 07:23:43 +00:00
|
|
|
|
2017-04-15 05:24:49 +00:00
|
|
|
PROCESS_UPDATE_DIFF(QueryTypeId, types_derived, derived, QueryTypeId);
|
2017-04-21 07:03:33 +00:00
|
|
|
PROCESS_UPDATE_DIFF(QueryTypeId, types_instances, instances, QueryVarId);
|
2017-04-15 05:41:35 +00:00
|
|
|
PROCESS_UPDATE_DIFF(QueryTypeId, types_uses, uses, QueryLocation);
|
2017-02-25 23:59:09 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
// Functions
|
2017-05-12 06:08:15 +00:00
|
|
|
CompareGroups<IndexFunc>(previous_file.funcs, current_file.funcs,
|
2017-05-25 00:18:04 +00:00
|
|
|
/*onRemoved:*/[this, &previous_id_map](IndexFunc* func) {
|
|
|
|
if (func->def.definition_spelling) {
|
|
|
|
funcs_removed.push_back(func->def.usr);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (!func->declarations.empty())
|
|
|
|
funcs_declarations.push_back(QueryFunc::DeclarationsUpdate(previous_id_map.ToQuery(func->id), {}, previous_id_map.ToQuery(func->declarations)));
|
|
|
|
if (!func->derived.empty())
|
|
|
|
funcs_derived.push_back(QueryFunc::DerivedUpdate(previous_id_map.ToQuery(func->id), {}, previous_id_map.ToQuery(func->derived)));
|
|
|
|
if (!func->callers.empty())
|
|
|
|
funcs_callers.push_back(QueryFunc::CallersUpdate(previous_id_map.ToQuery(func->id), {}, previous_id_map.ToQuery(func->callers)));
|
|
|
|
}
|
2017-02-25 23:59:09 +00:00
|
|
|
},
|
2017-05-12 06:08:15 +00:00
|
|
|
/*onAdded:*/[this, ¤t_id_map](IndexFunc* func) {
|
2017-05-22 06:09:09 +00:00
|
|
|
optional<QueryFunc::DefUpdate> def_update = ToQuery(current_id_map, func->def);
|
|
|
|
if (def_update)
|
|
|
|
funcs_def_update.push_back(*def_update);
|
2017-04-14 02:17:15 +00:00
|
|
|
if (!func->declarations.empty())
|
2017-04-15 05:41:35 +00:00
|
|
|
funcs_declarations.push_back(QueryFunc::DeclarationsUpdate(current_id_map.ToQuery(func->id), current_id_map.ToQuery(func->declarations)));
|
2017-04-14 02:17:15 +00:00
|
|
|
if (!func->derived.empty())
|
2017-04-15 05:41:35 +00:00
|
|
|
funcs_derived.push_back(QueryFunc::DerivedUpdate(current_id_map.ToQuery(func->id), current_id_map.ToQuery(func->derived)));
|
2017-04-14 02:17:15 +00:00
|
|
|
if (!func->callers.empty())
|
2017-04-15 05:41:35 +00:00
|
|
|
funcs_callers.push_back(QueryFunc::CallersUpdate(current_id_map.ToQuery(func->id), current_id_map.ToQuery(func->callers)));
|
2017-02-25 23:59:09 +00:00
|
|
|
},
|
2017-05-12 06:08:15 +00:00
|
|
|
/*onFound:*/[this, &previous_id_map, ¤t_id_map](IndexFunc* previous_def, IndexFunc* current_def) {
|
2017-04-15 05:41:35 +00:00
|
|
|
optional<QueryFunc::DefUpdate> previous_remapped_def = ToQuery(previous_id_map, previous_def->def);
|
|
|
|
optional<QueryFunc::DefUpdate> current_remapped_def = ToQuery(current_id_map, current_def->def);
|
2017-04-19 17:55:54 +00:00
|
|
|
if (current_remapped_def && previous_remapped_def != current_remapped_def && !current_remapped_def->detailed_name.empty())
|
2017-04-14 02:17:15 +00:00
|
|
|
funcs_def_update.push_back(*current_remapped_def);
|
2017-02-27 07:23:43 +00:00
|
|
|
|
2017-04-15 05:41:35 +00:00
|
|
|
PROCESS_UPDATE_DIFF(QueryFuncId, funcs_declarations, declarations, QueryLocation);
|
2017-04-15 05:24:49 +00:00
|
|
|
PROCESS_UPDATE_DIFF(QueryFuncId, funcs_derived, derived, QueryFuncId);
|
|
|
|
PROCESS_UPDATE_DIFF(QueryFuncId, funcs_callers, callers, QueryFuncRef);
|
2017-02-25 23:59:09 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
// Variables
|
2017-05-12 06:08:15 +00:00
|
|
|
CompareGroups<IndexVar>(previous_file.vars, current_file.vars,
|
2017-05-25 00:18:04 +00:00
|
|
|
/*onRemoved:*/[this, &previous_id_map](IndexVar* var) {
|
|
|
|
if (var->def.definition_spelling) {
|
|
|
|
vars_removed.push_back(var->def.usr);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (!var->uses.empty())
|
|
|
|
vars_uses.push_back(QueryVar::UsesUpdate(previous_id_map.ToQuery(var->id), {}, previous_id_map.ToQuery(var->uses)));
|
|
|
|
}
|
2017-02-25 23:59:09 +00:00
|
|
|
},
|
2017-05-12 06:08:15 +00:00
|
|
|
/*onAdded:*/[this, ¤t_id_map](IndexVar* var) {
|
2017-05-22 06:09:09 +00:00
|
|
|
optional<QueryVar::DefUpdate> def_update = ToQuery(current_id_map, var->def);
|
|
|
|
if (def_update)
|
|
|
|
vars_def_update.push_back(*def_update);
|
2017-04-14 02:17:15 +00:00
|
|
|
if (!var->uses.empty())
|
2017-04-15 05:41:35 +00:00
|
|
|
vars_uses.push_back(QueryVar::UsesUpdate(current_id_map.ToQuery(var->id), current_id_map.ToQuery(var->uses)));
|
2017-02-25 23:59:09 +00:00
|
|
|
},
|
2017-05-12 06:08:15 +00:00
|
|
|
/*onFound:*/[this, &previous_id_map, ¤t_id_map](IndexVar* previous_def, IndexVar* current_def) {
|
2017-04-15 05:41:35 +00:00
|
|
|
optional<QueryVar::DefUpdate> previous_remapped_def = ToQuery(previous_id_map, previous_def->def);
|
|
|
|
optional<QueryVar::DefUpdate> current_remapped_def = ToQuery(current_id_map, current_def->def);
|
2017-04-19 17:55:54 +00:00
|
|
|
if (current_remapped_def && previous_remapped_def != current_remapped_def && !current_remapped_def->detailed_name.empty())
|
2017-04-14 02:17:15 +00:00
|
|
|
vars_def_update.push_back(*current_remapped_def);
|
2017-02-25 23:59:09 +00:00
|
|
|
|
2017-04-15 05:41:35 +00:00
|
|
|
PROCESS_UPDATE_DIFF(QueryVarId, vars_uses, uses, QueryLocation);
|
2017-02-27 07:23:43 +00:00
|
|
|
});
|
2017-02-25 23:59:09 +00:00
|
|
|
|
|
|
|
#undef PROCESS_UPDATE_DIFF
|
|
|
|
}
|
2017-02-21 09:08:52 +00:00
|
|
|
|
2017-02-27 07:23:43 +00:00
|
|
|
void IndexUpdate::Merge(const IndexUpdate& update) {
|
2017-04-19 06:56:37 +00:00
|
|
|
// This function runs on an indexer thread.
|
|
|
|
|
2017-04-16 08:31:28 +00:00
|
|
|
#define INDEX_UPDATE_APPEND(name) \
|
2017-03-02 18:30:21 +00:00
|
|
|
AddRange(&name, update.name);
|
2017-04-16 08:31:28 +00:00
|
|
|
#define INDEX_UPDATE_MERGE(name) \
|
|
|
|
AddMergeableRange(&name, update.name);
|
2017-02-27 07:23:43 +00:00
|
|
|
|
2017-04-16 08:31:28 +00:00
|
|
|
INDEX_UPDATE_APPEND(files_removed);
|
|
|
|
INDEX_UPDATE_APPEND(files_def_update);
|
2017-02-27 07:23:43 +00:00
|
|
|
|
2017-04-16 08:31:28 +00:00
|
|
|
INDEX_UPDATE_APPEND(types_removed);
|
|
|
|
INDEX_UPDATE_APPEND(types_def_update);
|
2017-02-27 07:23:43 +00:00
|
|
|
INDEX_UPDATE_MERGE(types_derived);
|
2017-04-21 07:03:33 +00:00
|
|
|
INDEX_UPDATE_MERGE(types_instances);
|
2017-02-27 07:23:43 +00:00
|
|
|
INDEX_UPDATE_MERGE(types_uses);
|
|
|
|
|
2017-04-16 08:31:28 +00:00
|
|
|
INDEX_UPDATE_APPEND(funcs_removed);
|
|
|
|
INDEX_UPDATE_APPEND(funcs_def_update);
|
2017-02-27 07:23:43 +00:00
|
|
|
INDEX_UPDATE_MERGE(funcs_declarations);
|
|
|
|
INDEX_UPDATE_MERGE(funcs_derived);
|
|
|
|
INDEX_UPDATE_MERGE(funcs_callers);
|
|
|
|
|
2017-04-16 08:31:28 +00:00
|
|
|
INDEX_UPDATE_APPEND(vars_removed);
|
|
|
|
INDEX_UPDATE_APPEND(vars_def_update);
|
2017-02-27 07:23:43 +00:00
|
|
|
INDEX_UPDATE_MERGE(vars_uses);
|
2017-02-26 19:45:59 +00:00
|
|
|
|
2017-04-16 08:31:28 +00:00
|
|
|
#undef INDEX_UPDATE_APPEND
|
2017-02-27 07:23:43 +00:00
|
|
|
#undef INDEX_UPDATE_MERGE
|
|
|
|
}
|
2017-02-26 19:45:59 +00:00
|
|
|
|
2017-05-19 07:02:01 +00:00
|
|
|
std::string IndexUpdate::ToString() {
|
|
|
|
rapidjson::StringBuffer output;
|
|
|
|
Writer writer(output);
|
|
|
|
IndexUpdate& update = *this;
|
|
|
|
Reflect(writer, update);
|
|
|
|
return output.GetString();
|
|
|
|
}
|
2017-02-26 19:45:59 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-02-26 08:11:47 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-02-26 19:45:59 +00:00
|
|
|
|
2017-04-19 06:56:37 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ------------------------
|
|
|
|
// QUERYDB THREAD FUNCTIONS
|
|
|
|
// ------------------------
|
2017-02-26 08:11:47 +00:00
|
|
|
|
2017-05-09 04:20:28 +00:00
|
|
|
void QueryDatabase::RemoveUsrs(SymbolKind usr_kind, const std::vector<Usr>& to_remove) {
|
2017-04-19 06:56:37 +00:00
|
|
|
// This function runs on the querydb thread.
|
|
|
|
|
2017-04-22 07:32:29 +00:00
|
|
|
// When we remove an element, we just erase the state from the storage. We do
|
|
|
|
// not update array indices because that would take a huge amount of time for
|
|
|
|
// a very large index.
|
2017-04-19 06:56:37 +00:00
|
|
|
//
|
2017-04-22 07:32:29 +00:00
|
|
|
// There means that there is some memory growth that will never be reclaimed,
|
|
|
|
// but it should be pretty minimal and is solved by simply restarting the
|
|
|
|
// indexer and loading from cache, which is a fast operation.
|
|
|
|
//
|
|
|
|
// TODO: Add "cquery: Reload Index" command which unloads all querydb state
|
|
|
|
// and fully reloads from cache. This will address the memory leak above.
|
|
|
|
|
2017-05-09 04:20:28 +00:00
|
|
|
switch (usr_kind) {
|
|
|
|
case SymbolKind::File: {
|
|
|
|
for (const Usr& usr : to_remove)
|
2017-05-22 07:14:11 +00:00
|
|
|
files[usr_to_file[LowerPathIfCaseInsensitive(usr)].id] = nullopt;
|
2017-05-09 04:20:28 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SymbolKind::Type: {
|
|
|
|
for (const Usr& usr : to_remove)
|
|
|
|
types[usr_to_type[usr].id] = nullopt;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SymbolKind::Func: {
|
|
|
|
for (const Usr& usr : to_remove)
|
|
|
|
funcs[usr_to_func[usr].id] = nullopt;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SymbolKind::Var: {
|
|
|
|
for (const Usr& usr : to_remove)
|
|
|
|
vars[usr_to_var[usr].id] = nullopt;
|
|
|
|
break;
|
2017-04-22 07:32:29 +00:00
|
|
|
}
|
2017-05-21 23:48:21 +00:00
|
|
|
case SymbolKind::Invalid:
|
|
|
|
break;
|
2017-04-22 07:32:29 +00:00
|
|
|
}
|
2017-04-19 06:56:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QueryDatabase::ApplyIndexUpdate(IndexUpdate* update) {
|
|
|
|
// This function runs on the querydb thread.
|
|
|
|
|
2017-04-22 07:32:29 +00:00
|
|
|
// Example types:
|
|
|
|
// storage_name => std::vector<optional<QueryType>>
|
|
|
|
// merge_update => QueryType::DerivedUpdate => MergeableUpdate<QueryTypeId, QueryTypeId>
|
|
|
|
// def => QueryType
|
|
|
|
// def->def_var_name => std::vector<QueryTypeId>
|
2017-04-19 06:56:37 +00:00
|
|
|
#define HANDLE_MERGEABLE(update_var_name, def_var_name, storage_name) \
|
|
|
|
for (auto merge_update : update->update_var_name) { \
|
2017-04-22 07:32:29 +00:00
|
|
|
auto& def = storage_name[merge_update.id.id]; \
|
|
|
|
if (!def) \
|
|
|
|
continue; /* TODO: Should we continue or create an empty def? */ \
|
2017-04-19 06:56:37 +00:00
|
|
|
AddRange(&def->def_var_name, merge_update.to_add); \
|
|
|
|
RemoveRange(&def->def_var_name, merge_update.to_remove); \
|
|
|
|
}
|
|
|
|
|
2017-05-09 04:20:28 +00:00
|
|
|
RemoveUsrs(SymbolKind::File, update->files_removed);
|
2017-04-19 06:56:37 +00:00
|
|
|
ImportOrUpdate(update->files_def_update);
|
|
|
|
|
2017-05-09 04:20:28 +00:00
|
|
|
RemoveUsrs(SymbolKind::Type, update->types_removed);
|
2017-04-19 06:56:37 +00:00
|
|
|
ImportOrUpdate(update->types_def_update);
|
|
|
|
HANDLE_MERGEABLE(types_derived, derived, types);
|
2017-04-21 07:03:33 +00:00
|
|
|
HANDLE_MERGEABLE(types_instances, instances, types);
|
2017-04-19 06:56:37 +00:00
|
|
|
HANDLE_MERGEABLE(types_uses, uses, types);
|
|
|
|
|
2017-05-09 04:20:28 +00:00
|
|
|
RemoveUsrs(SymbolKind::Func, update->funcs_removed);
|
2017-04-19 06:56:37 +00:00
|
|
|
ImportOrUpdate(update->funcs_def_update);
|
|
|
|
HANDLE_MERGEABLE(funcs_declarations, declarations, funcs);
|
|
|
|
HANDLE_MERGEABLE(funcs_derived, derived, funcs);
|
|
|
|
HANDLE_MERGEABLE(funcs_callers, callers, funcs);
|
|
|
|
|
2017-05-09 04:20:28 +00:00
|
|
|
RemoveUsrs(SymbolKind::Var, update->vars_removed);
|
2017-04-19 06:56:37 +00:00
|
|
|
ImportOrUpdate(update->vars_def_update);
|
|
|
|
HANDLE_MERGEABLE(vars_uses, uses, vars);
|
|
|
|
|
|
|
|
#undef HANDLE_MERGEABLE
|
2017-02-27 07:23:43 +00:00
|
|
|
}
|
2017-02-26 19:45:59 +00:00
|
|
|
|
2017-04-15 05:41:35 +00:00
|
|
|
void QueryDatabase::ImportOrUpdate(const std::vector<QueryFile::DefUpdate>& updates) {
|
2017-04-19 06:56:37 +00:00
|
|
|
// This function runs on the querydb thread.
|
|
|
|
|
2017-04-07 05:42:57 +00:00
|
|
|
for (auto& def : updates) {
|
2017-05-22 07:14:11 +00:00
|
|
|
auto it = usr_to_file.find(LowerPathIfCaseInsensitive(def.path));
|
2017-05-09 04:20:28 +00:00
|
|
|
assert(it != usr_to_file.end());
|
2017-04-08 06:45:28 +00:00
|
|
|
|
2017-05-09 04:20:28 +00:00
|
|
|
optional<QueryFile>& existing = files[it->second.id];
|
2017-04-22 07:32:29 +00:00
|
|
|
if (!existing)
|
|
|
|
existing = QueryFile(def.path);
|
|
|
|
|
|
|
|
existing->def = def;
|
2017-05-09 04:20:28 +00:00
|
|
|
UpdateDetailedNames(&existing->detailed_name_idx, SymbolKind::File, it->second.id, def.path);
|
2017-02-26 19:45:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-15 05:41:35 +00:00
|
|
|
void QueryDatabase::ImportOrUpdate(const std::vector<QueryType::DefUpdate>& updates) {
|
2017-04-19 06:56:37 +00:00
|
|
|
// This function runs on the querydb thread.
|
|
|
|
|
2017-04-07 05:42:57 +00:00
|
|
|
for (auto& def : updates) {
|
2017-04-19 06:56:37 +00:00
|
|
|
assert(!def.detailed_name.empty());
|
2017-04-08 06:45:28 +00:00
|
|
|
|
2017-05-09 04:20:28 +00:00
|
|
|
auto it = usr_to_type.find(def.usr);
|
|
|
|
assert(it != usr_to_type.end());
|
2017-05-21 07:56:21 +00:00
|
|
|
|
2017-05-09 04:20:28 +00:00
|
|
|
assert(it->second.id >= 0 && it->second.id < types.size());
|
|
|
|
optional<QueryType>& existing = types[it->second.id];
|
2017-04-22 07:32:29 +00:00
|
|
|
if (!existing)
|
|
|
|
existing = QueryType(def.usr);
|
2017-04-19 06:56:37 +00:00
|
|
|
|
|
|
|
// Keep the existing definition if it is higher quality.
|
2017-04-22 07:32:29 +00:00
|
|
|
if (existing->def.definition_spelling && !def.definition_spelling)
|
2017-04-13 08:31:25 +00:00
|
|
|
continue;
|
|
|
|
|
2017-04-22 07:32:29 +00:00
|
|
|
existing->def = def;
|
2017-05-09 04:20:28 +00:00
|
|
|
UpdateDetailedNames(&existing->detailed_name_idx, SymbolKind::Type, it->second.id, def.detailed_name);
|
2017-02-26 19:45:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-15 05:41:35 +00:00
|
|
|
void QueryDatabase::ImportOrUpdate(const std::vector<QueryFunc::DefUpdate>& updates) {
|
2017-04-19 06:56:37 +00:00
|
|
|
// This function runs on the querydb thread.
|
|
|
|
|
2017-04-07 05:42:57 +00:00
|
|
|
for (auto& def : updates) {
|
2017-04-19 06:56:37 +00:00
|
|
|
assert(!def.detailed_name.empty());
|
2017-04-08 06:45:28 +00:00
|
|
|
|
2017-05-09 04:20:28 +00:00
|
|
|
auto it = usr_to_func.find(def.usr);
|
|
|
|
assert(it != usr_to_func.end());
|
2017-05-09 01:21:21 +00:00
|
|
|
|
2017-05-09 04:20:28 +00:00
|
|
|
assert(it->second.id >= 0 && it->second.id < funcs.size());
|
|
|
|
optional<QueryFunc>& existing = funcs[it->second.id];
|
2017-04-22 07:32:29 +00:00
|
|
|
if (!existing)
|
|
|
|
existing = QueryFunc(def.usr);
|
2017-04-19 06:56:37 +00:00
|
|
|
|
|
|
|
// Keep the existing definition if it is higher quality.
|
2017-04-22 07:32:29 +00:00
|
|
|
if (existing->def.definition_spelling && !def.definition_spelling)
|
2017-04-13 08:31:25 +00:00
|
|
|
continue;
|
|
|
|
|
2017-04-22 07:32:29 +00:00
|
|
|
existing->def = def;
|
2017-05-09 04:20:28 +00:00
|
|
|
UpdateDetailedNames(&existing->detailed_name_idx, SymbolKind::Func, it->second.id, def.detailed_name);
|
2017-02-26 19:45:59 +00:00
|
|
|
}
|
|
|
|
}
|
2017-02-26 08:11:47 +00:00
|
|
|
|
2017-04-15 05:41:35 +00:00
|
|
|
void QueryDatabase::ImportOrUpdate(const std::vector<QueryVar::DefUpdate>& updates) {
|
2017-04-19 06:56:37 +00:00
|
|
|
// This function runs on the querydb thread.
|
|
|
|
|
2017-04-07 05:42:57 +00:00
|
|
|
for (auto& def : updates) {
|
2017-04-19 06:56:37 +00:00
|
|
|
assert(!def.detailed_name.empty());
|
2017-04-08 06:45:28 +00:00
|
|
|
|
2017-05-09 04:20:28 +00:00
|
|
|
auto it = usr_to_var.find(def.usr);
|
|
|
|
assert(it != usr_to_var.end());
|
2017-05-09 01:21:21 +00:00
|
|
|
|
2017-05-09 04:20:28 +00:00
|
|
|
assert(it->second.id >= 0 && it->second.id < vars.size());
|
|
|
|
optional<QueryVar>& existing = vars[it->second.id];
|
2017-04-22 07:32:29 +00:00
|
|
|
if (!existing)
|
|
|
|
existing = QueryVar(def.usr);
|
2017-04-19 06:56:37 +00:00
|
|
|
|
|
|
|
// Keep the existing definition if it is higher quality.
|
2017-04-22 07:32:29 +00:00
|
|
|
if (existing->def.definition_spelling && !def.definition_spelling)
|
2017-04-13 08:31:25 +00:00
|
|
|
continue;
|
|
|
|
|
2017-04-22 07:32:29 +00:00
|
|
|
existing->def = def;
|
2017-06-16 01:38:40 +00:00
|
|
|
if (!def.is_local)
|
2017-05-09 04:20:28 +00:00
|
|
|
UpdateDetailedNames(&existing->detailed_name_idx, SymbolKind::Var, it->second.id, def.detailed_name);
|
2017-02-26 08:11:47 +00:00
|
|
|
}
|
2017-02-26 19:45:59 +00:00
|
|
|
}
|
2017-02-26 08:11:47 +00:00
|
|
|
|
2017-04-19 06:56:37 +00:00
|
|
|
void QueryDatabase::UpdateDetailedNames(size_t* qualified_name_index, SymbolKind kind, size_t symbol_index, const std::string& name) {
|
|
|
|
if (*qualified_name_index == -1) {
|
|
|
|
detailed_names.push_back(name);
|
|
|
|
symbols.push_back(SymbolIdx(kind, symbol_index));
|
|
|
|
*qualified_name_index = detailed_names.size() - 1;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
detailed_names[*qualified_name_index] = name;
|
2017-02-26 08:11:47 +00:00
|
|
|
}
|
2017-05-09 01:21:21 +00:00
|
|
|
}
|
2017-05-12 06:08:15 +00:00
|
|
|
|
2017-05-15 03:51:53 +00:00
|
|
|
TEST_SUITE("query");
|
|
|
|
|
|
|
|
IndexUpdate GetDelta(IndexFile previous, IndexFile current) {
|
|
|
|
QueryDatabase db;
|
|
|
|
IdMap previous_map(&db, previous.id_cache);
|
|
|
|
IdMap current_map(&db, current.id_cache);
|
|
|
|
return IndexUpdate::CreateDelta(&previous_map, ¤t_map, &previous, ¤t);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST_CASE("remove defs") {
|
2017-05-12 06:08:15 +00:00
|
|
|
IndexFile previous("foo.cc");
|
|
|
|
IndexFile current("foo.cc");
|
|
|
|
|
2017-05-25 00:18:04 +00:00
|
|
|
previous.Resolve(previous.ToTypeId("usr1"))->def.definition_spelling = Range(Position(1, 0));
|
|
|
|
previous.Resolve(previous.ToFuncId("usr2"))->def.definition_spelling = Range(Position(2, 0));
|
|
|
|
previous.Resolve(previous.ToVarId("usr3"))->def.definition_spelling = Range(Position(3, 0));
|
2017-05-12 06:08:15 +00:00
|
|
|
|
2017-05-15 03:51:53 +00:00
|
|
|
IndexUpdate update = GetDelta(previous, current);
|
2017-05-12 06:08:15 +00:00
|
|
|
|
2017-05-15 03:51:53 +00:00
|
|
|
REQUIRE(update.types_removed == std::vector<Usr>{ "usr1" });
|
|
|
|
REQUIRE(update.funcs_removed == std::vector<Usr>{ "usr2" });
|
|
|
|
REQUIRE(update.vars_removed == std::vector<Usr>{ "usr3" });
|
|
|
|
}
|
|
|
|
|
2017-05-25 00:18:04 +00:00
|
|
|
TEST_CASE("do not remove ref-only defs") {
|
|
|
|
IndexFile previous("foo.cc");
|
|
|
|
IndexFile current("foo.cc");
|
|
|
|
|
|
|
|
previous.Resolve(previous.ToTypeId("usr1"))->uses.push_back(Range(Position(1, 0)));
|
|
|
|
previous.Resolve(previous.ToFuncId("usr2"))->callers.push_back(IndexFuncRef(IndexFuncId(0), Range(Position(2, 0)), false /*is_implicit*/));
|
|
|
|
previous.Resolve(previous.ToVarId("usr3"))->uses.push_back(Range(Position(3, 0)));
|
|
|
|
|
|
|
|
IndexUpdate update = GetDelta(previous, current);
|
|
|
|
|
|
|
|
REQUIRE(update.types_removed == std::vector<Usr>{});
|
|
|
|
REQUIRE(update.funcs_removed == std::vector<Usr>{});
|
|
|
|
REQUIRE(update.vars_removed == std::vector<Usr>{});
|
|
|
|
}
|
|
|
|
|
2017-05-15 03:51:53 +00:00
|
|
|
TEST_CASE("func callers") {
|
|
|
|
IndexFile previous("foo.cc");
|
|
|
|
IndexFile current("foo.cc");
|
|
|
|
|
|
|
|
IndexFunc* pf = previous.Resolve(previous.ToFuncId("usr"));
|
|
|
|
IndexFunc* cf = current.Resolve(current.ToFuncId("usr"));
|
|
|
|
|
2017-05-23 06:47:27 +00:00
|
|
|
pf->callers.push_back(IndexFuncRef(IndexFuncId(0), Range(Position(1, 0)), false /*is_implicit*/));
|
|
|
|
cf->callers.push_back(IndexFuncRef(IndexFuncId(0), Range(Position(2, 0)), false /*is_implicit*/));
|
2017-05-15 03:51:53 +00:00
|
|
|
|
|
|
|
IndexUpdate update = GetDelta(previous, current);
|
|
|
|
|
|
|
|
REQUIRE(update.funcs_removed == std::vector<Usr>{});
|
|
|
|
REQUIRE(update.funcs_callers.size() == 1);
|
|
|
|
REQUIRE(update.funcs_callers[0].id == QueryFuncId(0));
|
|
|
|
REQUIRE(update.funcs_callers[0].to_remove.size() == 1);
|
|
|
|
REQUIRE(update.funcs_callers[0].to_remove[0].loc.range == Range(Position(1, 0)));
|
|
|
|
REQUIRE(update.funcs_callers[0].to_add.size() == 1);
|
|
|
|
REQUIRE(update.funcs_callers[0].to_add[0].loc.range == Range(Position(2, 0)));
|
2017-05-12 06:08:15 +00:00
|
|
|
}
|
2017-05-15 03:51:53 +00:00
|
|
|
|
|
|
|
TEST_CASE("type usages") {
|
|
|
|
IndexFile previous("foo.cc");
|
|
|
|
IndexFile current("foo.cc");
|
|
|
|
|
|
|
|
IndexType* pt = previous.Resolve(previous.ToTypeId("usr"));
|
|
|
|
IndexType* ct = current.Resolve(current.ToTypeId("usr"));
|
|
|
|
|
|
|
|
pt->uses.push_back(Range(Position(1, 0)));
|
|
|
|
ct->uses.push_back(Range(Position(2, 0)));
|
|
|
|
|
|
|
|
IndexUpdate update = GetDelta(previous, current);
|
2017-05-21 07:56:21 +00:00
|
|
|
|
2017-05-15 03:51:53 +00:00
|
|
|
REQUIRE(update.types_removed == std::vector<Usr>{});
|
|
|
|
REQUIRE(update.types_def_update == std::vector<QueryType::DefUpdate>{});
|
|
|
|
REQUIRE(update.types_uses.size() == 1);
|
|
|
|
REQUIRE(update.types_uses[0].to_remove.size() == 1);
|
|
|
|
REQUIRE(update.types_uses[0].to_remove[0].range == Range(Position(1, 0)));
|
|
|
|
REQUIRE(update.types_uses[0].to_add.size() == 1);
|
|
|
|
REQUIRE(update.types_uses[0].to_add[0].range == Range(Position(2, 0)));
|
|
|
|
}
|
|
|
|
|
2017-05-20 07:33:11 +00:00
|
|
|
TEST_CASE("apply delta") {
|
|
|
|
IndexFile previous("foo.cc");
|
|
|
|
IndexFile current("foo.cc");
|
|
|
|
|
|
|
|
IndexFunc* pf = previous.Resolve(previous.ToFuncId("usr"));
|
|
|
|
IndexFunc* cf = current.Resolve(current.ToFuncId("usr"));
|
2017-05-23 06:47:27 +00:00
|
|
|
pf->callers.push_back(IndexFuncRef(IndexFuncId(0), Range(Position(1, 0)), false /*is_implicit*/));
|
|
|
|
pf->callers.push_back(IndexFuncRef(IndexFuncId(0), Range(Position(2, 0)), false /*is_implicit*/));
|
|
|
|
cf->callers.push_back(IndexFuncRef(IndexFuncId(0), Range(Position(4, 0)), false /*is_implicit*/));
|
|
|
|
cf->callers.push_back(IndexFuncRef(IndexFuncId(0), Range(Position(5, 0)), false /*is_implicit*/));
|
2017-05-20 07:33:11 +00:00
|
|
|
|
|
|
|
QueryDatabase db;
|
|
|
|
IdMap previous_map(&db, previous.id_cache);
|
|
|
|
IdMap current_map(&db, current.id_cache);
|
|
|
|
REQUIRE(db.funcs.size() == 1);
|
|
|
|
|
|
|
|
IndexUpdate import_update = IndexUpdate::CreateDelta(nullptr, &previous_map, nullptr, &previous);
|
|
|
|
IndexUpdate delta_update = IndexUpdate::CreateDelta(&previous_map, ¤t_map, &previous, ¤t);
|
2017-05-21 07:56:21 +00:00
|
|
|
|
2017-05-20 07:33:11 +00:00
|
|
|
db.ApplyIndexUpdate(&import_update);
|
|
|
|
REQUIRE(db.funcs[0]->callers.size() == 2);
|
|
|
|
REQUIRE(db.funcs[0]->callers[0].loc.range == Range(Position(1, 0)));
|
|
|
|
REQUIRE(db.funcs[0]->callers[1].loc.range == Range(Position(2, 0)));
|
|
|
|
|
|
|
|
db.ApplyIndexUpdate(&delta_update);
|
|
|
|
REQUIRE(db.funcs[0]->callers.size() == 2);
|
|
|
|
REQUIRE(db.funcs[0]->callers[0].loc.range == Range(Position(4, 0)));
|
|
|
|
REQUIRE(db.funcs[0]->callers[1].loc.range == Range(Position(5, 0)));
|
|
|
|
}
|
2017-05-15 03:51:53 +00:00
|
|
|
|
|
|
|
TEST_SUITE_END();
|