From 4c63c524879f4ec9de9cd13332925a794f47c413 Mon Sep 17 00:00:00 2001 From: ykiko Date: Tue, 7 Oct 2025 18:21:44 +0800 Subject: [PATCH] Serialize index to binary (#273) --- CMakeLists.txt | 2 +- cmake/croaring-fix.patch | 30 -- cmake/package.cmake | 21 +- include/AST/Semantic.h | 11 +- include/Compiler/Command.h | 12 + include/Index/Contextual.h | 38 -- include/Index/HeaderIndex.h | 184 ------- include/Index/IncludeGraph.h | 11 +- include/Index/MergedIndex.h | 143 +++--- include/Index/RawIndex.h | 90 ---- include/Index/TUIndex.h | 21 +- include/Index/schema.fbs | 4 +- include/Support/Bitmap.h | 11 + src/Index/HeaderIndex.cpp | 238 --------- src/Index/IncludeGraph.cpp | 16 +- src/Index/MergedIndex.cpp | 122 ++++- src/Index/TUIndex.cpp | 20 +- tests/unit/Index/HeaderIndex.cpp | 836 ------------------------------- tests/unit/Index/MergedIndex.cpp | 31 +- tests/unit/Index/TUIndex.cpp | 204 +++++++- tests/unit/Index/Template.cpp | 166 ------ 21 files changed, 505 insertions(+), 1706 deletions(-) delete mode 100644 cmake/croaring-fix.patch delete mode 100644 include/Index/Contextual.h delete mode 100644 include/Index/HeaderIndex.h delete mode 100644 include/Index/RawIndex.h create mode 100644 include/Support/Bitmap.h delete mode 100644 src/Index/HeaderIndex.cpp delete mode 100644 tests/unit/Index/HeaderIndex.cpp delete mode 100644 tests/unit/Index/Template.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 26a8a164..fafb1a55 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -88,7 +88,7 @@ target_include_directories(clice-core PUBLIC ) target_link_libraries(clice-core PUBLIC clice_options - uv_a + libuv::libuv spdlog::spdlog tomlplusplus::tomlplusplus roaring::roaring diff --git a/cmake/croaring-fix.patch b/cmake/croaring-fix.patch deleted file mode 100644 index d4e2ad53..00000000 --- a/cmake/croaring-fix.patch +++ /dev/null @@ -1,30 +0,0 @@ -diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt -index addff26d..72d4b2f8 100644 ---- a/src/CMakeLists.txt -+++ b/src/CMakeLists.txt -@@ -90,7 +90,7 @@ foreach(tree_type BUILD INSTALL) - "${build_location}/roaring-config-version.cmake" - VERSION ${ROARING_LIB_VERSION} - COMPATIBILITY SameMajorVersion) -- configure_package_config_file("${CMAKE_SOURCE_DIR}/cmake/config.cmake.in" -+ configure_package_config_file("${PROJECT_SOURCE_DIR}/cmake/config.cmake.in" - "${build_location}/roaring-config.cmake" - INSTALL_DESTINATION "${install_location}") - -diff --git a/tools/cmake/FindOptions.cmake b/tools/cmake/FindOptions.cmake -index 69b40ed8..55cbece4 100644 ---- a/tools/cmake/FindOptions.cmake -+++ b/tools/cmake/FindOptions.cmake -@@ -38,10 +38,11 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXXSTD_FLAGS} ${OPT_FLAGS} ${INCLUDE_F - - if(MSVC) - add_definitions( "/W3 /D_CRT_SECURE_NO_WARNINGS /wd4005 /wd4996 /wd4267 /wd4244 /wd4113 /nologo") --endif() - if(MSVC_VERSION GREATER 1910) - add_definitions("/permissive-") - endif() -+endif() -+ - if(ROARING_LINK_STATIC) - if(NOT MSVC) - set(CMAKE_EXE_LINKER_FLAGS "-static") diff --git a/cmake/package.cmake b/cmake/package.cmake index 10fed4e6..8eddc9e4 100644 --- a/cmake/package.cmake +++ b/cmake/package.cmake @@ -93,10 +93,12 @@ FetchContent_Declare( GIT_TAG v1.x ) -if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_BUILD_TYPE STREQUAL "Debug") +if(NOT WIN32 AND CMAKE_BUILD_TYPE STREQUAL "Debug") set(ASAN ON CACHE BOOL "Enable AddressSanitizer for libuv" FORCE) endif() -set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build dependencies as static libs") +set(LIBUV_BUILD_SHARED OFF CACHE BOOL "" FORCE) +set(LIBUV_BUILD_TESTS OFF CACHE BOOL "" FORCE) +set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE) # spdlog FetchContent_Declare( @@ -115,13 +117,11 @@ FetchContent_Declare( FetchContent_Declare( croaring GIT_REPOSITORY https://github.com/RoaringBitmap/CRoaring.git - GIT_TAG v4.4.0 - # Workaround for https://github.com/RoaringBitmap/CRoaring/pull/750 - PATCH_COMMAND git apply --reverse --check ${PROJECT_SOURCE_DIR}/cmake/croaring-fix.patch 2> ${NULL_DEVICE} - || git apply ${PROJECT_SOURCE_DIR}/cmake/croaring-fix.patch + GIT_TAG v4.4.2 ) -set(ENABLE_ROARING_TESTS OFF CACHE INTERNAL "") +set(ENABLE_ROARING_TESTS OFF CACHE INTERNAL "" FORCE) +# flatbuffers FetchContent_Declare( flatbuffers GIT_REPOSITORY https://github.com/google/flatbuffers.git @@ -137,7 +137,7 @@ if(WIN32) target_compile_definitions(uv_a PRIVATE _CRT_SECURE_NO_WARNINGS) endif() -if(CMAKE_C_COMPILER_ID MATCHES "Clang" AND TARGET uv_a) +if(NOT MSVC AND TARGET uv_a) target_compile_options(uv_a PRIVATE "-Wno-unused-function" "-Wno-unused-variable" @@ -147,4 +147,7 @@ if(CMAKE_C_COMPILER_ID MATCHES "Clang" AND TARGET uv_a) ) endif() -target_compile_definitions(spdlog PUBLIC SPDLOG_USE_STD_FORMAT=1 SPDLOG_NO_EXCEPTIONS=1) +target_compile_definitions(spdlog PUBLIC + SPDLOG_USE_STD_FORMAT=1 + SPDLOG_NO_EXCEPTIONS=1 +) diff --git a/include/AST/Semantic.h b/include/AST/Semantic.h index 4d5c1e6d..c2ad3e46 100644 --- a/include/AST/Semantic.h +++ b/include/AST/Semantic.h @@ -29,13 +29,15 @@ public: void handleDeclOccurrence(const clang::NamedDecl* decl, RelationKind kind, clang::SourceLocation location) { - assert(decl && "Invalid decl"); + if(!decl || location.isInvalid()) { + return; + } + assert(kind.is_one_of(RelationKind::Declaration, RelationKind::Definition, RelationKind::Reference, RelationKind::WeakReference) && "Invalid kind"); - assert(location.isValid() && "Invalid location"); /// Forwards to the derived class. Check whether the derived class has /// its own implementation to avoid infinite recursion. @@ -94,8 +96,9 @@ public: RelationKind kind, const clang::NamedDecl* target, clang::SourceRange range) { - assert(decl && "Invalid decl"); - assert(target && "Invalid target"); + if(!decl || !target || range.isInvalid()) { + return; + } if constexpr(!std::same_as) { diff --git a/include/Compiler/Command.h b/include/Compiler/Command.h index 39a03de4..df429ad1 100644 --- a/include/Compiler/Command.h +++ b/include/Compiler/Command.h @@ -147,6 +147,18 @@ public: llvm::ArrayRef compile_commands_dirs, llvm::StringRef workspace) -> void; + auto commands_size() { + return command_infos.size(); + } + + auto begin() { + return command_infos.begin(); + } + + auto end() { + return command_infos.end(); + } + private: /// If file not found in CDB file, try to guess commands or use the default case. auto guess_or_fallback(this Self& self, llvm::StringRef file) -> LookupInfo; diff --git a/include/Index/Contextual.h b/include/Index/Contextual.h deleted file mode 100644 index 53d55ceb..00000000 --- a/include/Index/Contextual.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include "llvm/ADT/DenseSet.h" -#include "llvm/ADT/StringMap.h" -#include "llvm/ADT/BitVector.h" - -namespace clice::index { - -struct Contextual { - /// The actual element id, - std::uint32_t element_id; - - constexpr inline static std::uint32_t FLAG = (1ull << 31); - - static Contextual from(bool is_dependent, std::uint32_t offset) { - Contextual ctx; - ctx.element_id = offset; - if(!is_dependent) { - ctx.element_id |= FLAG; - } - return ctx; - } - - bool is_dependent() { - return (element_id & FLAG) == 0; - } - - std::uint32_t offset() { - return element_id & ~FLAG; - } -}; - -} // namespace clice::index diff --git a/include/Index/HeaderIndex.h b/include/Index/HeaderIndex.h deleted file mode 100644 index b8000729..00000000 --- a/include/Index/HeaderIndex.h +++ /dev/null @@ -1,184 +0,0 @@ -#pragma once - -#include "RawIndex.h" - -namespace clice::index::memory { - -/// HeaderIndex store extra information to merge raw index from different header contexts. -class HeaderIndex : public RawIndex { -public: - std::uint32_t file_count() { - return header_contexts.size(); - } - - /// The count of active header contexts in this index. - std::uint32_t header_context_count() { - return max_hctx_id - erased_hctx_ids.size(); - } - - /// The count of active canonical contexts in this index. - std::uint32_t canonical_context_count() { - return max_cctx_id - erased_cctx_ids.size(); - } - - /// Whether this contexts has only one single context. - bool is_single_header_context() { - return max_hctx_id == 1 && erased_hctx_ids.empty(); - } - - auto erased_flag() { - Bitmap map; - map.set(); - for(auto cctx_id: erased_cctx_ids) { - map.reset(cctx_id); - } - return map; - } - - /// Get a new header context id. - std::uint32_t alloc_hctx_id(); - - /// Get a new canonical context id. - std::uint32_t alloc_cctx_id(); - - std::uint32_t alloc_dependent_elem_id() { - auto id = dependent_elem_states.size(); - dependent_elem_states.emplace_back(false); - return id; - } - - std::uint32_t alloc_independent_elem_id() { - auto id = independent_elem_states.size(); - independent_elem_states.emplace_back(); - return id; - } - - struct HeaderContext { - /// The include location id of this header context. - std::uint32_t include; - - /// The header context id of this header context. - std::uint32_t hctx_id; - - /// The canonical context id of this header context. - std::uint32_t cctx_id; - }; - - void remove(this HeaderIndex& self, llvm::StringRef path); - - HeaderContext add_context(llvm::StringRef path, std::uint32_t include) { - assert(!merged && ""); - auto& context = header_contexts[path].emplace_back(); - context.include = include; - context.cctx_id = alloc_cctx_id(); - context.hctx_id = alloc_hctx_id(); - return context; - } - - HeaderContext - merge(this HeaderIndex& self, llvm::StringRef path, std::uint32_t include, RawIndex& raw); - -public: - bool merged = false; - - /// The max header context id. - std::uint32_t max_hctx_id = 0; - - /// The max canonical context id. - std::uint32_t max_cctx_id = 0; - - /// The erased header context id. if a header context is erased, - /// we add its id for later reusing. - std::deque erased_hctx_ids; - - /// Same as above but for canonical context id. - std::deque erased_cctx_ids; - - /// A map between source file path and its header contexts. - llvm::StringMap> header_contexts; - - /// A map between canonical context id and corresponding ref counts - /// referenced by header contexts. - llvm::SmallVector cctx_hctx_refs; - - /// A map between canonical context id and corresponding ref counts - /// referenced by contextual elements. - llvm::SmallVector cctx_element_refs; - - using Bitmap = std::bitset<64>; /// use llvm::BitVector? - - /// A map between dependent element id and its state, for dependent element - /// we use bitmap to store states. Each bit in bitmap represents whether - /// this element occurs in corresponding canonical context id. - llvm::SmallVector dependent_elem_states; - - /// A map between independent element id and its state, for independent element - /// we directly store the header context ids that it occurs in. - std::vector> independent_elem_states; -}; - -} // namespace clice::index::memory - -namespace llvm { - -template -unsigned dense_hash(const Ts&... ts) { - return llvm::DenseMapInfo>::getHashValue(std::tuple{ts...}); -} - -template <> -struct DenseMapInfo { - using R = clice::LocalSourceRange; - - inline static R getEmptyKey() { - return R(0, -1); - } - - inline static R getTombstoneKey() { - return R(-1, 0); - } - - static auto getHashValue(const R& r) { - return dense_hash(r.begin, r.end); - } - - static bool isEqual(const R& lhs, const R& rhs) { - return lhs == rhs; - } -}; - -template <> -struct DenseMapInfo { - using R = clice::index::memory::Relation; - - inline static R getEmptyKey() { - return R{ - .kind = clice::RelationKind(), - .range = clice::LocalSourceRange(0, 0), - .target_symbol = 0, - }; - } - - inline static R getTombstoneKey() { - return R{ - .kind = clice::RelationKind(), - .range = clice::LocalSourceRange(-1, -1), - .target_symbol = 0, - }; - } - - /// Contextual doen't take part in hashing and equality. - static auto getHashValue(const R& relation) { - return dense_hash(relation.kind.value(), - relation.range.begin, - relation.range.end, - relation.target_symbol); - } - - static bool isEqual(const R& lhs, const R& rhs) { - return lhs.kind == rhs.kind && lhs.range == rhs.range && - lhs.target_symbol == rhs.target_symbol; - } -}; - -} // namespace llvm diff --git a/include/Index/IncludeGraph.h b/include/Index/IncludeGraph.h index d7eb209e..32233ded 100644 --- a/include/Index/IncludeGraph.h +++ b/include/Index/IncludeGraph.h @@ -42,11 +42,20 @@ struct IncludeGraph { return paths[path_ref]; } - std::uint32_t getInclude(clang::FileID fid) const { + std::uint32_t include_location_id(clang::FileID fid) const { auto it = file_table.find(fid); assert(it != file_table.end()); return it->second; } + + std::uint32_t path_id(clang::FileID fid) { + auto include = include_location_id(fid); + if(include != -1) { + return locations[include].path; + } else { + return paths.size() - 1; + } + } }; } // namespace clice::index diff --git a/include/Index/MergedIndex.h b/include/Index/MergedIndex.h index 7ff8278e..85fe8fd2 100644 --- a/include/Index/MergedIndex.h +++ b/include/Index/MergedIndex.h @@ -1,77 +1,9 @@ #pragma once #include "TUIndex.h" - -#define ROARING_EXCEPTIONS 0 -#define ROARING_TERMINATE(message) std::abort() -#include "roaring/roaring.hh" - +#include "Support/Bitmap.h" #include "llvm/Support/Allocator.h" -namespace clice::index { - -/// struct CompilationContext { -/// /// The target of this compilation. -/// llvm::StringRef target; -/// -/// /// The canonical compilation command. -/// llvm::StringRef command; -/// -/// /// A version field for verification. -/// std::uint32_t version; -/// }; -/// -/// struct HeaderContext : CompilationContext { -/// /// The include location in the include graph. -/// std::uint32_t include; -/// -/// /// The path of the file includes this header. -/// llvm::StringRef path; -/// }; - -struct HeaderContexts { - std::uint32_t version = 0; - - using Context = std::pair; - - /// A array of include location and its context id. - llvm::SmallVector includes; -}; - -struct MergedIndex { - /// For each merged index, we will give it a canonical id. - /// The max canonical id. - std::uint32_t max_canonical_id = 0; - - /// We use the value of SHA256 to judge whether two indices are same. - /// Index with same content will be given same canonical id. - llvm::DenseMap canonical_cache; - - /// The allocator for storing sha256 hash. - llvm::BumpPtrAllocator allocator; - - /// The reference count of each canonical id. - std::vector canonical_ref_counts; - - /// The canonical id set of removed index. - roaring::Roaring removed; - - /// A map between source file path and its header contexts. - llvm::StringMap contexts; - - /// All merged symbol occurrences. - llvm::DenseMap occurrences; - - /// All merged symbol relations. - llvm::DenseMap> relations; - - void remove(llvm::StringRef path); - - void merge(llvm::StringRef path, std::uint32_t include, FileIndex& index); -}; - -} // namespace clice::index - namespace llvm { template @@ -136,3 +68,76 @@ struct DenseMapInfo { }; } // namespace llvm + +namespace clice::index { + +/// struct CompilationContext { +/// /// The target of this compilation. +/// llvm::StringRef target; +/// +/// /// The canonical compilation command. +/// llvm::StringRef command; +/// +/// /// A version field for verification. +/// std::uint32_t version; +/// }; +/// +/// struct HeaderContext : CompilationContext { +/// /// The include location in the include graph. +/// std::uint32_t include; +/// +/// /// The path of the file includes this header. +/// llvm::StringRef path; +/// }; + +struct HeaderContexts { + std::uint32_t version = 0; + + using Context = std::pair; + + /// A array of include location and its context id. + llvm::SmallVector includes; + + friend bool operator== (const HeaderContexts& lhs, const HeaderContexts& rhs) = default; +}; + +struct MergedIndex { + /// For each merged index, we will give it a canonical id. + /// The max canonical id. + std::uint32_t max_canonical_id = 0; + + /// We use the value of SHA256 to judge whether two indices are same. + /// Index with same content will be given same canonical id. + llvm::StringMap canonical_cache; + + /// The reference count of each canonical id. + std::vector canonical_ref_counts; + + /// The canonical id set of removed index. + roaring::Roaring removed; + + /// A map between source file path and its header contexts. + llvm::StringMap contexts; + + /// All merged symbol occurrences. + llvm::DenseMap occurrences; + + /// All merged symbol relations. + llvm::DenseMap> relations; + + void remove(llvm::StringRef path); + + void merge(llvm::StringRef path, std::uint32_t include, FileIndex& index); + + void serialize(this MergedIndex& self, llvm::raw_ostream& out); + + friend bool operator== (const MergedIndex& lhs, const MergedIndex& rhs) = default; +}; + +struct MergedIndexView { + const void* data; + + MergedIndex deserialize(); +}; + +} // namespace clice::index diff --git a/include/Index/RawIndex.h b/include/Index/RawIndex.h deleted file mode 100644 index d162cd7c..00000000 --- a/include/Index/RawIndex.h +++ /dev/null @@ -1,90 +0,0 @@ -#pragma once - -#include "Contextual.h" -#include "AST/SymbolID.h" -#include "AST/SymbolKind.h" -#include "AST/RelationKind.h" -#include "AST/SourceCode.h" - -namespace clice::index::memory { - -using SymbolID = std::uint64_t; - -struct Relation { - /// The context of this relation. - Contextual ctx; - - /// The relation kind of this relation. - RelationKind kind; - - /// The range of this relation. - LocalSourceRange range; - - /// A field to store extra information depend on kind. - union { - SymbolID target_symbol; - LocalSourceRange definition_range; - }; -}; - -struct Symbol { - /// The symbol id. - SymbolID id; - - /// The symbol kind. - SymbolKind kind; - - /// Whether this symbol is not visible to other tu. - bool is_tu_local = false; - - /// Whether this symbol is defined in function scope. - bool is_function_local = false; - - /// The symbol name. - std::string name; - - /// All relations of this symbol. - llvm::DenseSet relations; -}; - -struct Occurrence { - /// The context of this occurrence. - Contextual ctx; - - /// The target symbol of this occurrence. - SymbolID target_symbol; -}; - -/// The raw index directly generated by indexing the source file. -struct RawIndex { -public: - Symbol& get_symbol(SymbolID ID) { - /// If not found, create a new symbol and return it. - auto [it, _] = symbols.try_emplace(ID, Symbol{.id = ID}); - return it->second; - } - - void add_relation(Symbol& symbol, Relation relation, bool is_dependent = true) { - relation.ctx = Contextual::from(is_dependent, 0); - symbol.relations.insert(relation); - } - - void add_occurrence(LocalSourceRange range, SymbolID target_symbol, bool is_dependent = true) { - occurrences[range].emplace_back(Contextual::from(is_dependent, 0), target_symbol); - } - -public: - /// The path of source file. - std::string path; - - /// The content of source file. - std::string content; - - /// All symbols in this index. - llvm::DenseMap symbols; - - /// All occurrences in this index. - llvm::DenseMap> occurrences; -}; - -} // namespace clice::index::memory diff --git a/include/Index/TUIndex.h b/include/Index/TUIndex.h index 98df7d55..a42c78e9 100644 --- a/include/Index/TUIndex.h +++ b/include/Index/TUIndex.h @@ -2,7 +2,9 @@ #include "IncludeGraph.h" #include "AST/SourceCode.h" +#include "AST/SymbolKind.h" #include "AST/RelationKind.h" +#include "Support/Bitmap.h" namespace clice::index { @@ -12,15 +14,19 @@ using SymbolHash = std::uint64_t; struct Relation { RelationKind kind; - char padding[4] = {0, 0, 0, 0}; + std::uint32_t padding = 0; LocalSourceRange range; - union { - LocalSourceRange definition_range; + SymbolHash target_symbol; - SymbolHash target_symbol; - }; + void set_definition_range(LocalSourceRange range) { + target_symbol = std::bit_cast(range); + } + + auto definition_range() { + return std::bit_cast(target_symbol); + } }; struct Occurrence { @@ -40,7 +46,10 @@ struct FileIndex { struct Symbol { std::string name; - /// ... + SymbolKind kind; + + /// All files that referenced this symbol. + Bitmap reference_files; }; struct TUIndex { diff --git a/include/Index/schema.fbs b/include/Index/schema.fbs index c64d178e..1542a9e6 100644 --- a/include/Index/schema.fbs +++ b/include/Index/schema.fbs @@ -11,8 +11,8 @@ struct Occurrence { } struct Relation { - kind: int; - padding: int; + kind: uint; + padding: uint; range: Range; target_symbol: ulong; } diff --git a/include/Support/Bitmap.h b/include/Support/Bitmap.h new file mode 100644 index 00000000..44f8dacf --- /dev/null +++ b/include/Support/Bitmap.h @@ -0,0 +1,11 @@ +#pragma once + +#define ROARING_EXCEPTIONS 0 +#define ROARING_TERMINATE(message) std::abort() +#include "roaring/roaring.hh" + +namespace clice { + +using Bitmap = roaring::Roaring; + +} diff --git a/src/Index/HeaderIndex.cpp b/src/Index/HeaderIndex.cpp deleted file mode 100644 index 8f803215..00000000 --- a/src/Index/HeaderIndex.cpp +++ /dev/null @@ -1,238 +0,0 @@ -#include "Index/HeaderIndex.h" - -namespace clice::index::memory { - -std::uint32_t HeaderIndex::alloc_hctx_id() { - std::uint32_t new_hctx_id; - if(erased_hctx_ids.empty()) { - new_hctx_id = max_hctx_id; - max_hctx_id += 1; - } else { - new_hctx_id = erased_hctx_ids.front(); - erased_hctx_ids.pop_front(); - } - return new_hctx_id; -} - -std::uint32_t HeaderIndex::alloc_cctx_id() { - std::uint32_t new_cctx_id; - if(erased_cctx_ids.empty()) { - new_cctx_id = max_cctx_id; - max_cctx_id += 1; - cctx_hctx_refs.emplace_back(1); - cctx_element_refs.emplace_back(0); - } else { - new_cctx_id = erased_cctx_ids.front(); - erased_cctx_ids.pop_front(); - cctx_hctx_refs[new_cctx_id] = 1; - cctx_element_refs[new_cctx_id] = 0; - } - return new_cctx_id; -} - -void HeaderIndex::remove(this HeaderIndex& self, llvm::StringRef path) { - auto it = self.header_contexts.find(path); - - /// If no such file, nothing to do. - if(it == self.header_contexts.end()) { - return; - } - - llvm::SmallVector erased_hctx_ids; - llvm::SmallVector erased_cctx_ids; - - for(auto& context: it->second) { - erased_hctx_ids.push_back(context.hctx_id); - self.erased_hctx_ids.push_back(context.hctx_id); - - auto cctx_id = context.cctx_id; - auto& ref_count = self.cctx_hctx_refs[cctx_id]; - assert(ref_count > 0); - - /// If the ref count of the canonical context id drops to 0, - /// we need to delete it. - ref_count -= 1; - if(ref_count == 0) { - erased_cctx_ids.push_back(cctx_id); - self.erased_cctx_ids.push_back(cctx_id); - self.cctx_element_refs[cctx_id] = 0; - } - } - - self.header_contexts.erase(it); - - /// Remove all refs to this header context id. - for(auto& state: self.independent_elem_states) { - for(auto hctx_id: erased_hctx_ids) { - state.erase(hctx_id); - } - } - - /// Remove all refs to this canonical context id. - Bitmap erased_flag = self.erased_flag(); - - for(auto& state: self.dependent_elem_states) { - state &= erased_flag; - } -} - -/// Merge all elements from other into self. And update_context is invoked every time -/// when a element is inserted. The second argument is inserted `Contextual` in the -/// other, the first element is inserted element in the self, empty if the element -/// is new to self. -static void merge_elements(HeaderIndex& self, RawIndex& raw, auto& update_context) { - /// Merge symbols from other into self. - for(auto& [symbol_id, symbol]: raw.symbols) { - auto [it, success] = self.symbols.try_emplace(symbol_id, std::move(symbol)); - auto& self_symbol = it->second; - - if(success) [[unlikely]] { - /// If insert successfully, this is a new symbol and it means - /// we need update all context states of this symbol. - for(auto& relation: self_symbol.relations) { - update_context(relation.ctx, relation.ctx.is_dependent(), true); - } - continue; - } - - /// If self already has this symbol, try to merge all relations. - for(auto& relation: symbol.relations) { - auto [it, success] = self_symbol.relations.insert(relation); - update_context(it->ctx, relation.ctx.is_dependent(), success); - } - } - - for(auto& [range, occurrence_group]: raw.occurrences) { - auto [it, success] = self.occurrences.try_emplace(range, std::move(occurrence_group)); - auto& self_occurrence_group = it->second; - - if(success) [[unlikely]] { - /// Insert successfully. - for(auto& occurrence: self_occurrence_group) { - update_context(occurrence.ctx, occurrence.ctx.is_dependent(), true); - } - continue; - } - - for(auto& occurrence: occurrence_group) { - auto i = 0; - - /// In most of cases, there is only one element in the group. - /// So don't worry about the performance. - for(auto& self_occurrence: self_occurrence_group) { - if(occurrence.target_symbol == self_occurrence.target_symbol) { - break; - } - i += 1; - } - - if(i != self_occurrence_group.size()) { - update_context(self_occurrence_group[i].ctx, occurrence.ctx.is_dependent(), false); - } else { - /// If not found insert new occurrence. - auto& o = self_occurrence_group.emplace_back(occurrence); - update_context(o.ctx, occurrence.ctx.is_dependent(), true); - } - } - } -} - -auto HeaderIndex::merge(this HeaderIndex& self, - llvm::StringRef path, - std::uint32_t include, - RawIndex& raw) -> HeaderContext { - /// We could make sure the other has only one header context. - std::uint32_t new_hctx_id = self.alloc_hctx_id(); - - Bitmap flag = self.erased_flag(); - bool is_new_cctx = false; - std::uint32_t new_cctx_id = -1; - - llvm::SmallVector visited_elem_ids; - - /// TODO: simplify the logic of update context. - - std::uint32_t old_elements_refs = 0; - - auto update_context = [&](Contextual& self_elem, bool is_dependent, bool is_new) { - std::uint32_t new_elem_id; - - if(is_new) { - /// If this a new element, it means that the other index must introduce - /// a new canonical context id, we don't need to do following calculation. - is_new_cctx = true; - - if(new_cctx_id == -1) { - new_cctx_id = self.alloc_cctx_id(); - } - - if(is_dependent) { - old_elements_refs += 1; - new_elem_id = self.alloc_dependent_elem_id(); - self.dependent_elem_states[new_elem_id].set(new_cctx_id); - } else { - new_elem_id = self.alloc_independent_elem_id(); - self.independent_elem_states[new_elem_id].insert(new_hctx_id); - } - - self_elem = Contextual::from(is_dependent, new_elem_id); - } else { - if(self_elem.is_dependent()) { - old_elements_refs += 1; - if(is_new_cctx) { - /// If this element is not new, but we already make sure the context is new - /// add its context. - self.dependent_elem_states[self_elem.offset()].set(new_cctx_id); - } else { - /// If this element is not new and we still cannot make sure whether this is - /// new canonical context. - flag &= self.dependent_elem_states[self_elem.offset()]; - visited_elem_ids.emplace_back(self_elem.offset()); - if(flag.none()) { - is_new_cctx = true; - } - } - } else { - self.independent_elem_states[self_elem.offset()].insert(new_hctx_id); - } - } - }; - - /// Merge all elements from other into self and calculate the bitmap state. - merge_elements(self, raw, update_context); - - if(!is_new_cctx) { - assert(new_cctx_id == -1 && flag.any()); - for(auto i = 0; i < self.max_cctx_id; i++) { - if(!flag.test(i)) { - continue; - } - - if(self.cctx_element_refs[i] == old_elements_refs) { - new_cctx_id = i; - break; - } - } - } - - if(new_cctx_id == -1) { - new_cctx_id = self.alloc_cctx_id(); - is_new_cctx = true; - } - - if(is_new_cctx) { - /// In the end we set all visited element ids. - for(auto id: visited_elem_ids) { - self.dependent_elem_states[id].set(new_cctx_id); - } - self.cctx_element_refs[new_cctx_id] = old_elements_refs; - } - - return self.header_contexts[path].emplace_back(HeaderContext{ - .include = include, - .hctx_id = new_hctx_id, - .cctx_id = new_cctx_id, - }); -} - -} // namespace clice::index::memory diff --git a/src/Index/IncludeGraph.cpp b/src/Index/IncludeGraph.cpp index 9e3c7c3c..a1e0e7e8 100644 --- a/src/Index/IncludeGraph.cpp +++ b/src/Index/IncludeGraph.cpp @@ -7,7 +7,13 @@ static std::uint32_t addIncludeChain(CompilationUnit& unit, clang::FileID fid, IncludeGraph& graph, llvm::StringMap& path_table) { + auto include_loc = unit.include_location(fid); + if(include_loc.isInvalid()) { + return -1; + } + auto& [paths, locations, file_table] = graph; + auto [iter, success] = file_table.try_emplace(fid, locations.size()); if(!success) { return iter->second; @@ -15,13 +21,12 @@ static std::uint32_t addIncludeChain(CompilationUnit& unit, auto index = iter->second; - auto includeLoc = unit.include_location(fid); - if(includeLoc.isValid()) { - auto presumed = unit.presumed_location(includeLoc); + { + auto presumed = unit.presumed_location(include_loc); locations.emplace_back(); locations[index].line = presumed.getLine(); - auto path = unit.file_path(presumed.getFileID()); + auto path = unit.file_path(fid); auto [iter, success] = path_table.try_emplace(path, paths.size()); if(success) { paths.emplace_back(path); @@ -43,8 +48,9 @@ IncludeGraph IncludeGraph::from(CompilationUnit& unit) { llvm::StringMap path_table; IncludeGraph graph; for(auto fid: unit.files()) { - addIncludeChain(unit, fid, graph, path_table); + graph.file_table[fid] = addIncludeChain(unit, fid, graph, path_table); } + graph.paths.emplace_back(unit.file_path(unit.interested_file())); return graph; } diff --git a/src/Index/MergedIndex.cpp b/src/Index/MergedIndex.cpp index 6dded7b1..aed13727 100644 --- a/src/Index/MergedIndex.cpp +++ b/src/Index/MergedIndex.cpp @@ -1,6 +1,7 @@ +#include "schema_generated.h" #include "Index/MergedIndex.h" #include "llvm/Support/SHA256.h" -#include "schema_generated.h" +#include "llvm/Support/raw_os_ostream.h" namespace clice::index { @@ -68,10 +69,6 @@ void MergedIndex::merge(llvm::StringRef path, std::uint32_t include, FileIndex& return; } - auto data = allocator.Allocate(32); - std::ranges::copy(hash, data); - it->first = llvm::StringRef(data, hash.size()); - for(auto& occurrence: index.occurrences) { this->occurrences[occurrence].add(canonical_id); } @@ -87,4 +84,119 @@ void MergedIndex::merge(llvm::StringRef path, std::uint32_t include, FileIndex& max_canonical_id += 1; } +void MergedIndex::serialize(this MergedIndex& self, llvm::raw_ostream& out) { + namespace fbs = flatbuffers; + fbs::FlatBufferBuilder builder(1024); + + std::vector> canonical_cache; + canonical_cache.reserve(self.canonical_cache.size()); + for(auto& [hash, canonical_id]: self.canonical_cache) { + canonical_cache.emplace_back( + binary::CreateCacheEntry(builder, + builder.CreateString(hash.data(), hash.size()), + canonical_id)); + }; + + std::vector> header_contexts; + header_contexts.reserve(self.contexts.size()); + for(auto& [path, contexts]: self.contexts) { + header_contexts.emplace_back(binary::CreateHeaderContextsEntry( + builder, + builder.CreateString(path.data(), path.size()), + binary::CreateHeaderContexts( + builder, + contexts.version, + builder.CreateVectorOfStructs( + reinterpret_cast(contexts.includes.data()), + contexts.includes.size())))); + }; + + llvm::SmallVector buffer; + + std::vector> occurrences; + occurrences.reserve(self.occurrences.size()); + for(auto& [occurrence, bitmap]: self.occurrences) { + buffer.resize_for_overwrite(bitmap.getSizeInBytes(false)); + bitmap.write(buffer.data(), false); + occurrences.emplace_back(binary::CreateOccurrenceEntry( + builder, + reinterpret_cast(&occurrence), + builder.CreateVector(reinterpret_cast(buffer.data()), buffer.size()))); + buffer.clear(); + } + + std::vector> relations; + relations.reserve(self.relations.size()); + for(auto& [symbold_id, symbol_relations]: self.relations) { + std::vector> entries; + entries.reserve(symbol_relations.size()); + for(auto& [relation, bitmap]: symbol_relations) { + buffer.resize_for_overwrite(bitmap.getSizeInBytes(false)); + bitmap.write(buffer.data(), false); + entries.emplace_back(binary::CreateRelationEntry( + builder, + reinterpret_cast(&relation), + builder.CreateVector(reinterpret_cast(buffer.data()), + buffer.size()))); + buffer.clear(); + } + + relations.emplace_back( + binary::CreateSymbolRelationsEntryDirect(builder, symbold_id, &entries)); + } + + auto merged_index = binary::CreateMergedIndexDirect(builder, + self.max_canonical_id, + &canonical_cache, + &header_contexts, + &occurrences, + &relations); + builder.Finish(merged_index); + + out.write(reinterpret_cast(builder.GetBufferPointer()), builder.GetSize()); +} + +MergedIndex MergedIndexView::deserialize() { + namespace fbs = flatbuffers; + auto root = fbs::GetRoot(data); + + MergedIndex index; + index.max_canonical_id = root->max_canonical_id(); + + for(auto entry: *root->canonical_cache()) { + index.canonical_cache.try_emplace(entry->sha256()->string_view(), entry->canonical_id()); + } + + index.canonical_ref_counts.resize(index.max_canonical_id, 0); + + HeaderContexts contexts; + for(auto entry: *root->contexts()) { + auto path = entry->path()->string_view(); + contexts.version = entry->contexts()->version(); + for(auto include: *entry->contexts()->includes()) { + index.canonical_ref_counts[include->canonical_id()] += 1; + contexts.includes.emplace_back(include->include_(), include->canonical_id()); + } + index.contexts.try_emplace(path, std::move(contexts)); + } + + for(auto entry: *root->occurrences()) { + index.occurrences.try_emplace( + *reinterpret_cast(entry->occurrence()), + Bitmap::read(reinterpret_cast(entry->context()->data()), false)); + } + + for(auto entry: *root->relations()) { + auto& relations = index.relations[entry->symbol()]; + for(auto relation_entry: *entry->relations()) { + relations.try_emplace( + *reinterpret_cast(relation_entry->relation()), + Bitmap::read(reinterpret_cast(relation_entry->context()->data()), + false)); + } + } + + return index; +} + } // namespace clice::index diff --git a/src/Index/TUIndex.cpp b/src/Index/TUIndex.cpp index 693c17ba..5b21f208 100644 --- a/src/Index/TUIndex.cpp +++ b/src/Index/TUIndex.cpp @@ -16,7 +16,6 @@ public: void handleDeclOccurrence(const clang::NamedDecl* decl, RelationKind kind, clang::SourceLocation location) { - assert(decl && "Invalid decl"); decl = ast::normalize(decl); if(location.isMacroID()) { @@ -37,6 +36,12 @@ public: auto& index = result.file_indices[fid]; auto symbol_id = unit.getSymbolID(decl); + auto [it, success] = result.symbols.try_emplace(symbol_id.hash); + if(success) { + auto& symbol = it->second; + symbol.name = ast::display_name_of(decl); + symbol.kind = SymbolKind::from(decl); + } index.occurrences.emplace_back(range, symbol_id.hash); } @@ -72,10 +77,10 @@ public: Relation relation{.kind = kind}; if(kind.isDeclOrDef()) { - auto [fid2, definitionRange] = unit.decompose_expansion_range(decl->getSourceRange()); + auto [fid2, definition_range] = unit.decompose_expansion_range(decl->getSourceRange()); assert(fid == fid2 && "Invalid definition location"); relation.range = relationRange; - relation.definition_range = definitionRange; + relation.set_definition_range(definition_range); } else if(kind.isReference()) { relation.range = relationRange; relation.target_symbol = 0; @@ -99,7 +104,16 @@ public: run(); for(auto& [fid, index]: result.file_indices) { + for(auto& [symbol_id, relations]: index.relations) { + std::ranges::sort(relations, refl::less); + auto range = std::ranges::unique(relations, refl::equal); + relations.erase(range.begin(), range.end()); + result.symbols[symbol_id].reference_files.add(result.graph.path_id(fid)); + } + std::ranges::sort(index.occurrences, refl::less); + auto range = std::ranges::unique(index.occurrences, refl::equal); + index.occurrences.erase(range.begin(), range.end()); } } diff --git a/tests/unit/Index/HeaderIndex.cpp b/tests/unit/Index/HeaderIndex.cpp deleted file mode 100644 index e6f79b86..00000000 --- a/tests/unit/Index/HeaderIndex.cpp +++ /dev/null @@ -1,836 +0,0 @@ -#include "Test/Tester.h" -#include "Index/HeaderIndex.h" - -namespace clice::testing { - -namespace { - -using namespace clice::index::memory; - -struct DumpConfig { - bool enable_total = false; - bool enable_contexts = false; - bool enable_symbol = false; - bool enable_occurrence = false; -}; - -auto dump = [](HeaderIndex& index, DumpConfig config) { - if(config.enable_total) { - std::println("\n---------------------------Total Info---------------------------"); - std::println("file count: {}", index.file_count()); - std::println("header context count: {}", index.header_context_count()); - std::println("canonical context count: {}", index.canonical_context_count()); - std::println("symbol count: {}", index.symbols.size()); - std::println("occurrence count: {}", index.occurrences.size()); - } - - if(config.enable_contexts) { - std::println("\n--------------------------Contexts Info-------------------------"); - for(auto& [path, contents]: index.header_contexts) { - std::println("{}:", path); - for(auto& context: contents) { - std::println(" include: {}, hctx_id: {}, cctx_id: {}", - context.include, - context.hctx_id, - context.cctx_id); - } - } - } - - if(config.enable_symbol) { - std::println("\n-------------------------Symbols Info--------------------------"); - for(auto& [symbol_id, symbol]: index.symbols) { - std::println("symbol: {}, kind: {}", symbol.name, symbol.kind.name()); - for(auto& relation: symbol.relations) { - if(relation.ctx.is_dependent()) { - auto context = index.dependent_elem_states[relation.ctx.offset()]; - std::println(" kind: {}, context: {:#b}", - relation.kind.name(), - context.to_ulong()); - } - } - } - } - - if(config.enable_occurrence) { - std::println("\n-------------------------Occurrences Info--------------------------"); - for(auto& [range, occurrences]: index.occurrences) { - std::println("occurrence: {} {}", range.begin, range.end); - for(auto& occurrence: occurrences) { - if(occurrence.ctx.is_dependent()) { - auto context = index.dependent_elem_states[occurrence.ctx.offset()]; - std::println(" target: {}, context: {:#b}", - occurrence.target_symbol, - context.to_ulong()); - } - } - } - } -}; - -/// TODO: We should have a more clean way to save test data(like json), rather than out put code -/// directly. -auto test_code = [](HeaderIndex& index, std::uint32_t id) { - std::string code; - std::string index_name = std::format("index{}", id); - code += std::format("RawIndex {};", index_name); - auto& [path, contexts] = *index.header_contexts.begin(); - code += std::format(R"({}.add_context("{}", {});)", index_name, path, contexts[0].include); - code += "\n"; - - for(auto& [symbol_id, symbol]: index.symbols) { - code += "{"; - code += std::format(R"( - auto& symbol = {}.get_symbol({}ull); - symbol.name = "{}"; - symbol.kind = SymbolKind::{}; - )", - index_name, - symbol_id, - symbol.name, - symbol.kind.name()); - code += "\n"; - - for(auto& relation: symbol.relations) { - code += std::format(R"({}.add_relation( - symbol, - Relation{{ - .kind = RelationKind::{}, - .range = {{ {}, {} }}, - .target_symbol = {}ull, - }} - );)", - index_name, - relation.kind.name(), - relation.range.begin, - relation.range.end, - relation.target_symbol); - } - code += "}\n"; - } - - // for(auto& [range, occurrence_group]: index.occurrences) { - // code += "{"; - // code += std::format("LocalSourceRange range{{ {}, {} }};\n", range.begin, range.end); - // for(auto& occurrence: occurrence_group) { - // code += std::format("index.add_occurrence(range, {});\n", - // occurrence.target_symbol); - // } - // code += "}"; - // } - - return code; -}; - -suite<"HeaderIndex"> header_index = [] { - test("AddRemoveContext") = [&] { - HeaderIndex index; - - { - auto context = index.add_context("test.h", 1); - expect(that % context.cctx_id == 0); - expect(that % context.hctx_id == 0); - expect(that % index.header_context_count() == 1); - expect(that % index.canonical_context_count() == 1); - } - - { - auto context = index.add_context("test.h", 2); - expect(that % context.cctx_id == 1); - expect(that % context.hctx_id == 1); - expect(that % index.header_context_count() == 2); - expect(that % index.canonical_context_count() == 2); - } - - expect(that % index.file_count() == 1); - - { - auto context = index.add_context("test2.h", 1); - expect(that % context.cctx_id == 2); - expect(that % context.hctx_id == 2); - expect(that % index.header_context_count() == 3); - expect(that % index.canonical_context_count() == 3); - } - - expect(that % index.file_count() == 2); - - index.remove("test.h"); - - expect(that % index.header_context_count() == 1); - expect(that % index.canonical_context_count() == 1); - - /// Test reuse context id and context ref. - { - auto context = index.add_context("test3.h", 1); - expect(that % context.cctx_id == 0); - expect(that % context.hctx_id == 0); - expect(that % index.header_context_count() == 2); - expect(that % index.canonical_context_count() == 2); - } - - { - index.add_context("test4.h", 1); - expect(that % index.header_context_count() == 3); - expect(that % index.canonical_context_count() == 3); - } - - { - index.add_context("test5.h", 1); - expect(that % index.header_context_count() == 4); - expect(that % index.canonical_context_count() == 4); - } - - expect(that % index.file_count() == 4); - }; - - test("SymbolInsert") = [&] { - HeaderIndex index; - index.add_context("test.h", 1); - index.add_occurrence({1, 2}, 1); - }; - - test("MergeEmpty") = [&] { - HeaderIndex base; - - RawIndex index; - index.add_occurrence({1, 2}, 1); - base.merge("test.h", 1, index); - expect(that % base.header_context_count() == 1); - expect(that % base.canonical_context_count() == 1); - expect(that % base.file_count() == 1); - - RawIndex index2; - base.merge("test2.h", 1, index2); - expect(that % base.header_context_count() == 2); - expect(that % base.canonical_context_count() == 2); - expect(that % base.file_count() == 2); - - RawIndex index3; - base.merge("test3.h", 1, index3); - expect(that % base.header_context_count() == 3); - expect(that % base.canonical_context_count() == 2); - expect(that % base.file_count() == 3); - }; - - test("MergeOccurrence") = [&] { - HeaderIndex base; - - RawIndex index; - index.add_occurrence({1, 2}, 1); - base.merge("test.h", 1, index); - - RawIndex index2; - index2.add_occurrence({1, 2}, 1); - base.merge("test2.h", 1, index2); - expect(that % base.header_context_count() == 2); - expect(that % base.canonical_context_count() == 1); - expect(that % base.file_count() == 2); - - RawIndex index3; - index3.add_occurrence({1, 2}, 2); - base.merge("test3.h", 1, index3); - expect(that % base.header_context_count() == 3); - expect(that % base.canonical_context_count() == 2); - expect(that % base.file_count() == 3); - }; - - test("MergeSymbol") = [&] { - LocalSourceRange range = {0, 0}; - - HeaderIndex base; - { - RawIndex index; - auto& symbol = index.get_symbol(1); - index.add_relation(symbol, Relation{.kind = RelationKind::Reference, .range = range}); - base.merge("test.h", 1, index); - } - - /// Same canonical context. - { - RawIndex index; - auto& symbol = index.get_symbol(1); - index.add_relation(symbol, Relation{.kind = RelationKind::Reference, .range = range}); - auto context = base.merge("test2.h", 1, index); - expect(that % context.hctx_id == 1); - expect(that % context.cctx_id == 0); - expect(that % base.header_context_count() == 2); - expect(that % base.canonical_context_count() == 1); - expect(that % base.file_count() == 2); - } - - /// New canonical context. - { - RawIndex index; - auto& symbol = index.get_symbol(1); - index.add_relation(symbol, Relation{.kind = RelationKind::Definition, .range = range}); - - auto context = base.merge("test3.h", 1, index); - expect(that % context.hctx_id == 2); - expect(that % context.cctx_id == 1); - expect(that % base.header_context_count() == 3); - expect(that % base.canonical_context_count() == 2); - expect(that % base.file_count() == 3); - } - - /// New canonical context. - { - RawIndex index; - auto& symbol = index.get_symbol(1); - index.add_relation(symbol, Relation{.kind = RelationKind::Definition, .range = range}); - index.add_relation(symbol, Relation{.kind = RelationKind::Declaration, .range = range}); - - auto context = base.merge("test4.h", 1, index); - expect(that % context.hctx_id == 3); - expect(that % context.cctx_id == 2); - expect(that % base.header_context_count() == 4); - expect(that % base.canonical_context_count() == 3); - expect(that % base.file_count() == 4); - } - }; - - test("MergeReuse") = [&] { - LocalSourceRange range = {0, 0}; - HeaderIndex base; - - RawIndex index; - index.add_occurrence(range, 1); - base.merge("test.h", 1, index); - - /// Same context - RawIndex index2; - index2.add_occurrence(range, 1); - base.merge("test.h", 2, index2); - expect(that % base.canonical_context_count() == 1); - - /// New Context - RawIndex index3; - index3.add_occurrence(range, 2); - base.merge("test.h", 3, index3); - expect(that % base.canonical_context_count() == 2); - - /// Same Context - RawIndex index4; - index4.add_occurrence(range, 1); - base.merge("test.h", 4, index4); - expect(that % base.canonical_context_count() == 2); - }; - - test("MergeComplex") = [] { - RawIndex index1; - { - auto& symbol = index1.get_symbol(5617328926567294902ull); - symbol.name = "__need_wchar_t"; - symbol.kind = SymbolKind::Macro; - - index1.add_relation(symbol, - Relation{ - .kind = RelationKind::Definition, - .range = {1948, 1962}, - .target_symbol = 8426725836700ull, - }); - index1.add_relation(symbol, - Relation{ - .kind = RelationKind::Reference, - .range = {4100, 4114}, - .target_symbol = 0ull, - }); - } - { - auto& symbol = index1.get_symbol(17660704465322401956ull); - symbol.name = "__need_offsetof"; - symbol.kind = SymbolKind::Macro; - - index1.add_relation(symbol, - Relation{ - .kind = RelationKind::Reference, - .range = {4720, 4735}, - .target_symbol = 0ull, - }); - index1.add_relation(symbol, - Relation{ - .kind = RelationKind::Definition, - .range = {3433, 3448}, - .target_symbol = 14809047240041ull, - }); - } - { - auto& symbol = index1.get_symbol(12199573421319547529ull); - symbol.name = "__need_size_t"; - symbol.kind = SymbolKind::Macro; - - index1.add_relation(symbol, - Relation{ - .kind = RelationKind::Reference, - .range = {3867, 3880}, - .target_symbol = 0ull, - }); - index1.add_relation(symbol, - Relation{ - .kind = RelationKind::Definition, - .range = {1734, 1747}, - .target_symbol = 7503307867846ull, - }); - } - { - auto& symbol = index1.get_symbol(447841485290421751ull); - symbol.name = "__need_max_align_t"; - symbol.kind = SymbolKind::Macro; - - index1.add_relation(symbol, - Relation{ - .kind = RelationKind::Definition, - .range = {3399, 3417}, - .target_symbol = 14675903253831ull, - }); - index1.add_relation(symbol, - Relation{ - .kind = RelationKind::Reference, - .range = {4592, 4610}, - .target_symbol = 0ull, - }); - } - { - auto& symbol = index1.get_symbol(3892980363519083943ull); - symbol.name = "__need_nullptr_t"; - symbol.kind = SymbolKind::Macro; - - index1.add_relation(symbol, - Relation{ - .kind = RelationKind::Definition, - .range = {3138, 3154}, - .target_symbol = 13546326854722ull, - }); - index1.add_relation(symbol, - Relation{ - .kind = RelationKind::Reference, - .range = {4328, 4344}, - .target_symbol = 0ull, - }); - } - { - auto& symbol = index1.get_symbol(6389328935281374692ull); - symbol.name = "__need_NULL"; - symbol.kind = SymbolKind::Macro; - - index1.add_relation(symbol, - Relation{ - .kind = RelationKind::Reference, - .range = {4212, 4223}, - .target_symbol = 0ull, - }); - index1.add_relation(symbol, - Relation{ - .kind = RelationKind::Definition, - .range = {3005, 3016}, - .target_symbol = 12953621367741ull, - }); - } - { - auto& symbol = index1.get_symbol(13138966718646481517ull); - symbol.name = "__cplusplus"; - symbol.kind = SymbolKind::Macro; - - index1.add_relation(symbol, - Relation{ - .kind = RelationKind::Reference, - .range = {3367, 3378}, - .target_symbol = 0ull, - }); - } - { - auto& symbol = index1.get_symbol(4048786579988097027ull); - symbol.name = "__need_ptrdiff_t"; - symbol.kind = SymbolKind::Macro; - - index1.add_relation(symbol, - Relation{ - .kind = RelationKind::Definition, - .range = {1709, 1725}, - .target_symbol = 7408818587309ull, - }); - index1.add_relation(symbol, - Relation{ - .kind = RelationKind::Reference, - .range = {3747, 3763}, - .target_symbol = 0ull, - }); - } - - RawIndex index2; - { - auto& symbol = index2.get_symbol(12199573421319547529ull); - symbol.name = "__need_size_t"; - symbol.kind = SymbolKind::Macro; - - index2.add_relation(symbol, - Relation{ - .kind = RelationKind::Reference, - .range = {3867, 3880}, - .target_symbol = 0ull, - }); - } - { - auto& symbol = index2.get_symbol(6389328935281374692ull); - symbol.name = "__need_NULL"; - symbol.kind = SymbolKind::Macro; - - index2.add_relation(symbol, - Relation{ - .kind = RelationKind::Reference, - .range = {4212, 4223}, - .target_symbol = 0ull, - }); - } - - RawIndex index3; - - { - auto& symbol = index3.get_symbol(5617328926567294902ull); - symbol.name = "__need_wchar_t"; - symbol.kind = SymbolKind::Macro; - - index3.add_relation(symbol, - Relation{ - .kind = RelationKind::Reference, - .range = {4100, 4114}, - .target_symbol = 0ull, - }); - } - { - auto& symbol = index3.get_symbol(12199573421319547529ull); - symbol.name = "__need_size_t"; - symbol.kind = SymbolKind::Macro; - - index3.add_relation(symbol, - Relation{ - .kind = RelationKind::Reference, - .range = {3867, 3880}, - .target_symbol = 0ull, - }); - } - { - auto& symbol = index3.get_symbol(6389328935281374692ull); - symbol.name = "__need_NULL"; - symbol.kind = SymbolKind::Macro; - - index3.add_relation(symbol, - Relation{ - .kind = RelationKind::Reference, - .range = {4212, 4223}, - .target_symbol = 0ull, - }); - } - - RawIndex index4; - { - auto& symbol = index4.get_symbol(12199573421319547529ull); - symbol.name = "__need_size_t"; - symbol.kind = SymbolKind::Macro; - - index4.add_relation(symbol, - Relation{ - .kind = RelationKind::Reference, - .range = {3867, 3880}, - .target_symbol = 0ull, - }); - } - { - auto& symbol = index4.get_symbol(6389328935281374692ull); - symbol.name = "__need_NULL"; - symbol.kind = SymbolKind::Macro; - - index4.add_relation(symbol, - Relation{ - .kind = RelationKind::Reference, - .range = {4212, 4223}, - .target_symbol = 0ull, - }); - } - - RawIndex index5; - { - auto& symbol = index5.get_symbol(12199573421319547529ull); - symbol.name = "__need_size_t"; - symbol.kind = SymbolKind::Macro; - - index5.add_relation(symbol, - Relation{ - .kind = RelationKind::Reference, - .range = {3867, 3880}, - .target_symbol = 0ull, - }); - } - - RawIndex index6; - { - auto& symbol = index6.get_symbol(12199573421319547529ull); - symbol.name = "__need_size_t"; - symbol.kind = SymbolKind::Macro; - - index6.add_relation(symbol, - Relation{ - .kind = RelationKind::Reference, - .range = {3867, 3880}, - .target_symbol = 0ull, - }); - } - { - auto& symbol = index6.get_symbol(6389328935281374692ull); - symbol.name = "__need_NULL"; - symbol.kind = SymbolKind::Macro; - - index6.add_relation(symbol, - Relation{ - .kind = RelationKind::Reference, - .range = {4212, 4223}, - .target_symbol = 0ull, - }); - } - - RawIndex index7; - { - auto& symbol = index7.get_symbol(5617328926567294902ull); - symbol.name = "__need_wchar_t"; - symbol.kind = SymbolKind::Macro; - - index7.add_relation(symbol, - Relation{ - .kind = RelationKind::Definition, - .range = {1948, 1962}, - .target_symbol = 8426725836700ull, - }); - index7.add_relation(symbol, - Relation{ - .kind = RelationKind::Reference, - .range = {4100, 4114}, - .target_symbol = 0ull, - }); - } - { - auto& symbol = index7.get_symbol(17660704465322401956ull); - symbol.name = "__need_offsetof"; - symbol.kind = SymbolKind::Macro; - - index7.add_relation(symbol, - Relation{ - .kind = RelationKind::Reference, - .range = {4720, 4735}, - .target_symbol = 0ull, - }); - index7.add_relation(symbol, - Relation{ - .kind = RelationKind::Definition, - .range = {3433, 3448}, - .target_symbol = 14809047240041ull, - }); - } - { - auto& symbol = index7.get_symbol(12199573421319547529ull); - symbol.name = "__need_size_t"; - symbol.kind = SymbolKind::Macro; - - index7.add_relation(symbol, - Relation{ - .kind = RelationKind::Reference, - .range = {3867, 3880}, - .target_symbol = 0ull, - }); - index7.add_relation(symbol, - Relation{ - .kind = RelationKind::Definition, - .range = {1734, 1747}, - .target_symbol = 7503307867846ull, - }); - } - { - auto& symbol = index7.get_symbol(447841485290421751ull); - symbol.name = "__need_max_align_t"; - symbol.kind = SymbolKind::Macro; - - index7.add_relation(symbol, - Relation{ - .kind = RelationKind::Definition, - .range = {3399, 3417}, - .target_symbol = 14675903253831ull, - }); - index7.add_relation(symbol, - Relation{ - .kind = RelationKind::Reference, - .range = {4592, 4610}, - .target_symbol = 0ull, - }); - } - { - auto& symbol = index7.get_symbol(3892980363519083943ull); - symbol.name = "__need_nullptr_t"; - symbol.kind = SymbolKind::Macro; - - index7.add_relation(symbol, - Relation{ - .kind = RelationKind::Definition, - .range = {3138, 3154}, - .target_symbol = 13546326854722ull, - }); - index7.add_relation(symbol, - Relation{ - .kind = RelationKind::Reference, - .range = {4328, 4344}, - .target_symbol = 0ull, - }); - } - { - auto& symbol = index7.get_symbol(6389328935281374692ull); - symbol.name = "__need_NULL"; - symbol.kind = SymbolKind::Macro; - - index7.add_relation(symbol, - Relation{ - .kind = RelationKind::Reference, - .range = {4212, 4223}, - .target_symbol = 0ull, - }); - index7.add_relation(symbol, - Relation{ - .kind = RelationKind::Definition, - .range = {3005, 3016}, - .target_symbol = 12953621367741ull, - }); - } - { - auto& symbol = index7.get_symbol(13138966718646481517ull); - symbol.name = "__cplusplus"; - symbol.kind = SymbolKind::Macro; - - index7.add_relation(symbol, - Relation{ - .kind = RelationKind::Reference, - .range = {3367, 3378}, - .target_symbol = 0ull, - }); - } - { - auto& symbol = index7.get_symbol(4048786579988097027ull); - symbol.name = "__need_ptrdiff_t"; - symbol.kind = SymbolKind::Macro; - - index7.add_relation(symbol, - Relation{ - .kind = RelationKind::Definition, - .range = {1709, 1725}, - .target_symbol = 7408818587309ull, - }); - index7.add_relation(symbol, - Relation{ - .kind = RelationKind::Reference, - .range = {3747, 3763}, - .target_symbol = 0ull, - }); - } - - RawIndex index8; - { - auto& symbol = index8.get_symbol(5617328926567294902ull); - symbol.name = "__need_wchar_t"; - symbol.kind = SymbolKind::Macro; - - index8.add_relation(symbol, - Relation{ - .kind = RelationKind::Reference, - .range = {4100, 4114}, - .target_symbol = 0ull, - }); - } - { - auto& symbol = index8.get_symbol(12199573421319547529ull); - symbol.name = "__need_size_t"; - symbol.kind = SymbolKind::Macro; - - index8.add_relation(symbol, - Relation{ - .kind = RelationKind::Reference, - .range = {3867, 3880}, - .target_symbol = 0ull, - }); - } - { - auto& symbol = index8.get_symbol(6389328935281374692ull); - symbol.name = "__need_NULL"; - symbol.kind = SymbolKind::Macro; - - index8.add_relation(symbol, - Relation{ - .kind = RelationKind::Reference, - .range = {4212, 4223}, - .target_symbol = 0ull, - }); - } - - RawIndex index9; - { - auto& symbol = index9.get_symbol(6389328935281374692ull); - symbol.name = "__need_NULL"; - symbol.kind = SymbolKind::Macro; - - index9.add_relation(symbol, - Relation{ - .kind = RelationKind::Reference, - .range = {4212, 4223}, - .target_symbol = 0ull, - }); - } - - RawIndex index10; - { - auto& symbol = index10.get_symbol(12199573421319547529ull); - symbol.name = "__need_size_t"; - symbol.kind = SymbolKind::Macro; - - index10.add_relation(symbol, - Relation{ - .kind = RelationKind::Reference, - .range = {3867, 3880}, - .target_symbol = 0ull, - }); - } - - { - HeaderIndex base; - auto context = base.merge("main.cpp", 56, index1); - expect(refl::equal(context, HeaderIndex::HeaderContext{56, 0, 0})); - - context = base.merge("main.cpp", 83, index2); - expect(refl::equal(context, HeaderIndex::HeaderContext{83, 1, 1})); - - context = base.merge("main.cpp", 87, index3); - expect(refl::equal(context, HeaderIndex::HeaderContext{87, 2, 2})); - - context = base.merge("main.cpp", 118, index4); - expect(refl::equal(context, HeaderIndex::HeaderContext{118, 3, 1})); - - context = base.merge("main.cpp", 135, index5); - expect(refl::equal(context, HeaderIndex::HeaderContext{135, 4, 3})); - - context = base.merge("main.cpp", 147, index6); - expect(refl::equal(context, HeaderIndex::HeaderContext{147, 5, 1})); - - context = base.merge("main.cpp", 150, index7); - expect(refl::equal(context, HeaderIndex::HeaderContext{150, 6, 0})); - - context = base.merge("main.cpp", 178, index8); - expect(refl::equal(context, HeaderIndex::HeaderContext{178, 7, 2})); - - context = base.merge("main.cpp", 212, index9); - expect(refl::equal(context, HeaderIndex::HeaderContext{212, 8, 4})); - - context = base.merge("main.cpp", 226, index10); - expect(refl::equal(context, HeaderIndex::HeaderContext{226, 9, 3})); - } - }; -}; - -#if 0 - -#endif -} // namespace - -} // namespace clice::testing diff --git a/tests/unit/Index/MergedIndex.cpp b/tests/unit/Index/MergedIndex.cpp index 6685890d..c3ba7bf3 100644 --- a/tests/unit/Index/MergedIndex.cpp +++ b/tests/unit/Index/MergedIndex.cpp @@ -1,5 +1,6 @@ #include "Test/Tester.h" #include "Index/MergedIndex.h" +#include "Async/Async.h" namespace clice::testing { @@ -43,21 +44,33 @@ suite<"MergedIndex"> suite = [] { expect(eq(dump(it->range), dump(range)), location); }; - test("Assert") = [&] { + test("Serialization") = [&] { build_index(R"( #include + + int main () { + std::cout << "Hello world!" << std::endl; + return 0; + } )"); - std::println("{}", tu_index.file_indices.size()); - - index::MergedIndex merged; - + llvm::StringMap merged_indices; + auto& graph = tu_index.graph; for(auto& [fid, index]: tu_index.file_indices) { - auto path = tester.unit->file_path(fid); + llvm::StringRef path = graph.paths[graph.path_id(fid)]; + merged_indices[path].merge("main.cpp", graph.include_location_id(fid), index); + } - if(path.ends_with("stddef.h")) { - merged.merge(path, tu_index.graph.getInclude(fid), index); - } + for(auto& [path, merged]: merged_indices) { + llvm::SmallString<1024> s; + llvm::raw_svector_ostream os(s); + + merged.serialize(os); + + index::MergedIndexView view(s.data()); + auto merged2 = view.deserialize(); + + expect(merged == merged2); } }; }; diff --git a/tests/unit/Index/TUIndex.cpp b/tests/unit/Index/TUIndex.cpp index af12e0e0..1af2fc01 100644 --- a/tests/unit/Index/TUIndex.cpp +++ b/tests/unit/Index/TUIndex.cpp @@ -18,13 +18,9 @@ suite<"TUIndex"> suite = [] { tu_index = index::TUIndex::build(*tester.unit); }; - auto expect_select = [&](llvm::StringRef pos, - llvm::StringRef expect_range, - llvm::StringRef file = "", - std::source_location location = std::source_location::current()) { + auto select = [&](llvm::StringRef pos, + llvm::StringRef file = "") -> std::vector { auto offset = tester.point(pos, file); - auto range = tester.range(expect_range, file); - auto fid = file.empty() ? tester.unit->interested_file() : tester.unit->file_id(file); auto& index = tu_index.file_indices[fid]; @@ -34,13 +30,63 @@ suite<"TUIndex"> suite = [] { {}, [](index::Occurrence& occurrence) { return occurrence.range.end; }); - auto err = - std::format("Fail to find symbol for offser: {} range: range: {}", offset, dump(range)); + std::vector occurrences; + while(it != index.occurrences.end()) { + if(it->range.contains(offset)) { + occurrences.emplace_back(*it); + it++; + continue; + } - fatal / expect(it != index.occurrences.end(), location) << err; + break; + } + return occurrences; + }; + + auto expect_select = [&](llvm::StringRef pos, + llvm::StringRef expect_range, + llvm::StringRef file = "", + std::source_location location = std::source_location::current()) { + auto offset = tester.point(pos, file); + auto range = tester.range(expect_range, file); + auto occurrences = select(pos, file); + + fatal / expect(!occurrences.empty(), location) + << std::format("Fail to find symbol for offset: {}, target range: {}", + offset, + dump(range)); /// FIXME: Make eq pretty print reflectable struct. - expect(eq(dump(it->range), dump(range)), location); + expect(eq(dump(occurrences.front().range), dump(range)), location); + }; + + auto go_to_definition = [&](llvm::StringRef pos, + llvm::StringRef definition, + llvm::StringRef file = "", + std::source_location location = std::source_location::current()) { + auto offset = tester.point(pos, file); + auto range = tester.range(definition, file); + auto occurrences = select(pos, file); + + fatal / expect(occurrences.size() == 1, location) + << std::format("Fail to find symbol for offset: {}, target range: {}", + offset, + dump(range)); + + auto fid = file.empty() ? tester.unit->interested_file() : tester.unit->file_id(file); + auto& index = tu_index.file_indices[fid]; + + auto it = index.relations.find(occurrences.front().target); + fatal / expect(it != index.relations.end(), location) + << std::format("Cannot find target: {}", occurrences.front().target); + + auto& relations = it->second; + auto target = + std::ranges::find(relations, RelationKind::Definition, &index::Relation::kind); + + fatal / expect(target != relations.end(), location) + << std::format("Fail to find definition in {}", dump(relations)); + expect(eq(dump(target->range), dump(range)), location); }; test("Basic") = [&] { @@ -61,6 +107,144 @@ suite<"TUIndex"> suite = [] { expect_select("2", "2"); expect_select("3", "3"); }; + + test("ClassTemplate") = [&] { + build_index(R"( + template + struct $(primary_decl)foo; + + /// using type = $(forward_full)foo; + + template + struct @primary[foo] {}; + + template + struct $(partial_spec_decl)foo; + + template + struct @partial_spec[foo] {}; + + template <> + struct $(full_spec_decl)foo; + + template <> + struct @full_spec[foo] {}; + + template struct $(explicit_primary)foo; + + template struct $(explicit_partial)foo; + + $(implicit_primary_1)foo b; + $(implicit_primary_2)foo c; + $(implicit_partial)foo d; + $(implicit_full)foo a; + )"); + + go_to_definition("primary_decl", "primary"); + go_to_definition("explicit_primary", "primary"); + go_to_definition("implicit_primary_1", "primary"); + go_to_definition("implicit_primary_2", "primary"); + go_to_definition("partial_spec_decl", "partial_spec"); + go_to_definition("explicit_partial", "partial_spec"); + go_to_definition("implicit_partial", "partial_spec"); + /// FIXME: Figure forward template declaration. + /// go_to_definition("forward_full", "full_spec"); + go_to_definition("full_spec_decl", "full_spec"); + go_to_definition("implicit_full", "full_spec"); + }; + + test("FunctionTemplate") = [&] { + build_index(R"( + template void $(primary_decl)foo(); + + template void @primary[foo]() {} + + template <> void $(spec_decl)foo(); + + template <> void @spec[foo]() {} + + template void $(explicit_primary)foo(); + + int main() { + $(implicit_primary)foo(); + $(implicit_spec)foo(); + } + )"); + + go_to_definition("primary_decl", "primary"); + /// FIXME: clang doen't record location info of explicit function instantiation/ + /// See https://github.com/llvm/llvm-project/issues/115418. + /// go_to_definition("explicit_primary", "primary"); + go_to_definition("implicit_primary", "primary"); + go_to_definition("spec_decl", "spec"); + go_to_definition("implicit_spec", "spec"); + }; + + test("AliasTemplate") = [&] { + build_index(R"( + template + using @primary[foo] = T; + + $(implicit_primary)foo a; + )"); + + go_to_definition("implicit_primary", "primary"); + }; + + test("VarTemplate") = [&] { + build_index(R"( + template + extern int $(primary_decl)foo; + + template + int @primary[foo] = 1; + + template + extern int $(partial_spec_decl)foo; + + template + int @partial_spec[foo] = 2; + + template <> + float @full_spec[foo] = 1.0f; + + template int $(explicit_primary)foo; + + template int $(explicit_partial)foo; + + int main() { + $(implicit_primary_1)foo = 1; + $(implicit_primary_2)foo = 2; + $(implicit_partial)foo = 3; + $(implicit_full)foo = 4; + return 0; + } + )"); + + go_to_definition("primary_decl", "primary"); + /// go_to_definition("explicit_primary", "primary"); + go_to_definition("implicit_primary_1", "primary"); + go_to_definition("implicit_primary_2", "primary"); + go_to_definition("partial_spec_decl", "partial_spec"); + /// tester.GotoDefinition("explicit_partial", "partial_spec"); + go_to_definition("implicit_partial", "partial_spec"); + go_to_definition("implicit_full", "full_spec"); + }; + + test("Concept") = [&] { + build_index(R"( + template + concept @primary[$(primary)foo] = true; + + static_assert($(implicit)foo); + + $(implicit2)foo auto bar = 1; + )"); + + go_to_definition("primary", "primary"); + go_to_definition("implicit", "primary"); + go_to_definition("implicit2", "primary"); + }; }; } // namespace diff --git a/tests/unit/Index/Template.cpp b/tests/unit/Index/Template.cpp deleted file mode 100644 index 9f3299cd..00000000 --- a/tests/unit/Index/Template.cpp +++ /dev/null @@ -1,166 +0,0 @@ -/// #include "Test/IndexTester.h" -/// -/// namespace clice::testing { -/// -/// namespace { -/// -/// TEST(Index, ClassTemplate) { -/// const char* code = R"cpp( -/// template -/// struct $(primary_decl)foo; -/// -/// /// using type = $(forward_full)foo; -/// -/// template -/// struct $(primary)foo {}; -/// -/// template -/// struct $(partial_spec_decl)foo; -/// -/// template -/// struct $(partial_spec)foo {}; -/// -/// template <> -/// struct $(full_spec_decl)foo; -/// -/// template <> -/// struct $(full_spec)foo {}; -/// -/// template struct $(explicit_primary)foo; -/// -/// template struct $(explicit_partial)foo; -/// -/// $(implicit_primary_1)foo b; -/// $(implicit_primary_2)foo c; -/// $(implicit_partial)foo d; -/// $(implicit_full)foo a; -///)cpp"; -/// -/// IndexTester tester("main.cpp", code); -/// tester.run(); -/// tester.GotoDefinition("primary_decl", "primary"); -/// tester.GotoDefinition("explicit_primary", "primary"); -/// tester.GotoDefinition("implicit_primary_1", "primary"); -/// tester.GotoDefinition("implicit_primary_2", "primary"); -/// tester.GotoDefinition("partial_spec_decl", "partial_spec"); -/// tester.GotoDefinition("explicit_partial", "partial_spec"); -/// tester.GotoDefinition("implicit_partial", "partial_spec"); -/// /// FIXME: Figure forward template declaration. -/// /// tester.GotoDefinition("forward_full", "full_spec"); -/// tester.GotoDefinition("full_spec_decl", "full_spec"); -/// tester.GotoDefinition("implicit_full", "full_spec"); -/// -/// /// TODO: add more tests, FunctionTemplate, VarTemplate, ..., Dependent Name, ..., etc. -/// /// add tests for find references ..., !test symbol count. -/// } -/// -/// TEST(Index, FunctionTemplate) { -/// /// Function template doesn't have partial specialization. -/// const char* code = R"cpp( -/// template void $(primary_decl)foo(); -/// -/// template void $(primary)foo() {} -/// -/// template <> void $(spec_decl)foo(); -/// -/// template <> void $(spec)foo() {} -/// -/// template void $(explicit_primary)foo(); -/// -/// int main() { -/// $(implicit_primary)foo(); -/// $(implicit_spec)foo(); -/// } -///)cpp"; -/// -/// IndexTester tester("main.cpp", code); -/// tester.run(); -/// -/// tester.GotoDefinition("primary_decl", "primary"); -/// /// FIXME: -/// /// tester.GotoDefinition("explicit_primary", "primary"); -/// tester.GotoDefinition("implicit_primary", "primary"); -/// -/// tester.GotoDefinition("spec_decl", "spec"); -/// tester.GotoDefinition("implicit_spec", "spec"); -/// } -/// -/// TEST(Index, AliasTemplate) { -/// const char* code = R"cpp( -/// template -/// using $(primary)foo = T; -/// -/// $(implicit_primary)foo a; -///)cpp"; -/// -/// IndexTester tester("main.cpp", code); -/// tester.run(); -/// tester.GotoDefinition("implicit_primary", "primary"); -/// } -/// -/// TEST(Index, VarTemplate) { -/// const char* code = R"cpp( -/// template -/// extern int $(primary_decl)foo; -/// -/// template -/// int $(primary)foo = 1; -/// -/// template -/// extern int $(partial_spec_decl)foo; -/// -/// template -/// int $(partial_spec)foo = 2; -/// -/// template <> -/// float $(full_spec)foo = 1.0f; -/// -/// template int $(explicit_primary)foo; -/// -/// template int $(explicit_partial)foo; -/// -/// int main() { -/// $(implicit_primary_1)foo = 1; -/// $(implicit_primary_2)foo = 2; -/// $(implicit_partial)foo = 3; -/// $(implicit_full)foo = 4; -/// return 0; -/// } -///)cpp"; -/// -/// IndexTester tester("main.cpp", code); -/// tester.run(); -/// -/// tester.GotoDefinition("primary_decl", "primary"); -/// /// tester.GotoDefinition("explicit_primary", "primary"); -/// tester.GotoDefinition("implicit_primary_1", "primary"); -/// tester.GotoDefinition("implicit_primary_2", "primary"); -/// -/// tester.GotoDefinition("partial_spec_decl", "partial_spec"); -/// /// tester.GotoDefinition("explicit_partial", "partial_spec"); -/// tester.GotoDefinition("implicit_partial", "partial_spec"); -/// -/// tester.GotoDefinition("implicit_full", "full_spec"); -/// } -/// -/// TEST(Index, Concept) { -/// const char* code = R"cpp( -/// template -/// concept $(primary)foo = true; -/// -/// static_assert($(implicit)foo); -/// -/// $(implicit2)foo auto bar = 1; -///)cpp"; -/// -/// IndexTester tester("main.cpp", code); -/// tester.run(); -/// -/// tester.GotoDefinition("primary", "primary"); -/// tester.GotoDefinition("implicit", "primary"); -/// tester.GotoDefinition("implicit2", "primary"); -/// } -/// -/// } // namespace -/// -/// } // namespace clice::testing