ccls/src/query_utils.cc

555 lines
16 KiB
C++
Raw Normal View History

2017-06-15 05:32:23 +00:00
#include "query_utils.h"
#include "queue_manager.h"
#include <loguru.hpp>
2017-06-16 17:14:09 +00:00
#include <climits>
2018-02-07 05:26:38 +00:00
#include <queue>
2017-06-16 17:14:09 +00:00
namespace {
// Computes roughly how long |range| is.
int ComputeRangeSize(const Range& range) {
if (range.start.line != range.end.line)
return INT_MAX;
return range.end.column - range.start.column;
}
template <typename Q>
std::vector<Use> ToUsesHelper(std::vector<Q>& entities,
const std::vector<Id<Q>>& ids) {
std::vector<Use> ret;
ret.reserve(ids.size());
for (auto id : ids) {
Q& entity = entities[id.id];
bool has_def = false;
for (auto& def : entity.def)
if (def.spell) {
ret.push_back(*def.spell);
has_def = true;
break;
}
if (!has_def && entity.declarations.size())
ret.push_back(entity.declarations[0]);
}
return ret;
}
2018-02-09 05:11:35 +00:00
} // namespace
2018-02-11 04:30:27 +00:00
Maybe<Use> GetDefinitionSpellingOfSymbol(QueryDatabase* db,
SymbolIdx sym) {
2018-02-09 17:42:10 +00:00
switch (sym.kind) {
case SymbolKind::File:
2017-09-22 01:14:57 +00:00
break;
case SymbolKind::Func: {
for (auto& def : db->GetFunc(sym).def)
if (def.spell)
return def.spell;
break;
}
case SymbolKind::Type: {
for (auto& def : db->GetType(sym).def)
if (def.spell)
return def.spell;
2017-09-22 01:14:57 +00:00
break;
}
case SymbolKind::Var: {
for (auto& def : db->GetVar(sym).def)
if (def.spell)
return def.spell;
2017-09-22 01:14:57 +00:00
break;
}
2018-02-13 20:15:31 +00:00
case SymbolKind::Invalid:
2017-09-22 01:14:57 +00:00
assert(false && "unexpected");
break;
2017-06-15 05:32:23 +00:00
}
return nullopt;
}
Maybe<Use> GetDefinitionExtentOfSymbol(QueryDatabase* db, SymbolIdx sym) {
2018-02-09 17:42:10 +00:00
switch (sym.kind) {
case SymbolKind::File:
return Use(Range(Position(0, 0), Position(0, 0)), sym.id, sym.kind,
Role::None, QueryFileId(sym.id));
case SymbolKind::Func: {
for (auto& def : db->GetFunc(sym).def)
if (def.extent)
return def.extent;
2017-09-22 01:14:57 +00:00
break;
}
case SymbolKind::Type: {
for (auto& def : db->GetType(sym).def)
if (def.extent)
return def.extent;
2017-09-22 01:14:57 +00:00
break;
}
case SymbolKind::Var: {
for (auto& def : db->GetVar(sym).def)
if (def.extent)
return def.extent;
2017-09-22 01:14:57 +00:00
break;
}
case SymbolKind::Invalid: {
assert(false && "unexpected");
break;
}
2017-06-15 05:32:23 +00:00
}
return nullopt;
}
Maybe<QueryFileId> GetDeclarationFileForSymbol(QueryDatabase* db,
SymbolIdx sym) {
2018-02-09 17:42:10 +00:00
switch (sym.kind) {
case SymbolKind::File:
return QueryFileId(sym.id);
2017-09-22 01:14:57 +00:00
case SymbolKind::Func: {
2018-02-10 03:07:45 +00:00
QueryFunc& func = db->GetFunc(sym);
if (!func.declarations.empty())
return func.declarations[0].file;
if (const auto* def = func.AnyDef())
return def->file;
break;
}
case SymbolKind::Type: {
if (const auto* def = db->GetType(sym).AnyDef())
return def->file;
2017-09-22 01:14:57 +00:00
break;
}
case SymbolKind::Var: {
if (const auto* def = db->GetVar(sym).AnyDef())
return def->file;
2017-09-22 01:14:57 +00:00
break;
}
case SymbolKind::Invalid: {
assert(false && "unexpected");
break;
}
2017-06-15 05:32:23 +00:00
}
return nullopt;
}
std::vector<Use> ToUses(QueryDatabase* db,
const std::vector<QueryFuncId>& ids) {
return ToUsesHelper(db->funcs, ids);
2018-02-10 06:51:58 +00:00
}
std::vector<Use> ToUses(QueryDatabase* db,
const std::vector<QueryTypeId>& ids) {
return ToUsesHelper(db->types, ids);
2018-02-10 06:51:58 +00:00
}
std::vector<Use> ToUses(QueryDatabase* db, const std::vector<QueryVarId>& ids) {
return ToUsesHelper(db->vars, ids);
2018-02-10 06:51:58 +00:00
}
std::vector<Use> GetUsesOfSymbol(QueryDatabase* db,
SymbolIdx sym,
bool include_decl) {
2018-02-09 17:42:10 +00:00
switch (sym.kind) {
2017-09-22 01:14:57 +00:00
case SymbolKind::Type: {
QueryType& type = db->GetType(sym);
std::vector<Use> ret = type.uses;
if (include_decl) {
for (auto& def : type.def)
if (def.spell)
ret.push_back(*def.spell);
AddRange(&ret, type.declarations);
}
2018-01-29 07:05:51 +00:00
return ret;
2017-09-22 01:14:57 +00:00
}
case SymbolKind::Func: {
QueryFunc& func = db->GetFunc(sym);
std::vector<Use> ret = func.uses;
if (include_decl) {
for (auto& def : func.def)
if (def.spell)
ret.push_back(*def.spell);
AddRange(&ret, func.declarations);
}
2018-02-09 05:11:35 +00:00
return ret;
2017-09-22 01:14:57 +00:00
}
case SymbolKind::Var: {
QueryVar& var = db->GetVar(sym);
std::vector<Use> ret = var.uses;
if (include_decl) {
for (auto& def : var.def)
if (def.spell)
ret.push_back(*def.spell);
2018-01-30 00:27:43 +00:00
ret.insert(ret.end(), var.declarations.begin(), var.declarations.end());
}
return ret;
2017-09-22 01:14:57 +00:00
}
case SymbolKind::File:
case SymbolKind::Invalid: {
assert(false && "unexpected");
2018-02-09 05:11:35 +00:00
return {};
2017-09-22 01:14:57 +00:00
}
2017-06-15 05:32:23 +00:00
}
}
std::vector<Use> GetDeclarationsOfSymbolForGotoDefinition(
2017-09-22 01:14:57 +00:00
QueryDatabase* db,
SymbolIdx sym) {
2018-02-09 17:42:10 +00:00
switch (sym.kind) {
2018-02-09 05:11:35 +00:00
case SymbolKind::Func:
return db->GetFunc(sym).declarations;
case SymbolKind::Type:
return db->GetType(sym).declarations;
2018-02-09 05:11:35 +00:00
case SymbolKind::Var:
return db->GetVar(sym).declarations;
2017-09-22 01:14:57 +00:00
default:
return {};
2017-06-15 05:32:23 +00:00
}
}
bool HasCallersOnSelfOrBaseOrDerived(QueryDatabase* db, QueryFunc& root) {
// Check self.
if (!root.uses.empty())
return true;
const QueryFunc::Def* def = root.AnyDef();
// Check for base calls.
2018-02-04 00:20:14 +00:00
std::queue<QueryFunc*> queue;
EachWithGen<QueryFunc>(db->funcs, def->base, [&](QueryFunc& func) {
2018-02-04 00:20:14 +00:00
queue.push(&func);
});
2017-12-19 06:15:46 +00:00
while (!queue.empty()) {
2018-02-04 00:20:14 +00:00
QueryFunc& func = *queue.front();
2017-12-19 06:15:46 +00:00
queue.pop();
if (!func.uses.empty())
return true;
if (def)
EachWithGen<QueryFunc>(db->funcs, def->base, [&](QueryFunc& func1) {
2018-02-04 00:20:14 +00:00
queue.push(&func1);
});
}
// Check for derived calls.
2018-02-04 00:20:14 +00:00
EachWithGen<QueryFunc>(db->funcs, root.derived, [&](QueryFunc& func1) {
queue.push(&func1);
});
while (!queue.empty()) {
2018-02-04 00:20:14 +00:00
QueryFunc& func = *queue.front();
queue.pop();
if (!func.uses.empty())
return true;
2018-02-04 00:20:14 +00:00
EachWithGen<QueryFunc>(db->funcs, func.derived, [&](QueryFunc& func1) {
queue.push(&func1);
});
}
return false;
}
std::vector<Use> GetCallersForAllBaseFunctions(QueryDatabase* db,
QueryFunc& root) {
std::vector<Use> callers;
const QueryFunc::Def* def = root.AnyDef();
if (!def)
2018-01-30 04:18:08 +00:00
return callers;
2017-06-15 05:32:23 +00:00
2018-02-04 00:20:14 +00:00
std::queue<QueryFunc*> queue;
EachWithGen<QueryFunc>(db->funcs, def->base, [&](QueryFunc& func1) {
2018-02-04 00:20:14 +00:00
queue.push(&func1);
});
2017-12-19 06:15:46 +00:00
while (!queue.empty()) {
2018-02-04 00:20:14 +00:00
QueryFunc& func = *queue.front();
2017-12-19 06:15:46 +00:00
queue.pop();
2017-06-15 05:32:23 +00:00
AddRange(&callers, func.uses);
if (const QueryFunc::Def* def1 = func.AnyDef()) {
EachWithGen<QueryFunc>(db->funcs, def1->base, [&](QueryFunc& func1) {
2018-02-04 00:20:14 +00:00
queue.push(&func1);
});
}
2017-06-15 05:32:23 +00:00
}
return callers;
}
std::vector<Use> GetCallersForAllDerivedFunctions(QueryDatabase* db,
QueryFunc& root) {
std::vector<Use> callers;
2017-06-15 05:32:23 +00:00
2018-02-04 00:20:14 +00:00
std::queue<QueryFunc*> queue;
EachWithGen<QueryFunc>(db->funcs, root.derived, [&](QueryFunc& func) {
queue.push(&func);
});
2017-06-15 05:32:23 +00:00
while (!queue.empty()) {
2018-02-04 00:20:14 +00:00
QueryFunc& func = *queue.front();
2017-06-15 05:32:23 +00:00
queue.pop();
2018-02-04 00:20:14 +00:00
EachWithGen<QueryFunc>(db->funcs, func.derived, [&](QueryFunc& func1) {
queue.push(&func1);
});
AddRange(&callers, func.uses);
2017-06-15 05:32:23 +00:00
}
return callers;
}
2017-09-22 01:14:57 +00:00
optional<lsPosition> GetLsPosition(WorkingFile* working_file,
const Position& position) {
2017-06-15 05:32:23 +00:00
if (!working_file)
return lsPosition(position.line, position.column);
2017-06-15 05:32:23 +00:00
int column = position.column;
2018-01-30 00:27:43 +00:00
optional<int> start =
working_file->GetBufferPosFromIndexPos(position.line, &column, false);
2017-06-15 05:32:23 +00:00
if (!start)
return nullopt;
return lsPosition(*start, column);
2017-06-15 05:32:23 +00:00
}
optional<lsRange> GetLsRange(WorkingFile* working_file, const Range& location) {
if (!working_file) {
2018-01-30 00:27:43 +00:00
return lsRange(lsPosition(location.start.line, location.start.column),
lsPosition(location.end.line, location.end.column));
2017-06-15 05:32:23 +00:00
}
int start_column = location.start.column, end_column = location.end.column;
2018-01-30 00:27:43 +00:00
optional<int> start = working_file->GetBufferPosFromIndexPos(
location.start.line, &start_column, false);
optional<int> end = working_file->GetBufferPosFromIndexPos(location.end.line,
&end_column, true);
2017-06-15 05:32:23 +00:00
if (!start || !end)
return nullopt;
// If remapping end fails (end can never be < start), just guess that the
// final location didn't move. This only screws up the highlighted code
// region if we guess wrong, so not a big deal.
//
// Remapping fails often in C++ since there are a lot of "};" at the end of
// class/struct definitions.
if (*end < *start)
*end = *start + (location.end.line - location.start.line);
2018-01-15 06:53:51 +00:00
if (*start == *end && start_column > end_column)
end_column = start_column;
2017-06-15 05:32:23 +00:00
return lsRange(lsPosition(*start, start_column),
lsPosition(*end, end_column));
2017-06-15 05:32:23 +00:00
}
2017-09-22 01:14:57 +00:00
lsDocumentUri GetLsDocumentUri(QueryDatabase* db,
QueryFileId file_id,
std::string* path) {
QueryFile& file = db->files[file_id.id];
if (file.def) {
*path = file.def->path;
2017-06-15 05:32:23 +00:00
return lsDocumentUri::FromPath(*path);
2017-09-22 01:14:57 +00:00
} else {
2017-06-15 05:32:23 +00:00
*path = "";
return lsDocumentUri::FromPath("");
}
}
lsDocumentUri GetLsDocumentUri(QueryDatabase* db, QueryFileId file_id) {
QueryFile& file = db->files[file_id.id];
if (file.def) {
return lsDocumentUri::FromPath(file.def->path);
2017-09-22 01:14:57 +00:00
} else {
2017-06-15 05:32:23 +00:00
return lsDocumentUri::FromPath("");
}
}
2017-09-22 01:14:57 +00:00
optional<lsLocation> GetLsLocation(QueryDatabase* db,
WorkingFiles* working_files,
Use use) {
2017-06-15 05:32:23 +00:00
std::string path;
lsDocumentUri uri = GetLsDocumentUri(db, use.file, &path);
2017-09-22 01:14:57 +00:00
optional<lsRange> range =
GetLsRange(working_files->GetFileByFilename(path), use.range);
2017-06-15 05:32:23 +00:00
if (!range)
return nullopt;
return lsLocation(uri, *range);
}
optional<lsLocationEx> GetLsLocationEx(QueryDatabase* db,
WorkingFiles* working_files,
Use use,
bool extension) {
optional<lsLocation> ls_loc = GetLsLocation(db, working_files, use);
if (!ls_loc)
return nullopt;
lsLocationEx ret;
ret.lsLocation::operator=(*ls_loc);
if (extension)
switch (use.kind) {
default:
break;
case SymbolKind::Func: {
const QueryFunc::Def* def = db->GetFunc(use).AnyDef();
if (def)
ret.containerName = std::string_view(def->detailed_name);
break;
}
case SymbolKind::Type: {
const QueryType::Def* def = db->GetType(use).AnyDef();
if (def)
ret.containerName = std::string_view(def->detailed_name);
break;
}
case SymbolKind::Var: {
const QueryVar::Def* def = db->GetVar(use).AnyDef();
if (def)
ret.containerName = std::string_view(def->detailed_name);
break;
}
}
return ret;
}
std::vector<lsLocation> GetLsLocations(QueryDatabase* db,
WorkingFiles* working_files,
const std::vector<Use>& uses,
int limit) {
std::vector<lsLocation> ret;
for (Use use : uses) {
2017-09-22 01:14:57 +00:00
optional<lsLocation> location =
GetLsLocation(db, working_files, use);
if (location)
ret.push_back(*location);
2017-06-15 05:32:23 +00:00
}
std::sort(ret.begin(), ret.end());
ret.erase(std::unique(ret.begin(), ret.end()), ret.end());
if (ret.size() > limit)
ret.resize(limit);
return ret;
2017-06-15 05:32:23 +00:00
}
// Returns a symbol. The symbol will have *NOT* have a location assigned.
2017-09-22 01:14:57 +00:00
optional<lsSymbolInformation> GetSymbolInfo(QueryDatabase* db,
WorkingFiles* working_files,
SymbolIdx sym,
bool use_short_name) {
2018-02-09 17:42:10 +00:00
switch (sym.kind) {
2017-09-22 01:14:57 +00:00
case SymbolKind::File: {
QueryFile& file = db->GetFile(sym);
if (!file.def)
2018-02-09 17:42:10 +00:00
break;
2017-06-15 05:32:23 +00:00
2017-09-22 01:14:57 +00:00
lsSymbolInformation info;
info.name = file.def->path;
2017-09-22 01:14:57 +00:00
info.kind = lsSymbolKind::File;
return info;
2017-06-15 05:32:23 +00:00
}
2017-09-22 01:14:57 +00:00
case SymbolKind::Type: {
const QueryType::Def* def = db->GetType(sym).AnyDef();
if (!def)
2018-02-09 17:42:10 +00:00
break;
2017-09-22 01:14:57 +00:00
lsSymbolInformation info;
if (use_short_name)
info.name = def->ShortName();
else
info.name = def->detailed_name;
info.kind = def->kind;
if (def->detailed_name.c_str() != def->ShortName())
info.containerName = def->detailed_name;
2017-09-22 01:14:57 +00:00
return info;
}
case SymbolKind::Func: {
const QueryFunc::Def* def = db->GetFunc(sym).AnyDef();
if (!def)
2018-02-09 17:42:10 +00:00
break;
2017-09-22 01:14:57 +00:00
lsSymbolInformation info;
if (use_short_name)
info.name = def->ShortName();
else
info.name = def->detailed_name;
info.kind = def->kind;
info.containerName = def->detailed_name;
2017-09-22 01:14:57 +00:00
return info;
}
case SymbolKind::Var: {
const QueryVar::Def* def = db->GetVar(sym).AnyDef();
if (!def)
2018-02-09 17:42:10 +00:00
break;
2017-09-22 01:14:57 +00:00
lsSymbolInformation info;
if (use_short_name)
info.name = def->ShortName();
else
info.name = def->detailed_name;
info.kind = def->kind;
info.containerName = def->detailed_name;
2017-09-22 01:14:57 +00:00
return info;
}
2018-02-09 17:42:10 +00:00
case SymbolKind::Invalid:
break;
}
2017-06-15 05:32:23 +00:00
return nullopt;
}
// TODO Sort only by range length, not |kind| or |idx|
2017-09-22 01:14:57 +00:00
std::vector<SymbolRef> FindSymbolsAtLocation(WorkingFile* working_file,
QueryFile* file,
lsPosition position) {
2017-06-15 05:32:23 +00:00
std::vector<SymbolRef> symbols;
symbols.reserve(1);
int target_line = position.line;
int target_column = position.character;
2017-06-15 05:32:23 +00:00
if (working_file) {
optional<int> index_line = working_file->GetIndexPosFromBufferPos(
target_line, &target_column, false);
2017-06-15 05:32:23 +00:00
if (index_line)
target_line = *index_line;
}
2018-02-09 17:42:10 +00:00
for (const SymbolRef& sym : file->def->all_symbols) {
if (sym.range.Contains(target_line, target_column))
symbols.push_back(sym);
2017-06-15 05:32:23 +00:00
}
// Order shorter ranges first, since they are more detailed/precise. This is
// important for macros which generate code so that we can resolving the
// macro argument takes priority over the entire macro body.
//
2017-12-23 16:01:43 +00:00
// Order SymbolKind::Var before SymbolKind::Type. Macro calls are treated as
// Var currently. If a macro expands to tokens led by a SymbolKind::Type, the
// macro and the Type have the same range. We want to find the macro
// definition instead of the Type definition.
//
// Then order functions before other types, which makes goto definition work
// better on constructors.
2018-01-30 00:27:43 +00:00
std::sort(symbols.begin(), symbols.end(),
[](const SymbolRef& a, const SymbolRef& b) {
int t = ComputeRangeSize(a.range) - ComputeRangeSize(b.range);
if (t)
return t < 0;
t = (a.role & Role::Definition) - (b.role & Role::Definition);
if (t)
return t > 0;
2018-01-30 00:27:43 +00:00
// operator> orders Var/Func before Type.
t = static_cast<int>(a.kind) - static_cast<int>(b.kind);
2018-01-30 00:27:43 +00:00
if (t)
return t > 0;
return a.id < b.id;
2018-01-30 00:27:43 +00:00
});
2017-06-15 05:32:23 +00:00
return symbols;
}
void EmitDiagnostics(WorkingFiles* working_files,
std::string path,
std::vector<lsDiagnostic> diagnostics) {
// Emit diagnostics.
Out_TextDocumentPublishDiagnostics out;
out.params.uri = lsDocumentUri::FromPath(path);
out.params.diagnostics = diagnostics;
QueueManager::WriteStdout(IpcId::TextDocumentPublishDiagnostics, out);
// Cache diagnostics so we can show fixits.
working_files->DoActionOnFile(path, [&](WorkingFile* working_file) {
if (working_file)
working_file->diagnostics_ = diagnostics;
});
}