#pragma once #include "clang_cursor.h" #include "clang_translation_unit.h" #include "clang_utils.h" #include "file_consumer.h" #include "file_contents.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 #include #include struct IndexFile; struct IndexType; struct IndexFunc; struct IndexVar; struct QueryFile; using RawId = uint32_t; template struct Id { RawId id; // Invalid id. Id() : id(-1) {} explicit Id(RawId id) : id(id) {} // Id -> Id or Id -> Id is allowed implicitly. template ::value || std::is_same::value, bool>::type = false> Id(Id o) : id(o.id) {} template ::value || std::is_same::value), bool>::type = false> explicit Id(Id o) : id(o.id) {} // Needed for google::dense_hash_map. explicit operator RawId() const { return id; } bool HasValueForMaybe_() const { return id != RawId(-1); } bool operator==(const Id& o) const { return id == o.id; } bool operator!=(const Id& o) const { return id != o.id; } bool operator<(const Id& o) const { return id < o.id; } }; namespace std { template struct hash> { size_t operator()(const Id& k) const { return hash()(k.id); } }; } // namespace std template void Reflect(TVisitor& visitor, Id& id) { Reflect(visitor, id.id); } using IndexFileId = Id; using IndexTypeId = Id; using IndexFuncId = Id; using IndexVarId = Id; struct SymbolIdx { Id id; SymbolKind kind; bool operator==(const SymbolIdx& o) const { return id == o.id && kind == o.kind; } bool operator!=(const SymbolIdx& o) const { return !(*this == o); } bool operator<(const SymbolIdx& o) const { if (id != o.id) return id < o.id; return kind < o.kind; } }; MAKE_REFLECT_STRUCT(SymbolIdx, kind, id); MAKE_HASHABLE(SymbolIdx, t.kind, t.id); struct Reference { Range range; Id id; SymbolKind kind; Role role; bool HasValueForMaybe_() const { return range.HasValueForMaybe_(); } operator SymbolIdx() const { return {id, kind}; } std::tuple, SymbolKind, Role> ToTuple() const { return std::make_tuple(range, id, 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, Id id, SymbolKind kind, Role role) : Reference{range, id, 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* Id file; Use() = default; Use(Range range, Id id, SymbolKind kind, Role role, Id file) : Reference{range, id, kind, role}, file(file) {} }; // Used by |HANDLE_MERGEABLE| so only |range| is needed. MAKE_HASHABLE(Use, t.range); void Reflect(Reader& visitor, Reference& value); void Reflect(Writer& visitor, Reference& value); struct IndexFamily { using FileId = Id; using FuncId = Id; using TypeId = Id; using VarId = Id; using Range = ::Range; }; 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); } }; template 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; typename F::FileId file; // If set, then this is the same underlying type as the given value (ie, this // type comes from a using or typedef statement). Maybe alias_of; 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; } bool operator!=(const TypeDef& o) const { return !(*this == o); } }; template void Reflect(TVisitor& visitor, TypeDef& value) { REFLECT_MEMBER_START(); REFLECT_MEMBER(detailed_name); REFLECT_MEMBER(qual_name_offset); REFLECT_MEMBER(short_name_offset); REFLECT_MEMBER(short_name_size); REFLECT_MEMBER(kind); REFLECT_MEMBER(hover); REFLECT_MEMBER(comments); REFLECT_MEMBER(spell); REFLECT_MEMBER(extent); REFLECT_MEMBER(file); REFLECT_MEMBER(alias_of); REFLECT_MEMBER(bases); REFLECT_MEMBER(types); REFLECT_MEMBER(funcs); REFLECT_MEMBER(vars); REFLECT_MEMBER_END(); } struct IndexType { using Def = TypeDef; Usr usr; IndexTypeId id; Def def; std::vector declarations; // Immediate derived types. std::vector derived; // Declared variables of this type. std::vector instances; // Every usage, useful for things like renames. // NOTE: Do not insert directly! Use AddUsage instead. std::vector uses; IndexType() {} // For serialization. IndexType(IndexTypeId id, Usr usr); bool operator<(const IndexType& other) const { return id < other.id; } }; MAKE_HASHABLE(IndexType, t.id); template 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; typename F::FileId file; // Type which declares this one (ie, it is a method) Maybe declaring_type; 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; } bool operator!=(const FuncDef& o) const { return !(*this == o); } }; template void Reflect(TVisitor& visitor, FuncDef& value) { REFLECT_MEMBER_START(); REFLECT_MEMBER(detailed_name); REFLECT_MEMBER(qual_name_offset); REFLECT_MEMBER(short_name_offset); REFLECT_MEMBER(short_name_size); REFLECT_MEMBER(kind); REFLECT_MEMBER(storage); REFLECT_MEMBER(hover); REFLECT_MEMBER(comments); REFLECT_MEMBER(spell); REFLECT_MEMBER(extent); REFLECT_MEMBER(file); REFLECT_MEMBER(declaring_type); REFLECT_MEMBER(bases); REFLECT_MEMBER(vars); REFLECT_MEMBER(callees); REFLECT_MEMBER_END(); } struct IndexFunc : NameMixin { using Def = FuncDef; Usr usr; IndexFuncId id; Def def; struct Declaration { // Range of only the function name. Use spell; // Location of the parameter names. std::vector param_spellings; }; // Places the function is forward-declared. std::vector declarations; // Methods which directly override this one. std::vector derived; // Calls/usages of this function. If the call is coming from outside a // function context then the FuncRef will not have an associated id. // // To get all usages, also include the ranges inside of declarations and // def.spell. std::vector uses; IndexFunc() {} // For serialization. IndexFunc(IndexFuncId id, Usr usr) : usr(usr), id(id) {} bool operator<(const IndexFunc& other) const { return id < other.id; } }; MAKE_HASHABLE(IndexFunc, t.id); MAKE_REFLECT_STRUCT(IndexFunc::Declaration, spell, param_spellings); template 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; typename F::FileId file; // Type of the variable. Maybe type; // 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; } bool operator!=(const VarDef& o) const { return !(*this == o); } }; template void Reflect(TVisitor& visitor, VarDef& value) { REFLECT_MEMBER_START(); REFLECT_MEMBER(detailed_name); REFLECT_MEMBER(qual_name_offset); REFLECT_MEMBER(short_name_offset); REFLECT_MEMBER(short_name_size); REFLECT_MEMBER(hover); REFLECT_MEMBER(comments); REFLECT_MEMBER(spell); REFLECT_MEMBER(extent); REFLECT_MEMBER(file); REFLECT_MEMBER(type); REFLECT_MEMBER(kind); REFLECT_MEMBER(storage); REFLECT_MEMBER_END(); } struct IndexVar { using Def = VarDef; Usr usr; IndexVarId id; Def def; std::vector declarations; std::vector uses; IndexVar() {} // For serialization. IndexVar(IndexVarId id, Usr usr) : usr(usr), id(id) {} bool operator<(const IndexVar& other) const { return id < other.id; } }; MAKE_HASHABLE(IndexVar, t.id); struct IdCache { std::string primary_file; std::unordered_map usr_to_type_id; std::unordered_map usr_to_func_id; std::unordered_map usr_to_var_id; std::unordered_map type_id_to_usr; std::unordered_map func_id_to_usr; std::unordered_map var_id_to_usr; IdCache(const std::string& primary_file); }; 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 { IdCache id_cache; // 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_modification_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::vector dependencies; std::vector types; std::vector funcs; std::vector vars; // 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); IndexTypeId ToTypeId(Usr usr); IndexFuncId ToFuncId(Usr usr); IndexVarId ToVarId(Usr usr); IndexTypeId ToTypeId(const CXCursor& usr); IndexFuncId ToFuncId(const CXCursor& usr); IndexVarId ToVarId(const CXCursor& usr); IndexType* Resolve(IndexTypeId id); IndexFunc* Resolve(IndexFuncId id); IndexVar* Resolve(IndexVarId id); 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( FileConsumerSharedState* file_consumer_shared, std::string file, const std::vector& args, const std::vector& file_contents, PerformanceImportFile* perf, ClangIndex* index, bool dump_ast = false); std::vector> ParseWithTu( FileConsumerSharedState* file_consumer_shared, 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(); void ClangSanityCheck(); std::string GetClangVersion();