#pragma once #include "clang_cursor.h" #include "clang_index.h" #include "clang_symbol_kind.h" #include "clang_translation_unit.h" #include "clang_utils.h" #include "file_consumer.h" #include "file_contents.h" #include "language_server_api.h" #include "maybe.h" #include "performance.h" #include "position.h" #include "serializer.h" #include "utils.h" #include #include #include #include #include #include #include #include #include #include #include struct IndexType; struct IndexFunc; struct IndexVar; // The order matters. In FindSymbolsAtLocation, we want Var/Func ordered in // front of others. enum class SymbolKind : uint8_t { Invalid, File, Type, Func, Var }; MAKE_REFLECT_TYPE_PROXY(SymbolKind); template struct Id { size_t id; // Invalid id. Id() : id(-1) {} explicit Id(size_t id) : id(id) {} template explicit Id(Id o) : id(o.id) {} // Needed for google::dense_hash_map. explicit operator size_t() const { return id; } bool HasValue() const { return id != size_t(-1); } bool operator==(const Id& other) const { return id == other.id; } bool operator<(const Id& other) const { return id < other.id; } }; namespace std { template struct hash> { size_t operator()(const Id& k) const { return hash()(k.id); } }; } // namespace std template bool operator==(const Id& a, const Id& b) { assert(a.group == b.group && "Cannot compare Ids from different groups"); return a.id == b.id; } template bool operator!=(const Id& a, const Id& b) { return !(a == b); } template void Reflect(Reader& visitor, Id& id) { id.id = visitor.GetUint64(); } template void Reflect(Writer& visitor, Id& value) { visitor.Uint64(value.id); } using IndexTypeId = Id; using IndexFuncId = Id; using IndexVarId = Id; struct IdCache; struct IndexFuncRef { // NOTE: id can be -1 if the function call is not coming from a function. IndexFuncId id; Range loc; bool is_implicit = false; IndexFuncRef() {} // For serialization. IndexFuncRef(IndexFuncId id, Range loc, bool is_implicit) : id(id), loc(loc), is_implicit(is_implicit) {} IndexFuncRef(Range loc, bool is_implicit) : id(IndexFuncId((size_t)-1)), loc(loc), is_implicit(is_implicit) {} inline bool operator==(const IndexFuncRef& other) { return id == other.id && loc == other.loc && is_implicit == other.is_implicit; } inline bool operator!=(const IndexFuncRef& other) { return !(*this == other); } inline bool operator<(const IndexFuncRef& other) const { if (id < other.id) return true; if (id == other.id && loc < other.loc) return true; return id == other.id && loc == other.loc && is_implicit < other.is_implicit; } }; inline bool operator==(const IndexFuncRef& a, const IndexFuncRef& b) { return a.id == b.id && a.loc == b.loc; } inline bool operator!=(const IndexFuncRef& a, const IndexFuncRef& b) { return !(a == b); } inline void Reflect(Reader& visitor, IndexFuncRef& value) { std::string s = visitor.GetString(); const char* str_value = s.c_str(); if (str_value[0] == '~') { value.is_implicit = true; ++str_value; } uint64_t id = atol(str_value); const char* loc_string = strchr(str_value, '@') + 1; value.id = IndexFuncId(id); value.loc = Range(loc_string); } inline void Reflect(Writer& visitor, IndexFuncRef& value) { std::string s; if (value.is_implicit) s += "~"; // id.id is unsigned, special case 0 value if (value.id.id == static_cast(-1)) { s += "-1"; } else { s += std::to_string(value.id.id); } s += "@" + value.loc.ToString(); visitor.String(s.c_str()); } template struct TypeDefDefinitionData { // General metadata. std::string detailed_name; std::string hover; std::string 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 definition_spelling; Maybe definition_extent; // 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; // Immediate parent types. std::vector parents; // Types, functions, and variables defined in this type. std::vector types; std::vector funcs; std::vector vars; int16_t short_name_offset = 0; int16_t short_name_size = 0; ClangSymbolKind kind = ClangSymbolKind::Unknown; bool operator==( const TypeDefDefinitionData& other) const { return detailed_name == other.detailed_name && definition_spelling == other.definition_spelling && definition_extent == other.definition_extent && alias_of == other.alias_of && parents == other.parents && types == other.types && funcs == other.funcs && vars == other.vars && hover == other.hover && comments == other.comments; } bool operator!=( const TypeDefDefinitionData& other) const { return !(*this == other); } std::string_view ShortName() const { return std::string_view(detailed_name.c_str() + short_name_offset, short_name_size); } }; template void Reflect(TVisitor& visitor, TypeDefDefinitionData& value) { REFLECT_MEMBER_START(); REFLECT_MEMBER(detailed_name); REFLECT_MEMBER(short_name_offset); REFLECT_MEMBER(short_name_size); REFLECT_MEMBER(kind); REFLECT_MEMBER(hover); REFLECT_MEMBER(comments); REFLECT_MEMBER(definition_spelling); REFLECT_MEMBER(definition_extent); REFLECT_MEMBER(alias_of); REFLECT_MEMBER(parents); REFLECT_MEMBER(types); REFLECT_MEMBER(funcs); REFLECT_MEMBER(vars); REFLECT_MEMBER_END(); } struct IndexType { using Def = TypeDefDefinitionData; Usr usr; IndexTypeId id; Def def; // 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 FuncDefDefinitionData { // General metadata. std::string detailed_name; std::string hover; std::string comments; Maybe definition_spelling; Maybe definition_extent; // Type which declares this one (ie, it is a method) Maybe declaring_type; // Method this method overrides. std::vector base; // Local variables defined in this function. std::vector locals; // Functions that this function calls. std::vector callees; int16_t short_name_offset = 0; int16_t short_name_size = 0; ClangSymbolKind kind = ClangSymbolKind::Unknown; StorageClass storage = StorageClass::Invalid; bool operator==( const FuncDefDefinitionData& other) const { return detailed_name == other.detailed_name && hover == other.hover && definition_spelling == other.definition_spelling && definition_extent == other.definition_extent && declaring_type == other.declaring_type && base == other.base && locals == other.locals && callees == other.callees && hover == other.hover && comments == other.comments; } bool operator!=( const FuncDefDefinitionData& other) const { return !(*this == other); } std::string_view ShortName() const { return std::string_view(detailed_name.c_str() + short_name_offset, short_name_size); } }; template void Reflect( TVisitor& visitor, FuncDefDefinitionData& value) { REFLECT_MEMBER_START(); REFLECT_MEMBER(detailed_name); REFLECT_MEMBER(short_name_offset); REFLECT_MEMBER(short_name_size); REFLECT_MEMBER(kind); REFLECT_MEMBER(storage); REFLECT_MEMBER(hover); REFLECT_MEMBER(comments); REFLECT_MEMBER(definition_spelling); REFLECT_MEMBER(definition_extent); REFLECT_MEMBER(declaring_type); REFLECT_MEMBER(base); REFLECT_MEMBER(locals); REFLECT_MEMBER(callees); REFLECT_MEMBER_END(); } struct IndexFunc { using Def = FuncDefDefinitionData; Usr usr; IndexFuncId id; Def def; struct Declaration { // Range of only the function name. Range spelling; // Full range of the declaration. Range extent; // Full text of the declaration. std::string content; // 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.definition_spelling. std::vector callers; IndexFunc() {} // For serialization. IndexFunc(IndexFuncId id, Usr usr) : usr(usr), id(id) { // assert(usr.size() > 0); } bool operator<(const IndexFunc& other) const { return id < other.id; } }; MAKE_HASHABLE(IndexFunc, t.id); MAKE_REFLECT_STRUCT(IndexFunc::Declaration, spelling, extent, content, param_spellings); template struct VarDefDefinitionData { // General metadata. std::string detailed_name; std::string hover; std::string comments; // TODO: definitions should be a list of ranges, since there can be more // than one - when?? Maybe definition_spelling; Maybe definition_extent; // Type of the variable. Maybe variable_type; // Function/type which declares this one. size_t parent_id = size_t(-1); int16_t short_name_offset = 0; int16_t short_name_size = 0; SymbolKind parent_kind = SymbolKind::Invalid; ClangSymbolKind kind = ClangSymbolKind::Unknown; // Note a variable may have instances of both |None| and |Extern| // (declaration). StorageClass storage = StorageClass::Invalid; bool is_local() const { return kind == ClangSymbolKind::Parameter || kind == ClangSymbolKind::Variable; } bool is_macro() const { return kind == ClangSymbolKind::Macro; } bool operator==( const VarDefDefinitionData& other) const { return detailed_name == other.detailed_name && hover == other.hover && definition_spelling == other.definition_spelling && definition_extent == other.definition_extent && variable_type == other.variable_type && comments == other.comments; } bool operator!=( const VarDefDefinitionData& other) const { return !(*this == other); } std::string_view ShortName() const { return std::string_view(detailed_name.c_str() + short_name_offset, short_name_size); } }; template void Reflect(TVisitor& visitor, VarDefDefinitionData& value) { REFLECT_MEMBER_START(); REFLECT_MEMBER(detailed_name); REFLECT_MEMBER(short_name_size); REFLECT_MEMBER(short_name_offset); REFLECT_MEMBER(hover); REFLECT_MEMBER(comments); REFLECT_MEMBER(definition_spelling); REFLECT_MEMBER(definition_extent); REFLECT_MEMBER(variable_type); REFLECT_MEMBER(parent_id); REFLECT_MEMBER(parent_kind); REFLECT_MEMBER(kind); REFLECT_MEMBER(storage); REFLECT_MEMBER_END(); } struct IndexVar { using Def = VarDefDefinitionData; Usr usr; IndexVarId id; Def def; std::vector declarations; // Usages. std::vector uses; IndexVar() {} // For serialization. IndexVar(IndexVarId id, Usr usr) : usr(usr), id(id) { // assert(usr.size() > 0); } 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; }; // Used to identify the language at a file level. The ordering is important, as // a file previously identified as `C`, will be changed to `Cpp` if it // encounters a c++ declaration. enum class LanguageId { Unknown = 0, C = 1, Cpp = 2, ObjC = 3 }; MAKE_REFLECT_TYPE_PROXY(LanguageId); 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 cquery. 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::string 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. optional>> Parse( Config* config, FileConsumerSharedState* file_consumer_shared, std::string file, const std::vector& args, const std::vector& file_contents, PerformanceImportFile* perf, ClangIndex* index, bool dump_ast = false); optional>> ParseWithTu( Config* config, FileConsumerSharedState* file_consumer_shared, PerformanceImportFile* perf, ClangTranslationUnit* tu, ClangIndex* index, const std::string& file, const std::vector& args, const std::vector& file_contents); void ConcatTypeAndName(std::string& type, const std::string& name); void IndexInit(); void ClangSanityCheck(); std::string GetClangVersion();