Support uses from other files and improve references in macro replacement-list

This commit is contained in:
Fangrui Song 2018-07-12 19:13:01 -07:00
parent 24e99fd767
commit b759798e5d
10 changed files with 281 additions and 165 deletions

View File

@ -23,14 +23,14 @@ OUTPUT:
"short_name": "a", "short_name": "a",
"kind": 12, "kind": 12,
"storage": 0, "storage": 0,
"declarations": ["12:1-12:4|0|1|1"], "declarations": ["12:1-12:20|0|1|1"],
"spell": "12:1-12:4|0|1|2", "spell": "12:1-12:20|0|1|2",
"extent": "1:1-1:1|0|1|0", "extent": "1:1-1:1|0|1|0",
"declaring_type": 0, "declaring_type": 0,
"bases": [], "bases": [],
"derived": [], "derived": [],
"vars": [], "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": [] "callees": []
}, { }, {
"usr": 14400399977994209582, "usr": 14400399977994209582,
@ -46,7 +46,7 @@ OUTPUT:
"bases": [], "bases": [],
"derived": [], "derived": [],
"vars": [], "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": [] "callees": []
}], }],
"usr2type": [{ "usr2type": [{
@ -75,7 +75,7 @@ OUTPUT:
"spell": "9:11-9:16|0|1|2", "spell": "9:11-9:16|0|1|2",
"extent": "9:1-9:20|0|1|0", "extent": "9:1-9:20|0|1|0",
"type": 53, "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, "kind": 13,
"storage": 0 "storage": 0
}, { }, {
@ -88,7 +88,7 @@ OUTPUT:
"spell": "1:9-1:12|0|1|2", "spell": "1:9-1:12|0|1|2",
"extent": "1:9-3:32|0|1|0", "extent": "1:9-3:32|0|1|0",
"type": 0, "type": 0,
"uses": ["12:1-12:20|0|1|4"], "uses": ["12:1-12:4|0|1|64"],
"kind": 255, "kind": 255,
"storage": 0 "storage": 0
}] }]

View File

