2017-02-23 09:23:23 +00:00
|
|
|
#include "query.h"
|
|
|
|
|
2017-03-25 20:32:44 +00:00
|
|
|
#include "indexer.h"
|
2018-02-12 01:15:56 +00:00
|
|
|
#include "serializer.h"
|
2018-01-06 21:46:41 +00:00
|
|
|
#include "serializers/json.h"
|
2017-03-25 20:32:44 +00:00
|
|
|
|
2017-05-15 03:51:53 +00:00
|
|
|
#include <doctest/doctest.h>
|
2018-03-31 03:16:33 +00:00
|
|
|
#include <optional>
|
2017-07-30 04:24:02 +00:00
|
|
|
#include <loguru.hpp>
|
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>
|
2018-04-08 06:32:35 +00:00
|
|
|
#include <iterator>
|
2017-09-22 01:14:57 +00:00
|
|
|
#include <string>
|
|
|
|
#include <unordered_map>
|
|
|
|
#include <unordered_set>
|
2017-02-21 09:08:52 +00:00
|
|
|
|
2017-09-22 01:14:57 +00:00
|
|
|
// TODO: Make all copy constructors explicit.
|
2017-02-26 19:45:59 +00:00
|
|
|
|
2018-04-08 06:32:35 +00:00
|
|
|
// Used by |HANDLE_MERGEABLE| so only |range| is needed.
|
|
|
|
MAKE_HASHABLE(Range, t.start, t.end);
|
|
|
|
MAKE_HASHABLE(Use, t.range);
|
|
|
|
|
2018-04-30 04:49:03 +00:00
|
|
|
template <typename TVisitor, typename TValue>
|
|
|
|
void Reflect(TVisitor& visitor, MergeableUpdate<TValue>& value) {
|
2018-04-09 07:52:04 +00:00
|
|
|
REFLECT_MEMBER_START();
|
2018-04-30 04:49:03 +00:00
|
|
|
REFLECT_MEMBER(usr);
|
2018-04-09 07:52:04 +00:00
|
|
|
REFLECT_MEMBER(to_remove);
|
2018-04-30 04:49:03 +00:00
|
|
|
REFLECT_MEMBER(to_add);
|
2018-04-09 07:52:04 +00:00
|
|
|
REFLECT_MEMBER_END();
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename TVisitor, typename T>
|
|
|
|
void Reflect(TVisitor& visitor, WithUsr<T>& value) {
|
|
|
|
REFLECT_MEMBER_START();
|
|
|
|
REFLECT_MEMBER(usr);
|
|
|
|
REFLECT_MEMBER(value);
|
|
|
|
REFLECT_MEMBER_END();
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename TVisitor, typename T>
|
|
|
|
void Reflect(TVisitor& visitor, WithFileContent<T>& value) {
|
|
|
|
REFLECT_MEMBER_START();
|
|
|
|
REFLECT_MEMBER(value);
|
|
|
|
REFLECT_MEMBER(file_content);
|
|
|
|
REFLECT_MEMBER_END();
|
|
|
|
}
|
|
|
|
|
|
|
|
// NOTICE: We're not reflecting on files_removed or files_def_update, it is too
|
|
|
|
// much output when logging
|
|
|
|
MAKE_REFLECT_STRUCT(IndexUpdate,
|
|
|
|
types_removed,
|
|
|
|
types_def_update,
|
|
|
|
types_derived,
|
|
|
|
types_instances,
|
|
|
|
types_uses,
|
|
|
|
funcs_removed,
|
|
|
|
funcs_def_update,
|
|
|
|
funcs_declarations,
|
|
|
|
funcs_derived,
|
|
|
|
funcs_uses,
|
|
|
|
vars_removed,
|
|
|
|
vars_def_update,
|
|
|
|
vars_declarations,
|
|
|
|
vars_uses);
|
|
|
|
|
2017-04-08 08:17:29 +00:00
|
|
|
namespace {
|
2017-04-07 06:57:26 +00:00
|
|
|
|
2018-04-30 04:49:03 +00:00
|
|
|
void AssignFileId(int file_id, SymbolRef& ref) {
|
|
|
|
if (ref.kind == SymbolKind::File)
|
|
|
|
ref.usr = file_id;
|
2018-04-08 06:32:35 +00:00
|
|
|
}
|
|
|
|
|
2018-04-30 04:49:03 +00:00
|
|
|
void AssignFileId(int file_id, Use& use) {
|
|
|
|
if (use.kind == SymbolKind::File)
|
|
|
|
use.usr = file_id;
|
|
|
|
use.file_id = file_id;
|
2018-04-08 06:32:35 +00:00
|
|
|
}
|
|
|
|
|
2018-02-24 02:24:54 +00:00
|
|
|
template <typename T>
|
2018-04-30 04:49:03 +00:00
|
|
|
void AssignFileId(int file_id, T&) {}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
void AssignFileId(int file_id, Maybe<T>& x) {
|
|
|
|
if (x)
|
|
|
|
AssignFileId(file_id, *x);
|
2018-02-24 02:24:54 +00:00
|
|
|
}
|
|
|
|
|
2018-04-30 04:49:03 +00:00
|
|
|
template <typename T>
|
|
|
|
void AssignFileId(int file_id, std::vector<T>& xs) {
|
|
|
|
for (T& x : xs)
|
|
|
|
AssignFileId(file_id, x);
|
2017-02-26 19:45:59 +00:00
|
|
|
}
|
2017-04-08 08:17:29 +00:00
|
|
|
|
2018-04-30 04:49:03 +00:00
|
|
|
|
|
|
|
void AddRange(int file_id, std::vector<Use>& into, const std::vector<Use>& from) {
|
|
|
|
into.reserve(into.size() + from.size());
|
|
|
|
for (Use use : from) {
|
|
|
|
use.file_id = file_id;
|
|
|
|
into.push_back(use);
|
|
|
|
}
|
2017-02-26 19:45:59 +00:00
|
|
|
}
|
2017-04-08 08:17:29 +00:00
|
|
|
|
2018-04-30 04:49:03 +00:00
|
|
|
void AddRange(int _, std::vector<Usr>& into, const std::vector<Usr>& from) {
|
|
|
|
into.insert(into.end(), from.begin(), from.end());
|
2017-02-26 08:11:47 +00:00
|
|
|
}
|
2017-02-25 23:59:09 +00:00
|
|
|
|
2018-04-30 04:49:03 +00:00
|
|
|
template <typename T>
|
|
|
|
void RemoveRange(std::vector<T>& dest, const std::vector<T>& to_remove) {
|
|
|
|
if (to_remove.size()) {
|
|
|
|
std::unordered_set<T> to_remove_set(to_remove.begin(), to_remove.end());
|
|
|
|
dest.erase(
|
|
|
|
std::remove_if(dest.begin(), dest.end(),
|
|
|
|
[&](const T& t) { return to_remove_set.count(t) > 0; }),
|
|
|
|
dest.end());
|
2017-04-16 08:31:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-11 21:42:48 +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|.
|
2017-04-19 06:56:37 +00:00
|
|
|
//
|
|
|
|
// Returns true iff |removed| or |added| are non-empty.
|
2017-09-22 01:14:57 +00:00
|
|
|
template <typename T>
|
2018-04-30 04:49:03 +00:00
|
|
|
bool ComputeDifferenceForUpdate(std::vector<T> previous,
|
|
|
|
std::vector<T> current,
|
2017-09-22 01:14:57 +00:00
|
|
|
std::vector<T>* removed,
|
|
|
|
std::vector<T>* added) {
|
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
|
|
|
|
2018-01-01 20:36:08 +00:00
|
|
|
auto it0 = previous.begin(), it1 = current.begin();
|
|
|
|
while (it0 != previous.end() && it1 != current.end()) {
|
|
|
|
// Elements in |previous| that are not in |current|.
|
|
|
|
if (*it0 < *it1)
|
2018-02-07 06:46:05 +00:00
|
|
|
removed->push_back(std::move(*it0++));
|
2018-01-01 20:36:08 +00:00
|
|
|
// Elements in |current| that are not in |previous|.
|
|
|
|
else if (*it1 < *it0)
|
2018-02-07 06:46:05 +00:00
|
|
|
added->push_back(std::move(*it1++));
|
2018-01-01 20:36:08 +00:00
|
|
|
else
|
|
|
|
++it0, ++it1;
|
|
|
|
}
|
|
|
|
while (it0 != previous.end())
|
2018-02-07 06:46:05 +00:00
|
|
|
removed->push_back(std::move(*it0++));
|
2018-01-01 20:36:08 +00:00
|
|
|
while (it1 != current.end())
|
2018-02-07 06:46:05 +00:00
|
|
|
added->push_back(std::move(*it1++));
|
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-09-22 01:14:57 +00:00
|
|
|
template <typename T>
|
2018-04-30 04:49:03 +00:00
|
|
|
void CompareGroups(std::unordered_map<Usr, T>& prev,
|
|
|
|
std::unordered_map<Usr, T>& curr,
|
|
|
|
std::function<void(T&)> on_remove,
|
|
|
|
std::function<void(T&)> on_add,
|
|
|
|
std::function<void(T&, T&)> on_found) {
|
|
|
|
for (auto& it : prev) {
|
|
|
|
auto it1 = curr.find(it.first);
|
|
|
|
if (it1 != curr.end())
|
|
|
|
on_found(it.second, it1->second);
|
|
|
|
else
|
|
|
|
on_remove(it.second);
|
2017-04-19 06:56:37 +00:00
|
|
|
}
|
2018-04-30 04:49:03 +00:00
|
|
|
for (auto& it : curr)
|
|
|
|
if (!prev.count(it.first))
|
|
|
|
on_add(it.second);
|
2017-04-19 06:56:37 +00:00
|
|
|
}
|
2017-04-07 07:12:53 +00:00
|
|
|
|
2018-04-30 04:49:03 +00:00
|
|
|
QueryFile::DefUpdate BuildFileDefUpdate(const IndexFile& indexed) {
|
2017-04-15 05:41:35 +00:00
|
|
|
QueryFile::Def def;
|
|
|
|
def.path = indexed.path;
|
2018-02-15 06:41:07 +00:00
|
|
|
def.args = indexed.args;
|
2017-05-21 03:46:15 +00:00
|
|
|
def.includes = indexed.includes;
|
2017-10-28 22:09:14 +00:00
|
|
|
def.inactive_regions = indexed.skipped_by_preprocessor;
|
2018-01-27 17:29:28 +00:00
|
|
|
def.dependencies = indexed.dependencies;
|
2017-04-07 05:42:57 +00:00
|
|
|
|
2017-11-30 21:56:55 +00:00
|
|
|
// Convert enum to markdown compatible strings
|
2018-02-08 02:29:34 +00:00
|
|
|
def.language = [&indexed]() {
|
2017-12-01 17:50:39 +00:00
|
|
|
switch (indexed.language) {
|
2017-11-30 21:56:55 +00:00
|
|
|
case LanguageId::C:
|
|
|
|
return "c";
|
|
|
|
case LanguageId::Cpp:
|
|
|
|
return "cpp";
|
|
|
|
case LanguageId::ObjC:
|
2018-02-15 04:58:42 +00:00
|
|
|
return "objective-c";
|
|
|
|
case LanguageId::ObjCpp:
|
|
|
|
return "objective-cpp";
|
2017-11-30 21:56:55 +00:00
|
|
|
default:
|
|
|
|
return "";
|
2017-12-01 17:50:39 +00:00
|
|
|
}
|
|
|
|
}();
|
2017-11-30 21:56:55 +00:00
|
|
|
|
2018-04-30 04:49:03 +00:00
|
|
|
auto add_all_symbols = [&](Use use, Usr usr, SymbolKind kind) {
|
|
|
|
def.all_symbols.push_back(SymbolRef(use.range, usr, kind, use.role));
|
2018-02-11 04:01:10 +00:00
|
|
|
};
|
2018-04-30 04:49:03 +00:00
|
|
|
auto add_outline = [&](Use use, Usr usr, SymbolKind kind) {
|
|
|
|
def.outline.push_back(SymbolRef(use.range, usr, kind, use.role));
|
2017-04-03 02:21:21 +00:00
|
|
|
};
|
2017-02-27 07:23:43 +00:00
|
|
|
|
2018-04-30 04:49:03 +00:00
|
|
|
for (auto& it : indexed.usr2type) {
|
|
|
|
const IndexType& type = it.second;
|
2018-02-11 04:01:10 +00:00
|
|
|
if (type.def.spell)
|
2018-04-30 04:49:03 +00:00
|
|
|
add_all_symbols(*type.def.spell, type.usr, SymbolKind::Type);
|
2018-02-11 04:01:10 +00:00
|
|
|
if (type.def.extent)
|
2018-04-30 04:49:03 +00:00
|
|
|
add_outline(*type.def.extent, type.usr, SymbolKind::Type);
|
2018-02-20 21:56:56 +00:00
|
|
|
for (Use decl : type.declarations) {
|
2018-04-30 04:49:03 +00:00
|
|
|
add_all_symbols(decl, type.usr, SymbolKind::Type);
|
2018-02-25 01:24:20 +00:00
|
|
|
// Constructor positions have references to the class,
|
|
|
|
// which we do not want to show in textDocument/documentSymbol
|
|
|
|
if (!(decl.role & Role::Reference))
|
2018-04-30 04:49:03 +00:00
|
|
|
add_outline(decl, type.usr, SymbolKind::Type);
|
2018-02-20 21:56:56 +00:00
|
|
|
}
|
2018-02-11 04:01:10 +00:00
|
|
|
for (Use use : type.uses)
|
2018-04-30 04:49:03 +00:00
|
|
|
add_all_symbols(use, type.usr, SymbolKind::Type);
|
2017-02-27 07:23:43 +00:00
|
|
|
}
|
2018-04-30 04:49:03 +00:00
|
|
|
for (auto& it: indexed.usr2func) {
|
|
|
|
const IndexFunc& func = it.second;
|
2018-02-11 21:56:34 +00:00
|
|
|
if (func.def.spell)
|
2018-04-30 04:49:03 +00:00
|
|
|
add_all_symbols(*func.def.spell, func.usr, SymbolKind::Func);
|
2018-02-11 21:56:34 +00:00
|
|
|
if (func.def.extent)
|
2018-04-30 04:49:03 +00:00
|
|
|
add_outline(*func.def.extent, func.usr, SymbolKind::Func);
|
|
|
|
for (Use use : func.declarations) {
|
|
|
|
add_all_symbols(use, func.usr, SymbolKind::Func);
|
|
|
|
add_outline(use, func.usr, SymbolKind::Func);
|
2017-04-03 02:21:21 +00:00
|
|
|
}
|
2018-02-12 04:22:47 +00:00
|
|
|
for (Use use : func.uses) {
|
2018-01-03 00:30:03 +00:00
|
|
|
// Make ranges of implicit function calls larger (spanning one more column
|
|
|
|
// to the left/right). This is hacky but useful. e.g.
|
2018-01-11 02:43:01 +00:00
|
|
|
// textDocument/definition on the space/semicolon in `A a;` or `return
|
|
|
|
// 42;` will take you to the constructor.
|
2018-02-12 04:22:47 +00:00
|
|
|
if (use.role & Role::Implicit) {
|
|
|
|
if (use.range.start.column > 0)
|
|
|
|
use.range.start.column--;
|
|
|
|
use.range.end.column++;
|
2018-01-03 00:30:03 +00:00
|
|
|
}
|
2018-04-30 04:49:03 +00:00
|
|
|
add_all_symbols(use, func.usr, SymbolKind::Func);
|
2017-05-23 06:57:46 +00:00
|
|
|
}
|
2017-02-27 07:23:43 +00:00
|
|
|
}
|
2018-04-30 04:49:03 +00:00
|
|
|
for (auto& it : indexed.usr2var) {
|
|
|
|
const IndexVar& var = it.second;
|
2018-02-22 07:23:39 +00:00
|
|
|
if (var.def.spell)
|
2018-04-30 04:49:03 +00:00
|
|
|
add_all_symbols(*var.def.spell, var.usr, SymbolKind::Var);
|
2018-02-22 07:23:39 +00:00
|
|
|
if (var.def.extent)
|
2018-04-30 04:49:03 +00:00
|
|
|
add_outline(*var.def.extent, var.usr, SymbolKind::Var);
|
2018-02-12 04:22:47 +00:00
|
|
|
for (Use decl : var.declarations) {
|
2018-04-30 04:49:03 +00:00
|
|
|
add_all_symbols(decl, var.usr, SymbolKind::Var);
|
|
|
|
add_outline(decl, var.usr, SymbolKind::Var);
|
2018-01-27 05:50:17 +00:00
|
|
|
}
|
2018-02-11 04:01:10 +00:00
|
|
|
for (Use use : var.uses)
|
2018-04-30 04:49:03 +00:00
|
|
|
add_all_symbols(use, var.usr, SymbolKind::Var);
|
2017-02-27 07:23:43 +00:00
|
|
|
}
|
|
|
|
|
2017-09-22 01:14:57 +00:00
|
|
|
std::sort(def.outline.begin(), def.outline.end(),
|
|
|
|
[](const SymbolRef& a, const SymbolRef& b) {
|
2018-02-09 17:42:10 +00:00
|
|
|
return a.range.start < b.range.start;
|
2017-09-22 01:14:57 +00:00
|
|
|
});
|
|
|
|
std::sort(def.all_symbols.begin(), def.all_symbols.end(),
|
|
|
|
[](const SymbolRef& a, const SymbolRef& b) {
|
2018-02-09 17:42:10 +00:00
|
|
|
return a.range.start < b.range.start;
|
2017-09-22 01:14:57 +00:00
|
|
|
});
|
2017-04-07 05:42:57 +00:00
|
|
|
|
2018-01-30 05:34:28 +00:00
|
|
|
return QueryFile::DefUpdate(def, indexed.file_contents);
|
2017-02-27 07:23:43 +00:00
|
|
|
}
|
|
|
|
|
2018-02-18 08:02:19 +00:00
|
|
|
// Returns true if an element with the same file is found.
|
|
|
|
template <typename Q>
|
|
|
|
bool TryReplaceDef(std::forward_list<Q>& def_list, Q&& def) {
|
|
|
|
for (auto& def1 : def_list)
|
2018-04-30 04:49:03 +00:00
|
|
|
if (def1.file_id == def.file_id) {
|
|
|
|
if (!def1.spell || def.spell) {
|
2018-02-18 08:02:19 +00:00
|
|
|
def1 = std::move(def);
|
2018-04-30 04:49:03 +00:00
|
|
|
}
|
2018-02-18 08:02:19 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-01-20 18:32:39 +00:00
|
|
|
} // namespace
|
|
|
|
|
2017-04-19 06:56:37 +00:00
|
|
|
// ----------------------
|
|
|
|
// INDEX THREAD FUNCTIONS
|
|
|
|
// ----------------------
|
|
|
|
|
2017-04-07 05:42:57 +00:00
|
|
|
// static
|
2018-04-30 04:49:03 +00:00
|
|
|
IndexUpdate IndexUpdate::CreateDelta(IndexFile* previous,
|
2017-09-22 01:14:57 +00:00
|
|
|
IndexFile* current) {
|
2018-04-30 04:49:03 +00:00
|
|
|
IndexUpdate r;
|
|
|
|
static IndexFile empty(current->path, "<empty>");
|
|
|
|
if (!previous)
|
|
|
|
previous = ∅
|
2017-09-22 01:14:57 +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.
|
2018-04-30 04:49:03 +00:00
|
|
|
#define PROCESS_DIFF(type_id, query_name, index_name, type) \
|
|
|
|
{ \
|
|
|
|
/* Check for changes. */ \
|
|
|
|
std::vector<type> removed, added; \
|
|
|
|
bool did_add = ComputeDifferenceForUpdate( \
|
|
|
|
prev.index_name, curr.index_name, &removed, &added); \
|
|
|
|
if (did_add) { \
|
|
|
|
r.query_name.push_back(MergeableUpdate<type>( \
|
|
|
|
curr.usr, std::move(removed), std::move(added))); \
|
|
|
|
} \
|
2017-04-08 07:52:57 +00:00
|
|
|
}
|
2017-02-27 07:23:43 +00:00
|
|
|
// File
|
2018-04-30 04:49:03 +00:00
|
|
|
r.files_def_update = BuildFileDefUpdate(*current);
|
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.
|
|
|
|
|
2018-04-30 04:49:03 +00:00
|
|
|
// Functions
|
|
|
|
CompareGroups<IndexFunc>(
|
|
|
|
previous->usr2func, current->usr2func,
|
2017-09-22 01:14:57 +00:00
|
|
|
/*onRemoved:*/
|
2018-04-30 04:49:03 +00:00
|
|
|
[&r](IndexFunc& func) {
|
|
|
|
if (func.def.spell)
|
|
|
|
r.funcs_removed.push_back(func.usr);
|
|
|
|
if (func.declarations.size())
|
|
|
|
r.funcs_declarations.push_back(UseUpdate{func.usr, func.declarations, {}});
|
|
|
|
if (func.uses.size())
|
|
|
|
r.funcs_uses.push_back(UseUpdate{func.usr, func.uses, {}});
|
|
|
|
if (func.derived.size())
|
|
|
|
r.funcs_derived.push_back(UsrUpdate{func.usr, func.derived, {}});
|
2017-09-22 01:14:57 +00:00
|
|
|
},
|
|
|
|
/*onAdded:*/
|
2018-04-30 04:49:03 +00:00
|
|
|
[&r](IndexFunc& func) {
|
|
|
|
if (func.def.detailed_name.size())
|
|
|
|
r.funcs_def_update.emplace_back(func.usr, func.def);
|
|
|
|
if (func.declarations.size())
|
|
|
|
r.funcs_declarations.push_back(UseUpdate{func.usr, {}, func.declarations});
|
|
|
|
if (func.uses.size())
|
|
|
|
r.funcs_uses.push_back(UseUpdate{func.usr, {}, func.uses});
|
|
|
|
if (func.derived.size())
|
|
|
|
r.funcs_derived.push_back(UsrUpdate{func.usr, {}, func.derived});
|
2017-09-22 01:14:57 +00:00
|
|
|
},
|
|
|
|
/*onFound:*/
|
2018-04-30 04:49:03 +00:00
|
|
|
[&r](IndexFunc& prev, IndexFunc& curr) {
|
|
|
|
if (curr.def.detailed_name.size() && !(prev.def == curr.def))
|
|
|
|
r.funcs_def_update.emplace_back(curr.usr, curr.def);
|
|
|
|
|
|
|
|
PROCESS_DIFF(QueryFuncId, funcs_declarations, declarations, Use);
|
|
|
|
PROCESS_DIFF(QueryFuncId, funcs_uses, uses, Use);
|
|
|
|
PROCESS_DIFF(QueryFuncId, funcs_derived, derived, Usr);
|
2017-09-22 01:14:57 +00:00
|
|
|
});
|
2017-02-25 23:59:09 +00:00
|
|
|
|
2018-04-30 04:49:03 +00:00
|
|
|
// Types
|
|
|
|
CompareGroups<IndexType>(
|
|
|
|
previous->usr2type, current->usr2type,
|
2017-09-22 01:14:57 +00:00
|
|
|
/*onRemoved:*/
|
2018-04-30 04:49:03 +00:00
|
|
|
[&r](IndexType& type) {
|
|
|
|
if (type.def.spell)
|
|
|
|
r.types_removed.push_back(type.usr);
|
|
|
|
if (type.declarations.size())
|
|
|
|
r.types_declarations.push_back(UseUpdate{type.usr, type.declarations, {}});
|
|
|
|
if (type.uses.size())
|
|
|
|
r.types_uses.push_back(UseUpdate{type.usr, type.uses, {}});
|
|
|
|
if (type.derived.size())
|
|
|
|
r.types_derived.push_back(UsrUpdate{type.usr, type.derived, {}});
|
|
|
|
if (type.instances.size())
|
|
|
|
r.types_instances.push_back(UsrUpdate{type.usr, type.instances, {}});
|
2017-09-22 01:14:57 +00:00
|
|
|
},
|
|
|
|
/*onAdded:*/
|
2018-04-30 04:49:03 +00:00
|
|
|
[&r](IndexType& type) {
|
|
|
|
if (type.def.detailed_name.size())
|
|
|
|
r.types_def_update.emplace_back(type.usr, type.def);
|
|
|
|
if (type.declarations.size())
|
|
|
|
r.types_declarations.push_back(UseUpdate{type.usr, {}, type.declarations});
|
|
|
|
if (type.uses.size())
|
|
|
|
r.types_uses.push_back(UseUpdate{type.usr, {}, type.uses});
|
|
|
|
if (type.derived.size())
|
|
|
|
r.types_derived.push_back(UsrUpdate{type.usr, {}, type.derived});
|
|
|
|
if (type.instances.size())
|
|
|
|
r.types_instances.push_back(UsrUpdate{type.usr, {}, type.instances});
|
2017-09-22 01:14:57 +00:00
|
|
|
},
|
|
|
|
/*onFound:*/
|
2018-04-30 04:49:03 +00:00
|
|
|
[&r](IndexType& prev, IndexType& curr) {
|
|
|
|
if (curr.def.detailed_name.size() && !(prev.def == curr.def))
|
|
|
|
r.types_def_update.emplace_back(curr.usr, curr.def);
|
|
|
|
|
|
|
|
PROCESS_DIFF(QueryTypeId, types_declarations, declarations, Use);
|
|
|
|
PROCESS_DIFF(QueryTypeId, types_uses, uses, Use);
|
|
|
|
PROCESS_DIFF(QueryTypeId, types_derived, derived, Usr);
|
|
|
|
PROCESS_DIFF(QueryTypeId, types_instances, instances, Usr);
|
2017-09-22 01:14:57 +00:00
|
|
|
});
|
2017-02-25 23:59:09 +00:00
|
|
|
|
|
|
|
// Variables
|
2017-09-22 01:14:57 +00:00
|
|
|
CompareGroups<IndexVar>(
|
2018-04-30 04:49:03 +00:00
|
|
|
previous->usr2var, current->usr2var,
|
2017-09-22 01:14:57 +00:00
|
|
|
/*onRemoved:*/
|
2018-04-30 04:49:03 +00:00
|
|
|
[&r](IndexVar& var) {
|
|
|
|
if (var.def.spell)
|
|
|
|
r.vars_removed.push_back(var.usr);
|
|
|
|
if (!var.declarations.empty())
|
|
|
|
r.vars_declarations.push_back(UseUpdate{var.usr, var.declarations, {}});
|
|
|
|
if (!var.uses.empty())
|
|
|
|
r.vars_uses.push_back(UseUpdate{var.usr, var.uses, {}});
|
2017-09-22 01:14:57 +00:00
|
|
|
},
|
|
|
|
/*onAdded:*/
|
2018-04-30 04:49:03 +00:00
|
|
|
[&r](IndexVar& var) {
|
|
|
|
if (var.def.detailed_name.size())
|
|
|
|
r.vars_def_update.emplace_back(var.usr, var.def);
|
|
|
|
if (var.declarations.size())
|
|
|
|
r.vars_declarations.push_back(UseUpdate{var.usr, {}, var.declarations});
|
|
|
|
if (var.uses.size())
|
|
|
|
r.vars_uses.push_back(UseUpdate{var.usr, {}, var.uses});
|
2017-09-22 01:14:57 +00:00
|
|
|
},
|
|
|
|
/*onFound:*/
|
2018-04-30 04:49:03 +00:00
|
|
|
[&r](IndexVar& prev, IndexVar& curr) {
|
|
|
|
if (curr.def.detailed_name.size() && !(prev.def == curr.def))
|
|
|
|
r.vars_def_update.emplace_back(curr.usr, curr.def);
|
2017-02-25 23:59:09 +00:00
|
|
|
|
2018-04-30 04:49:03 +00:00
|
|
|
PROCESS_DIFF(QueryVarId, vars_declarations, declarations, Use);
|
|
|
|
PROCESS_DIFF(QueryVarId, vars_uses, uses, Use);
|
|
|
|
});
|
2017-02-21 09:08:52 +00:00
|
|
|
|
2018-04-30 04:49:03 +00:00
|
|
|
return r;
|
|
|
|
#undef PROCESS_DIFF
|
2017-02-27 07:23:43 +00:00
|
|
|
}
|
2017-02-26 19:45:59 +00:00
|
|
|
|
2017-05-19 07:02:01 +00:00
|
|
|
std::string IndexUpdate::ToString() {
|
|
|
|
rapidjson::StringBuffer output;
|
2018-02-12 01:15:08 +00:00
|
|
|
rapidjson::Writer<rapidjson::StringBuffer> writer(output);
|
2018-01-06 21:46:41 +00:00
|
|
|
JsonWriter json_writer(&writer);
|
2017-05-19 07:02:01 +00:00
|
|
|
IndexUpdate& update = *this;
|
2018-01-06 21:46:41 +00:00
|
|
|
Reflect(json_writer, update);
|
2017-05-19 07:02:01 +00:00
|
|
|
return output.GetString();
|
|
|
|
}
|
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-09-22 01:14:57 +00:00
|
|
|
void QueryDatabase::RemoveUsrs(SymbolKind usr_kind,
|
|
|
|
const std::vector<Usr>& to_remove) {
|
2017-05-09 04:20:28 +00:00
|
|
|
switch (usr_kind) {
|
|
|
|
case SymbolKind::Type: {
|
2018-04-30 04:49:03 +00:00
|
|
|
for (Usr usr : to_remove) {
|
|
|
|
QueryType& type = usr2type[usr];
|
|
|
|
if (type.symbol_idx >= 0)
|
|
|
|
symbols[type.symbol_idx].kind = SymbolKind::Invalid;
|
2018-02-18 07:09:05 +00:00
|
|
|
type.def.clear();
|
2018-02-02 01:24:46 +00:00
|
|
|
}
|
2017-05-09 04:20:28 +00:00
|
|
|
break;
|
|
|
|
}
|
2018-02-18 08:02:19 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void QueryDatabase::RemoveUsrs(
|
2018-04-30 04:49:03 +00:00
|
|
|
SymbolKind kind,
|
|
|
|
int file_id,
|
|
|
|
const std::vector<Usr>& to_remove) {
|
|
|
|
switch (kind) {
|
2017-05-09 04:20:28 +00:00
|
|
|
case SymbolKind::Func: {
|
2018-04-30 04:49:03 +00:00
|
|
|
for (auto usr : to_remove) {
|
|
|
|
QueryFunc& func = Func(usr);
|
|
|
|
func.def.remove_if([=](const QueryFunc::Def& def) {
|
|
|
|
return def.file_id == file_id;
|
2018-02-18 08:02:19 +00:00
|
|
|
});
|
2018-02-02 01:24:46 +00:00
|
|
|
}
|
2017-05-09 04:20:28 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SymbolKind::Var: {
|
2018-04-30 04:49:03 +00:00
|
|
|
for (auto usr : to_remove) {
|
|
|
|
QueryVar& var = Var(usr);
|
|
|
|
var.def.remove_if([=](const QueryVar::Def& def) {
|
|
|
|
return def.file_id == file_id;
|
2018-02-18 08:02:19 +00:00
|
|
|
});
|
2018-02-02 01:24:46 +00:00
|
|
|
}
|
2017-05-09 04:20:28 +00:00
|
|
|
break;
|
2017-04-22 07:32:29 +00:00
|
|
|
}
|
2018-02-18 08:02:19 +00:00
|
|
|
default:
|
|
|
|
assert(false);
|
2017-05-21 23:48:21 +00:00
|
|
|
break;
|
2017-04-22 07:32:29 +00:00
|
|
|
}
|
2017-04-19 06:56:37 +00:00
|
|
|
}
|
|
|
|
|
2018-04-30 04:49:03 +00:00
|
|
|
void QueryDatabase::ApplyIndexUpdate(IndexUpdate* u) {
|
2017-09-22 01:14:57 +00:00
|
|
|
// This function runs on the querydb thread.
|
|
|
|
|
|
|
|
// Example types:
|
2018-03-31 03:16:33 +00:00
|
|
|
// storage_name => std::vector<std::optional<QueryType>>
|
2017-09-22 01:14:57 +00:00
|
|
|
// merge_update => QueryType::DerivedUpdate =>
|
|
|
|
// MergeableUpdate<QueryTypeId, QueryTypeId> def => QueryType
|
|
|
|
// def->def_var_name => std::vector<QueryTypeId>
|
2017-10-17 05:44:58 +00:00
|
|
|
#define HANDLE_MERGEABLE(update_var_name, def_var_name, storage_name) \
|
2018-04-30 04:49:03 +00:00
|
|
|
for (auto merge_update : u->update_var_name) { \
|
|
|
|
auto& entity = storage_name[merge_update.usr]; \
|
|
|
|
AssignFileId(u->file_id, merge_update.to_add); \
|
|
|
|
AddRange(u->file_id, entity.def_var_name, merge_update.to_add); \
|
|
|
|
RemoveRange(entity.def_var_name, merge_update.to_remove); \
|
2017-04-19 06:56:37 +00:00
|
|
|
}
|
|
|
|
|
2018-04-30 04:49:03 +00:00
|
|
|
if (u->files_removed)
|
|
|
|
files[name2file_id[LowerPathIfInsensitive(*u->files_removed)]].def =
|
|
|
|
std::nullopt;
|
|
|
|
u->file_id = u->files_def_update ? Update(std::move(*u->files_def_update)) : -1;
|
|
|
|
|
|
|
|
RemoveUsrs(SymbolKind::Func, u->file_id, u->funcs_removed);
|
|
|
|
Update(u->file_id, std::move(u->funcs_def_update));
|
|
|
|
HANDLE_MERGEABLE(funcs_declarations, declarations, usr2func);
|
|
|
|
HANDLE_MERGEABLE(funcs_derived, derived, usr2func);
|
|
|
|
HANDLE_MERGEABLE(funcs_uses, uses, usr2func);
|
|
|
|
|
|
|
|
RemoveUsrs(SymbolKind::Type, u->types_removed);
|
|
|
|
Update(u->file_id, std::move(u->types_def_update));
|
|
|
|
HANDLE_MERGEABLE(types_declarations, declarations, usr2type);
|
|
|
|
HANDLE_MERGEABLE(types_derived, derived, usr2type);
|
|
|
|
HANDLE_MERGEABLE(types_instances, instances, usr2type);
|
|
|
|
HANDLE_MERGEABLE(types_uses, uses, usr2type);
|
|
|
|
|
|
|
|
RemoveUsrs(SymbolKind::Var, u->file_id, u->vars_removed);
|
|
|
|
Update(u->file_id, std::move(u->vars_def_update));
|
|
|
|
HANDLE_MERGEABLE(vars_declarations, declarations, usr2var);
|
|
|
|
HANDLE_MERGEABLE(vars_uses, uses, usr2var);
|
2017-04-19 06:56:37 +00:00
|
|
|
|
|
|
|
#undef HANDLE_MERGEABLE
|
2017-02-27 07:23:43 +00:00
|
|
|
}
|
2017-02-26 19:45:59 +00:00
|
|
|
|
2018-04-30 04:49:03 +00:00
|
|
|
int QueryDatabase::Update(QueryFile::DefUpdate&& u) {
|
|
|
|
int id = files.size();
|
|
|
|
auto it = name2file_id.try_emplace(LowerPathIfInsensitive(u.value.path), id);
|
|
|
|
if (it.second)
|
|
|
|
files.emplace_back().id = id;
|
|
|
|
QueryFile& existing = files[it.first->second];
|
|
|
|
existing.def = u.value;
|
|
|
|
UpdateSymbols(&existing.symbol_idx, SymbolKind::File, it.first->second);
|
|
|
|
return existing.id;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QueryDatabase::Update(int file_id, std::vector<QueryFunc::DefUpdate>&& updates) {
|
|
|
|
for (auto& u : updates) {
|
|
|
|
auto& def = u.value;
|
|
|
|
assert(!def.detailed_name.empty());
|
|
|
|
AssignFileId(file_id, def.spell);
|
|
|
|
AssignFileId(file_id, def.extent);
|
|
|
|
AssignFileId(file_id, def.callees);
|
|
|
|
QueryFunc& existing = Func(u.usr);
|
|
|
|
if (!TryReplaceDef(existing.def, std::move(def))) {
|
|
|
|
existing.def.push_front(std::move(def));
|
|
|
|
UpdateSymbols(&existing.symbol_idx, SymbolKind::Type, existing.usr);
|
2018-02-04 02:34:07 +00:00
|
|
|
}
|
2017-02-26 19:45:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-30 04:49:03 +00:00
|
|
|
void QueryDatabase::Update(int file_id, std::vector<QueryType::DefUpdate>&& updates) {
|
|
|
|
for (auto& u : updates) {
|
|
|
|
auto& def = u.value;
|
|
|
|
assert(!def.detailed_name.empty());
|
|
|
|
AssignFileId(file_id, def.spell);
|
|
|
|
AssignFileId(file_id, def.extent);
|
|
|
|
QueryType& existing = Type(u.usr);
|
|
|
|
if (!TryReplaceDef(existing.def, std::move(def))) {
|
|
|
|
existing.def.push_front(std::move(def));
|
|
|
|
UpdateSymbols(&existing.symbol_idx, SymbolKind::Type, existing.usr);
|
2018-02-04 02:34:07 +00:00
|
|
|
}
|
2017-02-26 19:45:59 +00:00
|
|
|
}
|
|
|
|
}
|
2017-02-26 08:11:47 +00:00
|
|
|
|
2018-04-30 04:49:03 +00:00
|
|
|
void QueryDatabase::Update(int file_id, std::vector<QueryVar::DefUpdate>&& updates) {
|
|
|
|
for (auto& u : updates) {
|
|
|
|
auto& def = u.value;
|
|
|
|
assert(!def.detailed_name.empty());
|
|
|
|
AssignFileId(file_id, def.spell);
|
|
|
|
AssignFileId(file_id, def.extent);
|
|
|
|
QueryVar& existing = Var(u.usr);
|
|
|
|
if (!TryReplaceDef(existing.def, std::move(def))) {
|
|
|
|
existing.def.push_front(std::move(def));
|
2018-02-18 05:52:14 +00:00
|
|
|
if (!existing.def.front().is_local())
|
2018-04-30 04:49:03 +00:00
|
|
|
UpdateSymbols(&existing.symbol_idx, SymbolKind::Var, existing.usr);
|
2018-02-04 02:34:07 +00:00
|
|
|
}
|
2017-02-26 08:11:47 +00:00
|
|
|
}
|
2017-02-26 19:45:59 +00:00
|
|
|
}
|
2017-02-26 08:11:47 +00:00
|
|
|
|
2018-04-30 04:49:03 +00:00
|
|
|
void QueryDatabase::UpdateSymbols(int* symbol_idx,
|
2018-02-02 01:59:01 +00:00
|
|
|
SymbolKind kind,
|
2018-04-30 04:49:03 +00:00
|
|
|
Usr usr) {
|
|
|
|
if (*symbol_idx < 0) {
|
|
|
|
*symbol_idx = symbols.size();
|
|
|
|
symbols.push_back(SymbolIdx{usr, kind});
|
2017-02-26 08:11:47 +00:00
|
|
|
}
|
2017-05-09 01:21:21 +00:00
|
|
|
}
|
2017-05-12 06:08:15 +00:00
|
|
|
|
2018-04-30 04:49:03 +00:00
|
|
|
std::string_view QueryDatabase::GetSymbolName(int symbol_idx,
|
|
|
|
bool qualified) {
|
|
|
|
Usr usr = symbols[symbol_idx].usr;
|
2018-02-02 01:59:01 +00:00
|
|
|
switch (symbols[symbol_idx].kind) {
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
case SymbolKind::File:
|
2018-04-30 04:49:03 +00:00
|
|
|
if (files[usr].def)
|
|
|
|
return files[usr].def->path;
|
2018-02-02 01:59:01 +00:00
|
|
|
break;
|
|
|
|
case SymbolKind::Func:
|
2018-04-30 04:49:03 +00:00
|
|
|
if (const auto* def = Func(usr).AnyDef())
|
2018-04-06 00:00:07 +00:00
|
|
|
return def->Name(qualified);
|
2018-02-02 01:59:01 +00:00
|
|
|
break;
|
|
|
|
case SymbolKind::Type:
|
2018-04-30 04:49:03 +00:00
|
|
|
if (const auto* def = Type(usr).AnyDef())
|
2018-04-06 00:00:07 +00:00
|
|
|
return def->Name(qualified);
|
2018-02-02 01:59:01 +00:00
|
|
|
break;
|
|
|
|
case SymbolKind::Var:
|
2018-04-30 04:49:03 +00:00
|
|
|
if (const auto* def = Var(usr).AnyDef())
|
2018-04-06 00:00:07 +00:00
|
|
|
return def->Name(qualified);
|
2018-02-02 01:59:01 +00:00
|
|
|
break;
|
2018-02-02 01:24:46 +00:00
|
|
|
}
|
2018-02-02 01:59:01 +00:00
|
|
|
return "";
|
2018-02-02 01:24:46 +00:00
|
|
|
}
|