Merge query.hh and query_util.hh

This commit is contained in:
Fangrui Song 2018-11-26 22:29:28 -08:00
parent 5a723b489a
commit 5a5165faa8
21 changed files with 418 additions and 464 deletions

View File

@ -196,7 +196,6 @@ target_sources(ccls PRIVATE
src/platform_win.cc src/platform_win.cc
src/position.cc src/position.cc
src/project.cc src/project.cc
src/query_utils.cc
src/query.cc src/query.cc
src/serializer.cc src/serializer.cc
src/test.cc src/test.cc

View File

@ -18,7 +18,7 @@ limitations under the License.
#include "log.hh" #include "log.hh"
#include "pipeline.hh" #include "pipeline.hh"
#include "project.hh" #include "project.hh"
#include "query_utils.hh" #include "query.hh"
#include "serializers/json.hh" #include "serializers/json.hh"
#include <algorithm> #include <algorithm>

View File

@ -16,7 +16,7 @@ limitations under the License.
#include "hierarchy.hh" #include "hierarchy.hh"
#include "message_handler.hh" #include "message_handler.hh"
#include "pipeline.hh" #include "pipeline.hh"
#include "query_utils.hh" #include "query.hh"
#include <unordered_set> #include <unordered_set>

View File

@ -16,7 +16,7 @@ limitations under the License.
#include "message_handler.hh" #include "message_handler.hh"
#include "pipeline.hh" #include "pipeline.hh"
#include "project.hh" #include "project.hh"
#include "query_utils.hh" #include "query.hh"
namespace ccls { namespace ccls {
MAKE_REFLECT_STRUCT(QueryFile::Def, path, args, language, skipped_ranges, MAKE_REFLECT_STRUCT(QueryFile::Def, path, args, language, skipped_ranges,

View File

@ -16,7 +16,7 @@ limitations under the License.
#include "hierarchy.hh" #include "hierarchy.hh"
#include "message_handler.hh" #include "message_handler.hh"
#include "pipeline.hh" #include "pipeline.hh"
#include "query_utils.hh" #include "query.hh"
#include <unordered_set> #include <unordered_set>

View File

@ -17,7 +17,7 @@ limitations under the License.
#include "hierarchy.hh" #include "hierarchy.hh"
#include "message_handler.hh" #include "message_handler.hh"
#include "pipeline.hh" #include "pipeline.hh"
#include "query_utils.hh" #include "query.hh"
#include <clang/AST/Type.h> #include <clang/AST/Type.h>
#include <llvm/ADT/DenseSet.h> #include <llvm/ADT/DenseSet.h>

View File

@ -14,7 +14,7 @@ limitations under the License.
==============================================================================*/ ==============================================================================*/
#include "message_handler.hh" #include "message_handler.hh"
#include "query_utils.hh" #include "query.hh"
namespace ccls { namespace ccls {
namespace { namespace {

View File

@ -15,7 +15,7 @@ limitations under the License.
#include "message_handler.hh" #include "message_handler.hh"
#include "pipeline.hh" #include "pipeline.hh"
#include "query_utils.hh" #include "query.hh"
namespace ccls { namespace ccls {
namespace { namespace {

View File

@ -15,7 +15,7 @@ limitations under the License.
#include "message_handler.hh" #include "message_handler.hh"
#include "pipeline.hh" #include "pipeline.hh"
#include "query_utils.hh" #include "query.hh"
#include "serializers/json.hh" #include "serializers/json.hh"
#include <llvm/Support/FormatVariadic.h> #include <llvm/Support/FormatVariadic.h>

View File

@ -14,7 +14,7 @@ limitations under the License.
==============================================================================*/ ==============================================================================*/
#include "message_handler.hh" #include "message_handler.hh"
#include "query_utils.hh" #include "query.hh"
#include <ctype.h> #include <ctype.h>
#include <limits.h> #include <limits.h>

View File

@ -15,7 +15,7 @@ limitations under the License.
#include "message_handler.hh" #include "message_handler.hh"
#include "pipeline.hh" #include "pipeline.hh"
#include "query_utils.hh" #include "query.hh"
#include <algorithm> #include <algorithm>

View File

@ -16,7 +16,7 @@ limitations under the License.
#include "message_handler.hh" #include "message_handler.hh"
#include "pipeline.hh" #include "pipeline.hh"
#include "project.hh" #include "project.hh"
#include "query_utils.hh" #include "query.hh"
#include "working_files.hh" #include "working_files.hh"
namespace ccls { namespace ccls {

View File

@ -14,7 +14,7 @@ limitations under the License.
==============================================================================*/ ==============================================================================*/
#include "message_handler.hh" #include "message_handler.hh"
#include "query_utils.hh" #include "query.hh"
namespace ccls { namespace ccls {
namespace { namespace {

View File

@ -14,7 +14,7 @@ limitations under the License.
==============================================================================*/ ==============================================================================*/
#include "message_handler.hh" #include "message_handler.hh"
#include "query_utils.hh" #include "query.hh"
#include <unordered_set> #include <unordered_set>

View File

@ -14,7 +14,7 @@ limitations under the License.
==============================================================================*/ ==============================================================================*/
#include "message_handler.hh" #include "message_handler.hh"
#include "query_utils.hh" #include "query.hh"
namespace ccls { namespace ccls {
namespace { namespace {

View File

@ -19,7 +19,7 @@ limitations under the License.
#include "message_handler.hh" #include "message_handler.hh"
#include "pipeline.hh" #include "pipeline.hh"
#include "project.hh" #include "project.hh"
#include "query_utils.hh" #include "query.hh"
#include <llvm/ADT/STLExtras.h> #include <llvm/ADT/STLExtras.h>
#include <llvm/ADT/StringRef.h> #include <llvm/ADT/StringRef.h>

View File

@ -24,7 +24,7 @@ limitations under the License.
#include "pipeline.hh" #include "pipeline.hh"
#include "platform.hh" #include "platform.hh"
#include "project.hh" #include "project.hh"
#include "query_utils.hh" #include "query.hh"
#include "serializers/json.hh" #include "serializers/json.hh"
#include <rapidjson/document.h> #include <rapidjson/document.h>

View File

@ -16,11 +16,13 @@ limitations under the License.
#include "query.hh" #include "query.hh"
#include "indexer.hh" #include "indexer.hh"
#include "pipeline.hh"
#include "serializer.hh" #include "serializer.hh"
#include "serializers/json.hh" #include "serializers/json.hh"
#include <cassert> #include <assert.h>
#include <cstdint> #include <stdint.h>
#include <limits.h>
#include <functional> #include <functional>
#include <optional> #include <optional>
#include <string> #include <string>
@ -52,7 +54,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; QueryFile::Def def;
def.path = std::move(indexed.path); def.path = std::move(indexed.path);
def.args = std::move(indexed.args); def.args = std::move(indexed.args);
@ -87,7 +89,6 @@ IndexUpdate IndexUpdate::CreateDelta(IndexFile *previous, IndexFile *current) {
else else
previous = &empty; previous = &empty;
r.lid2path = std::move(current->lid2path); r.lid2path = std::move(current->lid2path);
r.files_def_update = BuildFileDefUpdate(std::move(*current));
r.funcs_hint = current->usr2func.size() - previous->usr2func.size(); r.funcs_hint = current->usr2func.size() - previous->usr2func.size();
for (auto &it : previous->usr2func) { for (auto &it : previous->usr2func) {
@ -143,6 +144,7 @@ IndexUpdate IndexUpdate::CreateDelta(IndexFile *previous, IndexFile *current) {
r.vars_uses[var.usr].second = std::move(var.uses); r.vars_uses[var.usr].second = std::move(var.uses);
} }
r.files_def_update = BuildFileDefUpdate(std::move(*current));
return r; return r;
} }
@ -486,4 +488,316 @@ std::vector<uint8_t> DB::GetFileSet(const std::vector<std::string> &folders) {
} }
return file_set; 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 } // namespace ccls

View File

@ -17,6 +17,7 @@ limitations under the License.
#include "indexer.hh" #include "indexer.hh"
#include "serializer.hh" #include "serializer.hh"
#include "working_files.hh"
#include <llvm/ADT/DenseMap.h> #include <llvm/ADT/DenseMap.h>
#include <llvm/ADT/SmallVector.h> #include <llvm/ADT/SmallVector.h>
@ -196,4 +197,87 @@ struct DB {
QueryType &GetType(SymbolIdx ref) { return Type(ref.usr); } QueryType &GetType(SymbolIdx ref) { return Type(ref.usr); }
QueryVar &GetVar(SymbolIdx ref) { return Var(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 } // namespace ccls

View File

@ -1,337 +0,0 @@
/* Copyright 2017-2018 ccls Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#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

View File

@ -1,106 +0,0 @@
/* Copyright 2017-2018 ccls Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#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