diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index faa196a2..76f46603 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,4 +1,5 @@ # .pre-commit-config.yaml +exclude: "\\.patch$" repos: - repo: https://github.com/pre-commit/pre-commit-hooks diff --git a/CMakeLists.txt b/CMakeLists.txt index 57977388..5bde928f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,236 +1,101 @@ cmake_minimum_required(VERSION 3.20) project(CLICE_PROJECT LANGUAGES C CXX) + set(CMAKE_CXX_STANDARD 23) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin) -include(cmake/llvm_setup.cmake) - -setup_llvm() - -get_filename_component(LLVM_INSTALL_PATH "${LLVM_INSTALL_PATH}" ABSOLUTE) - -if(NOT EXISTS "${LLVM_INSTALL_PATH}") - message(FATAL_ERROR "Error: The specified LLVM_INSTALL_PATH does not exist: ${LLVM_INSTALL_PATH}") -endif() -message(STATUS "Found llvm-libs ${LLVM_INSTALL_PATH}") -if (EXISTS "${LLVM_CMAKE_DIR}") - list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_DIR}") -else() - list(APPEND CMAKE_PREFIX_PATH "${LLVM_INSTALL_PATH}") -endif() -message(STATUS "CMake module path: ${CMAKE_MODULE_PATH}") -message(STATUS "CMake prefix path: ${CMAKE_PREFIX_PATH}") +include(GNUInstallDirs) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib") +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib") +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin") +# Make sure all third libraries are affected by ABI related options if(CLICE_USE_LIBCXX) - string(APPEND CMAKE_CXX_FLAGS "-stdlib=libc++") - string(APPEND CMAKE_EXE_LINKER_FLAGS "-stdlib=libc++") - string(APPEND CMAKE_SHARED_LINKER_FLAGS "-stdlib=libc++") - string(APPEND CMAKE_MODULE_LINKER_FLAGS "-stdlib=libc++") + string(APPEND CMAKE_CXX_FLAGS " -stdlib=libc++") + string(APPEND CMAKE_EXE_LINKER_FLAGS " -stdlib=libc++") + string(APPEND CMAKE_SHARED_LINKER_FLAGS " -stdlib=libc++") endif() -message(STATUS "Installing other dependencies, please wait...") -# install dependencies -include(FetchContent) -if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_BUILD_TYPE STREQUAL "Debug") - # libuv option - set(ASAN ON CACHE BOOL "" FORCE) +if(NOT WIN32 AND CMAKE_BUILD_TYPE STREQUAL "Debug") + string(APPEND CMAKE_CXX_FLAGS " -fsanitize=address") + string(APPEND CMAKE_EXE_LINKER_FLAGS " -fsanitize=address") + string(APPEND CMAKE_SHARED_LINKER_FLAGS " -fsanitize=address") endif() -message(STATUS "Fetching libuv...") -FetchContent_Declare( - libuv - GIT_REPOSITORY https://github.com/libuv/libuv.git - GIT_TAG v1.x -) +include("${PROJECT_SOURCE_DIR}/cmake/package.cmake") -set(BUILD_SHARED_LIBS OFF) -message(STATUS "Fetching spdlog...") -FetchContent_Declare( - spdlog - GIT_REPOSITORY https://github.com/gabime/spdlog.git - GIT_TAG v1.15.3 -) +add_library(clice_options INTERFACE) -message(STATUS "Fetching tomlplusplus...") -FetchContent_Declare( - tomlplusplus - GIT_REPOSITORY https://github.com/marzer/tomlplusplus.git -) - -FetchContent_MakeAvailable(libuv spdlog tomlplusplus) - -message(STATUS "Found tomlplusplus: ${tomlplusplus_SOURCE_DIR}") -message(STATUS "Found libuv: ${libuv_SOURCE_DIR}") -message(STATUS "Found spdlog: ${spdlog_SOURCE_DIR}") +if(CLICE_CI_ENVIRONMENT) + target_compile_definitions(clice_options INTERFACE CLICE_CI_ENVIRONMENT) +endif() if(WIN32) set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreadedDLL") - target_compile_definitions(uv_a PRIVATE _CRT_SECURE_NO_WARNINGS) + target_link_options(clice_options INTERFACE -fuse-ld=lld-link) +else() + target_link_options(clice_options INTERFACE -fuse-ld=lld) endif() -target_compile_definitions(spdlog PUBLIC SPDLOG_USE_STD_FORMAT=1 SPDLOG_NO_EXCEPTIONS=1) - if (MSVC) - # Remove any RTTI or exception enabling flags from CMAKE_CXX_FLAGS - string(REPLACE "/EHsc" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") - string(REPLACE "/GR" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") - - # Fix MSVC Non-standard preprocessor caused error C1189 - # While compiling Command.cpp, MSVC won't expand Options macro correctly - # Output: D:\Desktop\code\clice\build\.packages\l\llvm\20.1.5\cc2aa9f1d09a4b71b6fa3bf0011f6387\include\clang/Driver/Options.inc(3590): error C2365: “clang::driver::options::OPT_”: redefinition; previous definition was 'enumerator' - set(CLICE_CXX_FLAGS "/GR-;/EHsc-;/Zc:preprocessor;") + target_compile_options(clice_options INTERFACE + /GR- + /EHsc- + /Zc:preprocessor + ) else() - set(CLICE_CXX_FLAGS "-fno-rtti;-fno-exceptions;-Wno-deprecated-declarations;-Wno-undefined-inline;") + target_compile_options(clice_options INTERFACE + -fno-rtti + -fno-exceptions + -Wno-deprecated-declarations + -Wno-undefined-inline + ) endif() -set(CLICE_LINKER_FLAGS "") - -# Build-type specific flags -if(CMAKE_BUILD_TYPE STREQUAL "Debug") - message(STATUS "Debug build: Enabling -g -O0 -fsanitize=address") - list(APPEND CLICE_CXX_FLAGS "-g;-O0;-fsanitize=address") - list(APPEND CLICE_LINKER_FLAGS "-fsanitize=address") -elseif(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") - if(NOT MSVC) - # MSVC only supports ASAN - message(STATUS "RelWithDebInfo build: Enabling -fsanitize=thread") - list(APPEND CLICE_CXX_FLAGS "-fsanitize=thread") - list(APPEND CLICE_LINKER_FLAGS "-fsanitize=thread") - endif() -else() - message(STATUS "Release/Default build: Enabling -O3") - list(APPEND CLICE_CXX_FLAGS "-O3") -endif() - -if(CLICE_CI_ENVIRONMENT) - list(APPEND CLICE_CXX_FLAGS "-DCLICE_CI_ENVIRONMENT") -endif() - - -# Linker flags for LLD -if(WIN32) - list(APPEND CLICE_LINKER_FLAGS "-fuse-ld=lld-link") -else() - list(APPEND CLICE_LINKER_FLAGS "-fuse-ld=lld") -endif() - -# build clice core part as library file(GLOB_RECURSE CLICE_SOURCES CONFIGURE_DEPENDS - "${CMAKE_SOURCE_DIR}/src/AST/*.cpp" - "${CMAKE_SOURCE_DIR}/src/Async/*.cpp" - "${CMAKE_SOURCE_DIR}/src/Basic/*.cpp" - "${CMAKE_SOURCE_DIR}/src/Compiler/*.cpp" - "${CMAKE_SOURCE_DIR}/src/Index/*.cpp" - "${CMAKE_SOURCE_DIR}/src/Feature/*.cpp" - "${CMAKE_SOURCE_DIR}/src/Server/*.cpp" - "${CMAKE_SOURCE_DIR}/src/Support/*.cpp" + "${PROJECT_SOURCE_DIR}/src/AST/*.cpp" + "${PROJECT_SOURCE_DIR}/src/Async/*.cpp" + "${PROJECT_SOURCE_DIR}/src/Basic/*.cpp" + "${PROJECT_SOURCE_DIR}/src/Compiler/*.cpp" + "${PROJECT_SOURCE_DIR}/src/Index/*.cpp" + "${PROJECT_SOURCE_DIR}/src/Feature/*.cpp" + "${PROJECT_SOURCE_DIR}/src/Server/*.cpp" + "${PROJECT_SOURCE_DIR}/src/Support/*.cpp" ) add_library(clice-core STATIC "${CLICE_SOURCES}") -# set llvm include and lib path -add_library(llvm-libs INTERFACE IMPORTED) - -message(STATUS "LLVM include path: ${LLVM_INSTALL_PATH}/include") -# add to include directories -target_include_directories(llvm-libs INTERFACE "${LLVM_INSTALL_PATH}/include") - -if(EXISTS "${CMAKE_CURRENT_BINARY_DIR}/include") - # private files are downloaded here - message(STATUS "Fetched private headers to include: ${CMAKE_CURRENT_BINARY_DIR}/include") - target_include_directories(llvm-libs INTERFACE "${CMAKE_CURRENT_BINARY_DIR}/include") -endif() - -if(WIN32) - target_compile_definitions(llvm-libs INTERFACE "CLANG_BUILD_STATIC") - target_link_libraries(llvm-libs INTERFACE version ntdll) -endif() - -if(CMAKE_BUILD_TYPE STREQUAL "Debug") - target_link_directories(llvm-libs INTERFACE "${LLVM_INSTALL_PATH}/lib") - target_link_libraries(llvm-libs INTERFACE - LLVMSupport - LLVMFrontendOpenMP - LLVMOption - clangAST - clangASTMatchers - clangBasic - clangDependencyScanning - clangDriver - clangFormat - clangFrontend - clangIndex - clangLex - clangSema - clangSerialization - clangTidy - clangTidyUtils - # ALL_CLANG_TIDY_CHECKS - clangTidyAndroidModule - clangTidyAbseilModule - clangTidyAlteraModule - clangTidyBoostModule - clangTidyBugproneModule - clangTidyCERTModule - clangTidyConcurrencyModule - clangTidyCppCoreGuidelinesModule - clangTidyDarwinModule - clangTidyFuchsiaModule - clangTidyGoogleModule - clangTidyHICPPModule - clangTidyLinuxKernelModule - clangTidyLLVMModule - clangTidyLLVMLibcModule - clangTidyMiscModule - clangTidyModernizeModule - clangTidyObjCModule - clangTidyOpenMPModule - clangTidyPerformanceModule - clangTidyPortabilityModule - clangTidyReadabilityModule - clangTidyZirconModule - clangTooling - clangToolingCore - clangToolingInclusions - clangToolingInclusionsStdlib - clangToolingSyntax - ) -else() - file(GLOB LLVM_LIBRARIES CONFIGURE_DEPENDS "${LLVM_INSTALL_PATH}/lib/*${CMAKE_STATIC_LIBRARY_SUFFIX}") - target_link_libraries(llvm-libs INTERFACE ${LLVM_LIBRARIES}) -endif() - -target_compile_options(clice-core PUBLIC ${CLICE_CXX_FLAGS}) -target_link_options(clice-core PUBLIC ${CLICE_LINKER_FLAGS}) - -target_include_directories(clice-core PUBLIC "${CMAKE_SOURCE_DIR}/include") -target_link_libraries(clice-core PUBLIC uv_a spdlog::spdlog tomlplusplus::tomlplusplus llvm-libs) -target_compile_definitions(clice-core PUBLIC TOML_EXCEPTIONS=0) - -# clice executable -add_executable(clice "${CMAKE_SOURCE_DIR}/src/Driver/clice.cc") -target_link_libraries(clice PRIVATE clice-core) -target_compile_options(clice PUBLIC ${CLICE_CXX_FLAGS}) -target_link_options(clice PUBLIC ${CLICE_LINKER_FLAGS}) -# install clice executable -install(TARGETS clice RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) - -# copy resource dir -message(STATUS "Copying resource directory") -file(COPY - ${LLVM_INSTALL_PATH}/lib/clang/20/ - DESTINATION ${PROJECT_BINARY_DIR}/lib/clang/20 +target_include_directories(clice-core PUBLIC ${PROJECT_SOURCE_DIR}/include) +target_link_libraries(clice-core PUBLIC + clice_options + uv_a + spdlog::spdlog + tomlplusplus::tomlplusplus + roaring::roaring + llvm-libs ) -# clice tests -if(CLICE_ENABLE_TEST) - file(GLOB_RECURSE CLICE_TEST_SOURCES CONFIGURE_DEPENDS "${CMAKE_SOURCE_DIR}/tests/unit/*/*.cpp") - add_executable(unit_tests "${CLICE_TEST_SOURCES}" "${CMAKE_SOURCE_DIR}/src/Driver/unit_tests.cc") - target_include_directories(unit_tests PUBLIC "${CMAKE_SOURCE_DIR}") +add_executable(clice "${PROJECT_SOURCE_DIR}/bin/clice.cc") +target_link_libraries(clice PRIVATE clice-core) +install(TARGETS clice RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) +message(STATUS "Copying resource directory for development build") +file(COPY + "${LLVM_INSTALL_PATH}/lib/clang/20/" + DESTINATION "${PROJECT_BINARY_DIR}/lib/clang/20" +) +install(DIRECTORY "${LLVM_INSTALL_PATH}/lib/clang/20/" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/clang/20" +) + +if(CLICE_ENABLE_TEST) + file(GLOB_RECURSE CLICE_TEST_SOURCES CONFIGURE_DEPENDS + "${PROJECT_SOURCE_DIR}/tests/unit/*/*.cpp") + add_executable(unit_tests + "${CLICE_TEST_SOURCES}" + "${PROJECT_SOURCE_DIR}/bin/unit_tests.cc" + ) + target_include_directories(unit_tests PUBLIC "${PROJECT_SOURCE_DIR}") target_link_libraries(unit_tests PRIVATE clice-core) - target_compile_options(unit_tests PUBLIC ${CLICE_CXX_FLAGS}) - target_link_options(unit_tests PUBLIC ${CLICE_LINKER_FLAGS}) endif() diff --git a/src/Driver/clice.cc b/bin/clice.cc similarity index 100% rename from src/Driver/clice.cc rename to bin/clice.cc diff --git a/src/Driver/unit_tests.cc b/bin/unit_tests.cc similarity index 100% rename from src/Driver/unit_tests.cc rename to bin/unit_tests.cc diff --git a/cmake/croaring-fix.patch b/cmake/croaring-fix.patch new file mode 100644 index 00000000..d4e2ad53 --- /dev/null +++ b/cmake/croaring-fix.patch @@ -0,0 +1,30 @@ +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 new file mode 100644 index 00000000..74d18a8b --- /dev/null +++ b/cmake/package.cmake @@ -0,0 +1,142 @@ +include_guard() + +include(${CMAKE_CURRENT_LIST_DIR}/llvm_setup.cmake) + +setup_llvm() + +get_filename_component(LLVM_INSTALL_PATH "${LLVM_INSTALL_PATH}" ABSOLUTE) + +if(NOT EXISTS "${LLVM_INSTALL_PATH}") + message(FATAL_ERROR "Error: The specified LLVM_INSTALL_PATH does not exist: ${LLVM_INSTALL_PATH}") +endif() + +# set llvm include and lib path +add_library(llvm-libs INTERFACE IMPORTED) + +message(STATUS "LLVM include path: ${LLVM_INSTALL_PATH}/include") +# add to include directories +target_include_directories(llvm-libs INTERFACE "${LLVM_INSTALL_PATH}/include") + +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + target_link_directories(llvm-libs INTERFACE "${LLVM_INSTALL_PATH}/lib") + target_link_libraries(llvm-libs INTERFACE + LLVMSupport + LLVMFrontendOpenMP + LLVMOption + clangAST + clangASTMatchers + clangBasic + clangDependencyScanning + clangDriver + clangFormat + clangFrontend + clangIndex + clangLex + clangSema + clangSerialization + clangTidy + clangTidyUtils + # ALL_CLANG_TIDY_CHECKS + clangTidyAndroidModule + clangTidyAbseilModule + clangTidyAlteraModule + clangTidyBoostModule + clangTidyBugproneModule + clangTidyCERTModule + clangTidyConcurrencyModule + clangTidyCppCoreGuidelinesModule + clangTidyDarwinModule + clangTidyFuchsiaModule + clangTidyGoogleModule + clangTidyHICPPModule + clangTidyLinuxKernelModule + clangTidyLLVMModule + clangTidyLLVMLibcModule + clangTidyMiscModule + clangTidyModernizeModule + clangTidyObjCModule + clangTidyOpenMPModule + clangTidyPerformanceModule + clangTidyPortabilityModule + clangTidyReadabilityModule + clangTidyZirconModule + clangTooling + clangToolingCore + clangToolingInclusions + clangToolingInclusionsStdlib + clangToolingSyntax + ) +else() + file(GLOB LLVM_LIBRARIES CONFIGURE_DEPENDS "${LLVM_INSTALL_PATH}/lib/*${CMAKE_STATIC_LIBRARY_SUFFIX}") + target_link_libraries(llvm-libs INTERFACE ${LLVM_LIBRARIES}) +endif() + + +if(WIN32) + target_compile_definitions(llvm-libs INTERFACE "CLANG_BUILD_STATIC") + target_link_libraries(llvm-libs INTERFACE version ntdll) +endif() + +# install dependencies +include(FetchContent) + +if(WIN32) + set(NULL_DEVICE NUL) +else() + set(NULL_DEVICE /dev/null) +endif() + +# libuv +FetchContent_Declare( + libuv + GIT_REPOSITORY https://github.com/libuv/libuv.git + GIT_TAG v1.x +) + +if(CMAKE_SYSTEM_NAME STREQUAL "Linux" 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") + +# spdlog +FetchContent_Declare( + spdlog + GIT_REPOSITORY https://github.com/gabime/spdlog.git + GIT_TAG v1.15.3 +) + +# tomlplusplus +FetchContent_Declare( + tomlplusplus + GIT_REPOSITORY https://github.com/marzer/tomlplusplus.git +) + +# croaring +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 +) +set(ENABLE_ROARING_TESTS OFF CACHE INTERNAL "") + +set(CMAKE_MODULE_PATH "") +FetchContent_MakeAvailable(libuv spdlog tomlplusplus croaring) + +if(WIN32) + target_compile_definitions(uv_a PRIVATE _CRT_SECURE_NO_WARNINGS) +endif() + +if(CMAKE_C_COMPILER_ID MATCHES "Clang" AND TARGET uv_a) + target_compile_options(uv_a PRIVATE + "-Wno-unused-function" + "-Wno-unused-variable" + "-Wno-unused-but-set-variable" + "-Wno-deprecated-declarations" + "-Wno-missing-braces" + ) +endif() + +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 a591960e..4d5c1e6d 100644 --- a/include/AST/Semantic.h +++ b/include/AST/Semantic.h @@ -75,8 +75,10 @@ public: } void handleAttrOccurrence(const clang::Attr* attr, clang::SourceRange range) { - assert(attr && "Invalid attribute"); - assert(range.isValid() && "Invalid range"); + if(!attr || range.isInvalid()) { + return; + } + if constexpr(!std::same_as) { getDerived().handleAttrOccurrence(attr, range); @@ -633,6 +635,7 @@ public: } bool VisitAttr(clang::Attr* attr) { + getDerived().handleAttrOccurrence(attr, attr->getRange()); return true; } diff --git a/include/Index/FeatureIndex.h b/include/Index/FeatureIndex.h deleted file mode 100644 index 2518f20c..00000000 --- a/include/Index/FeatureIndex.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include - -#include "Shared.h" -#include "Feature/SemanticToken.h" -#include "Feature/FoldingRange.h" -#include "Feature/DocumentLink.h" -#include "Feature/DocumentSymbol.h" - -#include "llvm/ADT/DenseMap.h" -#include "clang/Basic/SourceLocation.h" - -namespace clice::index { - -class FeatureIndex { -public: - FeatureIndex(char* base, std::size_t size) : base(base), size(size) {} - - /// The path of source file. - llvm::StringRef path(); - - /// The content of source file. - llvm::StringRef content(); - - feature::SemanticTokens semanticTokens() const; - - feature::FoldingRanges foldingRanges() const; - - feature::DocumentLinks documentLinks() const; - - feature::DocumentSymbols documentSymbols() const; - - static Shared> build(CompilationUnit& unit); - -public: - char* base; - std::size_t size; -}; - -} // namespace clice::index diff --git a/include/Index/Index.h b/include/Index/Index.h index 93f7bbf4..e69de29b 100644 --- a/include/Index/Index.h +++ b/include/Index/Index.h @@ -1,22 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include "TUIndex.h" -#include "RawIndex.h" -#include "HeaderIndex.h" -#include "IncludeGraph.h" - -namespace clice::index::memory { - -struct Indices { - std::unique_ptr tu_index; - llvm::DenseMap> header_indices; -}; - -Indices index(CompilationUnit& unit); - -} // namespace clice::index::memory diff --git a/include/Index/LazyArray.h b/include/Index/LazyArray.h deleted file mode 100644 index e0f49d17..00000000 --- a/include/Index/LazyArray.h +++ /dev/null @@ -1,91 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -namespace clice::index { - -/// When serialize index to binary, we will transform all pointer to offset -/// to base address. And data only will be deserialized when it is accessed. -struct Relative { - const void* base = nullptr; - const void* data = nullptr; - - bool operator== (const Relative& other) const = default; -}; - -template -class LazyArray : Relative { -public: - LazyArray(const void* base, const void* data, std::uint32_t size, std::uint32_t stride) : - Relative{base, data}, size(size), stride(stride) {} - - using difference_type = std::ptrdiff_t; - using value_type = T; - - class Iterator : Relative { - public: - Iterator(const void* base, const void* data, std::size_t stride) : - Relative{base, data}, stride(stride) {} - - using difference_type = std::ptrdiff_t; - using value_type = T; - - Iterator() = default; - - Iterator(const Iterator&) = default; - - decltype(auto) operator* () const { - if constexpr(std::derived_from) { - return T{base, data}; - } else { - return *static_cast(data); - } - } - - Iterator& operator++ () { - data = static_cast(data) + stride; - return *this; - } - - Iterator operator++ (int) { - ++*this; - } - - bool operator== (const Iterator& other) const = default; - - private: - std::uint32_t stride; - }; - - Iterator begin() const { - return Iterator(base, data, stride); - } - - Iterator end() const { - return Iterator(base, static_cast(data) + size * stride, stride); - } - - uint32_t length() const { - return size; - } - - decltype(auto) operator[] (uint32_t index) const { - const void* data = static_cast(this->data) + index * stride; - if constexpr(std::derived_from) { - return T{base, data}; - } else { - return *static_cast(data); - } - } - - bool operator== (const LazyArray& other) const = default; - -private: - std::uint32_t size; - std::uint32_t stride; -}; - -} // namespace clice::index diff --git a/include/Index/MergedIndex.h b/include/Index/MergedIndex.h new file mode 100644 index 00000000..b2cec52d --- /dev/null +++ b/include/Index/MergedIndex.h @@ -0,0 +1,138 @@ +#pragma once + +#include "TUIndex.h" + +#define ROARING_EXCEPTIONS 0 +#define ROARING_TERMINATE(message) std::abort() +#include "roaring/roaring.hh" + +#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 relations. + llvm::DenseMap> relations; + + /// All merged symbol occurrences. + llvm::DenseMap occurrences; + + void remove(llvm::StringRef path); + + void merge(llvm::StringRef path, std::uint32_t include, FileIndex& index); +}; + +} // namespace clice::index + +namespace llvm { + +template +unsigned dense_hash(const Ts&... ts) { + return llvm::DenseMapInfo>::getHashValue(std::tuple{ts...}); +} + +template <> +struct DenseMapInfo { + using R = clice::LocalSourceRange; + using V = clice::index::Occurrence; + + inline static V getEmptyKey() { + return V(R(-1, 0), 0); + } + + inline static V getTombstoneKey() { + return V(R(-2, 0), 0); + } + + static auto getHashValue(const V& v) { + return dense_hash(v.range.begin, v.range.end, v.target); + } + + static bool isEqual(const V& lhs, const V& rhs) { + return lhs.range == rhs.range && lhs.target == rhs.target; + } +}; + +template <> +struct DenseMapInfo { + using R = clice::index::Relation; + + inline static R getEmptyKey() { + return R{ + .kind = clice::RelationKind(), + .range = clice::LocalSourceRange(-1, 0), + .target_symbol = 0, + }; + } + + inline static R getTombstoneKey() { + return R{ + .kind = clice::RelationKind(), + .range = clice::LocalSourceRange(-2, 0), + .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/SymbolIndex.h b/include/Index/SymbolIndex.h deleted file mode 100644 index ffd7723a..00000000 --- a/include/Index/SymbolIndex.h +++ /dev/null @@ -1,85 +0,0 @@ -#pragma once - -#include "Shared.h" -#include "LazyArray.h" -#include "AST/SymbolID.h" -#include "AST/SourceCode.h" -#include "AST/SymbolKind.h" -#include "AST/RelationKind.h" -#include "Support/JSON.h" - -namespace clice::index { - -struct Symbol; - -struct Relation : Relative { - /// Return the relation kind. - RelationKind kind() const; - - /// Return the range of relation. - LocalSourceRange range() const; - - /// Return the definition range. - LocalSourceRange sourceRange() const; - - /// The target symbol. - Symbol target() const; -}; - -struct Symbol : Relative { - /// Return the symbol id. - SymbolID id() const; - - /// Return the hash value of symbol id. - std::uint64_t hash() const; - - /// Return the symbol name. - llvm::StringRef name() const; - - /// Return the symbol kind. - SymbolKind kind() const; - - /// Return all relations of this symbol. - LazyArray relations() const; -}; - -struct Occurrence : Relative { - /// The source range of occurrence. - LocalSourceRange range() const; - - /// The target symbol of occurrence. - Symbol symbol() const; -}; - -class SymbolIndex { -public: - SymbolIndex(const char* data, std::uint32_t size) : data(data), size(size) {} - - /// The path of source file. - llvm::StringRef path() const; - - /// The content of source file. - llvm::StringRef content() const; - - /// All symbols in the index. - LazyArray symbols() const; - - /// All occurrences in the index. - LazyArray occurrences() const; - - /// Locate the symbols with given offset. - std::vector locateSymbol(uint32_t offset) const; - - /// Locate the symbol with given symbol id. - std::optional locateSymbol(const SymbolID& id) const; - - static Shared> build(CompilationUnit& unit); - - json::Value toJSON(bool line = true); - -private: - const char* data; - std::uint32_t size; -}; - -} // namespace clice::index diff --git a/include/Index/TUIndex.h b/include/Index/TUIndex.h index 25b0d237..98df7d55 100644 --- a/include/Index/TUIndex.h +++ b/include/Index/TUIndex.h @@ -1,19 +1,56 @@ #pragma once -#include "RawIndex.h" #include "IncludeGraph.h" +#include "AST/SourceCode.h" +#include "AST/RelationKind.h" -namespace clice::index::memory { +namespace clice::index { -class TUIndex : public RawIndex { -public: +using Range = LocalSourceRange; +using SymbolHash = std::uint64_t; -public: - /// The time of building this index. - std::int64_t time; +struct Relation { + RelationKind kind; - /// The include graph of this index. - IncludeGraph graph; + char padding[4] = {0, 0, 0, 0}; + + LocalSourceRange range; + + union { + LocalSourceRange definition_range; + + SymbolHash target_symbol; + }; }; -} // namespace clice::index::memory +struct Occurrence { + /// range of this occurrence. + Range range; + + /// + SymbolHash target; +}; + +struct FileIndex { + llvm::DenseMap> relations; + + std::vector occurrences; +}; + +struct Symbol { + std::string name; + + /// ... +}; + +struct TUIndex { + IncludeGraph graph; + + llvm::DenseMap symbols; + + llvm::DenseMap file_indices; + + static TUIndex build(CompilationUnit& unit); +}; + +} // namespace clice::index diff --git a/include/Server/Indexer.h b/include/Server/Indexer.h index 231578c7..82eefb6c 100644 --- a/include/Server/Indexer.h +++ b/include/Server/Indexer.h @@ -13,65 +13,6 @@ namespace clice { class CompilationUnit; -class Indexer { -public: - /// Index an opened file, its AST is already builtin - /// and PCH is used for it. - async::Task<> index(CompilationUnit& unit); - - /// Index an static file. - async::Task<> index(llvm::StringRef file); - - using Path = std::string; - using PathID = std::uint32_t; - using SymbolID = std::uint64_t; - -public: - Indexer(CompilationDatabase& database) : database(database) {} - - PathID getPath(llvm::StringRef path) { - auto it = paths.find(path); - if(it != paths.end()) { - return it->second; - } - - auto id = path_storage.size(); - path_storage.emplace_back(path); - paths.try_emplace(path, id); - return id; - } - -private: - struct HeaderIndices { - using RawIndex = std::pair>; - - /// The merged index. - std::unique_ptr merged; - - llvm::DenseMap> unmergeds; - }; - - CompilationDatabase& database; - - /// All paths of indices. - std::vector path_storage; - - /// A map between path and its id. - llvm::StringMap paths; - - /// A map between symbol id and files that contains it. - llvm::DenseMap> symbol_indices; - - /// A map between source file path and its static indices. - llvm::DenseMap static_indices; - - std::uint32_t unmerged_count = 0; - - /// In-memory header indices. - llvm::DenseMap> dynamic_header_indices; - - /// In-memory translation unit indices. - llvm::DenseMap> dynamic_tu_indices; -}; +class Indexer {}; } // namespace clice diff --git a/src/Index/FeatureIndex.cpp b/src/Index/FeatureIndex.cpp deleted file mode 100644 index f3831536..00000000 --- a/src/Index/FeatureIndex.cpp +++ /dev/null @@ -1,92 +0,0 @@ -#include "Compiler/CompilationUnit.h" -#include "Support/Binary.h" -#include "Index/FeatureIndex.h" - -namespace clice::index { - -namespace memory { - -struct FeatureIndex { - /// The path of source file. - std::string path; - - /// The content of source file. - std::string content; - - /// The index of semantic tokens. - feature::SemanticTokens tokens; - - /// The index of folding ranges. - feature::FoldingRanges foldings; - - /// The index of document links. - feature::DocumentLinks links; - - /// The index of document symbols. - feature::DocumentSymbols symbols; -}; - -} // namespace memory - -llvm::StringRef FeatureIndex::path() { - binary::Proxy index{base, base}; - return index.get<"path">().as_string(); -} - -llvm::StringRef FeatureIndex::content() { - binary::Proxy index{base, base}; - return index.get<"content">().as_string(); -} - -feature::SemanticTokens FeatureIndex::semanticTokens() const { - binary::Proxy index{base, base}; - return binary::deserialize(index.get<"tokens">()); -} - -feature::FoldingRanges FeatureIndex::foldingRanges() const { - binary::Proxy index{base, base}; - return binary::deserialize(index.get<"foldings">()); -} - -feature::DocumentLinks FeatureIndex::documentLinks() const { - binary::Proxy index{base, base}; - return binary::deserialize(index.get<"links">()); -} - -feature::DocumentSymbols FeatureIndex::documentSymbols() const { - binary::Proxy index{base, base}; - return binary::deserialize(index.get<"symbols">()); -} - -Shared> FeatureIndex::build(CompilationUnit& unit) { - Shared indices; - - for(auto&& [fid, result]: feature::index_semantic_token(unit)) { - indices[fid].tokens = std::move(result); - } - - for(auto&& [fid, result]: feature::index_folding_range(unit)) { - indices[fid].foldings = std::move(result); - } - - for(auto&& [fid, result]: feature::index_document_link(unit)) { - indices[fid].links = std::move(result); - } - - for(auto&& [fid, result]: feature::index_document_symbol(unit)) { - indices[fid].symbols = std::move(result); - } - - Shared> result; - - for(auto&& [fid, index]: indices) { - index.path = unit.file_path(fid); - index.content = unit.file_content(fid); - auto [buffer, _] = binary::serialize(index); - result.try_emplace(fid, std::move(buffer)); - } - - return result; -} - -} // namespace clice::index diff --git a/src/Index/Index.cpp b/src/Index/Index.cpp deleted file mode 100644 index f811b925..00000000 --- a/src/Index/Index.cpp +++ /dev/null @@ -1,145 +0,0 @@ -#include "AST/Semantic.h" -#include "Index/Index.h" -#include "Index/IncludeGraph.h" -#include "Support/Format.h" - -namespace clice::index::memory { - -namespace { - -class IndexBuilder : public Indices, public SemanticVisitor { -public: - IndexBuilder(CompilationUnit& unit) : SemanticVisitor(unit, false) { - tu_index = std::make_unique(); - tu_index->path = unit.file_path(unit.interested_file()); - tu_index->content = unit.file_content(unit.interested_file()); - tu_index->graph = IncludeGraph::from(unit); - } - - RawIndex& getIndex(clang::FileID fid) { - if(fid == unit.interested_file()) { - return *tu_index; - } - - if(auto it = header_indices.find(fid); it != header_indices.end()) { - return *it->second; - } - - auto [it, _] = header_indices.try_emplace(fid, new RawIndex()); - auto& index = *it->second; - index.path = unit.file_path(fid); - index.content = unit.file_content(fid); - return index; - } - - void handleDeclOccurrence(const clang::NamedDecl* decl, - RelationKind kind, - clang::SourceLocation location) { - assert(decl && "Invalid decl"); - decl = ast::normalize(decl); - - if(location.isMacroID()) { - auto spelling = unit.spelling_location(location); - auto expansion = unit.expansion_location(location); - - /// FIXME: For location from macro, we only handle the case that the - /// spelling and expansion are in the same file currently. - if(unit.file_id(spelling) != unit.file_id(expansion)) { - return; - } - - /// For occurrence, we always use spelling location. - location = spelling; - } - - auto [fid, range] = unit.decompose_range(location); - auto& index = getIndex(fid); - auto symbol_id = unit.getSymbolID(decl); - auto& symbol = index.get_symbol(symbol_id.hash); - symbol.kind = SymbolKind::from(decl); - index.add_occurrence(range, symbol_id.hash); - } - - void handleMacroOccurrence(const clang::MacroInfo* def, - RelationKind kind, - clang::SourceLocation location) { - /// FIXME: Figure out when location is MacroID. - if(location.isMacroID()) { - return; - } - - auto [fid, range] = unit.decompose_range(location); - auto& index = getIndex(fid); - auto symbol_id = unit.getSymbolID(def); - auto& symbol = index.get_symbol(symbol_id.hash); - symbol.kind = SymbolKind::Macro; - symbol.name = unit.token_spelling(def->getDefinitionLoc()); - index.add_occurrence(range, symbol_id.hash); - - if(kind & RelationKind::Definition) { - auto begin = def->getDefinitionLoc(); - auto end = def->getDefinitionEndLoc(); - assert(begin.isFileID() && end.isFileID() && "Invalid location"); - auto [fid2, definition_range] = unit.decompose_range(clang::SourceRange(begin, end)); - assert(fid == fid2 && "Invalid macro definition location"); - /// definitionLoc = builder.getLocation(range); - - index.add_relation(symbol, - Relation{ - .kind = RelationKind::Definition, - .range = range, - .definition_range = definition_range, - }); - } else { - index.add_relation(symbol, - Relation{ - .kind = RelationKind::Reference, - .range = range, - .target_symbol = 0, - }); - } - } - - void handleRelation(const clang::NamedDecl* decl, - RelationKind kind, - const clang::NamedDecl* target, - clang::SourceRange range) { - auto [fid, relationRange] = unit.decompose_expansion_range(range); - - Relation relation{.kind = kind}; - - if(kind.isDeclOrDef()) { - auto [fid2, definitionRange] = unit.decompose_expansion_range(decl->getSourceRange()); - assert(fid == fid2 && "Invalid definition location"); - relation.range = relationRange; - relation.definition_range = definitionRange; - } else if(kind.isReference()) { - relation.range = relationRange; - relation.target_symbol = 0; - } else if(kind.isBetweenSymbol()) { - auto symbol_id = unit.getSymbolID(ast::normalize(target)); - relation.target_symbol = symbol_id.hash; - } else if(kind.isCall()) { - auto symbol_id = unit.getSymbolID(ast::normalize(target)); - relation.range = relationRange; - relation.target_symbol = symbol_id.hash; - } else { - std::unreachable(); - } - - auto& index = getIndex(fid); - auto symbol_id = unit.getSymbolID(ast::normalize(decl)); - auto& symbol = index.get_symbol(symbol_id.hash); - index.add_relation(symbol, relation); - } -}; - -} // namespace - -Indices index(CompilationUnit& unit) { - IndexBuilder builder(unit); - builder.run(); - return std::move(builder); -} - -} // namespace clice::index::memory diff --git a/src/Index/MergedIndex.cpp b/src/Index/MergedIndex.cpp new file mode 100644 index 00000000..7d74bf5e --- /dev/null +++ b/src/Index/MergedIndex.cpp @@ -0,0 +1,89 @@ +#include "Index/MergedIndex.h" +#include "llvm/Support/SHA256.h" + +namespace clice::index { + +namespace { + +auto sha256_hash(FileIndex& index) { + llvm::SHA256 hasher; + + using u8 = std::uint8_t; + + if(!index.occurrences.empty()) { + static_assert(sizeof(Occurrence) == sizeof(Range) + sizeof(SymbolHash)); + static_assert(sizeof(Occurrence) % 8 == 0); + auto data = reinterpret_cast(index.occurrences.data()); + auto size = index.occurrences.size() * sizeof(Occurrence); + hasher.update(llvm::ArrayRef(data, size)); + } + + for(auto& [symbol_id, relations]: index.relations) { + hasher.update(std::bit_cast>(symbol_id)); + static_assert(sizeof(Relation) == + sizeof(RelationKind) + 4 + sizeof(Range) + sizeof(SymbolHash)); + static_assert(sizeof(Relation) % 8 == 0); + + if(!relations.empty()) { + auto data = reinterpret_cast(relations.data()); + auto size = relations.size() * sizeof(Relation); + hasher.update(llvm::ArrayRef(data, size)); + } + } + + return hasher.final(); +} + +} // namespace + +void MergedIndex::remove(llvm::StringRef path) { + auto& includes = contexts[path].includes; + + for(auto& [_, canonical_id]: includes) { + auto& ref_counts = canonical_ref_counts[canonical_id]; + ref_counts -= 1; + + if(ref_counts == 0) { + removed.add(canonical_id); + } + } + + includes.clear(); +} + +void MergedIndex::merge(llvm::StringRef path, std::uint32_t include, FileIndex& index) { + auto& context = contexts[path]; + + auto hash = sha256_hash(index); + auto hash_key = llvm::StringRef(reinterpret_cast(hash.data()), hash.size()); + auto [it, success] = canonical_cache.try_emplace(hash_key, max_canonical_id); + + auto canonical_id = it->second; + context.includes.emplace_back(include, canonical_id); + + if(!success) { + canonical_ref_counts[canonical_id] += 1; + removed.remove(canonical_id); + 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); + } + + for(auto& [symbol_id, relations]: index.relations) { + auto& target = this->relations[symbol_id]; + for(auto& relation: relations) { + target[relation].add(canonical_id); + } + } + + canonical_ref_counts.emplace_back(1); + max_canonical_id += 1; +} + +} // namespace clice::index diff --git a/src/Index/TUIndex.cpp b/src/Index/TUIndex.cpp new file mode 100644 index 00000000..693c17ba --- /dev/null +++ b/src/Index/TUIndex.cpp @@ -0,0 +1,119 @@ +#include "AST/Semantic.h" +#include "Index/TUIndex.h" +#include "Support/Compare.h" + +namespace clice::index { + +namespace { + +class Builder : public SemanticVisitor { +public: + Builder(TUIndex& result, CompilationUnit& unit) : + SemanticVisitor(unit, false), result(result) { + result.graph = IncludeGraph::from(unit); + } + + void handleDeclOccurrence(const clang::NamedDecl* decl, + RelationKind kind, + clang::SourceLocation location) { + assert(decl && "Invalid decl"); + decl = ast::normalize(decl); + + if(location.isMacroID()) { + auto spelling = unit.spelling_location(location); + auto expansion = unit.expansion_location(location); + + /// FIXME: For location from macro, we only handle the case that the + /// spelling and expansion are in the same file currently. + if(unit.file_id(spelling) != unit.file_id(expansion)) { + return; + } + + /// For occurrence, we always use spelling location. + location = spelling; + } + + auto [fid, range] = unit.decompose_range(location); + auto& index = result.file_indices[fid]; + + auto symbol_id = unit.getSymbolID(decl); + index.occurrences.emplace_back(range, symbol_id.hash); + } + + void handleMacroOccurrence(const clang::MacroInfo* def, + RelationKind kind, + clang::SourceLocation location) { + /// FIXME: Figure out when location is MacroID. + if(location.isMacroID()) { + return; + } + + auto [fid, range] = unit.decompose_range(location); + auto& index = result.file_indices[fid]; + + auto symbol_id = unit.getSymbolID(def); + index.occurrences.emplace_back(range, symbol_id.hash); + + Relation relation{ + .kind = RelationKind::Definition, + .range = range, + .target_symbol = 0, + }; + + index.relations[symbol_id.hash].emplace_back(relation); + } + + void handleRelation(const clang::NamedDecl* decl, + RelationKind kind, + const clang::NamedDecl* target, + clang::SourceRange range) { + auto [fid, relationRange] = unit.decompose_expansion_range(range); + + Relation relation{.kind = kind}; + + if(kind.isDeclOrDef()) { + auto [fid2, definitionRange] = unit.decompose_expansion_range(decl->getSourceRange()); + assert(fid == fid2 && "Invalid definition location"); + relation.range = relationRange; + relation.definition_range = definitionRange; + } else if(kind.isReference()) { + relation.range = relationRange; + relation.target_symbol = 0; + } else if(kind.isBetweenSymbol()) { + auto symbol_id = unit.getSymbolID(ast::normalize(target)); + relation.target_symbol = symbol_id.hash; + } else if(kind.isCall()) { + auto symbol_id = unit.getSymbolID(ast::normalize(target)); + relation.range = relationRange; + relation.target_symbol = symbol_id.hash; + } else { + std::unreachable(); + } + + auto& index = result.file_indices[fid]; + auto symbol_id = unit.getSymbolID(ast::normalize(decl)); + index.relations[symbol_id.hash].emplace_back(relation); + } + + void build() { + run(); + + for(auto& [fid, index]: result.file_indices) { + std::ranges::sort(index.occurrences, refl::less); + } + } + +private: + TUIndex& result; +}; + +} // namespace + +TUIndex TUIndex::build(CompilationUnit& unit) { + TUIndex index; + Builder builder(index, unit); + builder.build(); + return index; +} + +} // namespace clice::index diff --git a/src/Server/Indexer.cpp b/src/Server/Indexer.cpp index 6e8eb8a9..00fb6331 100644 --- a/src/Server/Indexer.cpp +++ b/src/Server/Indexer.cpp @@ -4,60 +4,4 @@ #include "Server/Indexer.h" #include "Support/Logging.h" -namespace clice { - -async::Task<> Indexer::index(CompilationUnit& unit) { - auto [tu_index, header_indices] = - co_await async::submit([&] { return index::memory::index(unit); }); - - auto tu_id = getPath(tu_index->path); - - llvm::DenseSet visited_headers; - - for(auto& [fid, index]: header_indices) { - auto id = getPath(index->path); - auto it = dynamic_header_indices.find(id); - - /// FIXME: If the value less than 0, it represents the file is from - /// PCH or module. How to handle such file? - if(fid < clang::FileID::getSentinel()) { - continue; - } - - auto include = tu_index->graph.getInclude(fid); - - if(it != dynamic_header_indices.end()) { - auto& indices = it->second; - - if(!visited_headers.contains(id)) { - indices->unmergeds[tu_id].clear(); - visited_headers.insert(id); - } - - indices->unmergeds[tu_id].emplace_back(include, std::move(index)); - } else { - auto [it, _] = dynamic_header_indices.try_emplace(id, new HeaderIndices()); - it->second->unmergeds[tu_id].emplace_back(include, std::move(index)); - visited_headers.insert(id); - } - } - - dynamic_tu_indices[tu_id] = std::move(tu_index); -} - -async::Task<> Indexer::index(llvm::StringRef file) { - CompilationParams params; - params.kind = CompilationUnit::Indexing; - params.arguments = database.get_command(file).arguments; - - auto AST = co_await async::submit([&] { return compile(params); }); - - if(!AST) { - logging::info("Fail to index background file {}", file); - co_return; - } - - co_await index(*AST); -} - -} // namespace clice +namespace clice {} // namespace clice diff --git a/tests/unit/Index/MergedIndex.cpp b/tests/unit/Index/MergedIndex.cpp new file mode 100644 index 00000000..6685890d --- /dev/null +++ b/tests/unit/Index/MergedIndex.cpp @@ -0,0 +1,67 @@ +#include "Test/Tester.h" +#include "Index/MergedIndex.h" + +namespace clice::testing { + +namespace { + +suite<"MergedIndex"> suite = [] { + Tester tester; + index::TUIndex tu_index; + + auto build_index = [&](llvm::StringRef code, + std::source_location location = std::source_location::current()) { + tester.clear(); + tester.add_main("main.cpp", code); + fatal / expect(tester.compile(), location); + + 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 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]; + + auto it = std::ranges::lower_bound( + index.occurrences, + offset, + {}, + [](index::Occurrence& occurrence) { return occurrence.range.end; }); + + auto err = + std::format("Fail to find symbol for offser: {} range: range: {}", offset, dump(range)); + + fatal / expect(it != index.occurrences.end(), location) << err; + + /// FIXME: Make eq pretty print reflectable struct. + expect(eq(dump(it->range), dump(range)), location); + }; + + test("Assert") = [&] { + build_index(R"( + #include + )"); + + std::println("{}", tu_index.file_indices.size()); + + index::MergedIndex merged; + + for(auto& [fid, index]: tu_index.file_indices) { + auto path = tester.unit->file_path(fid); + + if(path.ends_with("stddef.h")) { + merged.merge(path, tu_index.graph.getInclude(fid), index); + } + } + }; +}; + +} // namespace + +} // namespace clice::testing diff --git a/tests/unit/Index/TUIndex.cpp b/tests/unit/Index/TUIndex.cpp new file mode 100644 index 00000000..af12e0e0 --- /dev/null +++ b/tests/unit/Index/TUIndex.cpp @@ -0,0 +1,68 @@ +#include "Test/Tester.h" +#include "Index/TUIndex.h" + +namespace clice::testing { + +namespace { + +suite<"TUIndex"> suite = [] { + Tester tester; + index::TUIndex tu_index; + + auto build_index = [&](llvm::StringRef code, + std::source_location location = std::source_location::current()) { + tester.clear(); + tester.add_main("main.cpp", code); + fatal / expect(tester.compile(), location); + + 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 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]; + + auto it = std::ranges::lower_bound( + index.occurrences, + offset, + {}, + [](index::Occurrence& occurrence) { return occurrence.range.end; }); + + auto err = + std::format("Fail to find symbol for offser: {} range: range: {}", offset, dump(range)); + + fatal / expect(it != index.occurrences.end(), location) << err; + + /// FIXME: Make eq pretty print reflectable struct. + expect(eq(dump(it->range), dump(range)), location); + }; + + test("Basic") = [&] { + build_index(R"( + int @1[f$(1)oo](); + + int @2[b$(2)ar]() { + return @3[fo$(3)o]() + 1; + } + )"); + + expect(eq(tu_index.file_indices.size(), 1)); + auto& index = tu_index.file_indices.begin()->second; + expect(eq(index.relations.size(), 2)); + expect(eq(index.occurrences.size(), 3)); + + expect_select("1", "1"); + expect_select("2", "2"); + expect_select("3", "3"); + }; +}; + +} // namespace + +} // namespace clice::testing diff --git a/xmake.lua b/xmake.lua index 8e6fa591..73150128 100644 --- a/xmake.lua +++ b/xmake.lua @@ -16,7 +16,7 @@ if has_config("dev") then if is_plat("windows") then set_runtimes("MD") if is_mode("debug") then - print("Clice does not support build in debug mode with pre-compiled llvm binary on windows.\n" + print("clice does not support build in debug mode with pre-compiled llvm binary on windows.\n" .."See https://github.com/clice-io/clice/issues/42 for more information.") os.raise() end @@ -41,7 +41,7 @@ if has_config("release") then end add_defines("TOML_EXCEPTIONS=0") -add_requires(libuv_require, "spdlog[header_only=n,std_format,noexcept]" ,"toml++") +add_requires(libuv_require, "spdlog[header_only=n,std_format,noexcept]" ,"toml++", "croaring") add_requires("clice-llvm", {alias = "llvm"}) add_rules("mode.release", "mode.debug", "mode.releasedbg") @@ -53,7 +53,7 @@ target("clice-core") add_files("src/**.cpp|Driver/*.cpp") add_includedirs("include", {public = true}) - add_packages("libuv", "spdlog", "toml++", {public = true}) + add_packages("libuv", "spdlog", "toml++", "croaring", {public = true}) if is_mode("debug") then add_packages("llvm", { @@ -116,7 +116,7 @@ target("clice-core") target("clice") set_kind("binary") - add_files("src/Driver/clice.cc") + add_files("bin/clice.cc") add_deps("clice-core") @@ -136,7 +136,7 @@ target("clice") target("unit_tests") set_default(false) set_kind("binary") - add_files("src/Driver/unit_tests.cc", "tests/unit/**.cpp") + add_files("bin/unit_tests.cc", "tests/unit/**.cpp") add_includedirs(".", {public = true}) add_deps("clice-core")