// Copyright 2017-2018 ccls Authors // SPDX-License-Identifier: Apache-2.0 #pragma once #include "indexer.hh" #include "serializer.hh" #include "working_files.hh" #include #include #include namespace llvm { template <> struct DenseMapInfo { static inline ccls::ExtentRef getEmptyKey() { return {}; } static inline ccls::ExtentRef getTombstoneKey() { return {{ccls::Range(), ccls::Usr(-1)}}; } static unsigned getHashValue(ccls::ExtentRef sym) { return std::hash()(sym); } static bool isEqual(ccls::ExtentRef l, ccls::ExtentRef r) { return l == r; } }; } // namespace llvm namespace ccls { struct QueryFile { struct Def { std::string path; std::vector args; LanguageId language; // Includes in the file. std::vector includes; // Parts of the file which are disabled. std::vector skipped_ranges; // Used by |$ccls/reload|. std::vector dependencies; }; using DefUpdate = std::pair; int id = -1; std::optional def; // `extent` is valid => declaration; invalid => regular reference llvm::DenseMap symbol2refcnt; }; template struct QueryEntity { using Def = QDef; Def *anyDef() { Def *ret = nullptr; for (auto &i : static_cast(this)->def) { ret = &i; if (i.spell) break; } return ret; } const Def *anyDef() const { return const_cast(this)->anyDef(); } }; template using Update = std::unordered_map, std::vector>>; struct QueryFunc : QueryEntity> { Usr usr; llvm::SmallVector def; std::vector declarations; std::vector derived; std::vector uses; }; struct QueryType : QueryEntity> { Usr usr; llvm::SmallVector def; std::vector declarations; std::vector derived; std::vector instances; std::vector uses; }; struct QueryVar : QueryEntity { Usr usr; llvm::SmallVector def; std::vector declarations; std::vector uses; }; struct IndexUpdate { // Creates a new IndexUpdate based on the delta from previous to current. If // no delta computation should be done just pass null for previous. static IndexUpdate createDelta(IndexFile *previous, IndexFile *current); int file_id; // 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; // Function updates. int funcs_hint; std::vector> funcs_removed; std::vector> funcs_def_update; Update funcs_declarations; Update funcs_uses; Update funcs_derived; // Type updates. int types_hint; std::vector> types_removed; std::vector> types_def_update; Update types_declarations; Update types_uses; Update types_derived; Update types_instances; // Variable updates. int vars_hint; std::vector> vars_removed; std::vector> vars_def_update; Update vars_declarations; Update vars_uses; }; struct DenseMapInfoForUsr { static inline Usr getEmptyKey() { return 0; } static inline Usr getTombstoneKey() { return ~0ULL; } static unsigned getHashValue(Usr w) { return w; } static bool isEqual(Usr l, Usr r) { return l == r; } }; using Lid2file_id = std::unordered_map; // The query database is heavily optimized for fast queries. It is stored // in-memory. struct DB { std::vector files; llvm::StringMap name2file_id; llvm::DenseMap func_usr, type_usr, var_usr; llvm::SmallVector funcs; llvm::SmallVector types; llvm::SmallVector vars; void clear(); template void removeUsrs(Kind 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(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); std::vector getFileSet(const std::vector &folders); bool hasFunc(Usr usr) const { return func_usr.count(usr); } bool hasType(Usr usr) const { return type_usr.count(usr); } bool hasVar(Usr usr) const { return var_usr.count(usr); } QueryFunc &getFunc(Usr usr) { return funcs[func_usr[usr]]; } QueryType &getType(Usr usr) { return types[type_usr[usr]]; } QueryVar &getVar(Usr usr) { return vars[var_usr[usr]]; } QueryFile &getFile(SymbolIdx ref) { return files[ref.usr]; } QueryFunc &getFunc(SymbolIdx ref) { return getFunc(ref.usr); } QueryType &getType(SymbolIdx ref) { return getType(ref.usr); } QueryVar &getVar(SymbolIdx ref) { return getVar(ref.usr); } }; Maybe getDefinitionSpell(DB *db, SymbolIdx sym); // Get defining declaration (if exists) or an arbitrary declaration (otherwise) // for each id. std::vector getFuncDeclarations(DB *, const std::vector &); std::vector getFuncDeclarations(DB *, const Vec &); std::vector getTypeDeclarations(DB *, const std::vector &); std::vector getVarDeclarations(DB *, const std::vector &, unsigned); // Get non-defining declarations. std::vector &getNonDefDeclarations(DB *db, SymbolIdx sym); std::vector getUsesForAllBases(DB *db, QueryFunc &root); std::vector getUsesForAllDerived(DB *db, QueryFunc &root); std::optional getLsRange(WorkingFile *working_file, const Range &location); DocumentUri getLsDocumentUri(DB *db, int file_id, std::string *path); DocumentUri getLsDocumentUri(DB *db, int file_id); std::optional getLsLocation(DB *db, WorkingFiles *wfiles, Use use); std::optional getLsLocation(DB *db, WorkingFiles *wfiles, SymbolRef sym, int file_id); LocationLink getLocationLink(DB *db, WorkingFiles *wfiles, DeclRef dr); // Returns a symbol. The symbol will *NOT* have a location assigned. std::optional getSymbolInfo(DB *db, SymbolIdx sym, bool detailed); std::vector findSymbolsAtLocation(WorkingFile *working_file, QueryFile *file, Position &ls_pos, bool smallest = false); template void withEntity(DB *db, SymbolIdx sym, Fn &&fn) { switch (sym.kind) { case Kind::Invalid: case Kind::File: break; case Kind::Func: fn(db->getFunc(sym)); break; case Kind::Type: fn(db->getType(sym)); break; case Kind::Var: fn(db->getVar(sym)); break; } } template void eachEntityDef(DB *db, SymbolIdx sym, Fn &&fn) { withEntity(db, sym, [&](const auto &entity) { for (auto &def : entity.def) if (!fn(def)) break; }); } template void eachOccurrence(DB *db, SymbolIdx sym, bool include_decl, Fn &&fn) { withEntity(db, sym, [&](const auto &entity) { for (Use use : entity.uses) fn(use); if (include_decl) { for (auto &def : entity.def) if (def.spell) fn(*def.spell); for (Use use : entity.declarations) fn(use); } }); } SymbolKind getSymbolKind(DB *db, SymbolIdx sym); template void eachDefinedFunc(DB *db, const C &usrs, Fn &&fn) { for (Usr usr : usrs) { auto &obj = db->getFunc(usr); if (!obj.def.empty()) fn(obj); } } } // namespace ccls