@ -20,13 +20,13 @@ OUTPUT:
"kind": 9, "kind": 9,
"storage": 0, "storage": 0,
"declarations": [], "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", "extent": "1:1-1:1|15041163540773201510|2|0",
"declaring_type": 0, "declaring_type": 0,
"bases": [], "bases": [],
"derived": [], "derived": [],
"vars": [], "vars": [],
"uses": [], "uses": ["5:12-5:15|0|1|64|0"],
"callees": [] "callees": []
}], }],
"usr2type": [{ "usr2type": [{
@ -60,7 +60,7 @@ OUTPUT:
"funcs": [13788753348312146871], "funcs": [13788753348312146871],
"vars": [], "vars": [],
"instances": [], "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": [{ "usr2var": [{
"usr": 1569772797058982873, "usr": 1569772797058982873,
@ -72,7 +72,7 @@ OUTPUT:
"spell": "1:9-1:10|0|1|2", "spell": "1:9-1:10|0|1|2",
"extent": "1:9-1:12|0|1|0", "extent": "1:9-1:12|0|1|0",
"type": 0, "type": 0,
"uses": ["8:9-8:10|0|1|4"], "uses": ["8:9-8:10|0|1|64"],
"kind": 255, "kind": 255,
"storage": 0 "storage": 0
}, { }, {
@ -85,7 +85,7 @@ OUTPUT:
"spell": "2:9-2:17|0|1|2", "spell": "2:9-2:17|0|1|2",
"extent": "2:9-2:46|0|1|0", "extent": "2:9-2:46|0|1|0",
"type": 0, "type": 0,
"uses": ["5:3-5:16|0|1|4"], "uses": ["5:3-5:11|0|1|64"],
"kind": 255, "kind": 255,
"storage": 0 "storage": 0
}, { }, {

View File

@ -33,7 +33,7 @@ OUTPUT: funky_enum.h
"qual_name_offset": 0, "qual_name_offset": 0,
"short_name": "A", "short_name": "A",
"hover": "A = 0", "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": [], "declarations": [],
"spell": "4:1-4:2|16985894625255407295|2|1026", "spell": "4:1-4:2|16985894625255407295|2|1026",
"extent": "4:1-4:2|16985894625255407295|2|0", "extent": "4:1-4:2|16985894625255407295|2|0",
@ -47,7 +47,7 @@ OUTPUT: funky_enum.h
"qual_name_offset": 0, "qual_name_offset": 0,
"short_name": "C", "short_name": "C",
"hover": "C = 2", "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": [], "declarations": [],
"spell": "6:1-6:2|16985894625255407295|2|1026", "spell": "6:1-6:2|16985894625255407295|2|1026",
"extent": "6:1-6:2|16985894625255407295|2|0", "extent": "6:1-6:2|16985894625255407295|2|0",
@ -61,7 +61,7 @@ OUTPUT: funky_enum.h
"qual_name_offset": 0, "qual_name_offset": 0,
"short_name": "B", "short_name": "B",
"hover": "B = 1", "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": [], "declarations": [],
"spell": "5:1-5:2|16985894625255407295|2|1026", "spell": "5:1-5:2|16985894625255407295|2|1026",
"extent": "5:1-5:2|16985894625255407295|2|0", "extent": "5:1-5:2|16985894625255407295|2|0",

View File

@ -23,7 +23,7 @@ OUTPUT:
"bases": [], "bases": [],
"derived": [], "derived": [],
"vars": [], "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": [] "callees": []
}, { }, {
"usr": 11404881820527069090, "usr": 11404881820527069090,
@ -53,7 +53,7 @@ OUTPUT:
"spell": "1:9-1:19|0|1|2", "spell": "1:9-1:19|0|1|2",
"extent": "1:9-1:24|0|1|0", "extent": "1:9-1:24|0|1|0",
"type": 0, "type": 0,
"uses": ["6:3-6:33|0|1|4"], "uses": ["6:3-6:13|0|1|64"],
"kind": 255, "kind": 255,
"storage": 0 "storage": 0
}] }]

View File

@ -15,6 +15,7 @@ using ccls::Intern;
#include <clang/Lex/PreprocessorOptions.h> #include <clang/Lex/PreprocessorOptions.h>
#include <llvm/ADT/DenseSet.h> #include <llvm/ADT/DenseSet.h>
#include <llvm/Support/CrashRecoveryContext.h> #include <llvm/Support/CrashRecoveryContext.h>
#include <llvm/Support/Path.h>
#include <llvm/Support/Timer.h> #include <llvm/Support/Timer.h>
using namespace clang; using namespace clang;
using llvm::Timer; using llvm::Timer;
@ -47,8 +48,7 @@ struct IndexParam {
: Unit(Unit), file_consumer(file_consumer) {} : Unit(Unit), file_consumer(file_consumer) {}
IndexFile *ConsumeFile(const FileEntry &File) { IndexFile *ConsumeFile(const FileEntry &File) {
IndexFile *db = IndexFile *db = file_consumer->TryConsumeFile(File, &file_contents);
file_consumer->TryConsumeFile(File, &file_contents);
// If this is the first time we have seen the file (ignoring if we are // If this is the first time we have seen the file (ignoring if we are
// generating an index for it): // 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 { SourceLocation Spell) const {
const FileEntry *FE = SM.getFileEntryForID(SM.getFileID(Spell)); const FileEntry *FE = SM.getFileEntryForID(SM.getFileID(Spell));
if (!FE) return; if (!FE) return;
IndexFile *db = param.ConsumeFile(*FE); auto UID = FE->getUniqueID();
if (!db) return; 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 = Range spell =
FromTokenRange(SM, Ctx->getLangOpts(), SourceRange(Spell, 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) { switch (kind) {
case SymbolKind::Func: case SymbolKind::Func:
db->ToFunc(usr).uses.push_back(use); db->ToFunc(usr).uses.push_back(use);
@ -582,12 +592,8 @@ public:
FileID LocFID; FileID LocFID;
#endif #endif
SourceLocation Spell = SM.getSpellingLoc(Loc); SourceLocation Spell = SM.getSpellingLoc(Loc);
Loc = SM.getFileLoc(Loc); const FileEntry *FE;
Range loc = FromTokenRange(SM, Lang, SourceRange(Loc, Loc)); Range loc;
LocFID = SM.getFileID(Loc);
const FileEntry *FE = SM.getFileEntryForID(LocFID);
if (!FE) {
// TODO
#if LLVM_VERSION_MAJOR < 7 #if LLVM_VERSION_MAJOR < 7
auto P = SM.getExpansionRange(Loc); auto P = SM.getExpansionRange(Loc);
loc = FromCharRange(SM, Ctx->getLangOpts(), SourceRange(P.first, P.second)); loc = FromCharRange(SM, Ctx->getLangOpts(), SourceRange(P.first, P.second));
@ -601,7 +607,6 @@ public:
#endif #endif
if (!FE) if (!FE)
return true; return true;
}
IndexFile *db = param.ConsumeFile(*FE); IndexFile *db = param.ConsumeFile(*FE);
if (!db) if (!db)
return true; return true;
@ -647,7 +652,7 @@ public:
func = &db->ToFunc(usr); func = &db->ToFunc(usr);
do_def_decl(func); do_def_decl(func);
if (Spell != Loc) if (Spell != Loc)
AddMacroUse(SM, usr, SymbolKind::Func, Spell); AddMacroUse(db, SM, usr, SymbolKind::Func, Spell);
if (func->def.detailed_name[0] == '\0') if (func->def.detailed_name[0] == '\0')
SetName(OrigD, info->short_name, info->qualified, func->def); SetName(OrigD, info->short_name, info->qualified, func->def);
if (is_def || is_decl) { if (is_def || is_decl) {
@ -660,7 +665,7 @@ public:
type = &db->ToType(usr); type = &db->ToType(usr);
do_def_decl(type); do_def_decl(type);
if (Spell != Loc) if (Spell != Loc)
AddMacroUse(SM, usr, SymbolKind::Type, Spell); AddMacroUse(db, SM, usr, SymbolKind::Type, Spell);
if (type->def.detailed_name[0] == '\0') if (type->def.detailed_name[0] == '\0')
SetName(OrigD, info->short_name, info->qualified, type->def); SetName(OrigD, info->short_name, info->qualified, type->def);
if (is_def || is_decl) { if (is_def || is_decl) {
@ -673,7 +678,7 @@ public:
var = &db->ToVar(usr); var = &db->ToVar(usr);
do_def_decl(var); do_def_decl(var);
if (Spell != Loc) if (Spell != Loc)
AddMacroUse(SM, usr, SymbolKind::Var, Spell); AddMacroUse(db, SM, usr, SymbolKind::Var, Spell);
if (var->def.detailed_name[0] == '\0') if (var->def.detailed_name[0] == '\0')
SetVarName(OrigD, info->short_name, info->qualified, var->def); SetVarName(OrigD, info->short_name, info->qualified, var->def);
QualType T; QualType T;
@ -994,14 +999,16 @@ public:
void MacroExpands(const Token &Tok, const MacroDefinition &MD, void MacroExpands(const Token &Tok, const MacroDefinition &MD,
SourceRange R, const MacroArgs *Args) override { SourceRange R, const MacroArgs *Args) override {
llvm::sys::fs::UniqueID UniqueID; llvm::sys::fs::UniqueID UniqueID;
auto range = FromTokenRange(SM, param.Ctx->getLangOpts(), R, &UniqueID); SourceLocation L = SM.getSpellingLoc(R.getBegin());
const FileEntry *FE = SM.getFileEntryForID(SM.getFileID(R.getBegin())); const FileEntry *FE = SM.getFileEntryForID(SM.getFileID(L));
if (!FE) if (!FE)
return; return;
if (IndexFile *db = param.ConsumeFile(*FE)) { if (IndexFile *db = param.ConsumeFile(*FE)) {
auto[Name, usr] = GetMacro(Tok); auto[Name, usr] = GetMacro(Tok);
IndexVar &var = db->ToVar(usr); 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, void MacroUndefined(const Token &Tok, const MacroDefinition &MD,
@ -1033,8 +1040,8 @@ public:
}; };
} }
const int IndexFile::kMajorVersion = 16; const int IndexFile::kMajorVersion = 17;
const int IndexFile::kMinorVersion = 1; const int IndexFile::kMinorVersion = 0;
IndexFile::IndexFile(llvm::sys::fs::UniqueID UniqueID, const std::string &path, IndexFile::IndexFile(llvm::sys::fs::UniqueID UniqueID, const std::string &path,
const std::string &contents) const std::string &contents)
@ -1185,6 +1192,9 @@ std::vector<std::unique_ptr<IndexFile>> Index(
for (std::unique_ptr<IndexFile>& entry : result) { for (std::unique_ptr<IndexFile>& entry : result) {
entry->import_file = file; entry->import_file = file;
entry->args = args; 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) { for (auto& it : entry->usr2func) {
// e.g. declaration + out-of-line definition // e.g. declaration + out-of-line definition
Uniquify(it.second.derived); Uniquify(it.second.derived);
@ -1220,7 +1230,7 @@ std::vector<std::unique_ptr<IndexFile>> Index(
// Update dependencies for the file. Do not include the file in its own // Update dependencies for the file. Do not include the file in its own
// dependency set. // dependency set.
for (auto & [ _, path ] : param.SeenUniqueID) for (auto &[_, path] : param.SeenUniqueID)
if (path != entry->path && path != entry->import_file) if (path != entry->path && path != entry->import_file)
entry->dependencies[path] = param.file2write_time[path]; entry->dependencies[path] = param.file2write_time[path];
} }
@ -1232,34 +1242,67 @@ std::vector<std::unique_ptr<IndexFile>> Index(
// |SymbolRef| is serialized this way. // |SymbolRef| is serialized this way.
// |Use| also uses this though it has an extra field |file|, // |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. // which is not used by Index* so it does not need to be serialized.
void Reflect(Reader& visitor, Reference& value) { void Reflect(Reader &vis, Reference &v) {
if (visitor.Format() == SerializeFormat::Json) { if (vis.Format() == SerializeFormat::Json) {
std::string t = visitor.GetString(); std::string t = vis.GetString();
char* s = const_cast<char*>(t.c_str()); char *s = const_cast<char *>(t.c_str());
value.range = Range::FromString(s); v.range = Range::FromString(s);
s = strchr(s, '|'); s = strchr(s, '|');
value.usr = strtoull(s + 1, &s, 10); v.usr = strtoull(s + 1, &s, 10);
value.kind = static_cast<SymbolKind>(strtol(s + 1, &s, 10)); v.kind = static_cast<SymbolKind>(strtol(s + 1, &s, 10));
value.role = static_cast<Role>(strtol(s + 1, &s, 10)); v.role = static_cast<Role>(strtol(s + 1, &s, 10));
} else { } else {
Reflect(visitor, value.range); Reflect(vis, v.range);
Reflect(visitor, value.usr); Reflect(vis, v.usr);
Reflect(visitor, value.kind); Reflect(vis, v.kind);
Reflect(visitor, value.role); Reflect(vis, v.role);
} }
} }
void Reflect(Writer& visitor, Reference& value) { void Reflect(Writer &vis, Reference &v) {
if (visitor.Format() == SerializeFormat::Json) { if (vis.Format() == SerializeFormat::Json) {
char buf[99]; char buf[99];
snprintf(buf, sizeof buf, "%s|%" PRIu64 "|%d|%d", snprintf(buf, sizeof buf, "%s|%" PRIu64 "|%d|%d",
value.range.ToString().c_str(), value.usr, int(value.kind), v.range.ToString().c_str(), v.usr, int(v.kind), int(v.role));
int(value.role));
std::string s(buf); std::string s(buf);
Reflect(visitor, s); Reflect(vis, s);
} else { } else {
Reflect(visitor, value.range); Reflect(vis, v.range);
Reflect(visitor, value.usr); Reflect(vis, v.usr);
Reflect(visitor, value.kind); Reflect(vis, v.kind);
Reflect(visitor, value.role); 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);
} }
} }

View File

@ -52,16 +52,23 @@ struct Reference {
// |id,kind| refer to the referenced entity. // |id,kind| refer to the referenced entity.
struct SymbolRef : Reference {}; 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 // Represents an occurrence of a variable/type, |usr,kind| refer to the lexical
// parent. // parent.
struct Use : Reference { struct Use : Reference {
// |file| is used in Query* but not in Index* // |file| is used in Query* but not in Index*
int file_id = -1; 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(Reader& visitor, Reference& value);
void Reflect(Writer& 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); MAKE_REFLECT_TYPE_PROXY2(clang::StorageClass, uint8_t);
@ -255,6 +262,11 @@ struct IndexFile {
int64_t last_write_time = 0; int64_t last_write_time = 0;
LanguageId language = LanguageId::Unknown; 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 // The path to the translation unit cc file which caused the creation of this
// IndexFile. When parsing a translation unit we generate many IndexFile // IndexFile. When parsing a translation unit we generate many IndexFile
// instances (ie, each header has a separate one). When the user edits a // instances (ie, each header has a separate one). When the user edits a

View File

@ -17,41 +17,43 @@ MAKE_HASHABLE(Use, t.range, t.file_id);
namespace { namespace {
void AssignFileId(int file_id, SymbolRef& ref) { void AssignFileId(const Lid2file_id &, int file_id, SymbolRef &ref) {
if (ref.kind == SymbolKind::File) if (ref.kind == SymbolKind::File)
ref.usr = file_id; 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) if (use.kind == SymbolKind::File)
use.usr = file_id; use.usr = file_id;
if (use.file_id == -1)
use.file_id = file_id; use.file_id = file_id;
else
use.file_id = lid2file_id.find(use.file_id)->second;
} }
template <typename T> template <typename T>
void AssignFileId(int file_id, T&) {} void AssignFileId(const Lid2file_id &, int file_id, T &) {}
template <typename 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) if (x)
AssignFileId(file_id, *x); AssignFileId(lid2file_id, file_id, *x);
} }
template <typename T> template <typename T>
void AssignFileId(int file_id, std::vector<T>& xs) { void AssignFileId(const Lid2file_id &lid2file_id, int file_id,
for (T& x : xs) std::vector<T> &xs) {
AssignFileId(file_id, x); for (T &x : xs)
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()); into.reserve(into.size() + from.size());
for (Use use : from) { for (Use use : from)
use.file_id = file_id;
into.push_back(use); 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()); 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); add_outline(decl, type.usr, SymbolKind::Type);
} }
for (Use use : type.uses) for (Use use : type.uses)
if (use.file_id == -1)
add_all_symbols(use, type.usr, SymbolKind::Type); add_all_symbols(use, type.usr, SymbolKind::Type);
} }
for (auto& it: indexed.usr2func) { for (auto& it: indexed.usr2func) {
@ -110,9 +113,10 @@ QueryFile::DefUpdate BuildFileDefUpdate(const IndexFile& indexed) {
add_all_symbols(use, func.usr, SymbolKind::Func); add_all_symbols(use, func.usr, SymbolKind::Func);
add_outline(use, func.usr, SymbolKind::Func); add_outline(use, func.usr, SymbolKind::Func);
} }
for (Use use : func.uses) { for (Use use : func.uses)
// Make ranges of implicit function calls larger (spanning one more column if (use.file_id == -1) {
// to the left/right). This is hacky but useful. e.g. // 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 // textDocument/definition on the space/semicolon in `A a;` or `return
// 42;` will take you to the constructor. // 42;` will take you to the constructor.
if (use.role & Role::Implicit) { if (use.role & Role::Implicit) {
@ -134,6 +138,7 @@ QueryFile::DefUpdate BuildFileDefUpdate(const IndexFile& indexed) {
add_outline(decl, var.usr, SymbolKind::Var); add_outline(decl, var.usr, SymbolKind::Var);
} }
for (Use use : var.uses) for (Use use : var.uses)
if (use.file_id == -1)
add_all_symbols(use, var.usr, SymbolKind::Var); add_all_symbols(use, var.usr, SymbolKind::Var);
} }
@ -162,19 +167,16 @@ bool TryReplaceDef(llvm::SmallVectorImpl<Q>& def_list, Q&& def) {
} // namespace } // namespace
// ----------------------
// INDEX THREAD FUNCTIONS
// ----------------------
// static
IndexUpdate IndexUpdate::CreateDelta(IndexFile* previous, IndexUpdate IndexUpdate::CreateDelta(IndexFile* previous,
IndexFile* current) { IndexFile* current) {
IndexUpdate r; IndexUpdate r;
static IndexFile empty(llvm::sys::fs::UniqueID(0, 0), current->path, static IndexFile empty(llvm::sys::fs::UniqueID(0, 0), current->path,
"<empty>"); "<empty>");
if (!previous) if (previous)
r.prev_lid2path = std::move(previous->lid2path);
else
previous = &empty; previous = &empty;
r.lid2path = std::move(current->lid2path);
r.files_def_update = BuildFileDefUpdate(std::move(*current)); r.files_def_update = BuildFileDefUpdate(std::move(*current));
r.funcs_hint = int(current->usr2func.size() - previous->usr2func.size()); r.funcs_hint = int(current->usr2func.size() - previous->usr2func.size());
@ -282,23 +284,59 @@ void DB::RemoveUsrs(SymbolKind kind,
} }
} }
void DB::ApplyIndexUpdate(IndexUpdate* u) { void DB::ApplyIndexUpdate(IndexUpdate *u) {
#define REMOVE_ADD(C, F) \ #define REMOVE_ADD(C, F) \
for (auto& it : u->C##s_##F) { \ for (auto &it : u->C##s_##F) { \
auto R = C##_usr.try_emplace({it.first}, C##_usr.size()); \ auto R = C##_usr.try_emplace({it.first}, C##_usr.size()); \
if (R.second) \ if (R.second) \
C##s.emplace_back().usr = it.first; \ C##s.emplace_back().usr = it.first; \
auto& entity = C##s[R.first->second]; \ 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); \ RemoveRange(entity.F, it.second.first); \
AssignFileId(u->file_id, it.second.second); \ AssignFileId(lid2file_id, u->file_id, it.second.second); \
AddRange(u->file_id, entity.F, 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) if (u->files_removed)
files[name2file_id[LowerPathIfInsensitive(*u->files_removed)]].def = files[name2file_id[LowerPathIfInsensitive(*u->files_removed)]].def =
std::nullopt; 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; const double grow = 1.3;
size_t t; size_t t;
@ -309,10 +347,11 @@ void DB::ApplyIndexUpdate(IndexUpdate* u) {
func_usr.reserve(t); func_usr.reserve(t);
} }
RemoveUsrs(SymbolKind::Func, u->file_id, u->funcs_removed); 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, declarations);
REMOVE_ADD(func, derived); 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()) { if ((t = types.size() + u->types_hint) > types.capacity()) {
t = size_t(t * grow); t = size_t(t * grow);
@ -320,11 +359,12 @@ void DB::ApplyIndexUpdate(IndexUpdate* u) {
type_usr.reserve(t); type_usr.reserve(t);
} }
RemoveUsrs(SymbolKind::Type, u->file_id, u->types_removed); 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, declarations);
REMOVE_ADD(type, derived); REMOVE_ADD(type, derived);
REMOVE_ADD(type, instances); 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()) { if ((t = vars.size() + u->vars_hint) > vars.capacity()) {
t = size_t(t * grow); t = size_t(t * grow);
@ -332,30 +372,37 @@ void DB::ApplyIndexUpdate(IndexUpdate* u) {
var_usr.reserve(t); var_usr.reserve(t);
} }
RemoveUsrs(SymbolKind::Var, u->file_id, u->vars_removed); 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, declarations);
REMOVE_ADD(var, uses); for (auto & [ usr, p ] : u->vars_uses)
UpdateUses(usr, SymbolKind::Var, var_usr, vars, p);
#undef REMOVE_ADD #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(); int id = files.size();
auto it = name2file_id.try_emplace(LowerPathIfInsensitive(u.first.path), id); it.first->second = files.emplace_back().id = id;
if (it.second) }
files.emplace_back().id = id; return it.first->second;
QueryFile& existing = files[it.first->second];
existing.def = u.first;
return existing.id;
} }
void DB::Update(int file_id, std::vector<std::pair<Usr, QueryFunc::Def>>&& us) { int DB::Update(QueryFile::DefUpdate&& u) {
for (auto& u : us) { 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; auto& def = u.second;
assert(def.detailed_name[0]); assert(def.detailed_name[0]);
AssignFileId(file_id, def.spell); AssignFileId(lid2file_id, file_id, def.spell);
AssignFileId(file_id, def.extent); AssignFileId(lid2file_id, file_id, def.extent);
AssignFileId(file_id, def.callees); AssignFileId(lid2file_id, file_id, def.callees);
auto R = func_usr.try_emplace({u.first}, func_usr.size()); auto R = func_usr.try_emplace({u.first}, func_usr.size());
if (R.second) if (R.second)
funcs.emplace_back(); 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,
for (auto& u : us) { std::vector<std::pair<Usr, QueryType::Def>> &&us) {
for (auto &u : us) {
auto& def = u.second; auto& def = u.second;
assert(def.detailed_name[0]); assert(def.detailed_name[0]);
AssignFileId(file_id, def.spell); AssignFileId(lid2file_id, file_id, def.spell);
AssignFileId(file_id, def.extent); AssignFileId(lid2file_id, file_id, def.extent);
auto R = type_usr.try_emplace({u.first}, type_usr.size()); auto R = type_usr.try_emplace({u.first}, type_usr.size());
if (R.second) if (R.second)
types.emplace_back(); 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; existing.usr = u.first;
if (!TryReplaceDef(existing.def, std::move(def))) if (!TryReplaceDef(existing.def, std::move(def)))
existing.def.push_back(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,
for (auto& u : us) { std::vector<std::pair<Usr, QueryVar::Def>> &&us) {
for (auto &u : us) {
auto& def = u.second; auto& def = u.second;
assert(def.detailed_name[0]); assert(def.detailed_name[0]);
AssignFileId(file_id, def.spell); AssignFileId(lid2file_id, file_id, def.spell);
AssignFileId(file_id, def.extent); AssignFileId(lid2file_id, file_id, def.extent);
auto R = var_usr.try_emplace({u.first}, var_usr.size()); auto R = var_usr.try_emplace({u.first}, var_usr.size());
if (R.second) if (R.second)
vars.emplace_back(); vars.emplace_back();

View File

@ -28,6 +28,7 @@ struct QueryFile {
int id = -1; int id = -1;
std::optional<Def> def; std::optional<Def> def;
std::unordered_map<SymbolRef, int> symbol2refcnt;
}; };
template <typename Q, typename QDef> template <typename Q, typename QDef>
@ -85,6 +86,9 @@ struct IndexUpdate {
// Dummy one to refresh all semantic highlight. // Dummy one to refresh all semantic highlight.
bool refresh = false; bool refresh = false;
decltype(IndexFile::lid2path) prev_lid2path;
decltype(IndexFile::lid2path) lid2path;
// File updates. // File updates.
std::optional<std::string> files_removed; std::optional<std::string> files_removed;
std::optional<QueryFile::DefUpdate> files_def_update; 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; } 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 // The query database is heavily optimized for fast queries. It is stored
// in-memory. // in-memory.
struct DB { struct DB {
@ -135,15 +141,17 @@ struct DB {
std::vector<QueryType> types; std::vector<QueryType> types;
std::vector<QueryVar> vars; std::vector<QueryVar> vars;
// Marks the given Usrs as invalid. void RemoveUsrs(SymbolKind kind, int file_id, const std::vector<Usr>& to_remove);
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);
// Insert the contents of |update| into |db|. // Insert the contents of |update| into |db|.
void ApplyIndexUpdate(IndexUpdate* update); void ApplyIndexUpdate(IndexUpdate* update);
int GetFileId(const std::string& path);
int Update(QueryFile::DefUpdate&& u); int Update(QueryFile::DefUpdate&& u);
void Update(int file_id, std::vector<std::pair<Usr, QueryType::Def>>&& us); void Update(const Lid2file_id &, int file_id,
void Update(int file_id, std::vector<std::pair<Usr, QueryFunc::Def>>&& us); std::vector<std::pair<Usr, QueryType::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, 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); std::string_view GetSymbolName(SymbolIdx sym, bool qualified);
bool HasFunc(Usr usr) const { return func_usr.count({usr}); } bool HasFunc(Usr usr) const { return func_usr.count({usr}); }

View File

@ -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)) if (sym.range.Contains(ls_pos.line, ls_pos.character))
symbols.push_back(sym); 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 // Order shorter ranges first, since they are more detailed/precise. This is
// important for macros which generate code so that we can resolving the // 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); int t = ComputeRangeSize(a.range) - ComputeRangeSize(b.range);
if (t) if (t)
return t < 0; return t < 0;
t = (a.role & Role::Definition) - (b.role & Role::Definition); // MacroExpansion
if (t) 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; return t > 0;
// operator> orders Var/Func before Type. // operator> orders Var/Func before Type.
t = static_cast<int>(a.kind) - static_cast<int>(b.kind); t = static_cast<int>(a.kind) - static_cast<int>(b.kind);

View File

@ -330,6 +330,7 @@ void Reflect(TVisitor& visitor, IndexFile& value) {
if (!gTestOutputMode) { if (!gTestOutputMode) {
REFLECT_MEMBER(last_write_time); REFLECT_MEMBER(last_write_time);
REFLECT_MEMBER(language); REFLECT_MEMBER(language);
REFLECT_MEMBER(lid2path);
REFLECT_MEMBER(import_file); REFLECT_MEMBER(import_file);
REFLECT_MEMBER(args); REFLECT_MEMBER(args);
REFLECT_MEMBER(dependencies); REFLECT_MEMBER(dependencies);