#pragma once #include "clang_cursor.h" #include "clang_translation_unit.h" #include "clang_utils.h" #include "file_consumer.h" #include "language.h" #include "lsp.h" #include "maybe.h" #include "nt_string.h" #include "performance.h" #include "position.h" #include "serializer.h" #include "symbol.h" #include "utils.h" #include #include #include #include #include #include struct IndexFile; struct IndexType; struct IndexFunc; struct IndexVar; struct QueryFile; using RawId = uint32_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 { SymbolRef() = default; SymbolRef(Range range, Usr usr, SymbolKind kind, Role role) : Reference{range, usr, kind, role} {} }; // Represents an occurrence of a variable/type, |id,kind| refer to the lexical // parent. struct Use : Reference { // |file| is used in Query* but not in Index* int file_id = -1; }; void Reflect(Reader& visitor, Reference& value); void Reflect(Writer& visitor, Reference& value); template struct NameMixin { std::string_view Name(bool qualified) const { auto self = static_cast(this); return qualified ? std::string_view( self->detailed_name.c_str() + self->qual_name_offset, self->short_name_offset - self->qual_name_offset + self->short_name_size) : std::string_view(self->detailed_name.c_str() + self->short_name_offset, self->short_name_size); } }; struct FuncDef : NameMixin { // General metadata. std::string detailed_name; NtString hover; NtString 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; // Type which declares this one (ie, it is a method) Usr declaring_type = 0; int16_t qual_name_offset = 0; int16_t short_name_offset = 0; int16_t short_name_size = 0; lsSymbolKind kind = lsSymbolKind::Unknown; StorageClass storage = StorageClass::Invalid; bool operator==(const FuncDef& o) const { return detailed_name == o.detailed_name && spell == o.spell && extent == o.extent && declaring_type == o.declaring_type && bases == o.bases && vars == o.vars && callees == o.callees && kind == o.kind && storage == o.storage && hover == o.hover && comments == o.comments; } }; MAKE_REFLECT_STRUCT(FuncDef, detailed_name, qual_name_offset, short_name_offset, short_name_size, kind, storage, hover, comments, spell, extent, declaring_type, 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 { // General metadata. std::string detailed_name; NtString hover; NtString comments; // While a class/type can technically have a separate declaration/definition, // it doesn't really happen in practice. The declaration never contains // comments or insightful information. The user always wants to jump from // the declaration to the definition - never the other way around like in // functions and (less often) variables. // // It's also difficult to identify a `class Foo;` statement with the clang // indexer API (it's doable using cursor AST traversal), so we don't bother // supporting the feature. Maybe spell; Maybe extent; // Immediate parent types. 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; int16_t qual_name_offset = 0; int16_t short_name_offset = 0; int16_t short_name_size = 0; lsSymbolKind kind = lsSymbolKind::Unknown; bool operator==(const TypeDef& o) const { return detailed_name == o.detailed_name && spell == o.spell && extent == o.extent && alias_of == o.alias_of && bases == o.bases && types == o.types && funcs == o.funcs && vars == o.vars && kind == o.kind && hover == o.hover && comments == o.comments; } }; 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. std::string detailed_name; NtString hover; NtString comments; // TODO: definitions should be a list of ranges, since there can be more // than one - when?? Maybe spell; Maybe extent; // Type of the variable. Usr type = 0; // Function/type which declares this one. 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). StorageClass storage = StorageClass::Invalid; bool is_local() const { return kind == lsSymbolKind::Variable; } bool operator==(const VarDef& o) const { return detailed_name == o.detailed_name && spell == o.spell && extent == o.extent && type == o.type && kind == o.kind && storage == o.storage && hover == o.hover && comments == o.comments; } }; 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. std::string resolved_path; }; 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; std::string path; std::vector args; int64_t last_write_time = 0; LanguageId language = LanguageId::Unknown; // 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_by_preprocessor; std::vector includes; std::unordered_map dependencies; std::unordered_map usr2func; std::unordered_map usr2type; std::unordered_map usr2var; // Diagnostics found when indexing this file. Not serialized. std::vector diagnostics_; // File contents at the time of index. Not serialized. std::string file_contents; IndexFile(const std::string& path, const std::string& contents); IndexFunc& ToFunc(Usr usr); IndexType& ToType(Usr usr); IndexVar& ToVar(Usr usr); IndexFunc& ToFunc(const ClangCursor& c) { return ToFunc(c.get_usr_hash()); } IndexType& ToType(const ClangCursor& c) { return ToType(c.get_usr_hash()); } IndexVar& ToVar(const ClangCursor& c) { return ToVar(c.get_usr_hash()); } std::string ToString(); }; struct NamespaceHelper { std::unordered_map container_cursor_to_qualified_name; std::tuple QualifiedName( const CXIdxContainerInfo* container, std::string_view unqualified_name); }; // |import_file| is the cc file which is what gets passed to clang. // |desired_index_file| is the (h or cc) file which has actually changed. // |dependencies| are the existing dependencies of |import_file| if this is a // reparse. std::vector> Parse( VFS* vfs, std::string file, const std::vector& args, const std::vector& file_contents, PerformanceImportFile* perf, ClangIndex* index); std::vector> ParseWithTu( VFS* vfs, PerformanceImportFile* perf, ClangTranslationUnit* tu, ClangIndex* index, const std::string& file, const std::vector& args, const std::vector& file_contents); bool ConcatTypeAndName(std::string& type, const std::string& name); void IndexInit(); // Abstracts away the actual indexing process. Each IIndexer instance is // per-thread and constructing an instance may be extremely expensive (ie, // acquire a lock) and should be done as rarely as possible. struct IIndexer { struct TestEntry { std::string path; int num_indexes = 0; }; static std::unique_ptr MakeTestIndexer( std::initializer_list entries); virtual ~IIndexer() = default; virtual std::vector> Index( VFS* vfs, std::string file, const std::vector& args, const std::vector& file_contents, PerformanceImportFile* perf) = 0; }; struct ClangIndexer : IIndexer { std::vector> Index( VFS* vfs, std::string file, const std::vector& args, const std::vector& file_contents, PerformanceImportFile* perf) override { return Parse(vfs, file, args, file_contents, perf, &index); } // Note: constructing this acquires a global lock ClangIndex index; };