#pragma once #include "Config.h" #include "Database.h" #include "Protocol.h" #include "Async/Async.h" #include "Compiler/Compilation.h" #include "Support/JSON.h" #include "Index/SymbolIndex.h" namespace clice { struct TranslationUnit; struct HeaderIndex { /// The index file path(not include suffix, e.g. `.sidx` and `.fidx`). std::string path; /// The hash of the symbol index. llvm::XXH128_hash_t symbolHash; /// The hash of the feature index. llvm::XXH128_hash_t featureHash; }; struct Context { /// The include chain that introduces this context. uint32_t include = -1; /// The index information of this context. uint32_t index = -1; }; struct IncludeLocation { /// The location of the include directive. uint32_t line = -1; /// The index of the file that includes this header. uint32_t include = -1; /// The file name of the header in the string pool. Beacuse /// a header may be included by multiple files, so we use /// a string pool to cache the file name to reduce the memory /// usage. uint32_t filename = -1; }; struct Header { /// The path of the header file. std::string srcPath; /// All indices of this header. std::vector indices; /// All header contexts of this header. llvm::DenseMap> contexts; /// The active translation unit and the index of the context. std::pair active = {nullptr, -1}; }; struct TranslationUnit { /// The source file path. std::string srcPath; /// The index file path(not include suffix, e.g. `.sidx` and `.fidx`). std::string indexPath; /// All headers included by this translation unit. llvm::DenseSet headers; /// The time when this translation unit is indexed. Used to determine /// whether the index file is outdated. std::chrono::milliseconds mtime; /// All include locations introduced by this translation unit. /// Note that if a file has guard macro or pragma once, we will /// record it at most once. std::vector locations; /// The version of the translation unit. uint32_t version = 0; }; namespace proto { struct IncludeLocation { /// The line number of the include directive. uint32_t line; /// The filename of the included header. std::string filename; }; struct HeaderContext { /// The path of the source file. std::string srcFile; /// The path of the context file. std::string contextFile; /// The index of the context. uint32_t index = -1; /// The version of the context. uint32_t version = 0; }; using HeaderContextGroups = std::vector>; } // namespace proto class IncludeGraph { protected: IncludeGraph(const config::IndexOptions& options) : options(options) {} ~IncludeGraph(); void load(const json::Value& json); json::Value dump(); async::Task<> index(llvm::StringRef file, CompilationDatabase& database); public: /// Return all header context of the given file. /// FIXME: The results are grouped by the index file. And a header actually /// may have thousands of contexts, of course, users don't want to see all /// of them. For each index file, we return the first 10 contexts. In the future /// we may add a parameter to control the number of contexts or set filter. proto::HeaderContextGroups contextAll(llvm::StringRef file); /// Return current header context of the given file. std::optional contextCurrent(llvm::StringRef file); /// Switch to the given header context. void contextSwitch(const proto::HeaderContext& context); /// Resolve the header context to the include chain. std::vector contextResolve(const proto::HeaderContext& context); private: struct SymbolID { uint64_t hash; std::string name; }; /// Return all indices of the given translation unit. If the file is empty, /// return all indices of the IncludeGraph. std::vector indices(TranslationUnit* tu = nullptr); /// Resolve the symbol at the given position. async::Task> resolve(const proto::TextDocumentPositionParams& params); using LookupCallback = llvm::unique_function; async::Task<> lookup(llvm::ArrayRef targets, llvm::ArrayRef files, LookupCallback callback); public: /// Lookup the reference information according to the given position. async::Task lookup(const proto::ReferenceParams& params, RelationKind kind); /// According to the given file and offset, resolve the symbol at the offset. async::Task prepareHierarchy(const proto::HierarchyPrepareParams& params); async::Task incomingCalls(const proto::HierarchyParams& params); async::Task outgoingCalls(const proto::HierarchyParams& params); async::Task typeHierarchy(const proto::HierarchyParams& params, bool super); private: std::string getIndexPath(llvm::StringRef file); /// Check whether the given file needs to be updated. If so, /// return the translation unit. Otherwise, return nullptr. async::Task check(llvm::StringRef file); /// Add all possible header contexts for the tu from the AST info. uint32_t addIncludeChain(std::vector& locations, llvm::DenseMap& files, clang::SourceManager& SM, clang::FileID fid); void addContexts(ASTInfo& info, TranslationUnit* tu, llvm::DenseMap& files); async::Task<> updateIndices(ASTInfo& info, TranslationUnit* tu, llvm::DenseMap& files); private: const config::IndexOptions& options; llvm::StringMap headers; llvm::StringMap tus; std::vector pathPool; llvm::StringMap pathIndices; SourceConverter SC; }; } // namespace clice