mirror of
https://github.com/MaskRay/ccls.git
synced 2024-11-22 07:35:08 +00:00
Support uses from other files and improve references in macro replacement-list
This commit is contained in:
parent
24e99fd767
commit
b759798e5d
@ -23,14 +23,14 @@ OUTPUT:
|
||||
"short_name": "a",
|
||||
"kind": 12,
|
||||
"storage": 0,
|
||||
"declarations": ["12:1-12:4|0|1|1"],
|
||||
"spell": "12:1-12:4|0|1|2",
|
||||
"declarations": ["12:1-12:20|0|1|1"],
|
||||
"spell": "12:1-12:20|0|1|2",
|
||||
"extent": "1:1-1:1|0|1|0",
|
||||
"declaring_type": 0,
|
||||
"bases": [],
|
||||
"derived": [],
|
||||
"vars": [],
|
||||
"uses": ["2:7-2:8|0|1|64", "3:7-3:8|0|1|64"],
|
||||
"uses": ["2:7-2:8|0|1|64|0", "3:7-3:8|0|1|64|0"],
|
||||
"callees": []
|
||||
}, {
|
||||
"usr": 14400399977994209582,
|
||||
@ -46,7 +46,7 @@ OUTPUT:
|
||||
"bases": [],
|
||||
"derived": [],
|
||||
"vars": [],
|
||||
"uses": ["12:5-12:10|0|1|16420"],
|
||||
"uses": ["12:1-12:20|0|1|16420", "12:5-12:10|0|1|64|0"],
|
||||
"callees": []
|
||||
}],
|
||||
"usr2type": [{
|
||||
@ -75,7 +75,7 @@ OUTPUT:
|
||||
"spell": "9:11-9:16|0|1|2",
|
||||
"extent": "9:1-9:20|0|1|0",
|
||||
"type": 53,
|
||||
"uses": ["12:14-12:19|0|1|12"],
|
||||
"uses": ["12:1-12:20|0|1|12", "12:14-12:19|0|1|64|0"],
|
||||
"kind": 13,
|
||||
"storage": 0
|
||||
}, {
|
||||
@ -88,7 +88,7 @@ OUTPUT:
|
||||
"spell": "1:9-1:12|0|1|2",
|
||||
"extent": "1:9-3:32|0|1|0",
|
||||
"type": 0,
|
||||
"uses": ["12:1-12:20|0|1|4"],
|
||||
"uses": ["12:1-12:4|0|1|64"],
|
||||
"kind": 255,
|
||||
"storage": 0
|
||||
}]
|
||||
|
@ -20,13 +20,13 @@ OUTPUT:
|
||||
"kind": 9,
|
||||
"storage": 0,
|
||||
"declarations": [],
|
||||
"spell": "5:12-5:15|15041163540773201510|2|1026",
|
||||
"spell": "5:3-5:16|15041163540773201510|2|1026",
|
||||
"extent": "1:1-1:1|15041163540773201510|2|0",
|
||||
"declaring_type": 0,
|
||||
"bases": [],
|
||||
"derived": [],
|
||||
"vars": [],
|
||||
"uses": [],
|
||||
"uses": ["5:12-5:15|0|1|64|0"],
|
||||
"callees": []
|
||||
}],
|
||||
"usr2type": [{
|
||||
@ -60,7 +60,7 @@ OUTPUT:
|
||||
"funcs": [13788753348312146871],
|
||||
"vars": [],
|
||||
"instances": [],
|
||||
"uses": ["5:12-5:15|0|1|4"]
|
||||
"uses": ["5:3-5:16|0|1|4", "5:12-5:15|0|1|64|0"]
|
||||
}],
|
||||
"usr2var": [{
|
||||
"usr": 1569772797058982873,
|
||||
@ -72,7 +72,7 @@ OUTPUT:
|
||||
"spell": "1:9-1:10|0|1|2",
|
||||
"extent": "1:9-1:12|0|1|0",
|
||||
"type": 0,
|
||||
"uses": ["8:9-8:10|0|1|4"],
|
||||
"uses": ["8:9-8:10|0|1|64"],
|
||||
"kind": 255,
|
||||
"storage": 0
|
||||
}, {
|
||||
@ -85,7 +85,7 @@ OUTPUT:
|
||||
"spell": "2:9-2:17|0|1|2",
|
||||
"extent": "2:9-2:46|0|1|0",
|
||||
"type": 0,
|
||||
"uses": ["5:3-5:16|0|1|4"],
|
||||
"uses": ["5:3-5:11|0|1|64"],
|
||||
"kind": 255,
|
||||
"storage": 0
|
||||
}, {
|
||||
|
@ -33,7 +33,7 @@ OUTPUT: funky_enum.h
|
||||
"qual_name_offset": 0,
|
||||
"short_name": "A",
|
||||
"hover": "A = 0",
|
||||
"comments": "This file cannot be built directory. It is included in an enum definition of\r\nanother file.",
|
||||
"comments": "This file cannot be built directory. It is included in an enum definition of\nanother file.",
|
||||
"declarations": [],
|
||||
"spell": "4:1-4:2|16985894625255407295|2|1026",
|
||||
"extent": "4:1-4:2|16985894625255407295|2|0",
|
||||
@ -47,7 +47,7 @@ OUTPUT: funky_enum.h
|
||||
"qual_name_offset": 0,
|
||||
"short_name": "C",
|
||||
"hover": "C = 2",
|
||||
"comments": "This file cannot be built directory. It is included in an enum definition of\r\nanother file.",
|
||||
"comments": "This file cannot be built directory. It is included in an enum definition of\nanother file.",
|
||||
"declarations": [],
|
||||
"spell": "6:1-6:2|16985894625255407295|2|1026",
|
||||
"extent": "6:1-6:2|16985894625255407295|2|0",
|
||||
@ -61,7 +61,7 @@ OUTPUT: funky_enum.h
|
||||
"qual_name_offset": 0,
|
||||
"short_name": "B",
|
||||
"hover": "B = 1",
|
||||
"comments": "This file cannot be built directory. It is included in an enum definition of\r\nanother file.",
|
||||
"comments": "This file cannot be built directory. It is included in an enum definition of\nanother file.",
|
||||
"declarations": [],
|
||||
"spell": "5:1-5:2|16985894625255407295|2|1026",
|
||||
"extent": "5:1-5:2|16985894625255407295|2|0",
|
||||
|
@ -23,7 +23,7 @@ OUTPUT:
|
||||
"bases": [],
|
||||
"derived": [],
|
||||
"vars": [],
|
||||
"uses": ["6:14-6:20|0|1|16420"],
|
||||
"uses": ["6:3-6:33|0|1|16420", "6:14-6:20|0|1|64|0"],
|
||||
"callees": []
|
||||
}, {
|
||||
"usr": 11404881820527069090,
|
||||
@ -53,7 +53,7 @@ OUTPUT:
|
||||
"spell": "1:9-1:19|0|1|2",
|
||||
"extent": "1:9-1:24|0|1|0",
|
||||
"type": 0,
|
||||
"uses": ["6:3-6:33|0|1|4"],
|
||||
"uses": ["6:3-6:13|0|1|64"],
|
||||
"kind": 255,
|
||||
"storage": 0
|
||||
}]
|
||||
|
125
src/indexer.cc
125
src/indexer.cc
@ -15,6 +15,7 @@ using ccls::Intern;
|
||||
#include <clang/Lex/PreprocessorOptions.h>
|
||||
#include <llvm/ADT/DenseSet.h>
|
||||
#include <llvm/Support/CrashRecoveryContext.h>
|
||||
#include <llvm/Support/Path.h>
|
||||
#include <llvm/Support/Timer.h>
|
||||
using namespace clang;
|
||||
using llvm::Timer;
|
||||
@ -47,8 +48,7 @@ struct IndexParam {
|
||||
: Unit(Unit), file_consumer(file_consumer) {}
|
||||
|
||||
IndexFile *ConsumeFile(const FileEntry &File) {
|
||||
IndexFile *db =
|
||||
file_consumer->TryConsumeFile(File, &file_contents);
|
||||
IndexFile *db = file_consumer->TryConsumeFile(File, &file_contents);
|
||||
|
||||
// If this is the first time we have seen the file (ignoring if we are
|
||||
// generating an index for it):
|
||||
@ -530,15 +530,25 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void AddMacroUse(SourceManager &SM, Usr usr, SymbolKind kind,
|
||||
void AddMacroUse(IndexFile *db, SourceManager &SM, Usr usr, SymbolKind kind,
|
||||
SourceLocation Spell) const {
|
||||
const FileEntry *FE = SM.getFileEntryForID(SM.getFileID(Spell));
|
||||
if (!FE) return;
|
||||
IndexFile *db = param.ConsumeFile(*FE);
|
||||
if (!db) return;
|
||||
auto UID = FE->getUniqueID();
|
||||
auto [it, inserted] = db->uid2lid_and_path.try_emplace(UID);
|
||||
if (inserted) {
|
||||
it->second.first = db->uid2lid_and_path.size() - 1;
|
||||
SmallString<256> Path = FE->tryGetRealPathName();
|
||||
if (Path.empty())
|
||||
Path = FE->getName();
|
||||
if (!llvm::sys::path::is_absolute(Path) &&
|
||||
!SM.getFileManager().makeAbsolutePath(Path))
|
||||
return;
|
||||
it->second.second = Path.str();
|
||||
}
|
||||
Range spell =
|
||||
FromTokenRange(SM, Ctx->getLangOpts(), SourceRange(Spell, Spell));
|
||||
Use use = GetUse(db, spell, nullptr, Role::Dynamic);
|
||||
Use use{{spell, 0, SymbolKind::File, Role::Dynamic}, it->second.first};
|
||||
switch (kind) {
|
||||
case SymbolKind::Func:
|
||||
db->ToFunc(usr).uses.push_back(use);
|
||||
@ -582,12 +592,8 @@ public:
|
||||
FileID LocFID;
|
||||
#endif
|
||||
SourceLocation Spell = SM.getSpellingLoc(Loc);
|
||||
Loc = SM.getFileLoc(Loc);
|
||||
Range loc = FromTokenRange(SM, Lang, SourceRange(Loc, Loc));
|
||||
LocFID = SM.getFileID(Loc);
|
||||
const FileEntry *FE = SM.getFileEntryForID(LocFID);
|
||||
if (!FE) {
|
||||
// TODO
|
||||
const FileEntry *FE;
|
||||
Range loc;
|
||||
#if LLVM_VERSION_MAJOR < 7
|
||||
auto P = SM.getExpansionRange(Loc);
|
||||
loc = FromCharRange(SM, Ctx->getLangOpts(), SourceRange(P.first, P.second));
|
||||
@ -601,7 +607,6 @@ public:
|
||||
#endif
|
||||
if (!FE)
|
||||
return true;
|
||||
}
|
||||
IndexFile *db = param.ConsumeFile(*FE);
|
||||
if (!db)
|
||||
return true;
|
||||
@ -647,7 +652,7 @@ public:
|
||||
func = &db->ToFunc(usr);
|
||||
do_def_decl(func);
|
||||
if (Spell != Loc)
|
||||
AddMacroUse(SM, usr, SymbolKind::Func, Spell);
|
||||
AddMacroUse(db, SM, usr, SymbolKind::Func, Spell);
|
||||
if (func->def.detailed_name[0] == '\0')
|
||||
SetName(OrigD, info->short_name, info->qualified, func->def);
|
||||
if (is_def || is_decl) {
|
||||
@ -660,7 +665,7 @@ public:
|
||||
type = &db->ToType(usr);
|
||||
do_def_decl(type);
|
||||
if (Spell != Loc)
|
||||
AddMacroUse(SM, usr, SymbolKind::Type, Spell);
|
||||
AddMacroUse(db, SM, usr, SymbolKind::Type, Spell);
|
||||
if (type->def.detailed_name[0] == '\0')
|
||||
SetName(OrigD, info->short_name, info->qualified, type->def);
|
||||
if (is_def || is_decl) {
|
||||
@ -673,7 +678,7 @@ public:
|
||||
var = &db->ToVar(usr);
|
||||
do_def_decl(var);
|
||||
if (Spell != Loc)
|
||||
AddMacroUse(SM, usr, SymbolKind::Var, Spell);
|
||||
AddMacroUse(db, SM, usr, SymbolKind::Var, Spell);
|
||||
if (var->def.detailed_name[0] == '\0')
|
||||
SetVarName(OrigD, info->short_name, info->qualified, var->def);
|
||||
QualType T;
|
||||
@ -994,14 +999,16 @@ public:
|
||||
void MacroExpands(const Token &Tok, const MacroDefinition &MD,
|
||||
SourceRange R, const MacroArgs *Args) override {
|
||||
llvm::sys::fs::UniqueID UniqueID;
|
||||
auto range = FromTokenRange(SM, param.Ctx->getLangOpts(), R, &UniqueID);
|
||||
const FileEntry *FE = SM.getFileEntryForID(SM.getFileID(R.getBegin()));
|
||||
SourceLocation L = SM.getSpellingLoc(R.getBegin());
|
||||
const FileEntry *FE = SM.getFileEntryForID(SM.getFileID(L));
|
||||
if (!FE)
|
||||
return;
|
||||
if (IndexFile *db = param.ConsumeFile(*FE)) {
|
||||
auto[Name, usr] = GetMacro(Tok);
|
||||
IndexVar &var = db->ToVar(usr);
|
||||
var.uses.push_back({{range, 0, SymbolKind::File, Role::Reference}});
|
||||
var.uses.push_back(
|
||||
{{FromTokenRange(SM, param.Ctx->getLangOpts(), {L, L}, &UniqueID), 0,
|
||||
SymbolKind::File, Role::Dynamic}});
|
||||
}
|
||||
}
|
||||
void MacroUndefined(const Token &Tok, const MacroDefinition &MD,
|
||||
@ -1033,8 +1040,8 @@ public:
|
||||
};
|
||||
}
|
||||
|
||||
const int IndexFile::kMajorVersion = 16;
|
||||
const int IndexFile::kMinorVersion = 1;
|
||||
const int IndexFile::kMajorVersion = 17;
|
||||
const int IndexFile::kMinorVersion = 0;
|
||||
|
||||
IndexFile::IndexFile(llvm::sys::fs::UniqueID UniqueID, const std::string &path,
|
||||
const std::string &contents)
|
||||
@ -1185,6 +1192,9 @@ std::vector<std::unique_ptr<IndexFile>> Index(
|
||||
for (std::unique_ptr<IndexFile>& entry : result) {
|
||||
entry->import_file = file;
|
||||
entry->args = args;
|
||||
for (auto &[_, it] : entry->uid2lid_and_path)
|
||||
entry->lid2path.emplace_back(it.first, std::move(it.second));
|
||||
entry->uid2lid_and_path.clear();
|
||||
for (auto& it : entry->usr2func) {
|
||||
// e.g. declaration + out-of-line definition
|
||||
Uniquify(it.second.derived);
|
||||
@ -1232,34 +1242,67 @@ std::vector<std::unique_ptr<IndexFile>> Index(
|
||||
// |SymbolRef| is serialized this way.
|
||||
// |Use| also uses this though it has an extra field |file|,
|
||||
// which is not used by Index* so it does not need to be serialized.
|
||||
void Reflect(Reader& visitor, Reference& value) {
|
||||
if (visitor.Format() == SerializeFormat::Json) {
|
||||
std::string t = visitor.GetString();
|
||||
void Reflect(Reader &vis, Reference &v) {
|
||||
if (vis.Format() == SerializeFormat::Json) {
|
||||
std::string t = vis.GetString();
|
||||
char *s = const_cast<char *>(t.c_str());
|
||||
value.range = Range::FromString(s);
|
||||
v.range = Range::FromString(s);
|
||||
s = strchr(s, '|');
|
||||
value.usr = strtoull(s + 1, &s, 10);
|
||||
value.kind = static_cast<SymbolKind>(strtol(s + 1, &s, 10));
|
||||
value.role = static_cast<Role>(strtol(s + 1, &s, 10));
|
||||
v.usr = strtoull(s + 1, &s, 10);
|
||||
v.kind = static_cast<SymbolKind>(strtol(s + 1, &s, 10));
|
||||
v.role = static_cast<Role>(strtol(s + 1, &s, 10));
|
||||
} else {
|
||||
Reflect(visitor, value.range);
|
||||
Reflect(visitor, value.usr);
|
||||
Reflect(visitor, value.kind);
|
||||
Reflect(visitor, value.role);
|
||||
Reflect(vis, v.range);
|
||||
Reflect(vis, v.usr);
|
||||
Reflect(vis, v.kind);
|
||||
Reflect(vis, v.role);
|
||||
}
|
||||
}
|
||||
void Reflect(Writer& visitor, Reference& value) {
|
||||
if (visitor.Format() == SerializeFormat::Json) {
|
||||
void Reflect(Writer &vis, Reference &v) {
|
||||
if (vis.Format() == SerializeFormat::Json) {
|
||||
char buf[99];
|
||||
snprintf(buf, sizeof buf, "%s|%" PRIu64 "|%d|%d",
|
||||
value.range.ToString().c_str(), value.usr, int(value.kind),
|
||||
int(value.role));
|
||||
v.range.ToString().c_str(), v.usr, int(v.kind), int(v.role));
|
||||
std::string s(buf);
|
||||
Reflect(visitor, s);
|
||||
Reflect(vis, s);
|
||||
} else {
|
||||
Reflect(visitor, value.range);
|
||||
Reflect(visitor, value.usr);
|
||||
Reflect(visitor, value.kind);
|
||||
Reflect(visitor, value.role);
|
||||
Reflect(vis, v.range);
|
||||
Reflect(vis, v.usr);
|
||||
Reflect(vis, v.kind);
|
||||
Reflect(vis, v.role);
|
||||
}
|
||||
}
|
||||
|
||||
void Reflect(Reader& vis, Use& v) {
|
||||
if (vis.Format() == SerializeFormat::Json) {
|
||||
std::string t = vis.GetString();
|
||||
char* s = const_cast<char*>(t.c_str());
|
||||
v.range = Range::FromString(s);
|
||||
s = strchr(s, '|');
|
||||
v.usr = strtoull(s + 1, &s, 10);
|
||||
v.kind = static_cast<SymbolKind>(strtol(s + 1, &s, 10));
|
||||
v.role = static_cast<Role>(strtol(s + 1, &s, 10));
|
||||
if (*s == '|')
|
||||
v.file_id = static_cast<int>(strtol(s + 1, &s, 10));
|
||||
} else {
|
||||
Reflect(vis, static_cast<Reference&>(v));
|
||||
Reflect(vis, v.file_id);
|
||||
}
|
||||
}
|
||||
void Reflect(Writer& vis, Use& v) {
|
||||
if (vis.Format() == SerializeFormat::Json) {
|
||||
char buf[99];
|
||||
if (v.file_id == -1)
|
||||
snprintf(buf, sizeof buf, "%s|%" PRIu64 "|%d|%d",
|
||||
v.range.ToString().c_str(), v.usr, int(v.kind), int(v.role));
|
||||
else
|
||||
snprintf(buf, sizeof buf, "%s|%" PRIu64 "|%d|%d|%d",
|
||||
v.range.ToString().c_str(), v.usr, int(v.kind), int(v.role),
|
||||
v.file_id);
|
||||
std::string s(buf);
|
||||
Reflect(vis, s);
|
||||
} else {
|
||||
Reflect(vis, static_cast<Reference&>(v));
|
||||
Reflect(vis, v.file_id);
|
||||
}
|
||||
}
|
||||
|
@ -52,16 +52,23 @@ struct Reference {
|
||||
|
||||
// |id,kind| refer to the referenced entity.
|
||||
struct SymbolRef : Reference {};
|
||||
MAKE_HASHABLE(SymbolRef, t.range, t.usr, t.kind, t.role);
|
||||
|
||||
// Represents an occurrence of a variable/type, |usr,kind| refer to the lexical
|
||||
// parent.
|
||||
struct Use : Reference {
|
||||
// |file| is used in Query* but not in Index*
|
||||
int file_id = -1;
|
||||
bool operator==(const Use& o) const {
|
||||
return range == o.range && usr == o.usr && kind == o.kind &&
|
||||
role == o.role && file_id == o.file_id;
|
||||
}
|
||||
};
|
||||
|
||||
void Reflect(Reader& visitor, Reference& value);
|
||||
void Reflect(Writer& visitor, Reference& value);
|
||||
void Reflect(Reader& visitor, Use& value);
|
||||
void Reflect(Writer& visitor, Use& value);
|
||||
|
||||
MAKE_REFLECT_TYPE_PROXY2(clang::StorageClass, uint8_t);
|
||||
|
||||
@ -255,6 +262,11 @@ struct IndexFile {
|
||||
int64_t last_write_time = 0;
|
||||
LanguageId language = LanguageId::Unknown;
|
||||
|
||||
// uid2lid_and_path is used to generate lid2path, but not serialized.
|
||||
std::unordered_map<llvm::sys::fs::UniqueID, std::pair<int, std::string>>
|
||||
uid2lid_and_path;
|
||||
std::vector<std::pair<int, std::string>> lid2path;
|
||||
|
||||
// The path to the translation unit cc file which caused the creation of this
|
||||
// IndexFile. When parsing a translation unit we generate many IndexFile
|
||||
// instances (ie, each header has a separate one). When the user edits a
|
||||
|
148
src/query.cc
148
src/query.cc
@ -17,41 +17,43 @@ MAKE_HASHABLE(Use, t.range, t.file_id);
|
||||
|
||||
namespace {
|
||||
|
||||
void AssignFileId(int file_id, SymbolRef& ref) {
|
||||
void AssignFileId(const Lid2file_id &, int file_id, SymbolRef &ref) {
|
||||
if (ref.kind == SymbolKind::File)
|
||||
ref.usr = file_id;
|
||||
}
|
||||
|
||||
void AssignFileId(int file_id, Use& use) {
|
||||
void AssignFileId(const Lid2file_id& lid2file_id, int file_id, Use& use) {
|
||||
if (use.kind == SymbolKind::File)
|
||||
use.usr = file_id;
|
||||
if (use.file_id == -1)
|
||||
use.file_id = file_id;
|
||||
else
|
||||
use.file_id = lid2file_id.find(use.file_id)->second;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void AssignFileId(int file_id, T&) {}
|
||||
void AssignFileId(const Lid2file_id &, int file_id, T &) {}
|
||||
|
||||
template <typename T>
|
||||
void AssignFileId(int file_id, Maybe<T>& x) {
|
||||
void AssignFileId(const Lid2file_id &lid2file_id, int file_id, Maybe<T> &x) {
|
||||
if (x)
|
||||
AssignFileId(file_id, *x);
|
||||
AssignFileId(lid2file_id, file_id, *x);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void AssignFileId(int file_id, std::vector<T>& xs) {
|
||||
void AssignFileId(const Lid2file_id &lid2file_id, int file_id,
|
||||
std::vector<T> &xs) {
|
||||
for (T &x : xs)
|
||||
AssignFileId(file_id, x);
|
||||
AssignFileId(lid2file_id, file_id, x);
|
||||
}
|
||||
|
||||
void AddRange(int file_id, std::vector<Use>& into, const std::vector<Use>& from) {
|
||||
void AddRange(std::vector<Use>& into, const std::vector<Use>& from) {
|
||||
into.reserve(into.size() + from.size());
|
||||
for (Use use : from) {
|
||||
use.file_id = file_id;
|
||||
for (Use use : from)
|
||||
into.push_back(use);
|
||||
}
|
||||
}
|
||||
|
||||
void AddRange(int _, std::vector<Usr>& into, const std::vector<Usr>& from) {
|
||||
void AddRange(std::vector<Usr>& into, const std::vector<Usr>& from) {
|
||||
into.insert(into.end(), from.begin(), from.end());
|
||||
}
|
||||
|
||||
@ -98,6 +100,7 @@ QueryFile::DefUpdate BuildFileDefUpdate(const IndexFile& indexed) {
|
||||
add_outline(decl, type.usr, SymbolKind::Type);
|
||||
}
|
||||
for (Use use : type.uses)
|
||||
if (use.file_id == -1)
|
||||
add_all_symbols(use, type.usr, SymbolKind::Type);
|
||||
}
|
||||
for (auto& it: indexed.usr2func) {
|
||||
@ -110,9 +113,10 @@ QueryFile::DefUpdate BuildFileDefUpdate(const IndexFile& indexed) {
|
||||
add_all_symbols(use, func.usr, SymbolKind::Func);
|
||||
add_outline(use, func.usr, SymbolKind::Func);
|
||||
}
|
||||
for (Use use : func.uses) {
|
||||
// Make ranges of implicit function calls larger (spanning one more column
|
||||
// to the left/right). This is hacky but useful. e.g.
|
||||
for (Use use : func.uses)
|
||||
if (use.file_id == -1) {
|
||||
// Make ranges of implicit function calls larger (spanning one more
|
||||
// column to the left/right). This is hacky but useful. e.g.
|
||||
// textDocument/definition on the space/semicolon in `A a;` or `return
|
||||
// 42;` will take you to the constructor.
|
||||
if (use.role & Role::Implicit) {
|
||||
@ -134,6 +138,7 @@ QueryFile::DefUpdate BuildFileDefUpdate(const IndexFile& indexed) {
|
||||
add_outline(decl, var.usr, SymbolKind::Var);
|
||||
}
|
||||
for (Use use : var.uses)
|
||||
if (use.file_id == -1)
|
||||
add_all_symbols(use, var.usr, SymbolKind::Var);
|
||||
}
|
||||
|
||||
@ -162,19 +167,16 @@ bool TryReplaceDef(llvm::SmallVectorImpl<Q>& def_list, Q&& def) {
|
||||
|
||||
} // namespace
|
||||
|
||||
// ----------------------
|
||||
// INDEX THREAD FUNCTIONS
|
||||
// ----------------------
|
||||
|
||||
// static
|
||||
IndexUpdate IndexUpdate::CreateDelta(IndexFile* previous,
|
||||
IndexFile* current) {
|
||||
IndexUpdate r;
|
||||
static IndexFile empty(llvm::sys::fs::UniqueID(0, 0), current->path,
|
||||
"<empty>");
|
||||
if (!previous)
|
||||
if (previous)
|
||||
r.prev_lid2path = std::move(previous->lid2path);
|
||||
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());
|
||||
@ -289,16 +291,52 @@ void DB::ApplyIndexUpdate(IndexUpdate* u) {
|
||||
if (R.second) \
|
||||
C##s.emplace_back().usr = it.first; \
|
||||
auto &entity = C##s[R.first->second]; \
|
||||
AssignFileId(u->file_id, it.second.first); \
|
||||
AssignFileId(prev_lid2file_id, u->file_id, it.second.first); \
|
||||
RemoveRange(entity.F, it.second.first); \
|
||||
AssignFileId(u->file_id, it.second.second); \
|
||||
AddRange(u->file_id, entity.F, it.second.second); \
|
||||
AssignFileId(lid2file_id, u->file_id, it.second.second); \
|
||||
AddRange(entity.F, it.second.second); \
|
||||
}
|
||||
|
||||
std::unordered_map<int, int> prev_lid2file_id, lid2file_id;
|
||||
for (auto & [ lid, path ] : u->prev_lid2path)
|
||||
prev_lid2file_id[lid] = GetFileId(path);
|
||||
for (auto & [ lid, path ] : u->lid2path)
|
||||
lid2file_id[lid] = GetFileId(path);
|
||||
|
||||
auto UpdateUses = [&](Usr usr, SymbolKind kind,
|
||||
llvm::DenseMap<WrappedUsr, int> &entity_usr,
|
||||
auto &entities, auto &p) {
|
||||
auto R = entity_usr.try_emplace({usr}, entity_usr.size());
|
||||
if (R.second)
|
||||
vars.emplace_back().usr = usr;
|
||||
auto &entity = entities[R.first->second];
|
||||
for (Use &use : p.first) {
|
||||
if (use.file_id == -1)
|
||||
use.file_id = u->file_id;
|
||||
else {
|
||||
use.file_id = prev_lid2file_id.find(use.file_id)->second;
|
||||
files[use.file_id]
|
||||
.symbol2refcnt[SymbolRef{{use.range, usr, kind, use.role}}]--;
|
||||
}
|
||||
}
|
||||
RemoveRange(entity.uses, p.first);
|
||||
for (Use &use : p.second) {
|
||||
if (use.file_id == -1)
|
||||
use.file_id = u->file_id;
|
||||
else {
|
||||
use.file_id = lid2file_id.find(use.file_id)->second;
|
||||
files[use.file_id]
|
||||
.symbol2refcnt[SymbolRef{{use.range, usr, kind, use.role}}]++;
|
||||
}
|
||||
}
|
||||
AddRange(entity.uses, p.second);
|
||||
};
|
||||
|
||||
if (u->files_removed)
|
||||
files[name2file_id[LowerPathIfInsensitive(*u->files_removed)]].def =
|
||||
std::nullopt;
|
||||
u->file_id = u->files_def_update ? Update(std::move(*u->files_def_update)) : -1;
|
||||
u->file_id =
|
||||
u->files_def_update ? Update(std::move(*u->files_def_update)) : -1;
|
||||
|
||||
const double grow = 1.3;
|
||||
size_t t;
|
||||
@ -309,10 +347,11 @@ void DB::ApplyIndexUpdate(IndexUpdate* u) {
|
||||
func_usr.reserve(t);
|
||||
}
|
||||
RemoveUsrs(SymbolKind::Func, u->file_id, u->funcs_removed);
|
||||
Update(u->file_id, std::move(u->funcs_def_update));
|
||||
Update(lid2file_id, u->file_id, std::move(u->funcs_def_update));
|
||||
REMOVE_ADD(func, declarations);
|
||||
REMOVE_ADD(func, derived);
|
||||
REMOVE_ADD(func, uses);
|
||||
for (auto & [ usr, p ] : u->funcs_uses)
|
||||
UpdateUses(usr, SymbolKind::Func, func_usr, funcs, p);
|
||||
|
||||
if ((t = types.size() + u->types_hint) > types.capacity()) {
|
||||
t = size_t(t * grow);
|
||||
@ -320,11 +359,12 @@ void DB::ApplyIndexUpdate(IndexUpdate* u) {
|
||||
type_usr.reserve(t);
|
||||
}
|
||||
RemoveUsrs(SymbolKind::Type, u->file_id, u->types_removed);
|
||||
Update(u->file_id, std::move(u->types_def_update));
|
||||
Update(lid2file_id, u->file_id, std::move(u->types_def_update));
|
||||
REMOVE_ADD(type, declarations);
|
||||
REMOVE_ADD(type, derived);
|
||||
REMOVE_ADD(type, instances);
|
||||
REMOVE_ADD(type, uses);
|
||||
for (auto & [ usr, p ] : u->types_uses)
|
||||
UpdateUses(usr, SymbolKind::Type, type_usr, types, p);
|
||||
|
||||
if ((t = vars.size() + u->vars_hint) > vars.capacity()) {
|
||||
t = size_t(t * grow);
|
||||
@ -332,30 +372,37 @@ void DB::ApplyIndexUpdate(IndexUpdate* u) {
|
||||
var_usr.reserve(t);
|
||||
}
|
||||
RemoveUsrs(SymbolKind::Var, u->file_id, u->vars_removed);
|
||||
Update(u->file_id, std::move(u->vars_def_update));
|
||||
Update(lid2file_id, u->file_id, std::move(u->vars_def_update));
|
||||
REMOVE_ADD(var, declarations);
|
||||
REMOVE_ADD(var, uses);
|
||||
for (auto & [ usr, p ] : u->vars_uses)
|
||||
UpdateUses(usr, SymbolKind::Var, var_usr, vars, p);
|
||||
|
||||
#undef REMOVE_ADD
|
||||
}
|
||||
|
||||
int DB::Update(QueryFile::DefUpdate&& u) {
|
||||
int DB::GetFileId(const std::string& path) {
|
||||
auto it = name2file_id.try_emplace(LowerPathIfInsensitive(path));
|
||||
if (it.second) {
|
||||
int id = files.size();
|
||||
auto it = name2file_id.try_emplace(LowerPathIfInsensitive(u.first.path), id);
|
||||
if (it.second)
|
||||
files.emplace_back().id = id;
|
||||
QueryFile& existing = files[it.first->second];
|
||||
existing.def = u.first;
|
||||
return existing.id;
|
||||
it.first->second = files.emplace_back().id = id;
|
||||
}
|
||||
return it.first->second;
|
||||
}
|
||||
|
||||
void DB::Update(int file_id, std::vector<std::pair<Usr, QueryFunc::Def>>&& us) {
|
||||
int DB::Update(QueryFile::DefUpdate&& u) {
|
||||
int file_id = GetFileId(u.first.path);
|
||||
files[file_id].def = u.first;
|
||||
return file_id;
|
||||
}
|
||||
|
||||
void DB::Update(const Lid2file_id &lid2file_id, int file_id,
|
||||
std::vector<std::pair<Usr, QueryFunc::Def>> &&us) {
|
||||
for (auto &u : us) {
|
||||
auto& def = u.second;
|
||||
assert(def.detailed_name[0]);
|
||||
AssignFileId(file_id, def.spell);
|
||||
AssignFileId(file_id, def.extent);
|
||||
AssignFileId(file_id, def.callees);
|
||||
AssignFileId(lid2file_id, file_id, def.spell);
|
||||
AssignFileId(lid2file_id, file_id, def.extent);
|
||||
AssignFileId(lid2file_id, file_id, def.callees);
|
||||
auto R = func_usr.try_emplace({u.first}, func_usr.size());
|
||||
if (R.second)
|
||||
funcs.emplace_back();
|
||||
@ -366,12 +413,13 @@ void DB::Update(int file_id, std::vector<std::pair<Usr, QueryFunc::Def>>&& us) {
|
||||
}
|
||||
}
|
||||
|
||||
void DB::Update(int file_id, std::vector<std::pair<Usr, QueryType::Def>>&& us) {
|
||||
void DB::Update(const Lid2file_id &lid2file_id, int file_id,
|
||||
std::vector<std::pair<Usr, QueryType::Def>> &&us) {
|
||||
for (auto &u : us) {
|
||||
auto& def = u.second;
|
||||
assert(def.detailed_name[0]);
|
||||
AssignFileId(file_id, def.spell);
|
||||
AssignFileId(file_id, def.extent);
|
||||
AssignFileId(lid2file_id, file_id, def.spell);
|
||||
AssignFileId(lid2file_id, file_id, def.extent);
|
||||
auto R = type_usr.try_emplace({u.first}, type_usr.size());
|
||||
if (R.second)
|
||||
types.emplace_back();
|
||||
@ -379,16 +427,16 @@ void DB::Update(int file_id, std::vector<std::pair<Usr, QueryType::Def>>&& us) {
|
||||
existing.usr = u.first;
|
||||
if (!TryReplaceDef(existing.def, std::move(def)))
|
||||
existing.def.push_back(std::move(def));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void DB::Update(int file_id, std::vector<std::pair<Usr, QueryVar::Def>>&& us) {
|
||||
void DB::Update(const Lid2file_id &lid2file_id, int file_id,
|
||||
std::vector<std::pair<Usr, QueryVar::Def>> &&us) {
|
||||
for (auto &u : us) {
|
||||
auto& def = u.second;
|
||||
assert(def.detailed_name[0]);
|
||||
AssignFileId(file_id, def.spell);
|
||||
AssignFileId(file_id, def.extent);
|
||||
AssignFileId(lid2file_id, file_id, def.spell);
|
||||
AssignFileId(lid2file_id, file_id, def.extent);
|
||||
auto R = var_usr.try_emplace({u.first}, var_usr.size());
|
||||
if (R.second)
|
||||
vars.emplace_back();
|
||||
|
20
src/query.h
20
src/query.h
@ -28,6 +28,7 @@ struct QueryFile {
|
||||
|
||||
int id = -1;
|
||||
std::optional<Def> def;
|
||||
std::unordered_map<SymbolRef, int> symbol2refcnt;
|
||||
};
|
||||
|
||||
template <typename Q, typename QDef>
|
||||
@ -85,6 +86,9 @@ struct IndexUpdate {
|
||||
// Dummy one to refresh all semantic highlight.
|
||||
bool refresh = false;
|
||||
|
||||
decltype(IndexFile::lid2path) prev_lid2path;
|
||||
decltype(IndexFile::lid2path) lid2path;
|
||||
|
||||
// File updates.
|
||||
std::optional<std::string> files_removed;
|
||||
std::optional<QueryFile::DefUpdate> files_def_update;
|
||||
@ -125,6 +129,8 @@ struct llvm::DenseMapInfo<WrappedUsr> {
|
||||
static bool isEqual(WrappedUsr l, WrappedUsr r) { return l.usr == r.usr; }
|
||||
};
|
||||
|
||||
using Lid2file_id = std::unordered_map<int, int>;
|
||||
|
||||
// The query database is heavily optimized for fast queries. It is stored
|
||||
// in-memory.
|
||||
struct DB {
|
||||
@ -135,15 +141,17 @@ struct DB {
|
||||
std::vector<QueryType> types;
|
||||
std::vector<QueryVar> vars;
|
||||
|
||||
// Marks the given Usrs as invalid.
|
||||
void RemoveUsrs(SymbolKind usr_kind, const std::vector<Usr>& to_remove);
|
||||
void RemoveUsrs(SymbolKind usr_kind, int file_id, const std::vector<Usr>& to_remove);
|
||||
void RemoveUsrs(SymbolKind kind, int file_id, const std::vector<Usr>& to_remove);
|
||||
// Insert the contents of |update| into |db|.
|
||||
void ApplyIndexUpdate(IndexUpdate* update);
|
||||
int GetFileId(const std::string& path);
|
||||
int Update(QueryFile::DefUpdate&& u);
|
||||
void Update(int file_id, std::vector<std::pair<Usr, QueryType::Def>>&& us);
|
||||
void Update(int file_id, std::vector<std::pair<Usr, QueryFunc::Def>>&& us);
|
||||
void Update(int file_id, std::vector<std::pair<Usr, QueryVar::Def>>&& us);
|
||||
void Update(const Lid2file_id &, int file_id,
|
||||
std::vector<std::pair<Usr, QueryType::Def>> &&us);
|
||||
void Update(const Lid2file_id &, int file_id,
|
||||
std::vector<std::pair<Usr, QueryFunc::Def>> &&us);
|
||||
void Update(const Lid2file_id &, int file_id,
|
||||
std::vector<std::pair<Usr, QueryVar::Def>> &&us);
|
||||
std::string_view GetSymbolName(SymbolIdx sym, bool qualified);
|
||||
|
||||
bool HasFunc(Usr usr) const { return func_usr.count({usr}); }
|
||||
|
@ -319,10 +319,12 @@ std::vector<SymbolRef> FindSymbolsAtLocation(WorkingFile* working_file,
|
||||
}
|
||||
}
|
||||
|
||||
for (const SymbolRef& sym : file->def->all_symbols) {
|
||||
for (SymbolRef sym : file->def->all_symbols)
|
||||
if (sym.range.Contains(ls_pos.line, ls_pos.character))
|
||||
symbols.push_back(sym);
|
||||
}
|
||||
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
|
||||
@ -340,8 +342,10 @@ std::vector<SymbolRef> FindSymbolsAtLocation(WorkingFile* working_file,
|
||||
int t = ComputeRangeSize(a.range) - ComputeRangeSize(b.range);
|
||||
if (t)
|
||||
return t < 0;
|
||||
t = (a.role & Role::Definition) - (b.role & Role::Definition);
|
||||
if (t)
|
||||
// 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);
|
||||
|
@ -330,6 +330,7 @@ void Reflect(TVisitor& visitor, IndexFile& value) {
|
||||
if (!gTestOutputMode) {
|
||||
REFLECT_MEMBER(last_write_time);
|
||||
REFLECT_MEMBER(language);
|
||||
REFLECT_MEMBER(lid2path);
|
||||
REFLECT_MEMBER(import_file);
|
||||
REFLECT_MEMBER(args);
|
||||
REFLECT_MEMBER(dependencies);
|
||||
|
Loading…
Reference in New Issue
Block a user