diff --git a/index_tests/macros/complex.cc b/index_tests/macros/complex.cc index 70d81385..0fed44b4 100644 --- a/index_tests/macros/complex.cc +++ b/index_tests/macros/complex.cc @@ -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 }] diff --git a/index_tests/macros/foo.cc b/index_tests/macros/foo.cc index e1c33920..ddc7a704 100644 --- a/index_tests/macros/foo.cc +++ b/index_tests/macros/foo.cc @@ -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 }, { diff --git a/index_tests/multi_file/funky_enum.cc b/index_tests/multi_file/funky_enum.cc index d3504989..c76b4d47 100644 --- a/index_tests/multi_file/funky_enum.cc +++ b/index_tests/multi_file/funky_enum.cc @@ -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", diff --git a/index_tests/usage/func_called_from_macro_argument.cc b/index_tests/usage/func_called_from_macro_argument.cc index ce4e865c..42e26b30 100644 --- a/index_tests/usage/func_called_from_macro_argument.cc +++ b/index_tests/usage/func_called_from_macro_argument.cc @@ -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 }] diff --git a/src/indexer.cc b/src/indexer.cc index 36d9c4bf..d7178e4f 100644 --- a/src/indexer.cc +++ b/src/indexer.cc @@ -15,6 +15,7 @@ using ccls::Intern; #include #include #include +#include #include 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,26 +592,21 @@ 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)); - LocFID = SM.getFileID(P.first); - FE = SM.getFileEntryForID(LocFID); + auto P = SM.getExpansionRange(Loc); + loc = FromCharRange(SM, Ctx->getLangOpts(), SourceRange(P.first, P.second)); + LocFID = SM.getFileID(P.first); + FE = SM.getFileEntryForID(LocFID); #else - auto R = SM.getExpansionRange(Loc); - loc = FromTokenRange(SM, Lang, R.getAsRange()); - LocFID = SM.getFileID(R.getBegin()); - FE = SM.getFileEntryForID(LocFID); + auto R = SM.getExpansionRange(Loc); + loc = FromTokenRange(SM, Lang, R.getAsRange()); + LocFID = SM.getFileID(R.getBegin()); + FE = SM.getFileEntryForID(LocFID); #endif - if (!FE) - return true; - } + 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> Index( for (std::unique_ptr& 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); @@ -1220,7 +1230,7 @@ std::vector> Index( // Update dependencies for the file. Do not include the file in its own // dependency set. - for (auto & [ _, path ] : param.SeenUniqueID) + for (auto &[_, path] : param.SeenUniqueID) if (path != entry->path && path != entry->import_file) entry->dependencies[path] = param.file2write_time[path]; } @@ -1232,34 +1242,67 @@ std::vector> 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(); - char* s = const_cast(t.c_str()); - value.range = Range::FromString(s); +void Reflect(Reader &vis, Reference &v) { + if (vis.Format() == SerializeFormat::Json) { + std::string t = vis.GetString(); + char *s = const_cast(t.c_str()); + v.range = Range::FromString(s); s = strchr(s, '|'); - value.usr = strtoull(s + 1, &s, 10); - value.kind = static_cast(strtol(s + 1, &s, 10)); - value.role = static_cast(strtol(s + 1, &s, 10)); + v.usr = strtoull(s + 1, &s, 10); + v.kind = static_cast(strtol(s + 1, &s, 10)); + v.role = static_cast(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(t.c_str()); + v.range = Range::FromString(s); + s = strchr(s, '|'); + v.usr = strtoull(s + 1, &s, 10); + v.kind = static_cast(strtol(s + 1, &s, 10)); + v.role = static_cast(strtol(s + 1, &s, 10)); + if (*s == '|') + v.file_id = static_cast(strtol(s + 1, &s, 10)); + } else { + Reflect(vis, static_cast(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(v)); + Reflect(vis, v.file_id); } } diff --git a/src/indexer.h b/src/indexer.h index 681d2436..7d2bc137 100644 --- a/src/indexer.h +++ b/src/indexer.h @@ -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> + uid2lid_and_path; + std::vector> 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 diff --git a/src/query.cc b/src/query.cc index 316e3448..927a54ab 100644 --- a/src/query.cc +++ b/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; - use.file_id = file_id; -} - -template -void AssignFileId(int file_id, T&) {} - -template -void AssignFileId(int file_id, Maybe& x) { - if (x) - AssignFileId(file_id, *x); -} - -template -void AssignFileId(int file_id, std::vector& xs) { - for (T& x : xs) - AssignFileId(file_id, x); -} - -void AddRange(int file_id, std::vector& into, const std::vector& from) { - into.reserve(into.size() + from.size()); - for (Use use : from) { + if (use.file_id == -1) use.file_id = file_id; - into.push_back(use); - } + else + use.file_id = lid2file_id.find(use.file_id)->second; } -void AddRange(int _, std::vector& into, const std::vector& from) { +template +void AssignFileId(const Lid2file_id &, int file_id, T &) {} + +template +void AssignFileId(const Lid2file_id &lid2file_id, int file_id, Maybe &x) { + if (x) + AssignFileId(lid2file_id, file_id, *x); +} + +template +void AssignFileId(const Lid2file_id &lid2file_id, int file_id, + std::vector &xs) { + for (T &x : xs) + AssignFileId(lid2file_id, file_id, x); +} + +void AddRange(std::vector& into, const std::vector& from) { + into.reserve(into.size() + from.size()); + for (Use use : from) + into.push_back(use); +} + +void AddRange(std::vector& into, const std::vector& from) { into.insert(into.end(), from.begin(), from.end()); } @@ -98,7 +100,8 @@ QueryFile::DefUpdate BuildFileDefUpdate(const IndexFile& indexed) { add_outline(decl, type.usr, SymbolKind::Type); } for (Use use : type.uses) - add_all_symbols(use, type.usr, SymbolKind::Type); + if (use.file_id == -1) + add_all_symbols(use, type.usr, SymbolKind::Type); } for (auto& it: indexed.usr2func) { const IndexFunc& func = it.second; @@ -110,18 +113,19 @@ 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. - // textDocument/definition on the space/semicolon in `A a;` or `return - // 42;` will take you to the constructor. - if (use.role & Role::Implicit) { - if (use.range.start.column > 0) - use.range.start.column--; - use.range.end.column++; + 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) { + if (use.range.start.column > 0) + use.range.start.column--; + use.range.end.column++; + } + add_all_symbols(use, func.usr, SymbolKind::Func); } - add_all_symbols(use, func.usr, SymbolKind::Func); - } } for (auto& it : indexed.usr2var) { const IndexVar& var = it.second; @@ -134,7 +138,8 @@ QueryFile::DefUpdate BuildFileDefUpdate(const IndexFile& indexed) { add_outline(decl, var.usr, SymbolKind::Var); } for (Use use : var.uses) - add_all_symbols(use, var.usr, SymbolKind::Var); + if (use.file_id == -1) + add_all_symbols(use, var.usr, SymbolKind::Var); } std::sort(def.outline.begin(), def.outline.end(), @@ -162,19 +167,16 @@ bool TryReplaceDef(llvm::SmallVectorImpl& 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, ""); - 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()); @@ -282,23 +284,59 @@ void DB::RemoveUsrs(SymbolKind kind, } } -void DB::ApplyIndexUpdate(IndexUpdate* u) { -#define REMOVE_ADD(C, F) \ - for (auto& it : u->C##s_##F) { \ - auto R = C##_usr.try_emplace({it.first}, C##_usr.size()); \ - if (R.second) \ - C##s.emplace_back().usr = it.first; \ - auto& entity = C##s[R.first->second]; \ - AssignFileId(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); \ +void DB::ApplyIndexUpdate(IndexUpdate *u) { +#define REMOVE_ADD(C, F) \ + for (auto &it : u->C##s_##F) { \ + auto R = C##_usr.try_emplace({it.first}, C##_usr.size()); \ + if (R.second) \ + C##s.emplace_back().usr = it.first; \ + auto &entity = C##s[R.first->second]; \ + AssignFileId(prev_lid2file_id, u->file_id, it.second.first); \ + RemoveRange(entity.F, it.second.first); \ + AssignFileId(lid2file_id, u->file_id, it.second.second); \ + AddRange(entity.F, it.second.second); \ } + std::unordered_map 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 &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 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; +int DB::GetFileId(const std::string& path) { + auto it = name2file_id.try_emplace(LowerPathIfInsensitive(path)); + if (it.second) { + int id = files.size(); + it.first->second = files.emplace_back().id = id; + } + return it.first->second; } -void DB::Update(int file_id, std::vector>&& us) { - for (auto& u : 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> &&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>&& us) { } } -void DB::Update(int file_id, std::vector>&& us) { - for (auto& u : us) { +void DB::Update(const Lid2file_id &lid2file_id, int file_id, + std::vector> &&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>&& 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>&& us) { - for (auto& u : us) { +void DB::Update(const Lid2file_id &lid2file_id, int file_id, + std::vector> &&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(); diff --git a/src/query.h b/src/query.h index 70eb0672..65221144 100644 --- a/src/query.h +++ b/src/query.h @@ -28,6 +28,7 @@ struct QueryFile { int id = -1; std::optional def; + std::unordered_map symbol2refcnt; }; template @@ -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 files_removed; std::optional files_def_update; @@ -125,6 +129,8 @@ struct llvm::DenseMapInfo { static bool isEqual(WrappedUsr l, WrappedUsr r) { return l.usr == r.usr; } }; +using Lid2file_id = std::unordered_map; + // The query database is heavily optimized for fast queries. It is stored // in-memory. struct DB { @@ -135,15 +141,17 @@ struct DB { std::vector types; std::vector vars; - // Marks the given Usrs as invalid. - void RemoveUsrs(SymbolKind usr_kind, const std::vector& to_remove); - void RemoveUsrs(SymbolKind usr_kind, int file_id, const std::vector& to_remove); + void RemoveUsrs(SymbolKind kind, int file_id, const std::vector& 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>&& us); - void Update(int file_id, std::vector>&& us); - void Update(int file_id, std::vector>&& us); + void Update(const Lid2file_id &, int file_id, + std::vector> &&us); + void Update(const Lid2file_id &, int file_id, + std::vector> &&us); + void Update(const Lid2file_id &, int file_id, + std::vector> &&us); std::string_view GetSymbolName(SymbolIdx sym, bool qualified); bool HasFunc(Usr usr) const { return func_usr.count({usr}); } diff --git a/src/query_utils.cc b/src/query_utils.cc index 0ced17ba..0a61ffd2 100644 --- a/src/query_utils.cc +++ b/src/query_utils.cc @@ -319,10 +319,12 @@ std::vector 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 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(a.kind) - static_cast(b.kind); diff --git a/src/serializer.cc b/src/serializer.cc index 423154bd..8ab39b81 100644 --- a/src/serializer.cc +++ b/src/serializer.cc @@ -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);