mirror of
https://github.com/MaskRay/ccls.git
synced 2024-11-27 01:51:57 +00:00
Merge query.hh and query_util.hh
This commit is contained in:
parent
bf6fc50b54
commit
5b41788ebb
@ -196,7 +196,6 @@ target_sources(ccls PRIVATE
|
||||
src/platform_win.cc
|
||||
src/position.cc
|
||||
src/project.cc
|
||||
src/query_utils.cc
|
||||
src/query.cc
|
||||
src/serializer.cc
|
||||
src/test.cc
|
||||
|
@ -6,7 +6,7 @@
|
||||
#include "log.hh"
|
||||
#include "pipeline.hh"
|
||||
#include "project.hh"
|
||||
#include "query_utils.hh"
|
||||
#include "query.hh"
|
||||
#include "serializers/json.hh"
|
||||
|
||||
#include <algorithm>
|
||||
|
@ -4,7 +4,7 @@
|
||||
#include "hierarchy.hh"
|
||||
#include "message_handler.hh"
|
||||
#include "pipeline.hh"
|
||||
#include "query_utils.hh"
|
||||
#include "query.hh"
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
|
@ -16,7 +16,7 @@ limitations under the License.
|
||||
#include "message_handler.hh"
|
||||
#include "pipeline.hh"
|
||||
#include "project.hh"
|
||||
#include "query_utils.hh"
|
||||
#include "query.hh"
|
||||
|
||||
namespace ccls {
|
||||
MAKE_REFLECT_STRUCT(QueryFile::Def, path, args, language, skipped_ranges,
|
||||
|
@ -4,7 +4,7 @@
|
||||
#include "hierarchy.hh"
|
||||
#include "message_handler.hh"
|
||||
#include "pipeline.hh"
|
||||
#include "query_utils.hh"
|
||||
#include "query.hh"
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
#include "hierarchy.hh"
|
||||
#include "message_handler.hh"
|
||||
#include "pipeline.hh"
|
||||
#include "query_utils.hh"
|
||||
#include "query.hh"
|
||||
|
||||
#include <clang/AST/Type.h>
|
||||
#include <llvm/ADT/DenseSet.h>
|
||||
|
@ -14,7 +14,7 @@ limitations under the License.
|
||||
==============================================================================*/
|
||||
|
||||
#include "message_handler.hh"
|
||||
#include "query_utils.hh"
|
||||
#include "query.hh"
|
||||
|
||||
namespace ccls {
|
||||
namespace {
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
#include "message_handler.hh"
|
||||
#include "pipeline.hh"
|
||||
#include "query_utils.hh"
|
||||
#include "query.hh"
|
||||
|
||||
namespace ccls {
|
||||
namespace {
|
||||
|
@ -15,7 +15,7 @@ limitations under the License.
|
||||
|
||||
#include "message_handler.hh"
|
||||
#include "pipeline.hh"
|
||||
#include "query_utils.hh"
|
||||
#include "query.hh"
|
||||
#include "serializers/json.hh"
|
||||
|
||||
#include <llvm/Support/FormatVariadic.h>
|
||||
|
@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include "message_handler.hh"
|
||||
#include "query_utils.hh"
|
||||
#include "query.hh"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
#include "message_handler.hh"
|
||||
#include "pipeline.hh"
|
||||
#include "query_utils.hh"
|
||||
#include "query.hh"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
|
@ -16,7 +16,7 @@ limitations under the License.
|
||||
#include "message_handler.hh"
|
||||
#include "pipeline.hh"
|
||||
#include "project.hh"
|
||||
#include "query_utils.hh"
|
||||
#include "query.hh"
|
||||
#include "working_files.hh"
|
||||
|
||||
namespace ccls {
|
||||
|
@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include "message_handler.hh"
|
||||
#include "query_utils.hh"
|
||||
#include "query.hh"
|
||||
|
||||
namespace ccls {
|
||||
namespace {
|
||||
|
@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include "message_handler.hh"
|
||||
#include "query_utils.hh"
|
||||
#include "query.hh"
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include "message_handler.hh"
|
||||
#include "query_utils.hh"
|
||||
#include "query.hh"
|
||||
|
||||
namespace ccls {
|
||||
namespace {
|
||||
|
@ -19,7 +19,7 @@ limitations under the License.
|
||||
#include "message_handler.hh"
|
||||
#include "pipeline.hh"
|
||||
#include "project.hh"
|
||||
#include "query_utils.hh"
|
||||
#include "query.hh"
|
||||
|
||||
#include <llvm/ADT/STLExtras.h>
|
||||
#include <llvm/ADT/StringRef.h>
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include "pipeline.hh"
|
||||
#include "platform.hh"
|
||||
#include "project.hh"
|
||||
#include "query_utils.hh"
|
||||
#include "query.hh"
|
||||
#include "serializers/json.hh"
|
||||
|
||||
#include <rapidjson/document.h>
|
||||
|
322
src/query.cc
322
src/query.cc
@ -4,11 +4,13 @@
|
||||
#include "query.hh"
|
||||
|
||||
#include "indexer.hh"
|
||||
#include "pipeline.hh"
|
||||
#include "serializer.hh"
|
||||
#include "serializers/json.hh"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <limits.h>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
@ -40,7 +42,7 @@ void RemoveRange(std::vector<T> &from, const std::vector<T> &to_remove) {
|
||||
}
|
||||
}
|
||||
|
||||
QueryFile::DefUpdate BuildFileDefUpdate(const IndexFile &indexed) {
|
||||
QueryFile::DefUpdate BuildFileDefUpdate(IndexFile &&indexed) {
|
||||
QueryFile::Def def;
|
||||
def.path = std::move(indexed.path);
|
||||
def.args = std::move(indexed.args);
|
||||
@ -75,7 +77,6 @@ IndexUpdate IndexUpdate::CreateDelta(IndexFile *previous, IndexFile *current) {
|
||||
else
|
||||
previous = ∅
|
||||
r.lid2path = std::move(current->lid2path);
|
||||
r.files_def_update = BuildFileDefUpdate(std::move(*current));
|
||||
|
||||
r.funcs_hint = int(current->usr2func.size() - previous->usr2func.size());
|
||||
for (auto &it : previous->usr2func) {
|
||||
@ -131,6 +132,7 @@ IndexUpdate IndexUpdate::CreateDelta(IndexFile *previous, IndexFile *current) {
|
||||
r.vars_uses[var.usr].second = std::move(var.uses);
|
||||
}
|
||||
|
||||
r.files_def_update = BuildFileDefUpdate(std::move(*current));
|
||||
return r;
|
||||
}
|
||||
|
||||
@ -474,4 +476,316 @@ std::vector<uint8_t> DB::GetFileSet(const std::vector<std::string> &folders) {
|
||||
}
|
||||
return file_set;
|
||||
}
|
||||
|
||||
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>
|
||||
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) {
|
||||
Q &entity = entities[entity_usr[{usr}]];
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
Maybe<DeclRef> GetDefinitionSpell(DB *db, SymbolIdx sym) {
|
||||
Maybe<DeclRef> ret;
|
||||
EachEntityDef(db, sym, [&](const auto &def) { return !(ret = def.spell); });
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<Use> GetFuncDeclarations(DB *db, const std::vector<Usr> &usrs) {
|
||||
return GetDeclarations(db->func_usr, db->funcs, usrs);
|
||||
}
|
||||
std::vector<Use> GetTypeDeclarations(DB *db, const std::vector<Usr> &usrs) {
|
||||
return GetDeclarations(db->type_usr, db->types, usrs);
|
||||
}
|
||||
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) {
|
||||
QueryVar &var = db->Var(usr);
|
||||
bool has_def = false;
|
||||
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;
|
||||
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;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return empty;
|
||||
}
|
||||
|
||||
std::vector<Use> GetUsesForAllBases(DB *db, QueryFunc &root) {
|
||||
std::vector<Use> ret;
|
||||
std::vector<QueryFunc *> stack{&root};
|
||||
std::unordered_set<Usr> seen;
|
||||
seen.insert(root.usr);
|
||||
while (!stack.empty()) {
|
||||
QueryFunc &func = *stack.back();
|
||||
stack.pop_back();
|
||||
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());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<Use> GetUsesForAllDerived(DB *db, QueryFunc &root) {
|
||||
std::vector<Use> ret;
|
||||
std::vector<QueryFunc *> stack{&root};
|
||||
std::unordered_set<Usr> seen;
|
||||
seen.insert(root.usr);
|
||||
while (!stack.empty()) {
|
||||
QueryFunc &func = *stack.back();
|
||||
stack.pop_back();
|
||||
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());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::optional<lsRange> GetLsRange(WorkingFile *wfile,
|
||||
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}};
|
||||
|
||||
int start_column = location.start.column, end_column = location.end.column;
|
||||
std::optional<int> start = wfile->GetBufferPosFromIndexPos(
|
||||
location.start.line, &start_column, false);
|
||||
std::optional<int> end = wfile->GetBufferPosFromIndexPos(
|
||||
location.end.line, &end_column, true);
|
||||
if (!start || !end)
|
||||
return std::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);
|
||||
if (*start == *end && start_column > end_column)
|
||||
end_column = start_column;
|
||||
|
||||
return lsRange{Position{*start, start_column}, Position{*end, end_column}};
|
||||
}
|
||||
|
||||
DocumentUri GetLsDocumentUri(DB *db, int file_id, std::string *path) {
|
||||
QueryFile &file = db->files[file_id];
|
||||
if (file.def) {
|
||||
*path = file.def->path;
|
||||
return DocumentUri::FromPath(*path);
|
||||
} else {
|
||||
*path = "";
|
||||
return DocumentUri::FromPath("");
|
||||
}
|
||||
}
|
||||
|
||||
DocumentUri GetLsDocumentUri(DB *db, int file_id) {
|
||||
QueryFile &file = db->files[file_id];
|
||||
if (file.def) {
|
||||
return DocumentUri::FromPath(file.def->path);
|
||||
} else {
|
||||
return DocumentUri::FromPath("");
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<Location> GetLsLocation(DB *db, WorkingFiles *wfiles, Use use) {
|
||||
std::string path;
|
||||
DocumentUri uri = GetLsDocumentUri(db, use.file_id, &path);
|
||||
std::optional<lsRange> range =
|
||||
GetLsRange(wfiles->GetFileByFilename(path), use.range);
|
||||
if (!range)
|
||||
return std::nullopt;
|
||||
return Location{uri, *range};
|
||||
}
|
||||
|
||||
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());
|
||||
if (ret.size() > g_config->xref.maxNum)
|
||||
ret.resize(g_config->xref.maxNum);
|
||||
return ret;
|
||||
}
|
||||
|
||||
SymbolKind GetSymbolKind(DB *db, SymbolIdx sym) {
|
||||
SymbolKind ret;
|
||||
if (sym.kind == Kind::File)
|
||||
ret = SymbolKind::File;
|
||||
else {
|
||||
ret = SymbolKind::Unknown;
|
||||
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) {
|
||||
switch (sym.kind) {
|
||||
case Kind::Invalid:
|
||||
break;
|
||||
case Kind::File: {
|
||||
QueryFile &file = db->GetFile(sym);
|
||||
if (!file.def)
|
||||
break;
|
||||
|
||||
SymbolInformation info;
|
||||
info.name = file.def->path;
|
||||
info.kind = SymbolKind::File;
|
||||
return info;
|
||||
}
|
||||
default: {
|
||||
SymbolInformation info;
|
||||
EachEntityDef(db, sym, [&](const auto &def) {
|
||||
if (detailed)
|
||||
info.name = def.detailed_name;
|
||||
else
|
||||
info.name = def.Name(true);
|
||||
info.kind = def.kind;
|
||||
return false;
|
||||
});
|
||||
return info;
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::vector<SymbolRef> FindSymbolsAtLocation(WorkingFile *wfile,
|
||||
QueryFile *file, Position &ls_pos,
|
||||
bool smallest) {
|
||||
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 {};
|
||||
}
|
||||
}
|
||||
|
||||
for (auto [sym, refcnt] : file->symbol2refcnt)
|
||||
if (refcnt > 0 && sym.range.Contains(ls_pos.line, ls_pos.character))
|
||||
symbols.push_back(sym);
|
||||
|
||||
// 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.
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
return symbols;
|
||||
}
|
||||
} // namespace ccls
|
||||
|
84
src/query.hh
84
src/query.hh
@ -5,6 +5,7 @@
|
||||
|
||||
#include "indexer.hh"
|
||||
#include "serializer.hh"
|
||||
#include "working_files.hh"
|
||||
|
||||
#include <llvm/ADT/DenseMap.h>
|
||||
#include <llvm/ADT/SmallVector.h>
|
||||
@ -185,4 +186,87 @@ struct DB {
|
||||
QueryType &GetType(SymbolIdx ref) { return Type(ref.usr); }
|
||||
QueryVar &GetVar(SymbolIdx ref) { return Var(ref.usr); }
|
||||
};
|
||||
|
||||
Maybe<DeclRef> GetDefinitionSpell(DB *db, SymbolIdx sym);
|
||||
|
||||
// Get defining declaration (if exists) or an arbitrary declaration (otherwise)
|
||||
// for each id.
|
||||
std::vector<Use> GetFuncDeclarations(DB *, const std::vector<Usr> &);
|
||||
std::vector<Use> GetTypeDeclarations(DB *, const std::vector<Usr> &);
|
||||
std::vector<Use> GetVarDeclarations(DB *, const std::vector<Usr> &, unsigned);
|
||||
|
||||
// Get non-defining declarations.
|
||||
std::vector<DeclRef> &GetNonDefDeclarations(DB *db, SymbolIdx sym);
|
||||
|
||||
std::vector<Use> GetUsesForAllBases(DB *db, QueryFunc &root);
|
||||
std::vector<Use> GetUsesForAllDerived(DB *db, QueryFunc &root);
|
||||
std::optional<lsRange> GetLsRange(WorkingFile *working_file,
|
||||
const Range &location);
|
||||
DocumentUri GetLsDocumentUri(DB *db, int file_id, std::string *path);
|
||||
DocumentUri GetLsDocumentUri(DB *db, int file_id);
|
||||
|
||||
std::optional<Location> GetLsLocation(DB *db, WorkingFiles *wfiles, Use use);
|
||||
std::optional<Location> GetLsLocation(DB *db, WorkingFiles *wfiles,
|
||||
SymbolRef sym, int file_id);
|
||||
std::vector<Location> GetLsLocations(DB *db, WorkingFiles *wfiles,
|
||||
const std::vector<Use> &uses);
|
||||
// Returns a symbol. The symbol will *NOT* have a location assigned.
|
||||
std::optional<SymbolInformation> GetSymbolInfo(DB *db, SymbolIdx sym,
|
||||
bool detailed);
|
||||
|
||||
std::vector<SymbolRef> FindSymbolsAtLocation(WorkingFile *working_file,
|
||||
QueryFile *file,
|
||||
Position &ls_pos,
|
||||
bool smallest = false);
|
||||
|
||||
template <typename Fn> void WithEntity(DB *db, SymbolIdx sym, Fn &&fn) {
|
||||
switch (sym.kind) {
|
||||
case Kind::Invalid:
|
||||
case Kind::File:
|
||||
break;
|
||||
case Kind::Func:
|
||||
fn(db->GetFunc(sym));
|
||||
break;
|
||||
case Kind::Type:
|
||||
fn(db->GetType(sym));
|
||||
break;
|
||||
case Kind::Var:
|
||||
fn(db->GetVar(sym));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Fn> void EachEntityDef(DB *db, SymbolIdx sym, Fn &&fn) {
|
||||
WithEntity(db, sym, [&](const auto &entity) {
|
||||
for (auto &def : entity.def)
|
||||
if (!fn(def))
|
||||
break;
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Fn>
|
||||
void EachOccurrence(DB *db, SymbolIdx sym, bool include_decl, Fn &&fn) {
|
||||
WithEntity(db, sym, [&](const auto &entity) {
|
||||
for (Use use : entity.uses)
|
||||
fn(use);
|
||||
if (include_decl) {
|
||||
for (auto &def : entity.def)
|
||||
if (def.spell)
|
||||
fn(*def.spell);
|
||||
for (Use use : entity.declarations)
|
||||
fn(use);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
SymbolKind GetSymbolKind(DB *db, SymbolIdx sym);
|
||||
|
||||
template <typename Fn>
|
||||
void EachDefinedFunc(DB *db, const std::vector<Usr> &usrs, Fn &&fn) {
|
||||
for (Usr usr : usrs) {
|
||||
auto &obj = db->Func(usr);
|
||||
if (!obj.def.empty())
|
||||
fn(obj);
|
||||
}
|
||||
}
|
||||
} // namespace ccls
|
||||
|
@ -1,325 +0,0 @@
|
||||
// Copyright 2017-2018 ccls Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#include "query_utils.hh"
|
||||
|
||||
#include "pipeline.hh"
|
||||
|
||||
#include <limits.h>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace ccls {
|
||||
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>
|
||||
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) {
|
||||
Q &entity = entities[entity_usr[{usr}]];
|
||||
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;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Maybe<DeclRef> GetDefinitionSpell(DB *db, SymbolIdx sym) {
|
||||
Maybe<DeclRef> ret;
|
||||
EachEntityDef(db, sym, [&](const auto &def) { return !(ret = def.spell); });
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<Use> GetFuncDeclarations(DB *db, const std::vector<Usr> &usrs) {
|
||||
return GetDeclarations(db->func_usr, db->funcs, usrs);
|
||||
}
|
||||
std::vector<Use> GetTypeDeclarations(DB *db, const std::vector<Usr> &usrs) {
|
||||
return GetDeclarations(db->type_usr, db->types, usrs);
|
||||
}
|
||||
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) {
|
||||
QueryVar &var = db->Var(usr);
|
||||
bool has_def = false;
|
||||
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;
|
||||
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;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return empty;
|
||||
}
|
||||
|
||||
std::vector<Use> GetUsesForAllBases(DB *db, QueryFunc &root) {
|
||||
std::vector<Use> ret;
|
||||
std::vector<QueryFunc *> stack{&root};
|
||||
std::unordered_set<Usr> seen;
|
||||
seen.insert(root.usr);
|
||||
while (!stack.empty()) {
|
||||
QueryFunc &func = *stack.back();
|
||||
stack.pop_back();
|
||||
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());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<Use> GetUsesForAllDerived(DB *db, QueryFunc &root) {
|
||||
std::vector<Use> ret;
|
||||
std::vector<QueryFunc *> stack{&root};
|
||||
std::unordered_set<Usr> seen;
|
||||
seen.insert(root.usr);
|
||||
while (!stack.empty()) {
|
||||
QueryFunc &func = *stack.back();
|
||||
stack.pop_back();
|
||||
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());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::optional<lsRange> GetLsRange(WorkingFile *wfile,
|
||||
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}};
|
||||
|
||||
int start_column = location.start.column, end_column = location.end.column;
|
||||
std::optional<int> start = wfile->GetBufferPosFromIndexPos(
|
||||
location.start.line, &start_column, false);
|
||||
std::optional<int> end = wfile->GetBufferPosFromIndexPos(
|
||||
location.end.line, &end_column, true);
|
||||
if (!start || !end)
|
||||
return std::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);
|
||||
if (*start == *end && start_column > end_column)
|
||||
end_column = start_column;
|
||||
|
||||
return lsRange{Position{*start, start_column}, Position{*end, end_column}};
|
||||
}
|
||||
|
||||
DocumentUri GetLsDocumentUri(DB *db, int file_id, std::string *path) {
|
||||
QueryFile &file = db->files[file_id];
|
||||
if (file.def) {
|
||||
*path = file.def->path;
|
||||
return DocumentUri::FromPath(*path);
|
||||
} else {
|
||||
*path = "";
|
||||
return DocumentUri::FromPath("");
|
||||
}
|
||||
}
|
||||
|
||||
DocumentUri GetLsDocumentUri(DB *db, int file_id) {
|
||||
QueryFile &file = db->files[file_id];
|
||||
if (file.def) {
|
||||
return DocumentUri::FromPath(file.def->path);
|
||||
} else {
|
||||
return DocumentUri::FromPath("");
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<Location> GetLsLocation(DB *db, WorkingFiles *wfiles, Use use) {
|
||||
std::string path;
|
||||
DocumentUri uri = GetLsDocumentUri(db, use.file_id, &path);
|
||||
std::optional<lsRange> range =
|
||||
GetLsRange(wfiles->GetFileByFilename(path), use.range);
|
||||
if (!range)
|
||||
return std::nullopt;
|
||||
return Location{uri, *range};
|
||||
}
|
||||
|
||||
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());
|
||||
if (ret.size() > g_config->xref.maxNum)
|
||||
ret.resize(g_config->xref.maxNum);
|
||||
return ret;
|
||||
}
|
||||
|
||||
SymbolKind GetSymbolKind(DB *db, SymbolIdx sym) {
|
||||
SymbolKind ret;
|
||||
if (sym.kind == Kind::File)
|
||||
ret = SymbolKind::File;
|
||||
else {
|
||||
ret = SymbolKind::Unknown;
|
||||
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) {
|
||||
switch (sym.kind) {
|
||||
case Kind::Invalid:
|
||||
break;
|
||||
case Kind::File: {
|
||||
QueryFile &file = db->GetFile(sym);
|
||||
if (!file.def)
|
||||
break;
|
||||
|
||||
SymbolInformation info;
|
||||
info.name = file.def->path;
|
||||
info.kind = SymbolKind::File;
|
||||
return info;
|
||||
}
|
||||
default: {
|
||||
SymbolInformation info;
|
||||
EachEntityDef(db, sym, [&](const auto &def) {
|
||||
if (detailed)
|
||||
info.name = def.detailed_name;
|
||||
else
|
||||
info.name = def.Name(true);
|
||||
info.kind = def.kind;
|
||||
return false;
|
||||
});
|
||||
return info;
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::vector<SymbolRef> FindSymbolsAtLocation(WorkingFile *wfile,
|
||||
QueryFile *file, Position &ls_pos,
|
||||
bool smallest) {
|
||||
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 {};
|
||||
}
|
||||
}
|
||||
|
||||
for (auto [sym, refcnt] : file->symbol2refcnt)
|
||||
if (refcnt > 0 && sym.range.Contains(ls_pos.line, ls_pos.character))
|
||||
symbols.push_back(sym);
|
||||
|
||||
// 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.
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
return symbols;
|
||||
}
|
||||
} // namespace ccls
|
@ -1,94 +0,0 @@
|
||||
// Copyright 2017-2018 ccls Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "query.hh"
|
||||
#include "working_files.hh"
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace ccls {
|
||||
Maybe<DeclRef> GetDefinitionSpell(DB *db, SymbolIdx sym);
|
||||
|
||||
// Get defining declaration (if exists) or an arbitrary declaration (otherwise)
|
||||
// for each id.
|
||||
std::vector<Use> GetFuncDeclarations(DB *, const std::vector<Usr> &);
|
||||
std::vector<Use> GetTypeDeclarations(DB *, const std::vector<Usr> &);
|
||||
std::vector<Use> GetVarDeclarations(DB *, const std::vector<Usr> &, unsigned);
|
||||
|
||||
// Get non-defining declarations.
|
||||
std::vector<DeclRef> &GetNonDefDeclarations(DB *db, SymbolIdx sym);
|
||||
|
||||
std::vector<Use> GetUsesForAllBases(DB *db, QueryFunc &root);
|
||||
std::vector<Use> GetUsesForAllDerived(DB *db, QueryFunc &root);
|
||||
std::optional<lsRange> GetLsRange(WorkingFile *working_file,
|
||||
const Range &location);
|
||||
DocumentUri GetLsDocumentUri(DB *db, int file_id, std::string *path);
|
||||
DocumentUri GetLsDocumentUri(DB *db, int file_id);
|
||||
|
||||
std::optional<Location> GetLsLocation(DB *db, WorkingFiles *wfiles, Use use);
|
||||
std::optional<Location> GetLsLocation(DB *db, WorkingFiles *wfiles,
|
||||
SymbolRef sym, int file_id);
|
||||
std::vector<Location> GetLsLocations(DB *db, WorkingFiles *wfiles,
|
||||
const std::vector<Use> &uses);
|
||||
// Returns a symbol. The symbol will *NOT* have a location assigned.
|
||||
std::optional<SymbolInformation> GetSymbolInfo(DB *db, SymbolIdx sym,
|
||||
bool detailed);
|
||||
|
||||
std::vector<SymbolRef> FindSymbolsAtLocation(WorkingFile *working_file,
|
||||
QueryFile *file,
|
||||
Position &ls_pos,
|
||||
bool smallest = false);
|
||||
|
||||
template <typename Fn> void WithEntity(DB *db, SymbolIdx sym, Fn &&fn) {
|
||||
switch (sym.kind) {
|
||||
case Kind::Invalid:
|
||||
case Kind::File:
|
||||
break;
|
||||
case Kind::Func:
|
||||
fn(db->GetFunc(sym));
|
||||
break;
|
||||
case Kind::Type:
|
||||
fn(db->GetType(sym));
|
||||
break;
|
||||
case Kind::Var:
|
||||
fn(db->GetVar(sym));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Fn> void EachEntityDef(DB *db, SymbolIdx sym, Fn &&fn) {
|
||||
WithEntity(db, sym, [&](const auto &entity) {
|
||||
for (auto &def : entity.def)
|
||||
if (!fn(def))
|
||||
break;
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Fn>
|
||||
void EachOccurrence(DB *db, SymbolIdx sym, bool include_decl, Fn &&fn) {
|
||||
WithEntity(db, sym, [&](const auto &entity) {
|
||||
for (Use use : entity.uses)
|
||||
fn(use);
|
||||
if (include_decl) {
|
||||
for (auto &def : entity.def)
|
||||
if (def.spell)
|
||||
fn(*def.spell);
|
||||
for (Use use : entity.declarations)
|
||||
fn(use);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
SymbolKind GetSymbolKind(DB *db, SymbolIdx sym);
|
||||
|
||||
template <typename Fn>
|
||||
void EachDefinedFunc(DB *db, const std::vector<Usr> &usrs, Fn &&fn) {
|
||||
for (Usr usr : usrs) {
|
||||
auto &obj = db->Func(usr);
|
||||
if (!obj.def.empty())
|
||||
fn(obj);
|
||||
}
|
||||
}
|
||||
} // namespace ccls
|
Loading…
Reference in New Issue
Block a user