ccls/src/query_utils.cc

326 lines
9.5 KiB
C++
Raw Normal View History

2018-08-21 05:27:52 +00:00
// Copyright 2017-2018 ccls Authors
// SPDX-License-Identifier: Apache-2.0
2018-10-29 04:21:21 +00:00
#include "query_utils.hh"
2017-06-15 05:32:23 +00:00
2018-05-28 00:50:02 +00:00
#include "pipeline.hh"
2018-05-27 19:24:56 +00:00
#include <limits.h>
2018-02-21 01:50:48 +00:00
#include <unordered_set>
2017-06-16 17:14:09 +00:00
namespace ccls {
namespace {
// Computes roughly how long |range| is.
2018-08-09 17:08:14 +00:00
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>
2018-09-11 23:42:04 +00:00
std::vector<Use>
GetDeclarations(llvm::DenseMap<Usr, int, DenseMapInfoForUsr> &entity_usr,
std::vector<Q> &entities, const std::vector<Usr> &usrs) {
std::vector<Use> ret;
ret.reserve(usrs.size());
for (Usr usr : usrs) {
2018-08-09 17:08:14 +00:00
Q &entity = entities[entity_usr[{usr}]];
bool has_def = false;
2018-08-09 17:08:14 +00:00
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-08-09 17:08:14 +00:00
} // namespace
2018-02-09 05:11:35 +00:00
Maybe<DeclRef> GetDefinitionSpell(DB *db, SymbolIdx sym) {
Maybe<DeclRef> ret;
2018-08-09 17:08:14 +00:00
EachEntityDef(db, sym, [&](const auto &def) { return !(ret = def.spell); });
2018-02-21 01:50:48 +00:00
return ret;
2017-06-15 05:32:23 +00:00
}
2018-08-09 17:08:14 +00:00
std::vector<Use> GetFuncDeclarations(DB *db, const std::vector<Usr> &usrs) {
return GetDeclarations(db->func_usr, db->funcs, usrs);
}
2018-08-09 17:08:14 +00:00
std::vector<Use> GetTypeDeclarations(DB *db, const std::vector<Usr> &usrs) {
return GetDeclarations(db->type_usr, db->types, usrs);
}
2018-08-09 17:08:14 +00:00
std::vector<Use> GetVarDeclarations(DB *db, const std::vector<Usr> &usrs,
unsigned kind) {
std::vector<Use> ret;
ret.reserve(usrs.size());
for (Usr usr : usrs) {
2018-08-09 17:08:14 +00:00
QueryVar &var = db->Var(usr);
bool has_def = false;
2018-08-09 17:08:14 +00:00
for (auto &def : var.def)
if (def.spell) {
has_def = true;
// See messages/ccls_vars.cc
if (def.kind == SymbolKind::Field) {
if (!(kind & 1))
break;
} else if (def.kind == SymbolKind::Variable) {
if (!(kind & 2))
break;
} else if (def.kind == SymbolKind::Parameter) {
if (!(kind & 4))
break;
}
ret.push_back(*def.spell);
break;
}
if (!has_def && var.declarations.size())
ret.push_back(var.declarations[0]);
}
return ret;
}
std::vector<DeclRef> &GetNonDefDeclarations(DB *db, SymbolIdx sym) {
static std::vector<DeclRef> empty;
2018-02-09 17:42:10 +00:00
switch (sym.kind) {
case Kind::Func:
return db->GetFunc(sym).declarations;
case Kind::Type:
return db->GetType(sym).declarations;
case Kind::Var:
return db->GetVar(sym).declarations;
2018-08-09 17:08:14 +00:00
default:
break;
2017-06-15 05:32:23 +00:00
}
return empty;
2017-06-15 05:32:23 +00:00
}
2018-08-09 17:08:14 +00:00
std::vector<Use> GetUsesForAllBases(DB *db, QueryFunc &root) {
std::vector<Use> ret;
2018-08-09 17:08:14 +00:00
std::vector<QueryFunc *> stack{&root};
std::unordered_set<Usr> seen;
seen.insert(root.usr);
while (!stack.empty()) {
2018-08-09 17:08:14 +00:00
QueryFunc &func = *stack.back();
stack.pop_back();
2018-08-09 17:08:14 +00:00
if (auto *def = func.AnyDef()) {
EachDefinedFunc(db, def->bases, [&](QueryFunc &func1) {
if (!seen.count(func1.usr)) {
seen.insert(func1.usr);
stack.push_back(&func1);
ret.insert(ret.end(), func1.uses.begin(), func1.uses.end());
}
2018-02-04 00:20:14 +00:00
});
}
2017-06-15 05:32:23 +00:00
}
return ret;
2017-06-15 05:32:23 +00:00
}
2018-08-09 17:08:14 +00:00
std::vector<Use> GetUsesForAllDerived(DB *db, QueryFunc &root) {
std::vector<Use> ret;
2018-08-09 17:08:14 +00:00
std::vector<QueryFunc *> stack{&root};
std::unordered_set<Usr> seen;
seen.insert(root.usr);
while (!stack.empty()) {
2018-08-09 17:08:14 +00:00
QueryFunc &func = *stack.back();
stack.pop_back();
2018-08-09 17:08:14 +00:00
EachDefinedFunc(db, func.derived, [&](QueryFunc &func1) {
if (!seen.count(func1.usr)) {
seen.insert(func1.usr);
stack.push_back(&func1);
ret.insert(ret.end(), func1.uses.begin(), func1.uses.end());
}
2018-02-04 00:20:14 +00:00
});
2017-06-15 05:32:23 +00:00
}
return ret;
2017-06-15 05:32:23 +00:00
}
std::optional<lsRange> GetLsRange(WorkingFile *wfile,
2018-08-09 17:08:14 +00:00
const Range &location) {
if (!wfile || wfile->index_lines.empty())
return lsRange{Position{location.start.line, location.start.column},
Position{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;
std::optional<int> start = wfile->GetBufferPosFromIndexPos(
2018-01-30 00:27:43 +00:00
location.start.line, &start_column, false);
std::optional<int> end = wfile->GetBufferPosFromIndexPos(
location.end.line, &end_column, true);
2017-06-15 05:32:23 +00:00
if (!start || !end)
2018-03-31 03:16:33 +00:00
return std::nullopt;
2017-06-15 05:32:23 +00:00
// 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{Position{*start, start_column}, Position{*end, end_column}};
2017-06-15 05:32:23 +00:00
}
DocumentUri GetLsDocumentUri(DB *db, int file_id, std::string *path) {
2018-08-09 17:08:14 +00:00
QueryFile &file = db->files[file_id];
if (file.def) {
*path = file.def->path;
return DocumentUri::FromPath(*path);
2017-09-22 01:14:57 +00:00
} else {
2017-06-15 05:32:23 +00:00
*path = "";
return DocumentUri::FromPath("");
2017-06-15 05:32:23 +00:00
}
}
DocumentUri GetLsDocumentUri(DB *db, int file_id) {
2018-08-09 17:08:14 +00:00
QueryFile &file = db->files[file_id];
if (file.def) {
return DocumentUri::FromPath(file.def->path);
2017-09-22 01:14:57 +00:00
} else {
return DocumentUri::FromPath("");
2017-06-15 05:32:23 +00:00
}
}
std::optional<Location> GetLsLocation(DB *db, WorkingFiles *wfiles, Use use) {
2017-06-15 05:32:23 +00:00
std::string path;
DocumentUri uri = GetLsDocumentUri(db, use.file_id, &path);
2018-03-31 03:16:33 +00:00
std::optional<lsRange> range =
GetLsRange(wfiles->GetFileByFilename(path), use.range);
2017-06-15 05:32:23 +00:00
if (!range)
2018-03-31 03:16:33 +00:00
return std::nullopt;
return Location{uri, *range};
2017-06-15 05:32:23 +00:00
}
std::optional<Location> GetLsLocation(DB *db, WorkingFiles *wfiles,
SymbolRef sym, int file_id) {
return GetLsLocation(db, wfiles, Use{{sym.range, sym.role}, file_id});
}
std::vector<Location> GetLsLocations(DB *db, WorkingFiles *wfiles,
const std::vector<Use> &uses) {
std::vector<Location> ret;
for (Use use : uses)
if (auto loc = GetLsLocation(db, wfiles, use))
ret.push_back(*loc);
std::sort(ret.begin(), ret.end());
ret.erase(std::unique(ret.begin(), ret.end()), ret.end());
2018-04-04 06:05:41 +00:00
if (ret.size() > g_config->xref.maxNum)
ret.resize(g_config->xref.maxNum);
return ret;
2017-06-15 05:32:23 +00:00
}
SymbolKind GetSymbolKind(DB *db, SymbolIdx sym) {
SymbolKind ret;
if (sym.kind == Kind::File)
ret = SymbolKind::File;
else {
ret = SymbolKind::Unknown;
2018-08-09 17:08:14 +00:00
WithEntity(db, sym, [&](const auto &entity) {
for (auto &def : entity.def) {
ret = def.kind;
break;
}
});
}
return ret;
}
std::optional<SymbolInformation> GetSymbolInfo(DB *db, SymbolIdx sym,
bool detailed) {
2018-02-09 17:42:10 +00:00
switch (sym.kind) {
case Kind::Invalid:
2018-08-09 17:08:14 +00:00
break;
case Kind::File: {
2018-08-09 17:08:14 +00:00
QueryFile &file = db->GetFile(sym);
if (!file.def)
break;
2017-06-15 05:32:23 +00:00
SymbolInformation info;
2018-08-09 17:08:14 +00:00
info.name = file.def->path;
info.kind = SymbolKind::File;
2018-08-09 17:08:14 +00:00
return info;
}
default: {
SymbolInformation info;
2018-08-09 17:08:14 +00:00
EachEntityDef(db, sym, [&](const auto &def) {
2018-09-13 04:03:35 +00:00
if (detailed)
2018-08-09 17:08:14 +00:00
info.name = def.detailed_name;
else
info.name = def.Name(true);
info.kind = def.kind;
return false;
});
return info;
}
2018-02-09 17:42:10 +00:00
}
2017-06-15 05:32:23 +00:00
2018-03-31 03:16:33 +00:00
return std::nullopt;
2017-06-15 05:32:23 +00:00
}
std::vector<SymbolRef> FindSymbolsAtLocation(WorkingFile *wfile,
QueryFile *file, Position &ls_pos,
bool smallest) {
2017-06-15 05:32:23 +00:00
std::vector<SymbolRef> symbols;
// If multiVersion > 0, index may not exist and thus index_lines is empty.
if (wfile && wfile->index_lines.size()) {
if (auto line = wfile->GetIndexPosFromBufferPos(
ls_pos.line, &ls_pos.character, false)) {
ls_pos.line = *line;
} else {
ls_pos.line = -1;
return {};
}
2017-06-15 05:32:23 +00:00
}
2018-08-09 17:08:14 +00:00
for (auto [sym, refcnt] : file->symbol2refcnt)
if (refcnt > 0 && sym.range.Contains(ls_pos.line, ls_pos.character))
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.
//
// Order Kind::Var before Kind::Type. Macro calls are treated as Var
// currently. If a macro expands to tokens led by a Kind::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-08-09 17:08:14 +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;
// MacroExpansion
if ((t = (a.role & Role::Dynamic) - (b.role & Role::Dynamic)))
return t > 0;
if ((t = (a.role & Role::Definition) - (b.role & Role::Definition)))
return t > 0;
// operator> orders Var/Func before Type.
t = static_cast<int>(a.kind) - static_cast<int>(b.kind);
if (t)
return t > 0;
return a.usr < b.usr;
});
if (symbols.size() && smallest) {
SymbolRef sym = symbols[0];
for (size_t i = 1; i < symbols.size(); i++)
if (!(sym.range == symbols[i].range && sym.kind == symbols[i].kind)) {
symbols.resize(i);
break;
}
}
2017-06-15 05:32:23 +00:00
return symbols;
}
} // namespace ccls