// Copyright 2017-2018 ccls Authors // SPDX-License-Identifier: Apache-2.0 #pragma once #include "clang_utils.h" #include "language.h" #include "lsp.h" #include "lsp_diagnostic.h" #include "maybe.h" #include "position.h" #include "serializer.h" #include "symbol.h" #include "utils.h" #include #include #include #include #include #include #include #include #include using Usr = uint64_t; struct SymbolIdx { Usr usr; SymbolKind kind; bool operator==(const SymbolIdx &o) const { return usr == o.usr && kind == o.kind; } bool operator<(const SymbolIdx &o) const { return usr != o.usr ? usr < o.usr : kind < o.kind; } }; MAKE_REFLECT_STRUCT(SymbolIdx, usr, kind); struct Reference { Range range; Usr usr; SymbolKind kind; Role role; bool Valid() const { return range.Valid(); } operator SymbolIdx() const { return {usr, kind}; } std::tuple ToTuple() const { return std::make_tuple(range, usr, kind, role); } bool operator==(const Reference &o) const { return ToTuple() == o.ToTuple(); } bool operator<(const Reference &o) const { return ToTuple() < o.ToTuple(); } }; // |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 { // lexical container info is ignored. return range == o.range && file_id == o.file_id; } }; MAKE_HASHABLE(Use, t.range, t.file_id) struct DeclRef : Use { Range extent; }; MAKE_HASHABLE(DeclRef, t.range, t.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); void Reflect(Reader &visitor, DeclRef &value); void Reflect(Writer &visitor, DeclRef &value); template struct NameMixin { std::string_view Name(bool qualified) const { auto self = static_cast(this); return qualified ? std::string_view(self->detailed_name + self->qual_name_offset, self->short_name_offset - self->qual_name_offset + self->short_name_size) : std::string_view(self->detailed_name + self->short_name_offset, self->short_name_size); } }; struct FuncDef : NameMixin { // General metadata. const char *detailed_name = ""; const char *hover = ""; const char *comments = ""; Maybe spell; Maybe extent; // Method this method overrides. std::vector bases; // Local variables or parameters. std::vector vars; // Functions that this function calls. std::vector callees; int file_id = -1; int16_t qual_name_offset = 0; int16_t short_name_offset = 0; int16_t short_name_size = 0; lsSymbolKind kind = lsSymbolKind::Unknown; uint8_t storage = clang::SC_None; std::vector GetBases() const { return bases; } }; MAKE_REFLECT_STRUCT(FuncDef, detailed_name, qual_name_offset, short_name_offset, short_name_size, kind, storage, hover, comments, spell, extent, bases, vars, callees); struct IndexFunc : NameMixin { using Def = FuncDef; Usr usr; Def def; std::vector declarations; std::vector uses; std::vector derived; }; struct TypeDef : NameMixin { const char *detailed_name = ""; const char *hover = ""; const char *comments = ""; Maybe spell; Maybe extent; std::vector bases; // Types, functions, and variables defined in this type. std::vector types; std::vector funcs; std::vector> vars; // If set, then this is the same underlying type as the given value (ie, this // type comes from a using or typedef statement). Usr alias_of = 0; int file_id = -1; int16_t qual_name_offset = 0; int16_t short_name_offset = 0; int16_t short_name_size = 0; lsSymbolKind kind = lsSymbolKind::Unknown; std::vector GetBases() const { return bases; } }; MAKE_REFLECT_STRUCT(TypeDef, detailed_name, qual_name_offset, short_name_offset, short_name_size, kind, hover, comments, spell, extent, alias_of, bases, types, funcs, vars); struct IndexType { using Def = TypeDef; Usr usr; Def def; std::vector declarations; std::vector uses; std::vector derived; std::vector instances; }; struct VarDef : NameMixin { // General metadata. const char *detailed_name = ""; const char *hover = ""; const char *comments = ""; Maybe spell; Maybe extent; // Type of the variable. Usr type = 0; int file_id = -1; int16_t qual_name_offset = 0; int16_t short_name_offset = 0; int16_t short_name_size = 0; lsSymbolKind kind = lsSymbolKind::Unknown; // Note a variable may have instances of both |None| and |Extern| // (declaration). uint8_t storage = clang::SC_None; bool is_local() const { return spell && spell->kind == SymbolKind::Func && storage == clang::SC_None; } std::vector GetBases() const { return {}; } }; MAKE_REFLECT_STRUCT(VarDef, detailed_name, qual_name_offset, short_name_offset, short_name_size, hover, comments, spell, extent, type, kind, storage); struct IndexVar { using Def = VarDef; Usr usr; Def def; std::vector declarations; std::vector uses; }; struct IndexInclude { // Line that has the include directive. We don't have complete range // information - a line is good enough for clicking. int line = 0; // Absolute path to the index. const char *resolved_path; }; namespace std { template <> struct hash { std::size_t operator()(llvm::sys::fs::UniqueID ID) const { size_t ret = ID.getDevice(); hash_combine(ret, ID.getFile()); return ret; } }; } // namespace std struct IndexFile { // For both JSON and MessagePack cache files. static const int kMajorVersion; // For MessagePack cache files. // JSON has good forward compatibility because field addition/deletion do not // harm but currently no efforts have been made to make old MessagePack cache // files accepted by newer ccls. static const int kMinorVersion; llvm::sys::fs::UniqueID UniqueID; std::string path; std::vector args; // This is unfortunately time_t as used by clang::FileEntry int64_t mtime = 0; LanguageId language = LanguageId::C; // 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 // header we need to lookup the original translation unit and reindex that. std::string import_file; // Source ranges that were not processed. std::vector skipped_ranges; std::vector includes; llvm::DenseMap dependencies; std::unordered_map usr2func; std::unordered_map usr2type; std::unordered_map usr2var; // File contents at the time of index. Not serialized. std::string file_contents; IndexFile(llvm::sys::fs::UniqueID UniqueID, const std::string &path, const std::string &contents); IndexFunc &ToFunc(Usr usr); IndexType &ToType(Usr usr); IndexVar &ToVar(Usr usr); std::string ToString(); }; struct CompletionManager; struct WorkingFiles; struct VFS; namespace ccls::idx { void Init(); std::vector> Index(CompletionManager *complete, WorkingFiles *wfiles, VFS *vfs, const std::string &opt_wdir, const std::string &file, const std::vector &args, const std::vector> &remapped, bool &ok); } // namespace ccls::idx