ccls/src/query_utils.cc

597 lines
18 KiB
C++
Raw Normal View History

2017-06-15 05:32:23 +00:00
#include "query_utils.h"
2017-06-16 17:14:09 +00:00
#include <climits>
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;
}
} // namespace
2017-09-22 01:14:57 +00:00
optional<QueryLocation> GetDefinitionSpellingOfSymbol(QueryDatabase* db,
const QueryTypeId& id) {
QueryType& type = db->types[id.id];
if (type.def)
return type.def->definition_spelling;
2017-06-15 05:32:23 +00:00
return nullopt;
}
2017-09-22 01:14:57 +00:00
optional<QueryLocation> GetDefinitionSpellingOfSymbol(QueryDatabase* db,
const QueryFuncId& id) {
QueryFunc& func = db->funcs[id.id];
if (func.def)
return func.def->definition_spelling;
2017-06-15 05:32:23 +00:00
return nullopt;
}
2017-09-22 01:14:57 +00:00
optional<QueryLocation> GetDefinitionSpellingOfSymbol(QueryDatabase* db,
const QueryVarId& id) {
QueryVar& var = db->vars[id.id];
if (var.def)
return var.def->definition_spelling;
2017-06-15 05:32:23 +00:00
return nullopt;
}
2017-09-22 01:14:57 +00:00
optional<QueryLocation> GetDefinitionSpellingOfSymbol(QueryDatabase* db,
const SymbolIdx& symbol) {
2017-06-15 05:32:23 +00:00
switch (symbol.kind) {
2017-09-22 01:14:57 +00:00
case SymbolKind::Type: {
QueryType& type = db->types[symbol.idx];
if (type.def)
return type.def->definition_spelling;
2017-09-22 01:14:57 +00:00
break;
}
case SymbolKind::Func: {
QueryFunc& func = db->funcs[symbol.idx];
if (func.def)
return func.def->definition_spelling;
2017-09-22 01:14:57 +00:00
break;
}
case SymbolKind::Var: {
QueryVar& var = db->vars[symbol.idx];
if (var.def)
return var.def->definition_spelling;
2017-09-22 01:14:57 +00:00
break;
}
case SymbolKind::File:
case SymbolKind::Invalid: {
assert(false && "unexpected");
break;
}
2017-06-15 05:32:23 +00:00
}
return nullopt;
}
2017-09-22 01:14:57 +00:00
optional<QueryLocation> GetDefinitionExtentOfSymbol(QueryDatabase* db,
const SymbolIdx& symbol) {
2017-06-15 05:32:23 +00:00
switch (symbol.kind) {
2017-09-22 01:14:57 +00:00
case SymbolKind::Type: {
QueryType& type = db->types[symbol.idx];
if (type.def)
return type.def->definition_extent;
2017-09-22 01:14:57 +00:00
break;
}
case SymbolKind::Func: {
QueryFunc& func = db->funcs[symbol.idx];
if (func.def)
return func.def->definition_extent;
2017-09-22 01:14:57 +00:00
break;
}
case SymbolKind::Var: {
QueryVar& var = db->vars[symbol.idx];
if (var.def)
return var.def->definition_extent;
2017-09-22 01:14:57 +00:00
break;
}
case SymbolKind::File: {
return QueryLocation(QueryFileId(symbol.idx),
Range(Position(1, 1), Position(1, 1)));
}
case SymbolKind::Invalid: {
assert(false && "unexpected");
break;
}
2017-06-15 05:32:23 +00:00
}
return nullopt;
}
2017-09-22 01:14:57 +00:00
optional<QueryFileId> GetDeclarationFileForSymbol(QueryDatabase* db,
const SymbolIdx& symbol) {
2017-06-15 05:32:23 +00:00
switch (symbol.kind) {
2017-09-22 01:14:57 +00:00
case SymbolKind::Type: {
QueryType& type = db->types[symbol.idx];
if (type.def && type.def->definition_spelling)
return type.def->definition_spelling->path;
2017-09-22 01:14:57 +00:00
break;
}
case SymbolKind::Func: {
QueryFunc& func = db->funcs[symbol.idx];
if (!func.declarations.empty())
return func.declarations[0].path;
if (func.def && func.def->definition_spelling)
return func.def->definition_spelling->path;
2017-09-22 01:14:57 +00:00
break;
}
case SymbolKind::Var: {
QueryVar& var = db->vars[symbol.idx];
if (var.def && var.def->definition_spelling)
return var.def->definition_spelling->path;
2017-09-22 01:14:57 +00:00
break;
}
case SymbolKind::File: {
return QueryFileId(symbol.idx);
}
case SymbolKind::Invalid: {
assert(false && "unexpected");
break;
}
2017-06-15 05:32:23 +00:00
}
return nullopt;
}
2017-09-22 01:14:57 +00:00
std::vector<QueryLocation> ToQueryLocation(
QueryDatabase* db,
const std::vector<QueryFuncRef>& refs) {
2017-06-15 05:32:23 +00:00
std::vector<QueryLocation> locs;
locs.reserve(refs.size());
for (const QueryFuncRef& ref : refs)
locs.push_back(ref.loc);
return locs;
}
2017-09-22 01:14:57 +00:00
std::vector<QueryLocation> ToQueryLocation(
QueryDatabase* db,
const std::vector<QueryTypeId>& ids) {
2017-06-15 05:32:23 +00:00
std::vector<QueryLocation> locs;
locs.reserve(ids.size());
for (const QueryTypeId& id : ids) {
optional<QueryLocation> loc = GetDefinitionSpellingOfSymbol(db, id);
if (loc)
locs.push_back(loc.value());
}
return locs;
}
2017-09-22 01:14:57 +00:00
std::vector<QueryLocation> ToQueryLocation(
QueryDatabase* db,
const std::vector<QueryFuncId>& ids) {
2017-06-15 05:32:23 +00:00
std::vector<QueryLocation> locs;
locs.reserve(ids.size());
for (const QueryFuncId& id : ids) {
optional<QueryLocation> loc = GetDefinitionSpellingOfSymbol(db, id);
if (loc)
locs.push_back(loc.value());
}
return locs;
}
2017-09-22 01:14:57 +00:00
std::vector<QueryLocation> ToQueryLocation(QueryDatabase* db,
const std::vector<QueryVarId>& ids) {
2017-06-15 05:32:23 +00:00
std::vector<QueryLocation> locs;
locs.reserve(ids.size());
for (const QueryVarId& id : ids) {
optional<QueryLocation> loc = GetDefinitionSpellingOfSymbol(db, id);
if (loc)
locs.push_back(loc.value());
}
return locs;
}
2017-09-22 01:14:57 +00:00
std::vector<QueryLocation> GetUsesOfSymbol(QueryDatabase* db,
const SymbolIdx& symbol) {
2017-06-15 05:32:23 +00:00
switch (symbol.kind) {
2017-09-22 01:14:57 +00:00
case SymbolKind::Type: {
QueryType& type = db->types[symbol.idx];
return type.uses;
2017-09-22 01:14:57 +00:00
}
case SymbolKind::Func: {
// TODO: the vector allocation could be avoided.
QueryFunc& func = db->funcs[symbol.idx];
std::vector<QueryLocation> result = ToQueryLocation(db, func.callers);
AddRange(&result, func.declarations);
if (func.def && func.def->definition_spelling)
result.push_back(*func.def->definition_spelling);
return result;
2017-09-22 01:14:57 +00:00
}
case SymbolKind::Var: {
QueryVar& var = db->vars[symbol.idx];
return var.uses;
2017-09-22 01:14:57 +00:00
}
case SymbolKind::File:
case SymbolKind::Invalid: {
assert(false && "unexpected");
break;
}
2017-06-15 05:32:23 +00:00
}
return {};
}
2017-09-22 01:14:57 +00:00
std::vector<QueryLocation> GetDeclarationsOfSymbolForGotoDefinition(
QueryDatabase* db,
const SymbolIdx& symbol) {
2017-06-15 05:32:23 +00:00
switch (symbol.kind) {
2017-09-22 01:14:57 +00:00
case SymbolKind::Type: {
// Returning the definition spelling of a type is a hack (and is why the
// function has the postfix `ForGotoDefintion`, but it lets the user
// jump to the start of a type if clicking goto-definition on the same
// type from within the type definition.
QueryType& type = db->types[symbol.idx];
if (type.def) {
optional<QueryLocation> declaration = type.def->definition_spelling;
2017-09-22 01:14:57 +00:00
if (declaration)
return {*declaration};
}
break;
}
case SymbolKind::Func: {
QueryFunc& func = db->funcs[symbol.idx];
return func.declarations;
2017-09-22 01:14:57 +00:00
}
case SymbolKind::Var: {
QueryVar& var = db->vars[symbol.idx];
if (var.def) {
optional<QueryLocation> declaration = var.def->declaration;
2017-09-22 01:14:57 +00:00
if (declaration)
return {*declaration};
}
break;
}
default:
break;
2017-06-15 05:32:23 +00:00
}
return {};
}
2017-09-22 01:14:57 +00:00
optional<QueryLocation> GetBaseDefinitionOrDeclarationSpelling(
QueryDatabase* db,
QueryFunc& func) {
if (!func.def->base)
2017-06-15 05:32:23 +00:00
return nullopt;
QueryFunc& base = db->funcs[func.def->base->id];
2017-06-15 05:32:23 +00:00
optional<QueryLocation> def;
if (base.def)
def = base.def->definition_spelling;
if (!def && !base.declarations.empty())
def = base.declarations[0];
2017-06-15 05:32:23 +00:00
return def;
}
bool HasCallersOnSelfOrBaseOrDerived(QueryDatabase* db, QueryFunc& root) {
// Check self.
if (!root.callers.empty())
return true;
// Check for base calls.
optional<QueryFuncId> func_id = root.def->base;
while (func_id) {
QueryFunc& func = db->funcs[func_id->id];
if (!func.callers.empty())
return true;
if (!func.def)
break;
func_id = func.def->base;
}
// Check for derived calls.
std::queue<QueryFuncId> queue;
PushRange(&queue, root.derived);
while (!queue.empty()) {
QueryFunc& func = db->funcs[queue.front().id];
queue.pop();
if (!func.callers.empty())
return true;
PushRange(&queue, func.derived);
}
return false;
}
2017-09-22 01:14:57 +00:00
std::vector<QueryFuncRef> GetCallersForAllBaseFunctions(QueryDatabase* db,
QueryFunc& root) {
2017-06-15 05:32:23 +00:00
std::vector<QueryFuncRef> callers;
optional<QueryFuncId> func_id = root.def->base;
2017-06-15 05:32:23 +00:00
while (func_id) {
QueryFunc& func = db->funcs[func_id->id];
AddRange(&callers, func.callers);
2017-06-15 05:32:23 +00:00
if (!func.def)
break;
func_id = func.def->base;
2017-06-15 05:32:23 +00:00
}
return callers;
}
2017-09-22 01:14:57 +00:00
std::vector<QueryFuncRef> GetCallersForAllDerivedFunctions(QueryDatabase* db,
QueryFunc& root) {
2017-06-15 05:32:23 +00:00
std::vector<QueryFuncRef> callers;
std::queue<QueryFuncId> queue;
PushRange(&queue, root.derived);
while (!queue.empty()) {
QueryFunc& func = db->funcs[queue.front().id];
2017-06-15 05:32:23 +00:00
queue.pop();
PushRange(&queue, func.derived);
AddRange(&callers, func.callers);
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 - 1, position.column - 1);
optional<int> start = working_file->GetBufferLineFromIndexLine(position.line);
if (!start)
return nullopt;
return lsPosition(*start - 1, position.column - 1);
}
optional<lsRange> GetLsRange(WorkingFile* working_file, const Range& location) {
if (!working_file) {
return lsRange(
2017-09-22 01:14:57 +00:00
lsPosition(location.start.line - 1, location.start.column - 1),
lsPosition(location.end.line - 1, location.end.column - 1));
2017-06-15 05:32:23 +00:00
}
2017-09-22 01:14:57 +00:00
optional<int> start =
working_file->GetBufferLineFromIndexLine(location.start.line);
optional<int> end =
working_file->GetBufferLineFromIndexLine(location.end.line);
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);
2017-09-22 01:14:57 +00:00
return lsRange(lsPosition(*start - 1, location.start.column - 1),
lsPosition(*end - 1, location.end.column - 1));
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,
const QueryLocation& location) {
2017-06-15 05:32:23 +00:00
std::string path;
lsDocumentUri uri = GetLsDocumentUri(db, location.path, &path);
2017-09-22 01:14:57 +00:00
optional<lsRange> range =
GetLsRange(working_files->GetFileByFilename(path), location.range);
2017-06-15 05:32:23 +00:00
if (!range)
return nullopt;
return lsLocation(uri, *range);
}
2017-12-12 05:20:29 +00:00
std::vector<lsLocation> GetLsLocations(
2017-09-22 01:14:57 +00:00
QueryDatabase* db,
WorkingFiles* working_files,
const std::vector<QueryLocation>& locations) {
2017-06-15 05:32:23 +00:00
std::unordered_set<lsLocation> unique_locations;
for (const QueryLocation& query_location : locations) {
2017-09-22 01:14:57 +00:00
optional<lsLocation> location =
GetLsLocation(db, working_files, query_location);
2017-06-15 05:32:23 +00:00
if (!location)
continue;
unique_locations.insert(*location);
}
2017-12-12 05:20:29 +00:00
std::vector<lsLocation> result;
2017-06-15 05:32:23 +00:00
result.reserve(unique_locations.size());
result.assign(unique_locations.begin(), unique_locations.end());
return result;
}
// 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 symbol) {
2017-06-15 05:32:23 +00:00
switch (symbol.kind) {
2017-09-22 01:14:57 +00:00
case SymbolKind::File: {
QueryFile& file = db->files[symbol.idx];
if (!file.def)
2017-09-22 01:14:57 +00:00
return nullopt;
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: {
QueryType& type = db->types[symbol.idx];
if (!type.def)
2017-09-22 01:14:57 +00:00
return nullopt;
lsSymbolInformation info;
info.name = type.def->short_name;
if (type.def->detailed_name != type.def->short_name)
info.containerName = type.def->detailed_name;
2017-09-22 01:14:57 +00:00
info.kind = lsSymbolKind::Class;
return info;
}
case SymbolKind::Func: {
QueryFunc& func = db->funcs[symbol.idx];
if (!func.def)
2017-09-22 01:14:57 +00:00
return nullopt;
lsSymbolInformation info;
info.name = func.def->short_name;
info.containerName = func.def->detailed_name;
2017-09-22 01:14:57 +00:00
info.kind = lsSymbolKind::Function;
if (func.def->declaring_type.has_value()) {
2017-10-31 19:49:19 +00:00
QueryType& container = db->types[func.def->declaring_type->id];
if (container.def)
2017-09-22 01:14:57 +00:00
info.kind = lsSymbolKind::Method;
}
return info;
}
case SymbolKind::Var: {
QueryVar& var = db->vars[symbol.idx];
if (!var.def)
2017-09-22 01:14:57 +00:00
return nullopt;
lsSymbolInformation info;
info.name += var.def->short_name;
info.containerName = var.def->detailed_name;
2017-09-22 01:14:57 +00:00
info.kind = lsSymbolKind::Variable;
return info;
}
case SymbolKind::Invalid: {
2017-06-15 05:32:23 +00:00
return nullopt;
2017-09-22 01:14:57 +00:00
}
2017-06-15 05:32:23 +00:00
};
return nullopt;
}
2017-09-22 01:14:57 +00:00
lsWorkspaceEdit BuildWorkspaceEdit(QueryDatabase* db,
WorkingFiles* working_files,
const std::vector<QueryLocation>& locations,
const std::string& new_text) {
2017-06-15 05:32:23 +00:00
std::unordered_map<QueryFileId, lsTextDocumentEdit> path_to_edit;
for (auto& location : locations) {
2017-09-22 01:14:57 +00:00
optional<lsLocation> ls_location =
GetLsLocation(db, working_files, location);
2017-06-15 05:32:23 +00:00
if (!ls_location)
continue;
if (path_to_edit.find(location.path) == path_to_edit.end()) {
path_to_edit[location.path] = lsTextDocumentEdit();
QueryFile& file = db->files[location.path.id];
if (!file.def)
2017-06-15 05:32:23 +00:00
continue;
const std::string& path = file.def->path;
2017-09-22 01:14:57 +00:00
path_to_edit[location.path].textDocument.uri =
lsDocumentUri::FromPath(path);
2017-06-15 05:32:23 +00:00
WorkingFile* working_file = working_files->GetFileByFilename(path);
if (working_file)
2017-09-22 01:14:57 +00:00
path_to_edit[location.path].textDocument.version =
working_file->version;
2017-06-15 05:32:23 +00:00
}
lsTextEdit edit;
edit.range = ls_location->range;
edit.newText = new_text;
// vscode complains if we submit overlapping text edits.
auto& edits = path_to_edit[location.path].edits;
if (std::find(edits.begin(), edits.end(), edit) == edits.end())
edits.push_back(edit);
}
lsWorkspaceEdit edit;
for (const auto& changes : path_to_edit)
edit.documentChanges.push_back(changes.second);
return edit;
}
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 + 1;
int target_column = position.character + 1;
if (working_file) {
2017-09-22 01:14:57 +00:00
optional<int> index_line =
working_file->GetIndexLineFromBufferLine(target_line);
2017-06-15 05:32:23 +00:00
if (index_line)
target_line = *index_line;
}
for (const SymbolRef& ref : file->def->all_symbols) {
2017-06-15 05:32:23 +00:00
if (ref.loc.range.Contains(target_line, target_column))
symbols.push_back(ref);
}
// 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.
//
// 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.
2017-09-22 01:14:57 +00:00
std::sort(symbols.begin(), symbols.end(),
[](const SymbolRef& a, const SymbolRef& b) {
int a_size = ComputeRangeSize(a.loc.range);
int b_size = ComputeRangeSize(b.loc.range);
if (a_size != b_size)
return a_size < b_size;
// operator> orders Var/Func in front of orders.
return static_cast<int>(a.idx.kind) > static_cast<int>(b.idx.kind);
2017-09-22 01:14:57 +00:00
});
2017-06-15 05:32:23 +00:00
return symbols;
}
void InsertSymbolIntoResult(QueryDatabase* db,
WorkingFiles* working_files,
SymbolIdx symbol,
std::vector<lsSymbolInformation>* result) {
optional<lsSymbolInformation> info = GetSymbolInfo(db, working_files, symbol);
if (!info)
return;
optional<QueryLocation> location = GetDefinitionExtentOfSymbol(db, symbol);
if (!location) {
auto decls = GetDeclarationsOfSymbolForGotoDefinition(db, symbol);
if (decls.empty())
return;
location = decls[0];
}
optional<lsLocation> ls_location =
GetLsLocation(db, working_files, *location);
if (!ls_location)
return;
info->location = *ls_location;
result->push_back(*info);
}