diff --git a/.appveyor.yml b/.appveyor.yml index 0fc36d3fe..42a7a4c03 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -15,7 +15,7 @@ install: - 7z x llvm.tar - git submodule update --init --recursive build_script: - - cmake -G"Visual Studio 15 2017 Win64" -H. -Bbuild -DCMAKE_BUILD_TYPE=Release -DSYSTEM_CLANG=ON -DCLANG_ROOT=C:\projects\ccls\llvm+clang-7.0.0-win64-msvc-release + - cmake -G"Visual Studio 15 2017 Win64" -H. -Bbuild -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=C:\projects\ccls\llvm+clang-7.0.0-win64-msvc-release - cmake --build build --target ccls --config Release artifacts: diff --git a/.github/ISSUE_TEMPLATE b/.github/ISSUE_TEMPLATE new file mode 100644 index 000000000..1a27d0173 --- /dev/null +++ b/.github/ISSUE_TEMPLATE @@ -0,0 +1,36 @@ +Here are some things you should try before filing a bug report: + ++ For client issues related to [emacs-ccls](https://github.com/MaskRay/emacs-ccls) or [vscode-ccls](https://github.com/MaskRay/vscode-ccls), report in their own repository. ++ For build problems, check https://github.com/MaskRay/ccls/wiki/Build ++ Check https://github.com/MaskRay/ccls/wiki/Debugging ++ Check [the FAQ](https://github.com/MaskRay/ccls/wiki/FAQ) to see if your issue is mentioned. + +If none of those help, remove this section and fill out the four sections in the template below. + +--- + +### Observed behavior + +Describe what happened. Any aids you can include (that you think could be relevant) are a tremendous help. + +* `compile_commands.json` or `.ccls` ([wiki/Project-Setup](https://github.com/MaskRay/ccls/wiki/Project-Setup)) +* Reduce to A minimal set of `.c` `.cc` `.h` `.hh` files that can still demonstrate the issue. +* Consider a screencast gif. + +### Expected behavior + +Describe what you expected to happen. + +### Steps to reproduce + +1. Select these example steps, +2. Delete them, +3. And replace them with precise steps to reproduce your issue. + +### System information + +* ccls version (`git describe --tags --long`): +* clang version: +* OS: +* Editor: +* Language client (and version): diff --git a/.gitignore b/.gitignore index 87cc1bce2..6c3c5f19e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ build debug release /compile_commands.json +!.github/ diff --git a/CMakeLists.txt b/CMakeLists.txt index bf39ebfd1..471f4a639 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,46 +1,34 @@ cmake_minimum_required(VERSION 3.8) project(ccls LANGUAGES CXX) -list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/) -include(DefaultCMakeBuildType) - -# Required Clang version -set(CLANG_DOWNLOAD_LOCATION ${CMAKE_BINARY_DIR} - CACHE STRING "Downloaded Clang location") -option(SYSTEM_CLANG "Use system installation of Clang instead of \ - downloading Clang" OFF) -option(ASAN "Compile with address sanitizers" OFF) -option(LLVM_ENABLE_RTTI "-fno-rtti if OFF. This should match LLVM libraries" OFF) -option(CLANG_USE_BUNDLED_LIBC++ "Let Clang use bundled libc++" OFF) -option(USE_SHARED_LLVM "Link against libLLVM.so instead separate LLVM{Option,Support,...}" OFF) +option(USE_SYSTEM_RAPIDJSON "Use system RapidJSON instead of the git submodule if exists" ON) # Sources for the executable are specified at end of CMakeLists.txt add_executable(ccls "") -### Compile options +### Default build type + +set(DEFAULT_CMAKE_BUILD_TYPE Release) -# CMake default compile flags: -# MSVC + Clang(Windows): -# debug: /MDd /Zi /Ob0 /Od /RTC1 -# release: /MD /O2 /Ob2 /DNDEBUG -# GCC + Clang(Linux): -# debug: -g -# release: -O3 -DNDEBUG +# CMAKE_BUILD_TYPE is not available if a multi-configuration generator is used +# (eg Visual Studio generators) +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + message(STATUS "Setting build type to '${DEFAULT_CMAKE_BUILD_TYPE}' as none \ +was specified.") + set(CMAKE_BUILD_TYPE ${DEFAULT_CMAKE_BUILD_TYPE} + CACHE STRING "Choose the type of build." FORCE) + + # Set the possible values of build type for cmake-gui + set_property(CACHE CMAKE_BUILD_TYPE + PROPERTY STRINGS Debug Release MinSizeRel RelWithDebInfo) +endif() + +### Compile options # Enable C++17 (Required) set_property(TARGET ccls PROPERTY CXX_STANDARD 17) set_property(TARGET ccls PROPERTY CXX_STANDARD_REQUIRED ON) -# Disable gnu extensions except for Cygwin which needs them to build properly -if(NOT CYGWIN) - set_property(TARGET ccls PROPERTY CXX_EXTENSIONS OFF) -endif() - -if(NOT LLVM_ENABLE_RTTI) - # releases.llvm.org libraries are compiled with -fno-rtti - # The mismatch between lib{clang,LLVM}* and ccls can make libstdc++ std::make_shared return nullptr - # _Sp_counted_ptr_inplace::_M_get_deleter - target_compile_options(ccls PRIVATE -fno-rtti) -endif() +set_property(TARGET ccls PROPERTY CXX_EXTENSIONS OFF) # CMake sets MSVC for both MSVC and Clang(Windows) if(MSVC) @@ -76,44 +64,49 @@ else() target_compile_options(ccls PRIVATE $<$:-fno-limit-debug-info>) endif() - - if(ASAN) - target_compile_options(ccls PRIVATE -fsanitize=address,undefined) - # target_link_libraries also takes linker flags - target_link_libraries(ccls PRIVATE -fsanitize=address,undefined) - endif() endif() -### Download Clang if required - -if(NOT SYSTEM_CLANG) - message(STATUS "Using downloaded Clang") - - include(DownloadAndExtractClang) - download_and_extract_clang(${CLANG_DOWNLOAD_LOCATION}) - # Used by FindClang - set(CLANG_ROOT ${DOWNLOADED_CLANG_DIR}) - - if(${CMAKE_CXX_COMPILER_ID} STREQUAL Clang AND CLANG_USE_BUNDLED_LIBC++) - message(STATUS "Using bundled libc++") - target_compile_options(ccls PRIVATE -nostdinc++ -cxx-isystem ${CLANG_ROOT}/include/c++/v1) - if(${CMAKE_SYSTEM_NAME} STREQUAL Linux) - # Don't use -stdlib=libc++ because while ccls is linked with libc++, bundled clang+llvm require libstdc++ - target_link_libraries(ccls PRIVATE -L${CLANG_ROOT}/lib c++ c++abi) +### Libraries - # FreeBSD defaults to -stdlib=libc++ and uses system libcxxrt.a - endif() - endif() +find_package(Clang REQUIRED) +if(CLANG_LINK_CLANG_DYLIB) + target_link_libraries(ccls PRIVATE clang-cpp) else() - message(STATUS "Using system Clang") + target_link_libraries(ccls PRIVATE + clangIndex + clangFormat + clangTooling + clangToolingInclusions + clangToolingCore + clangFrontend + clangParse + clangSerialization + clangSema + clangAST + clangLex + clangDriver + clangBasic + ) endif() -### Libraries +if(LLVM_LINK_LLVM_DYLIB) + target_link_libraries(ccls PRIVATE LLVM) +else() + # In llvm 7, clangDriver headers reference LLVMOption + target_link_libraries(ccls PRIVATE LLVMOption LLVMSupport) +endif() -# See cmake/FindClang.cmake -find_package(Clang 6.0.0) -target_link_libraries(ccls PRIVATE Clang::Clang) +if(NOT LLVM_ENABLE_RTTI) + # releases.llvm.org libraries are compiled with -fno-rtti + # The mismatch between lib{clang,LLVM}* and ccls can make libstdc++ std::make_shared return nullptr + # _Sp_counted_ptr_inplace::_M_get_deleter + if(MSVC) + target_compile_options(ccls PRIVATE /GR-) + else() + target_compile_options(ccls PRIVATE -fno-rtti) + endif() +endif() # Enable threading support set(THREADS_PREFER_PTHREAD_FLAG ON) @@ -129,51 +122,56 @@ endif() ### Definitions -target_compile_definitions(ccls PRIVATE - DEFAULT_RESOURCE_DIRECTORY=R"\(${Clang_RESOURCE_DIR}\)") +# Find Clang resource directory with Clang executable. -### Includes - -target_include_directories(ccls PRIVATE src) -target_include_directories(ccls SYSTEM PRIVATE - third_party - third_party/rapidjson/include) +if(NOT CLANG_RESOURCE_DIR) + find_program(CLANG_EXECUTABLE clang) + if(NOT CLANG_EXECUTABLE) + message(FATAL_ERROR "clang executable not found.") + endif() -### Install + execute_process( + COMMAND ${CLANG_EXECUTABLE} -print-resource-dir + RESULT_VARIABLE CLANG_FIND_RESOURCE_DIR_RESULT + OUTPUT_VARIABLE CLANG_RESOURCE_DIR + ERROR_VARIABLE CLANG_FIND_RESOURCE_DIR_ERROR + OUTPUT_STRIP_TRAILING_WHITESPACE + ) -install(TARGETS ccls RUNTIME DESTINATION bin) + if(CLANG_FIND_RESOURCE_DIR_RESULT) + message(FATAL_ERROR "Error retrieving Clang resource directory with Clang \ + executable. Output:\n ${CLANG_FIND_RESOURCE_DIR_ERROR}") + endif() +endif() -if(NOT SYSTEM_CLANG AND NOT ${CMAKE_SYSTEM_NAME} STREQUAL Windows) +set_property(SOURCE src/utils.cc APPEND PROPERTY COMPILE_DEFINITIONS + CLANG_RESOURCE_DIRECTORY=R"\(${CLANG_RESOURCE_DIR}\)") - if(${CMAKE_SYSTEM_NAME} MATCHES Linux|FreeBSD) - set_property(TARGET ccls APPEND PROPERTY - INSTALL_RPATH $ORIGIN/../lib) - elseif(${CMAKE_SYSTEM_NAME} STREQUAL Darwin) - set_property(TARGET ccls APPEND PROPERTY - INSTALL_RPATH @loader_path/../lib) - endif() +### Includes - file(GLOB LIBCLANG_PLUS_SYMLINKS - ${DOWNLOADED_CLANG_DIR}/lib/libclang.[so,dylib]*) - install(FILES ${LIBCLANG_PLUS_SYMLINKS} DESTINATION lib) +if(USE_SYSTEM_RAPIDJSON) + find_package(RapidJSON QUIET) endif() - -# Allow running from build Windows by copying libclang.dll to build directory -if(NOT SYSTEM_CLANG AND ${CMAKE_SYSTEM_NAME} STREQUAL Windows) - add_custom_command(TARGET ccls - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy - ${DOWNLOADED_CLANG_DIR}/bin/libclang.dll - $ - COMMENT "Copying libclang.dll to build directory ...") +if(NOT RapidJSON_FOUND) + set(RapidJSON_INCLUDE_DIRS third_party/rapidjson/include) endif() -### Tools +target_include_directories(ccls PRIVATE src) + +foreach(include_dir third_party + ${LLVM_INCLUDE_DIRS} ${CLANG_INCLUDE_DIRS} ${RapidJSON_INCLUDE_DIRS}) + get_filename_component(include_dir_realpath ${include_dir} REALPATH) + # Don't add as SYSTEM if they are in CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES. + # It would reorder the system search paths and cause issues with libstdc++'s + # use of #include_next. See https://github.com/MaskRay/ccls/pull/417 + if(NOT "${include_dir_realpath}" IN_LIST CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES) + target_include_directories(ccls SYSTEM PRIVATE ${include_dir}) + endif() +endforeach() -# We use glob here since source files are already manually added with -# target_sources further down -file(GLOB SOURCES src/*.cc src/*.h src/serializers/*.cc src/serializers/*.h - src/messages/*.h src/messages/*.cc) +### Install + +install(TARGETS ccls RUNTIME DESTINATION bin) ### Sources @@ -225,3 +223,22 @@ target_sources(ccls PRIVATE src/messages/textDocument_signatureHelp.cc src/messages/workspace.cc ) + +### Obtain CCLS version information from Git +### This only happens when cmake is re-run! + +if(NOT CCLS_VERSION) + execute_process(COMMAND git describe --tag --long HEAD + OUTPUT_VARIABLE CCLS_VERSION + OUTPUT_STRIP_TRAILING_WHITESPACE + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}") + + if(NOT CCLS_VERSION) + set(CCLS_VERSION "") + endif() +endif() + +set_property(SOURCE src/main.cc APPEND PROPERTY + COMPILE_DEFINITIONS CCLS_VERSION=\"${CCLS_VERSION}\") +set_property(SOURCE src/messages/initialize.cc APPEND PROPERTY + COMPILE_DEFINITIONS CCLS_VERSION=\"${CCLS_VERSION}\") diff --git a/README.md b/README.md index 81d25dc5f..3cf69e41c 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ ccls, which originates from [cquery](https://github.com/cquery-project/cquery), * formatting * hierarchies: [call (caller/callee) hierarchy](src/messages/ccls_call.cc), [inheritance (base/derived) hierarchy](src/messages/ccls_inheritance.cc), [member hierarchy](src/messages/ccls_member.cc) * [symbol rename](src/messages/textDocument_rename.cc) - * [document symbols](src/messages/textDocument_documentSymbol.cc) and approximate search of [workspace symbol](src/messages/workspace_symbol.cc) + * [document symbols](src/messages/textDocument_document.cc) and approximate search of [workspace symbol](src/messages/workspace.cc) * [hover information](src/messages/textDocument_hover.cc) * diagnostics and code actions (clang FixIts) * semantic highlighting and preprocessor skipped regions @@ -21,28 +21,9 @@ It has a global view of the code base and support a lot of cross reference featu It starts indexing the whole project (including subprojects if exist) parallelly when you open the first file, while the main thread can serve requests before the indexing is complete. Saving files will incrementally update the index. -Compared with cquery, it makes use of C++17 features, has less third-party dependencies and slimmed-down code base. -It leverages Clang C++ API as [clangd](https://clang.llvm.org/extra/clangd.html) does, which provides better support for code completion and diagnostics. -Refactoring is a non-goal as it can be provided by clang-include-fixer and other Clang based tools. - -The comparison with cquery as noted on 2018-07-15: - -| | cquery | ccls | -|------------ |--------------------------------|------------------------------| -| third_party | more | fewer | -| C++ | C++14 | C++17 | -| clang API | libclang (C) | clang/llvm C++ | -| Filesystem | AbsolutePath + custom routines | llvm/Support | -| index | libclang | clangIndex, some enhancement | -| pipeline | index merge+id remapping | simpler and more robust | - -cquery has system include path detection (through running the compiler driver) while ccls uses clangDriver. - -# >>> [Getting started](../../wiki/Getting-started) (CLICK HERE) <<< +# >>> [Getting started](../../wiki/Home) (CLICK HERE) <<< * [Build](../../wiki/Build) -* [Emacs](../../wiki/Emacs) -* [LanguageClient-neovim](../../wiki/LanguageClient-neovim) * [FAQ](../../wiki/FAQ) -ccls can index itself (~180MiB RSS when ide, noted on 2018-09-01), FreeBSD, glibc, Linux, LLVM (~1800MiB RSS), musl (~60MiB RSS), ... with decent memory footprint. See [wiki/compile_commands.json](../../wiki/compile_commands.json) for examples. +ccls can index itself (~180MiB RSS when idle, noted on 2018-09-01), FreeBSD, glibc, Linux, LLVM (~1800MiB RSS), musl (~60MiB RSS), ... with decent memory footprint. See [wiki/Project-Setup](../../wiki/Project-Setup) for examples. diff --git a/clang_archive_hashes/LLVM-7.0.0-win64.exe.SHA256 b/clang_archive_hashes/LLVM-7.0.0-win64.exe.SHA256 deleted file mode 100644 index 54bea9e6c..000000000 --- a/clang_archive_hashes/LLVM-7.0.0-win64.exe.SHA256 +++ /dev/null @@ -1 +0,0 @@ -74b197a3959b0408adf0824be01db8dddfa2f9a967f4085af3fad900ed5fdbf6 \ No newline at end of file diff --git a/clang_archive_hashes/clang+llvm-7.0.0-amd64-unknown-freebsd11.tar.xz.SHA256 b/clang_archive_hashes/clang+llvm-7.0.0-amd64-unknown-freebsd11.tar.xz.SHA256 deleted file mode 100644 index b8a56bfa7..000000000 --- a/clang_archive_hashes/clang+llvm-7.0.0-amd64-unknown-freebsd11.tar.xz.SHA256 +++ /dev/null @@ -1 +0,0 @@ -95ceb933ccf76e3ddaa536f41ab82c442bbac07cdea6f9fbf6e3b13cc1711255 \ No newline at end of file diff --git a/clang_archive_hashes/clang+llvm-7.0.0-x86_64-apple-darwin.tar.xz.SHA256 b/clang_archive_hashes/clang+llvm-7.0.0-x86_64-apple-darwin.tar.xz.SHA256 deleted file mode 100644 index 86f733c96..000000000 --- a/clang_archive_hashes/clang+llvm-7.0.0-x86_64-apple-darwin.tar.xz.SHA256 +++ /dev/null @@ -1 +0,0 @@ -b3ad93c3d69dfd528df9c5bb1a434367babb8f3baea47fbb99bf49f1b03c94ca \ No newline at end of file diff --git a/clang_archive_hashes/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-14.04.tar.xz.SHA256 b/clang_archive_hashes/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-14.04.tar.xz.SHA256 deleted file mode 100644 index bc8306913..000000000 --- a/clang_archive_hashes/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-14.04.tar.xz.SHA256 +++ /dev/null @@ -1 +0,0 @@ -5c90e61b06d37270bc26edb305d7e498e2c7be22d99e0afd9f2274ef5458575a \ No newline at end of file diff --git a/clang_archive_hashes/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz.SHA256 b/clang_archive_hashes/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz.SHA256 deleted file mode 100644 index 1475a0a85..000000000 --- a/clang_archive_hashes/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz.SHA256 +++ /dev/null @@ -1 +0,0 @@ -69b85c833cd28ea04ce34002464f10a6ad9656dd2bba0f7133536a9927c660d2 \ No newline at end of file diff --git a/cmake/DefaultCMakeBuildType.cmake b/cmake/DefaultCMakeBuildType.cmake deleted file mode 100644 index ae440bf54..000000000 --- a/cmake/DefaultCMakeBuildType.cmake +++ /dev/null @@ -1,14 +0,0 @@ -set(DEFAULT_CMAKE_BUILD_TYPE Release) - -# CMAKE_BUILD_TYPE is not available if a multi-configuration generator is used -# (eg Visual Studio generators) -if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) - message(STATUS "Setting build type to '${DEFAULT_CMAKE_BUILD_TYPE}' as none \ -was specified.") - set(CMAKE_BUILD_TYPE ${DEFAULT_CMAKE_BUILD_TYPE} - CACHE STRING "Choose the type of build." FORCE) - - # Set the possible values of build type for cmake-gui - set_property(CACHE CMAKE_BUILD_TYPE - PROPERTY STRINGS Debug Release MinSizeRel RelWithDebInfo) -endif() diff --git a/cmake/DownloadAndExtract7zip.cmake b/cmake/DownloadAndExtract7zip.cmake deleted file mode 100644 index 72bc879d5..000000000 --- a/cmake/DownloadAndExtract7zip.cmake +++ /dev/null @@ -1,52 +0,0 @@ -# Downloads and extracts the 7-Zip MSI installer from https://www.7-zip.org/. -# -# Returns the extracted 7-Zip directory in DOWNLOADED_7ZIP_DIR -function(download_and_extract_7zip 7ZIP_DOWNLOAD_LOCATION) - -set(7ZIP_VERSION 1801) -set(7ZIP_EXT .msi) -set(7ZIP_NAME 7z${7ZIP_VERSION}-x64) -set(7ZIP_FULL_NAME ${7ZIP_NAME}${7ZIP_EXT}) - -set(7ZIP_FILE ${7ZIP_DOWNLOAD_LOCATION}/${7ZIP_FULL_NAME}) -set(7ZIP_EXTRACT_DIR ${7ZIP_DOWNLOAD_LOCATION}/${7ZIP_NAME}) -set(7ZIP_URL https://www.7-zip.org/a/${7ZIP_FULL_NAME}) - -# Exit if 7-Zip is already downloaded and extracted -find_program(7ZIP_EXECUTABLE 7z NO_DEFAULT_PATH - PATHS ${7ZIP_EXTRACT_DIR}/Files/7-Zip) -if(7ZIP_EXECUTABLE) - message(STATUS "7-Zip already downloaded") - return() -endif() - -message(STATUS "Downloading 7-Zip ${7ZIP_VERSION} (${7ZIP_URL}) ...") -file(DOWNLOAD ${7ZIP_URL} ${7ZIP_FILE}) - -find_program(MSIEXEC_EXECUTABLE msiexec) -if(NOT MSIEXEC_EXECUTABLE) - message(FATAL_ERROR "Unable to find msiexec (required to extract 7-Zip msi \ -installer). Install 7-Zip yourself and make sure it is available in the path") -endif() - -message(STATUS "Extracting downloaded 7-Zip ...") - -# msiexec requires Windows path separators (\) -file(TO_NATIVE_PATH ${7ZIP_FILE} 7ZIP_FILE) -file(TO_NATIVE_PATH ${7ZIP_EXTRACT_DIR} 7ZIP_EXTRACT_DIR) - -# msiexec with /a option allows extraction of msi installers without requiring -# admin privileges. We use this to extract the 7-Zip installer without -# requiring any actions from the user -execute_process(COMMAND ${MSIEXEC_EXECUTABLE} /a ${7ZIP_FILE} /qn - TARGETDIR=${7ZIP_EXTRACT_DIR} - WORKING_DIRECTORY ${7ZIP_DOWNLOAD_LOCATION} - OUTPUT_QUIET) - -# Convert back to CMake separators (/) before returning -file(TO_CMAKE_PATH ${7ZIP_EXTRACT_DIR} 7ZIP_EXTRACT_DIR) - -# Actual 7-Zip directory is nested inside the extract directory. -set(DOWNLOADED_7ZIP_DIR ${7ZIP_EXTRACT_DIR}/Files/7-Zip PARENT_SCOPE) - -endfunction() \ No newline at end of file diff --git a/cmake/DownloadAndExtractClang.cmake b/cmake/DownloadAndExtractClang.cmake deleted file mode 100644 index 9365b1881..000000000 --- a/cmake/DownloadAndExtractClang.cmake +++ /dev/null @@ -1,129 +0,0 @@ -# Downloads and extracts the Clang archive for the current system from -# https://releases.llvm.org -# -# Returns the extracted Clang archive directory in DOWNLOADED_CLANG_DIR -# -# Downloads 7-Zip to extract Clang if it isn't available in the PATH -function(download_and_extract_clang CLANG_DOWNLOAD_LOCATION) - -set(CLANG_VERSION 7.0.0) -set(CLANG_ARCHIVE_EXT .tar.xz) - -if(${CMAKE_SYSTEM_NAME} STREQUAL Linux) - - # Default to Ubuntu 16.04 - set(CLANG_ARCHIVE_NAME - clang+llvm-${CLANG_VERSION}-x86_64-linux-gnu-ubuntu-16.04) - -elseif(${CMAKE_SYSTEM_NAME} STREQUAL Darwin) - - set(CLANG_ARCHIVE_NAME clang+llvm-${CLANG_VERSION}-x86_64-apple-darwin) - -elseif(${CMAKE_SYSTEM_NAME} STREQUAL Windows) - - set(CLANG_ARCHIVE_NAME LLVM-${CLANG_VERSION}-win64) - set(CLANG_ARCHIVE_EXT .exe) - -elseif(${CMAKE_SYSTEM_NAME} STREQUAL FreeBSD) - - set(CLANG_ARCHIVE_NAME clang+llvm-${CLANG_VERSION}-amd64-unknown-freebsd11) - -endif() - -set(CLANG_ARCHIVE_FULL_NAME ${CLANG_ARCHIVE_NAME}${CLANG_ARCHIVE_EXT}) -set(CLANG_ARCHIVE_FILE ${CLANG_DOWNLOAD_LOCATION}/${CLANG_ARCHIVE_FULL_NAME}) -set(CLANG_ARCHIVE_EXTRACT_DIR ${CLANG_DOWNLOAD_LOCATION}/${CLANG_ARCHIVE_NAME}) -set(CLANG_ARCHIVE_URL - https://releases.llvm.org/${CLANG_VERSION}/${CLANG_ARCHIVE_FULL_NAME}) -set(CLANG_ARCHIVE_HASH_FILE - ${CMAKE_SOURCE_DIR}/clang_archive_hashes/${CLANG_ARCHIVE_FULL_NAME}.SHA256) - -# Exit if Clang is already downloaded and extracted -set(CLANG_ROOT ${CLANG_ARCHIVE_EXTRACT_DIR}) -find_package(Clang ${CLANG_VERSION} QUIET) -if(Clang_FOUND) - message(STATUS "Clang already downloaded") - set(DOWNLOADED_CLANG_DIR ${CLANG_ARCHIVE_EXTRACT_DIR} PARENT_SCOPE) - return() -endif() - -if(NOT CLANG_ARCHIVE_NAME) - message(FATAL_ERROR "No Clang archive url specified for current platform \ -(${CMAKE_SYSTEM_NAME}). Please file an issue to get it added.") -endif() - -if(NOT EXISTS ${CLANG_ARCHIVE_HASH_FILE}) - message(FATAL_ERROR "No SHA256 hash available for the current platform \ -(${CMAKE_SYSTEM_NAME}) + clang version (${CLANG_VERSION}) combination. Please \ -file an issue to get it added.") -endif() - -# Download Clang archive -message(STATUS "Downloading Clang ${CLANG_VERSION} (${CLANG_ARCHIVE_URL}) ...") -file(DOWNLOAD ${CLANG_ARCHIVE_URL} ${CLANG_ARCHIVE_FILE} - STATUS CLANG_ARCHIVE_DOWNLOAD_RESULT) - -# Abort if download failed -list(GET ${CLANG_ARCHIVE_DOWNLOAD_RESULT} 0 ERROR_CODE) -if(${ERROR_CODE}) - list(GET ${CLANG_ARCHIVE_DOWNLOAD_RESULT} 1 ERROR_STRING) - message(FATAL_ERROR ${ERROR_STRING}) -endif() - -# Retrieve expected hash from file and strip newline -file(READ ${CLANG_ARCHIVE_HASH_FILE} CLANG_ARCHIVE_EXPECTED_HASH) -string(STRIP ${CLANG_ARCHIVE_EXPECTED_HASH} CLANG_ARCHIVE_EXPECTED_HASH) -# Calculate actual hash -file(SHA256 ${CLANG_ARCHIVE_FILE} CLANG_ARCHIVE_HASH) -# Abort if hashes do not match -if(NOT ${CLANG_ARCHIVE_EXPECTED_HASH} STREQUAL ${CLANG_ARCHIVE_HASH}) - message(FATAL_ERROR "SHA256 hash of downloaded Clang does not match \ -expected hash. Remove the build directory and try running CMake again. If this \ -keeps happening, file an issue to report the problem.") -endif() - -if(${CLANG_ARCHIVE_EXT} STREQUAL .exe) - # Download and extract 7-zip if not found in PATH - find_program(7ZIP_EXECUTABLE 7z) - if(NOT 7ZIP_EXECUTABLE) - message(STATUS "7-Zip not found in PATH") - - include(DownloadAndExtract7zip) - download_and_extract_7zip(${CLANG_DOWNLOAD_LOCATION}) - find_program(7ZIP_EXECUTABLE - NAMES 7z - NO_DEFAULT_PATH - PATHS ${DOWNLOADED_7ZIP_DIR} - ) - else() - message(STATUS "7-Zip found in PATH") - endif() - - message(STATUS "Extracting downloaded Clang with 7-Zip ...") - - # Avoid running the Clang installer by extracting the exe with 7-Zip - execute_process( - COMMAND ${7ZIP_EXECUTABLE} x - -o${CLANG_ARCHIVE_EXTRACT_DIR} - -xr!$PLUGINSDIR ${CLANG_ARCHIVE_FILE} - WORKING_DIRECTORY ${CLANG_DOWNLOAD_LOCATION} - OUTPUT_QUIET - ) - -elseif(${CLANG_ARCHIVE_EXT} STREQUAL .tar.xz) - message(STATUS "Extracting downloaded Clang with CMake built-in tar ...") - - # CMake has builtin support for tar via the -E flag - execute_process( - COMMAND ${CMAKE_COMMAND} -E tar -xf ${CLANG_ARCHIVE_FILE} - # Specify working directory to allow running cmake from - # everywhere - # (example: cmake -H"$HOME/cquery" -B"$home/cquery/build") - WORKING_DIRECTORY ${CLANG_DOWNLOAD_LOCATION} - OUTPUT_QUIET - ) -endif() - -set(DOWNLOADED_CLANG_DIR ${CLANG_ARCHIVE_EXTRACT_DIR} PARENT_SCOPE) - -endfunction() diff --git a/cmake/FindClang.cmake b/cmake/FindClang.cmake deleted file mode 100644 index 194029005..000000000 --- a/cmake/FindClang.cmake +++ /dev/null @@ -1,154 +0,0 @@ -#.rst -# FindClang -# --------- -# -# Find Clang and LLVM libraries required by ccls -# -# Results are reported in the following variables:: -# -# Clang_FOUND - True if headers and requested libraries were found -# Clang_EXECUTABLE - Clang executable -# Clang_RESOURCE_DIR - Clang resource directory -# Clang_VERSION - Clang version as reported by Clang executable -# -# The following :prop_tgt:`IMPORTED` targets are also defined:: -# -# Clang::Clang - Target for all required Clang libraries and headers -# -# This module reads hints about which libraries to look for and where to find -# them from the following variables:: -# -# CLANG_ROOT - If set, only look for Clang components in CLANG_ROOT -# -# Example to link against Clang target:: -# -# target_link_libraries( PRIVATE Clang::Clang) - -### Definitions - -# Wrapper macro's around the find_* macro's from CMake that only search in -# CLANG_ROOT if it is defined - -macro(_Clang_find_library VAR NAME) - # Windows needs lib prefix - if (CLANG_ROOT) - find_library(${VAR} NAMES ${NAME} lib${NAME} - NO_DEFAULT_PATH PATHS ${CLANG_ROOT} PATH_SUFFIXES lib) - else() - find_library(${VAR} NAMES ${NAME} lib${NAME}) - endif() -endmacro() - -macro(_Clang_find_add_library NAME) - _Clang_find_library(${NAME}_LIBRARY ${NAME}) - list(APPEND _Clang_LIBRARIES ${${NAME}_LIBRARY}) -endmacro() - -macro(_Clang_find_path VAR INCLUDE_FILE) - if (CLANG_ROOT) - find_path(${VAR} ${INCLUDE_FILE} - NO_DEFAULT_PATH PATHS ${CLANG_ROOT} PATH_SUFFIXES include) - else() - find_path(${VAR} ${INCLUDE_FILE}) - endif() -endmacro() - -macro(_Clang_find_program VAR NAME) - if (CLANG_ROOT) - find_program(${VAR} ${NAME} - NO_DEFAULT_PATH PATHS ${CLANG_ROOT} PATH_SUFFIXES bin) - else() - find_program(${VAR} ${NAME}) - endif() -endmacro() - -### Start - -set(_Clang_REQUIRED_VARS Clang_LIBRARY Clang_INCLUDE_DIR Clang_EXECUTABLE - Clang_RESOURCE_DIR Clang_VERSION - LLVM_INCLUDE_DIR LLVM_BUILD_INCLUDE_DIR) - -_Clang_find_library(Clang_LIBRARY clangIndex) -_Clang_find_add_library(clangFormat) -_Clang_find_add_library(clangTooling) - -# TODO Remove after dropping clang 6 -_Clang_find_library(clangToolingInclusions_LIBRARY clangToolingInclusions) -if (clangToolingInclusions_LIBRARY) - list(APPEND _Clang_LIBRARIES ${clangToolingInclusions_LIBRARY}) -endif() - -_Clang_find_add_library(clangToolingCore) -_Clang_find_add_library(clangRewrite) -_Clang_find_add_library(clangFrontend) -_Clang_find_add_library(clangParse) -_Clang_find_add_library(clangSerialization) -_Clang_find_add_library(clangSema) -_Clang_find_add_library(clangAnalysis) -_Clang_find_add_library(clangEdit) -_Clang_find_add_library(clangAST) -_Clang_find_add_library(clangLex) -_Clang_find_add_library(clangDriver) -_Clang_find_add_library(clangBasic) -if(USE_SHARED_LLVM) - _Clang_find_add_library(LLVM) -else() - _Clang_find_add_library(LLVMMCParser) - _Clang_find_add_library(LLVMMC) - _Clang_find_add_library(LLVMBitReader) - _Clang_find_add_library(LLVMOption) - _Clang_find_add_library(LLVMProfileData) - _Clang_find_add_library(LLVMCore) - _Clang_find_add_library(LLVMBinaryFormat) - _Clang_find_add_library(LLVMSupport) - _Clang_find_add_library(LLVMDemangle) -endif() -_Clang_find_path(Clang_INCLUDE_DIR clang-c/Index.h) -_Clang_find_path(Clang_BUILD_INCLUDE_DIR clang/Driver/Options.inc) -_Clang_find_path(LLVM_INCLUDE_DIR llvm/PassInfo.h) -_Clang_find_path(LLVM_BUILD_INCLUDE_DIR llvm/Config/llvm-config.h) - -_Clang_find_program(Clang_EXECUTABLE clang) -if(Clang_EXECUTABLE) - # Find Clang resource directory with Clang executable - execute_process(COMMAND ${Clang_EXECUTABLE} -print-resource-dir - RESULT_VARIABLE _Clang_FIND_RESOURCE_DIR_RESULT - OUTPUT_VARIABLE Clang_RESOURCE_DIR - ERROR_VARIABLE _Clang_FIND_RESOURCE_DIR_ERROR - OUTPUT_STRIP_TRAILING_WHITESPACE) - - if(_Clang_FIND_RESOURCE_DIR_RESULT) - message(FATAL_ERROR "Error retrieving Clang resource directory with Clang \ -executable. Output:\n ${_Clang_FIND_RESOURCE_DIR_ERROR}") - endif() - - # Find Clang version - set(_Clang_VERSION_REGEX "([0-9]+)\\.([0-9]+)\\.([0-9]+)") - execute_process( - COMMAND ${Clang_EXECUTABLE} --version - OUTPUT_VARIABLE Clang_VERSION - ) - string(REGEX MATCH ${_Clang_VERSION_REGEX} Clang_VERSION ${Clang_VERSION}) -endif() - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Clang - FOUND_VAR Clang_FOUND - REQUIRED_VARS ${_Clang_REQUIRED_VARS} - VERSION_VAR Clang_VERSION -) - -if(Clang_FOUND AND NOT TARGET Clang::Clang) - add_library(Clang::Clang UNKNOWN IMPORTED) - set_target_properties(Clang::Clang PROPERTIES - IMPORTED_LOCATION ${Clang_LIBRARY} - INTERFACE_INCLUDE_DIRECTORIES "${Clang_INCLUDE_DIR};${Clang_BUILD_INCLUDE_DIR};${LLVM_INCLUDE_DIR};${LLVM_BUILD_INCLUDE_DIR}") - if(NOT MSVC) - find_package(Curses REQUIRED) - find_package(ZLIB REQUIRED) - endif() - set_property(TARGET Clang::Clang PROPERTY IMPORTED_LINK_INTERFACE_LIBRARIES "${_Clang_LIBRARIES};${CURSES_LIBRARIES};${ZLIB_LIBRARIES}") - if(MINGW) - set_property(TARGET Clang::Clang APPEND_STRING PROPERTY IMPORTED_LINK_INTERFACE_LIBRARIES ";version") - endif() -endif() diff --git a/src/clang_tu.cc b/src/clang_tu.cc index f7fb6d0f8..5042714f0 100644 --- a/src/clang_tu.cc +++ b/src/clang_tu.cc @@ -7,102 +7,145 @@ #include "platform.hh" #include +#include +#include +#include +#include #include +#include #include using namespace clang; namespace ccls { -std::string PathFromFileEntry(const FileEntry &file) { - StringRef Name = file.tryGetRealPathName(); - if (Name.empty()) - Name = file.getName(); - std::string ret = NormalizePath(Name); - // Resolve /usr/include/c++/7.3.0 symlink. - if (!llvm::any_of(g_config->workspaceFolders, [&](const std::string &root) { - return StringRef(ret).startswith(root); - })) { - SmallString<256> dest; - llvm::sys::fs::real_path(ret, dest); - ret = llvm::sys::path::convert_to_slash(dest.str()); - } - return ret; -} - -static Pos Decomposed2LineAndCol(const SourceManager &SM, - std::pair I) { - int l = (int)SM.getLineNumber(I.first, I.second) - 1, - c = (int)SM.getColumnNumber(I.first, I.second) - 1; - return {(int16_t)std::min(l, INT16_MAX), - (int16_t)std::min(c, INT16_MAX)}; +std::string pathFromFileEntry(const FileEntry &file) { + StringRef name = file.tryGetRealPathName(); + if (name.empty()) + name = file.getName(); + std::string ret = normalizePath(name); + // Resolve symlinks outside of workspace folders, e.g. /usr/include/c++/7.3.0 + return normalizeFolder(ret) ? ret : realPath(ret); } -Range FromCharSourceRange(const SourceManager &SM, const LangOptions &LangOpts, - CharSourceRange R, - llvm::sys::fs::UniqueID *UniqueID) { - SourceLocation BLoc = R.getBegin(), ELoc = R.getEnd(); - std::pair BInfo = SM.getDecomposedLoc(BLoc), - EInfo = SM.getDecomposedLoc(ELoc); - if (R.isTokenRange()) - EInfo.second += Lexer::MeasureTokenLength(ELoc, SM, LangOpts); - if (UniqueID) { - if (const FileEntry *F = SM.getFileEntryForID(BInfo.first)) - *UniqueID = F->getUniqueID(); - else - *UniqueID = llvm::sys::fs::UniqueID(0, 0); +static Pos decomposed2LineAndCol(const SourceManager &sm, + std::pair i) { + int l = (int)sm.getLineNumber(i.first, i.second) - 1, + c = (int)sm.getColumnNumber(i.first, i.second) - 1; + bool invalid = false; + StringRef buf = sm.getBufferData(i.first, &invalid); + if (!invalid) { + StringRef p = buf.substr(i.second - c, c); + c = 0; + for (size_t i = 0; i < p.size();) + if (c++, (uint8_t)p[i++] >= 128) + while (i < p.size() && (uint8_t)p[i] >= 128 && (uint8_t)p[i] < 192) + i++; } - return {Decomposed2LineAndCol(SM, BInfo), Decomposed2LineAndCol(SM, EInfo)}; + return {(uint16_t)std::min(l, UINT16_MAX), + (int16_t)std::min(c, INT16_MAX)}; } -Range FromCharRange(const SourceManager &SM, const LangOptions &Lang, - SourceRange R, llvm::sys::fs::UniqueID *UniqueID) { - return FromCharSourceRange(SM, Lang, CharSourceRange::getCharRange(R), - UniqueID); +Range fromCharSourceRange(const SourceManager &sm, const LangOptions &lang, + CharSourceRange csr, FileID *fid) { + SourceLocation bloc = csr.getBegin(), eloc = csr.getEnd(); + std::pair binfo = sm.getDecomposedLoc(bloc), + einfo = sm.getDecomposedLoc(eloc); + if (csr.isTokenRange()) + einfo.second += Lexer::MeasureTokenLength(eloc, sm, lang); + if (fid) + *fid = binfo.first; + return {decomposed2LineAndCol(sm, binfo), decomposed2LineAndCol(sm, einfo)}; } -Range FromTokenRange(const SourceManager &SM, const LangOptions &Lang, - SourceRange R, llvm::sys::fs::UniqueID *UniqueID) { - return FromCharSourceRange(SM, Lang, CharSourceRange::getTokenRange(R), - UniqueID); +Range fromTokenRange(const SourceManager &sm, const LangOptions &lang, + SourceRange sr, FileID *fid) { + return fromCharSourceRange(sm, lang, CharSourceRange::getTokenRange(sr), fid); } -Range FromTokenRangeDefaulted(const SourceManager &SM, const LangOptions &Lang, - SourceRange R, const FileEntry *FE, Range range) { - auto I = SM.getDecomposedLoc(SM.getExpansionLoc(R.getBegin())); - if (SM.getFileEntryForID(I.first) == FE) - range.start = Decomposed2LineAndCol(SM, I); - SourceLocation L = SM.getExpansionLoc(R.getEnd()); - I = SM.getDecomposedLoc(L); - if (SM.getFileEntryForID(I.first) == FE) { - I.second += Lexer::MeasureTokenLength(L, SM, Lang); - range.end = Decomposed2LineAndCol(SM, I); +Range fromTokenRangeDefaulted(const SourceManager &sm, const LangOptions &lang, + SourceRange sr, FileID fid, Range range) { + auto decomposed = sm.getDecomposedLoc(sm.getExpansionLoc(sr.getBegin())); + if (decomposed.first == fid) + range.start = decomposed2LineAndCol(sm, decomposed); + SourceLocation sl = sm.getExpansionLoc(sr.getEnd()); + decomposed = sm.getDecomposedLoc(sl); + if (decomposed.first == fid) { + decomposed.second += Lexer::MeasureTokenLength(sl, sm, lang); + range.end = decomposed2LineAndCol(sm, decomposed); } return range; } std::unique_ptr -BuildCompilerInvocation(const std::string &main, std::vector args, - IntrusiveRefCntPtr VFS) { +buildCompilerInvocation(const std::string &main, std::vector args, + IntrusiveRefCntPtr vfs) { std::string save = "-resource-dir=" + g_config->clang.resourceDir; args.push_back(save.c_str()); - IntrusiveRefCntPtr Diags( + args.push_back("-fsyntax-only"); + + // Similar to clang/tools/driver/driver.cpp:insertTargetAndModeArgs but don't + // require llvm::InitializeAllTargetInfos(). + auto target_and_mode = + driver::ToolChain::getTargetAndModeFromProgramName(args[0]); + if (target_and_mode.DriverMode) + args.insert(args.begin() + 1, target_and_mode.DriverMode); + if (!target_and_mode.TargetPrefix.empty()) { + const char *arr[] = {"-target", target_and_mode.TargetPrefix.c_str()}; + args.insert(args.begin() + 1, std::begin(arr), std::end(arr)); + } + + IntrusiveRefCntPtr diags( CompilerInstance::createDiagnostics(new DiagnosticOptions, new IgnoringDiagConsumer, true)); - std::unique_ptr CI = - createInvocationFromCommandLine(args, Diags, VFS); - if (CI) { - CI->getDiagnosticOpts().IgnoreWarnings = true; - CI->getFrontendOpts().DisableFree = false; - CI->getLangOpts()->SpellChecking = false; - auto &IS = CI->getFrontendOpts().Inputs; - if (IS.size()) - IS[0] = FrontendInputFile(main, IS[0].getKind(), IS[0].isSystem()); + driver::Driver d(args[0], llvm::sys::getDefaultTargetTriple(), *diags, vfs); + d.setCheckInputsExist(false); + std::unique_ptr comp(d.BuildCompilation(args)); + if (!comp) + return nullptr; + const driver::JobList &jobs = comp->getJobs(); + bool offload_compilation = false; + if (jobs.size() > 1) { + for (auto &a : comp->getActions()){ + // On MacOSX real actions may end up being wrapped in BindArchAction + if (isa(a)) + a = *a->input_begin(); + if (isa(a)) { + offload_compilation = true; + break; + } + } + if (!offload_compilation) + return nullptr; } - return CI; + if (jobs.size() == 0 || !isa(*jobs.begin())) + return nullptr; + + const driver::Command &cmd = cast(*jobs.begin()); + if (StringRef(cmd.getCreator().getName()) != "clang") + return nullptr; + const llvm::opt::ArgStringList &cc_args = cmd.getArguments(); + auto ci = std::make_unique(); +#if LLVM_VERSION_MAJOR >= 10 // rC370122 + if (!CompilerInvocation::CreateFromArgs(*ci, cc_args, *diags)) +#else + if (!CompilerInvocation::CreateFromArgs( + *ci, cc_args.data(), cc_args.data() + cc_args.size(), *diags)) +#endif + return nullptr; + + ci->getDiagnosticOpts().IgnoreWarnings = true; + ci->getFrontendOpts().DisableFree = false; + // Enable IndexFrontendAction::shouldSkipFunctionBody. + ci->getFrontendOpts().SkipFunctionBodies = true; + ci->getLangOpts()->SpellChecking = false; + auto &isec = ci->getFrontendOpts().Inputs; + if (isec.size()) + isec[0] = FrontendInputFile(main, isec[0].getKind(), isec[0].isSystem()); + return ci; } // clang::BuiltinType::getName without PrintingPolicy -const char *ClangBuiltinTypeName(int kind) { +const char *clangBuiltinTypeName(int kind) { switch (BuiltinType::Kind(kind)) { case BuiltinType::Void: return "void"; @@ -143,7 +186,6 @@ const char *ClangBuiltinTypeName(int kind) { return "double"; case BuiltinType::LongDouble: return "long double"; -#if LLVM_VERSION_MAJOR >= 7 case BuiltinType::ShortAccum: return "short _Accum"; case BuiltinType::Accum: @@ -192,7 +234,6 @@ const char *ClangBuiltinTypeName(int kind) { return "_Sat unsigned _Fract"; case BuiltinType::BuiltinType::SatULongFract: return "_Sat unsigned long _Fract"; -#endif case BuiltinType::Float16: return "_Float16"; case BuiltinType::Float128: @@ -200,10 +241,8 @@ const char *ClangBuiltinTypeName(int kind) { case BuiltinType::WChar_S: case BuiltinType::WChar_U: return "wchar_t"; -#if LLVM_VERSION_MAJOR >= 7 case BuiltinType::Char8: return "char8_t"; -#endif case BuiltinType::Char16: return "char16_t"; case BuiltinType::Char32: diff --git a/src/clang_tu.hh b/src/clang_tu.hh index 4adc46320..b53799a88 100644 --- a/src/clang_tu.hh +++ b/src/clang_tu.hh @@ -5,8 +5,8 @@ #include "position.hh" -#include #include +#include #include #include @@ -18,30 +18,25 @@ namespace vfs = clang::vfs; #endif namespace ccls { -std::string PathFromFileEntry(const clang::FileEntry &file); - -Range FromCharSourceRange(const clang::SourceManager &SM, - const clang::LangOptions &LangOpts, - clang::CharSourceRange R, - llvm::sys::fs::UniqueID *UniqueID = nullptr); +std::string pathFromFileEntry(const clang::FileEntry &file); -Range FromCharRange(const clang::SourceManager &SM, - const clang::LangOptions &LangOpts, clang::SourceRange R, - llvm::sys::fs::UniqueID *UniqueID = nullptr); +Range fromCharSourceRange(const clang::SourceManager &sm, + const clang::LangOptions &lang, + clang::CharSourceRange csr, + clang::FileID *fid = nullptr); -Range FromTokenRange(const clang::SourceManager &SM, - const clang::LangOptions &LangOpts, clang::SourceRange R, - llvm::sys::fs::UniqueID *UniqueID = nullptr); +Range fromTokenRange(const clang::SourceManager &sm, + const clang::LangOptions &lang, clang::SourceRange sr, + clang::FileID *fid = nullptr); -Range FromTokenRangeDefaulted(const clang::SourceManager &SM, - const clang::LangOptions &Lang, - clang::SourceRange R, const clang::FileEntry *FE, +Range fromTokenRangeDefaulted(const clang::SourceManager &sm, + const clang::LangOptions &lang, + clang::SourceRange sr, clang::FileID fid, Range range); std::unique_ptr -BuildCompilerInvocation(const std::string &main, - std::vector args, +buildCompilerInvocation(const std::string &main, std::vector args, llvm::IntrusiveRefCntPtr VFS); -const char *ClangBuiltinTypeName(int); +const char *clangBuiltinTypeName(int); } // namespace ccls diff --git a/src/config.cc b/src/config.cc index 34284c6a9..659956b07 100644 --- a/src/config.cc +++ b/src/config.cc @@ -6,7 +6,7 @@ namespace ccls { Config *g_config; -void DoPathMapping(std::string &arg) { +void doPathMapping(std::string &arg) { for (const std::string &mapping : g_config->clang.pathMappings) { auto sep = mapping.find('>'); if (sep != std::string::npos) { @@ -16,4 +16,4 @@ void DoPathMapping(std::string &arg) { } } } -} +} // namespace ccls diff --git a/src/config.hh b/src/config.hh index 073071db9..67d51e89b 100644 --- a/src/config.hh +++ b/src/config.hh @@ -20,7 +20,7 @@ initialization options specified by the client. For example, in shell syntax: struct Config { // **Not available for configuration** std::string fallbackFolder; - std::vector workspaceFolders; + std::vector> workspaceFolders; // If specified, this option overrides compile_commands.json and this // external command will be executed with an option |projectRoot|. // The initialization options will be provided as stdin. @@ -28,24 +28,64 @@ struct Config { std::string compilationDatabaseCommand; // Directory containing compile_commands.json. std::string compilationDatabaseDirectory; - // Cache directory for indexed files, either absolute or relative to the - // project root. - // If empty, cache will be stored in memory. - std::string cacheDirectory = ".ccls-cache"; - // Cache serialization format. - // - // "json" generates `cacheDirectory/.../xxx.json` files which can be pretty - // printed with jq. - // - // "binary" uses a compact binary serialization format. - // It is not schema-aware and you need to re-index whenever an internal struct - // member has changed. - SerializeFormat cacheFormat = SerializeFormat::Binary; - struct Clang { - // Arguments that should be excluded, e.g. ["-fopenmp", "-Wall"] + struct Cache { + // Cache directory for indexed files, either absolute or relative to the + // project root. + // + // If empty, retainInMemory will be set to 1 and cache will be stored in + // memory. + std::string directory = ".ccls-cache"; + + // Cache serialization format. + // + // "json" generates $directory/.../xxx.json files which can be pretty + // printed with jq. + // + // "binary" uses a compact binary serialization format. + // It is not schema-aware and you need to re-index whenever an internal + // struct member has changed. + SerializeFormat format = SerializeFormat::Binary; + + // If false, store cache files as $directory/@a@b/c.cc.blob // - // e.g. If your project is built by GCC and has an option thag clang does not understand. + // If true, $directory/a/b/c.cc.blob. If cache.directory is absolute, make + // sure different projects use different cache.directory as they would have + // conflicting cache files for system headers. + bool hierarchicalPath = false; + + // After this number of loads, keep a copy of file index in memory (which + // increases memory usage). During incremental updates, the index subtracted + // will come from the in-memory copy, instead of the on-disk file. + // + // The initial load or a save action is counted as one load. + // 0: never retain; 1: retain after initial load; 2: retain after 2 loads + // (initial load+first save) + int retainInMemory = 2; + } cache; + + struct ServerCap { + struct DocumentOnTypeFormattingOptions { + std::string firstTriggerCharacter = "}"; + std::vector moreTriggerCharacter; + } documentOnTypeFormattingProvider; + + // Set to false if you don't want folding ranges. + bool foldingRangeProvider = true; + + struct Workspace { + struct WorkspaceFolders { + // Set to false if you don't want workspace folders. + bool supported = true; + + bool changeNotifications = true; + } workspaceFolders; + } workspace; + } capabilities; + + struct Clang { + // Arguments matching any of these glob patterns will be excluded, e.g. + // ["-fopenmp", "-m*", "-Wall"]. std::vector excludeArgs; // Additional arguments to pass to clang. @@ -71,10 +111,14 @@ struct Config { } clang; struct ClientCapability { + // TextDocumentClientCapabilities.publishDiagnostics.relatedInformation + bool diagnosticsRelatedInformation = true; // TextDocumentClientCapabilities.documentSymbol.hierarchicalDocumentSymbolSupport bool hierarchicalDocumentSymbolSupport = true; // TextDocumentClientCapabilities.definition.linkSupport bool linkSupport = true; + + // If false, disable snippets and complete just the identifier part. // TextDocumentClientCapabilities.completion.completionItem.snippetSupport bool snippetSupport = true; } client; @@ -122,9 +166,6 @@ struct Config { // that implement their own filtering and sorting logic. bool filterAndSort = true; - // Maxmum number of results. - int maxNum = 100; - struct Include { // Regex patterns to match include completion candidates against. They // receive the absolute file path. @@ -146,6 +187,15 @@ struct Config { std::vector whitelist; } include; + + // Maxmum number of results. + int maxNum = 100; + + // Add placeholder text. Effective only if client.snippetSupport is true. + // + // false: foo($1)$0 + // true: foo(${1:int a}, ${2:int b})$0 + bool placeholder = true; } completion; struct Diagnostics { @@ -201,13 +251,21 @@ struct Config { // - https://github.com/autozimu/LanguageClient-neovim/issues/224 int comments = 2; - // By default, all project entries will be indexed on initialization. Use - // these two options to exclude some. They can still be indexed after you - // open them. + // If false, names of no linkage are not indexed in the background. They are + // indexed after the files are opened. + bool initialNoLinkage = false; + + // Use the two options to exclude files that should not be indexed in the + // background. std::vector initialBlacklist; std::vector initialWhitelist; - // If not 0, a file will be indexed in each tranlation unit that includes it. + // If a variable initializer/macro replacement-list has fewer than this many + // lines, include the initializer in detailed_name. + int maxInitializerLines = 5; + + // If not 0, a file will be indexed in each tranlation unit that includes + // it. int multiVersion = 0; // If multiVersion != 0, files that match blacklist but not whitelist will @@ -215,11 +273,20 @@ struct Config { std::vector multiVersionBlacklist; std::vector multiVersionWhitelist; + struct Name { + // Suppress inline and unnamed namespaces in identifier names. + bool suppressUnwrittenScope = false; + } name; + // Allow indexing on textDocument/didChange. // May be too slow for big projects, so it is off by default. bool onChange = false; - // Number of indexer threads. If 0, 80% of cores are used. + // If true, index parameters in declarations. + bool parametersInDeclarations = true; + + // Number of indexer threads. If 0, the number of index threads will match + // the number of cores (as reported by std::thread::hardware_concurrency). int threads = 0; // Whether to reparse a file if write times of its dependencies have @@ -255,33 +322,44 @@ struct Config { int maxNum = 2000; } xref; }; +REFLECT_STRUCT(Config::Cache, directory, format, hierarchicalPath, + retainInMemory); +REFLECT_STRUCT(Config::ServerCap::DocumentOnTypeFormattingOptions, + firstTriggerCharacter, moreTriggerCharacter); +REFLECT_STRUCT(Config::ServerCap::Workspace::WorkspaceFolders, supported, + changeNotifications); +REFLECT_STRUCT(Config::ServerCap::Workspace, workspaceFolders); +REFLECT_STRUCT(Config::ServerCap, documentOnTypeFormattingProvider, + foldingRangeProvider, workspace); REFLECT_STRUCT(Config::Clang, excludeArgs, extraArgs, pathMappings, resourceDir); -REFLECT_STRUCT(Config::ClientCapability, hierarchicalDocumentSymbolSupport, - linkSupport, snippetSupport); +REFLECT_STRUCT(Config::ClientCapability, diagnosticsRelatedInformation, + hierarchicalDocumentSymbolSupport, linkSupport, snippetSupport); REFLECT_STRUCT(Config::CodeLens, localVariables); REFLECT_STRUCT(Config::Completion::Include, blacklist, maxPathSize, suffixWhitelist, whitelist); REFLECT_STRUCT(Config::Completion, caseSensitivity, detailedLabel, dropOldRequests, duplicateOptional, filterAndSort, include, - maxNum); + maxNum, placeholder); REFLECT_STRUCT(Config::Diagnostics, blacklist, onChange, onOpen, onSave, spellChecking, whitelist) REFLECT_STRUCT(Config::Highlight, largeFileSize, lsRanges, blacklist, whitelist) -REFLECT_STRUCT(Config::Index, blacklist, comments, initialBlacklist, - initialWhitelist, multiVersion, multiVersionBlacklist, - multiVersionWhitelist, onChange, threads, trackDependency, +REFLECT_STRUCT(Config::Index::Name, suppressUnwrittenScope); +REFLECT_STRUCT(Config::Index, blacklist, comments, initialNoLinkage, + initialBlacklist, initialWhitelist, maxInitializerLines, + multiVersion, multiVersionBlacklist, multiVersionWhitelist, name, + onChange, parametersInDeclarations, threads, trackDependency, whitelist); REFLECT_STRUCT(Config::Request, timeout); REFLECT_STRUCT(Config::Session, maxNum); REFLECT_STRUCT(Config::WorkspaceSymbol, caseSensitivity, maxNum, sort); REFLECT_STRUCT(Config::Xref, maxNum); REFLECT_STRUCT(Config, compilationDatabaseCommand, compilationDatabaseDirectory, - cacheDirectory, cacheFormat, clang, client, codeLens, completion, + cache, capabilities, clang, client, codeLens, completion, diagnostics, highlight, index, request, session, workspaceSymbol, xref); extern Config *g_config; -void DoPathMapping(std::string &arg); -} +void doPathMapping(std::string &arg); +} // namespace ccls diff --git a/src/filesystem.cc b/src/filesystem.cc index e85e663fa..dad07dddb 100644 --- a/src/filesystem.cc +++ b/src/filesystem.cc @@ -9,16 +9,16 @@ using namespace llvm; #include #include -void GetFilesInFolder(std::string folder, bool recursive, bool dir_prefix, +void getFilesInFolder(std::string folder, bool recursive, bool dir_prefix, const std::function &handler) { - ccls::EnsureEndsInSlash(folder); - sys::fs::file_status Status; - if (sys::fs::status(folder, Status, true)) + ccls::ensureEndsInSlash(folder); + sys::fs::file_status status; + if (sys::fs::status(folder, status, true)) return; - sys::fs::UniqueID ID; + sys::fs::UniqueID id; std::vector curr{folder}; std::vector> succ; - std::set seen{Status.getUniqueID()}; + std::set seen{status.getUniqueID()}; while (curr.size() || succ.size()) { if (curr.empty()) { for (auto &it : succ) @@ -29,29 +29,30 @@ void GetFilesInFolder(std::string folder, bool recursive, bool dir_prefix, std::error_code ec; std::string folder1 = curr.back(); curr.pop_back(); - for (sys::fs::directory_iterator I(folder1, ec, false), E; I != E && !ec; - I.increment(ec)) { - std::string path = I->path(), filename = sys::path::filename(path); + for (sys::fs::directory_iterator i(folder1, ec, false), e; i != e && !ec; + i.increment(ec)) { + std::string path = i->path(); + std::string filename(sys::path::filename(path)); if ((filename[0] == '.' && filename != ".ccls") || - sys::fs::status(path, Status, false)) + sys::fs::status(path, status, false)) continue; - if (sys::fs::is_symlink_file(Status)) { - if (sys::fs::status(path, Status, true)) + if (sys::fs::is_symlink_file(status)) { + if (sys::fs::status(path, status, true)) continue; - if (sys::fs::is_directory(Status)) { + if (sys::fs::is_directory(status)) { if (recursive) - succ.emplace_back(path, Status); + succ.emplace_back(path, status); continue; } } - if (sys::fs::is_regular_file(Status)) { + if (sys::fs::is_regular_file(status)) { if (!dir_prefix) path = path.substr(folder.size()); handler(sys::path::convert_to_slash(path)); - } else if (recursive && sys::fs::is_directory(Status) && - !seen.count(ID = Status.getUniqueID())) { + } else if (recursive && sys::fs::is_directory(status) && + !seen.count(id = status.getUniqueID())) { curr.push_back(path); - seen.insert(ID); + seen.insert(id); } } } diff --git a/src/filesystem.hh b/src/filesystem.hh index 678b86eb5..81222a55a 100644 --- a/src/filesystem.hh +++ b/src/filesystem.hh @@ -9,7 +9,6 @@ #include #include -void GetFilesInFolder(std::string folder, - bool recursive, +void getFilesInFolder(std::string folder, bool recursive, bool add_folder_to_path, - const std::function& handler); + const std::function &handler); diff --git a/src/fuzzy_match.cc b/src/fuzzy_match.cc index a32691fa4..ddb2c3a64 100644 --- a/src/fuzzy_match.cc +++ b/src/fuzzy_match.cc @@ -13,7 +13,7 @@ namespace { enum CharClass { Other, Lower, Upper }; enum CharRole { None, Tail, Head }; -CharClass GetCharClass(int c) { +CharClass getCharClass(int c) { if (islower(c)) return Lower; if (isupper(c)) @@ -21,12 +21,12 @@ CharClass GetCharClass(int c) { return Other; } -void CalculateRoles(std::string_view s, int roles[], int *class_set) { +void calculateRoles(std::string_view s, int roles[], int *class_set) { if (s.empty()) { *class_set = 0; return; } - CharClass pre = Other, cur = GetCharClass(s[0]), suc; + CharClass pre = Other, cur = getCharClass(s[0]), suc; *class_set = 1 << cur; auto fn = [&]() { if (cur == Other) @@ -37,7 +37,7 @@ void CalculateRoles(std::string_view s, int roles[], int *class_set) { : Tail; }; for (size_t i = 0; i < s.size() - 1; i++) { - suc = GetCharClass(s[i + 1]); + suc = getCharClass(s[i + 1]); *class_set |= 1 << suc; roles[i] = fn(); pre = cur; @@ -47,7 +47,7 @@ void CalculateRoles(std::string_view s, int roles[], int *class_set) { } } // namespace -int FuzzyMatcher::MissScore(int j, bool last) { +int FuzzyMatcher::missScore(int j, bool last) { int s = -3; if (last) s -= 10; @@ -56,7 +56,7 @@ int FuzzyMatcher::MissScore(int j, bool last) { return s; } -int FuzzyMatcher::MatchScore(int i, int j, bool last) { +int FuzzyMatcher::matchScore(int i, int j, bool last) { int s = 0; // Case matching. if (pat[i] == text[j]) { @@ -81,7 +81,7 @@ int FuzzyMatcher::MatchScore(int i, int j, bool last) { } FuzzyMatcher::FuzzyMatcher(std::string_view pattern, int sensitivity) { - CalculateRoles(pattern, pat_role, &pat_set); + calculateRoles(pattern, pat_role, &pat_set); if (sensitivity == 1) sensitivity = pat_set & 1 << Upper ? 2 : 0; case_sensitivity = sensitivity; @@ -95,7 +95,7 @@ FuzzyMatcher::FuzzyMatcher(std::string_view pattern, int sensitivity) { } } -int FuzzyMatcher::Match(std::string_view text, bool strict) { +int FuzzyMatcher::match(std::string_view text, bool strict) { if (pat.empty() != text.empty()) return kMinScore; int n = int(text.size()); @@ -104,12 +104,12 @@ int FuzzyMatcher::Match(std::string_view text, bool strict) { this->text = text; for (int i = 0; i < n; i++) low_text[i] = (char)::tolower(text[i]); - CalculateRoles(text, text_role, &text_set); + calculateRoles(text, text_role, &text_set); if (strict && n && !!pat_role[0] != !!text_role[0]) return kMinScore; dp[0][0][0] = dp[0][0][1] = 0; for (int j = 0; j < n; j++) { - dp[0][j + 1][0] = dp[0][j][0] + MissScore(j, false); + dp[0][j + 1][0] = dp[0][j][0] + missScore(j, false); dp[0][j + 1][1] = kMinScore * 2; } for (int i = 0; i < int(pat.size()); i++) { @@ -117,16 +117,16 @@ int FuzzyMatcher::Match(std::string_view text, bool strict) { int(*cur)[2] = dp[(i + 1) & 1]; cur[i][0] = cur[i][1] = kMinScore; for (int j = i; j < n; j++) { - cur[j + 1][0] = std::max(cur[j][0] + MissScore(j, false), - cur[j][1] + MissScore(j, true)); + cur[j + 1][0] = std::max(cur[j][0] + missScore(j, false), + cur[j][1] + missScore(j, true)); // For the first char of pattern, apply extra restriction to filter bad // candidates (e.g. |int| in |PRINT|) cur[j + 1][1] = (case_sensitivity ? pat[i] == text[j] : low_pat[i] == low_text[j] && (i || text_role[j] != Tail || pat[i] == text[j])) - ? std::max(pre[j][0] + MatchScore(i, j, false), - pre[j][1] + MatchScore(i, j, true)) + ? std::max(pre[j][0] + matchScore(i, j, false), + pre[j][1] + matchScore(i, j, true)) : kMinScore * 2; } } diff --git a/src/fuzzy_match.hh b/src/fuzzy_match.hh index c76a7af4c..caab8f26f 100644 --- a/src/fuzzy_match.hh +++ b/src/fuzzy_match.hh @@ -17,7 +17,7 @@ public: constexpr static int kMinScore = INT_MIN / 4; FuzzyMatcher(std::string_view pattern, int case_sensitivity); - int Match(std::string_view text, bool strict); + int match(std::string_view text, bool strict); private: int case_sensitivity; @@ -28,7 +28,7 @@ private: int pat_role[kMaxPat], text_role[kMaxText]; int dp[2][kMaxText + 1][2]; - int MatchScore(int i, int j, bool last); - int MissScore(int j, bool last); + int matchScore(int i, int j, bool last); + int missScore(int j, bool last); }; } // namespace ccls diff --git a/src/hierarchy.hh b/src/hierarchy.hh index a3e6a4f59..24c2882c9 100644 --- a/src/hierarchy.hh +++ b/src/hierarchy.hh @@ -10,7 +10,7 @@ namespace ccls { template -std::vector FlattenHierarchy(const std::optional &root) { +std::vector flattenHierarchy(const std::optional &root) { if (!root) return {}; std::vector ret; diff --git a/src/include_complete.cc b/src/include_complete.cc index 83ac6750f..5fab581bb 100644 --- a/src/include_complete.cc +++ b/src/include_complete.cc @@ -24,7 +24,7 @@ struct CompletionCandidate { CompletionItem completion_item; }; -std::string ElideLongPath(const std::string &path) { +std::string elideLongPath(const std::string &path) { if (g_config->completion.include.maxPathSize <= 0 || (int)path.size() <= g_config->completion.include.maxPathSize) return path; @@ -33,7 +33,7 @@ std::string ElideLongPath(const std::string &path) { return ".." + path.substr(start + 2); } -size_t TrimCommonPathPrefix(const std::string &result, +size_t trimCommonPathPrefix(const std::string &result, const std::string &trimmer) { #ifdef _WIN32 std::string s = result, t = trimmer; @@ -48,21 +48,20 @@ size_t TrimCommonPathPrefix(const std::string &result, return 0; } -int TrimPath(Project *project, std::string &path) { +int trimPath(Project *project, std::string &path) { size_t pos = 0; int kind = 0; for (auto &[root, folder] : project->root2folder) for (auto &[search, search_dir_kind] : folder.search_dir2kind) - if (int t = TrimCommonPathPrefix(path, search); t > pos) + if (int t = trimCommonPathPrefix(path, search); t > pos) pos = t, kind = search_dir_kind; path = path.substr(pos); return kind; } -CompletionItem BuildCompletionItem(const std::string &path, - int kind) { +CompletionItem buildCompletionItem(const std::string &path, int kind) { CompletionItem item; - item.label = ElideLongPath(path); + item.label = elideLongPath(path); item.detail = path; // the include path, used in de-duplicating item.textEdit.newText = path; item.insertTextFormat = InsertTextFormat::PlainText; @@ -82,7 +81,7 @@ IncludeComplete::~IncludeComplete() { std::this_thread::sleep_for(std::chrono::milliseconds(100)); } -void IncludeComplete::Rescan() { +void IncludeComplete::rescan() { if (is_scanning || LLVM_VERSION_MAJOR >= 8) return; @@ -104,44 +103,42 @@ void IncludeComplete::Rescan() { const std::string &search = search_kind.first; int kind = search_kind.second; assert(search.back() == '/'); - if (match_ && !match_->Matches(search)) + if (match_ && !match_->matches(search)) return; bool include_cpp = search.find("include/c++") != std::string::npos; std::vector results; - GetFilesInFolder(search, true /*recursive*/, - false /*add_folder_to_path*/, - [&](const std::string &path) { - bool ok = include_cpp; - for (StringRef suffix : - g_config->completion.include.suffixWhitelist) - if (StringRef(path).endswith(suffix)) - ok = true; - if (!ok) - return; - if (match_ && !match_->Matches(search + path)) - return; - - CompletionCandidate candidate; - candidate.absolute_path = search + path; - candidate.completion_item = - BuildCompletionItem(path, kind); - results.push_back(candidate); - }); + getFilesInFolder( + search, true /*recursive*/, false /*add_folder_to_path*/, + [&](const std::string &path) { + bool ok = include_cpp; + for (StringRef suffix : + g_config->completion.include.suffixWhitelist) + if (StringRef(path).endswith(suffix)) + ok = true; + if (!ok) + return; + if (match_ && !match_->matches(search + path)) + return; + + CompletionCandidate candidate; + candidate.absolute_path = search + path; + candidate.completion_item = buildCompletionItem(path, kind); + results.push_back(candidate); + }); std::lock_guard lock(completion_items_mutex); for (CompletionCandidate &result : results) - InsertCompletionItem(result.absolute_path, + insertCompletionItem(result.absolute_path, std::move(result.completion_item)); } } is_scanning = false; - }) - .detach(); + }).detach(); } -void IncludeComplete::InsertCompletionItem(const std::string &absolute_path, +void IncludeComplete::insertCompletionItem(const std::string &absolute_path, CompletionItem &&item) { if (inserted_paths.try_emplace(item.detail, inserted_paths.size()).second) { completion_items.push_back(item); @@ -155,28 +152,28 @@ void IncludeComplete::InsertCompletionItem(const std::string &absolute_path, } } -void IncludeComplete::AddFile(const std::string &path) { +void IncludeComplete::addFile(const std::string &path) { bool ok = false; for (StringRef suffix : g_config->completion.include.suffixWhitelist) if (StringRef(path).endswith(suffix)) ok = true; if (!ok) return; - if (match_ && !match_->Matches(path)) + if (match_ && !match_->matches(path)) return; std::string trimmed_path = path; - int kind = TrimPath(project_, trimmed_path); - CompletionItem item = BuildCompletionItem(trimmed_path, kind); + int kind = trimPath(project_, trimmed_path); + CompletionItem item = buildCompletionItem(trimmed_path, kind); std::unique_lock lock(completion_items_mutex, std::defer_lock); if (is_scanning) lock.lock(); - InsertCompletionItem(path, std::move(item)); + insertCompletionItem(path, std::move(item)); } std::optional -IncludeComplete::FindCompletionItemForAbsolutePath( +IncludeComplete::findCompletionItemForAbsolutePath( const std::string &absolute_path) { std::lock_guard lock(completion_items_mutex); diff --git a/src/include_complete.hh b/src/include_complete.hh index 6be2505c6..49521f750 100644 --- a/src/include_complete.hh +++ b/src/include_complete.hh @@ -17,17 +17,17 @@ struct IncludeComplete { ~IncludeComplete(); // Starts scanning directories. Clears existing cache. - void Rescan(); + void rescan(); // Ensures the one-off file is inside |completion_items|. - void AddFile(const std::string &absolute_path); + void addFile(const std::string &absolute_path); std::optional - FindCompletionItemForAbsolutePath(const std::string &absolute_path); + findCompletionItemForAbsolutePath(const std::string &absolute_path); // Insert item to |completion_items|. // Update |absolute_path_to_completion_item| and |inserted_paths|. - void InsertCompletionItem(const std::string &absolute_path, + void insertCompletionItem(const std::string &absolute_path, ccls::CompletionItem &&item); // Guards |completion_items| when |is_scanning| is true. diff --git a/src/indexer.cc b/src/indexer.cc index d86316928..4c7a262cd 100644 --- a/src/indexer.cc +++ b/src/indexer.cc @@ -10,7 +10,9 @@ #include "sema_manager.hh" #include +#include #include +#include #include #include #include @@ -29,8 +31,6 @@ using namespace clang; namespace ccls { namespace { -constexpr int kInitializerMaxLines = 3; - GroupMatch *multiVersionMatcher; struct File { @@ -41,78 +41,83 @@ struct File { }; struct IndexParam { - std::unordered_map UID2File; - std::unordered_map UID2multi; + std::unordered_map uid2file; + std::unordered_map uid2multi; struct DeclInfo { Usr usr; std::string short_name; std::string qualified; }; - std::unordered_map Decl2Info; + std::unordered_map decl2Info; VFS &vfs; - ASTContext *Ctx; - IndexParam(VFS &vfs) : vfs(vfs) {} + ASTContext *ctx; + bool no_linkage; + IndexParam(VFS &vfs, bool no_linkage) : vfs(vfs), no_linkage(no_linkage) {} - void SeenFile(const FileEntry &File) { + void seenFile(FileID fid) { // If this is the first time we have seen the file (ignoring if we are // generating an index for it): - auto [it, inserted] = UID2File.try_emplace(File.getUniqueID()); + auto [it, inserted] = uid2file.try_emplace(fid); if (inserted) { - std::string path = PathFromFileEntry(File); + const FileEntry *fe = ctx->getSourceManager().getFileEntryForID(fid); + if (!fe) + return; + std::string path = pathFromFileEntry(*fe); it->second.path = path; - it->second.mtime = File.getModificationTime(); + it->second.mtime = fe->getModificationTime(); if (!it->second.mtime) - if (auto tim = LastWriteTime(path)) + if (auto tim = lastWriteTime(path)) it->second.mtime = *tim; - if (std::optional content = ReadContent(path)) + if (std::optional content = readContent(path)) it->second.content = *content; - if (!vfs.Stamp(path, it->second.mtime, 1)) + if (!vfs.stamp(path, it->second.mtime, no_linkage ? 3 : 1)) return; - it->second.db = std::make_unique(path, it->second.content); + it->second.db = + std::make_unique(path, it->second.content, no_linkage); } } - IndexFile *ConsumeFile(const FileEntry &FE) { - SeenFile(FE); - return UID2File[FE.getUniqueID()].db.get(); + IndexFile *consumeFile(FileID fid) { + seenFile(fid); + return uid2file[fid].db.get(); } - bool UseMultiVersion(const FileEntry &FE) { - auto it = UID2multi.try_emplace(FE.getUniqueID()); + bool useMultiVersion(FileID fid) { + auto it = uid2multi.try_emplace(fid); if (it.second) - it.first->second = multiVersionMatcher->Matches(PathFromFileEntry(FE)); + if (const FileEntry *fe = ctx->getSourceManager().getFileEntryForID(fid)) + it.first->second = multiVersionMatcher->matches(pathFromFileEntry(*fe)); return it.first->second; } }; -StringRef GetSourceInRange(const SourceManager &SM, const LangOptions &LangOpts, - SourceRange R) { - SourceLocation BLoc = R.getBegin(), ELoc = R.getEnd(); - std::pair BInfo = SM.getDecomposedLoc(BLoc), - EInfo = SM.getDecomposedLoc(ELoc); +StringRef getSourceInRange(const SourceManager &sm, const LangOptions &langOpts, + SourceRange sr) { + SourceLocation bloc = sr.getBegin(), eLoc = sr.getEnd(); + std::pair bInfo = sm.getDecomposedLoc(bloc), + eInfo = sm.getDecomposedLoc(eLoc); bool invalid = false; - StringRef Buf = SM.getBufferData(BInfo.first, &invalid); + StringRef buf = sm.getBufferData(bInfo.first, &invalid); if (invalid) return ""; - return Buf.substr(BInfo.second, - EInfo.second + - Lexer::MeasureTokenLength(ELoc, SM, LangOpts) - - BInfo.second); + return buf.substr(bInfo.second, + eInfo.second + + Lexer::MeasureTokenLength(eLoc, sm, langOpts) - + bInfo.second); } -Kind GetKind(const Decl *D, SymbolKind &kind) { - switch (D->getKind()) { +Kind getKind(const Decl *d, SymbolKind &kind) { + switch (d->getKind()) { case Decl::LinkageSpec: return Kind::Invalid; case Decl::Namespace: - kind = SymbolKind::Namespace; - return Kind::Type; case Decl::NamespaceAlias: - kind = SymbolKind::TypeAlias; + kind = SymbolKind::Namespace; return Kind::Type; case Decl::ObjCCategory: + case Decl::ObjCCategoryImpl: case Decl::ObjCImplementation: case Decl::ObjCInterface: case Decl::ObjCProtocol: @@ -146,19 +151,25 @@ Kind GetKind(const Decl *D, SymbolKind &kind) { case Decl::Record: kind = SymbolKind::Class; // spec has no Union, use Class - if (auto *RD = dyn_cast(D)) - if (RD->getTagKind() == TTK_Struct) + if (auto *rd = dyn_cast(d)) + if (rd->getTagKind() == TTK_Struct) kind = SymbolKind::Struct; return Kind::Type; case Decl::ClassTemplateSpecialization: case Decl::ClassTemplatePartialSpecialization: kind = SymbolKind::Class; return Kind::Type; + case Decl::TemplateTypeParm: + kind = SymbolKind::TypeParameter; + return Kind::Type; case Decl::TypeAlias: case Decl::Typedef: case Decl::UnresolvedUsingTypename: kind = SymbolKind::TypeAlias; return Kind::Type; + case Decl::Using: + kind = SymbolKind::Null; // ignored + return Kind::Invalid; case Decl::Binding: kind = SymbolKind::Variable; return Kind::Var; @@ -170,8 +181,8 @@ Kind GetKind(const Decl *D, SymbolKind &kind) { kind = SymbolKind::Function; return Kind::Func; case Decl::CXXMethod: { - const auto *MD = cast(D); - kind = MD->isStatic() ? SymbolKind::StaticMethod : SymbolKind::Method; + const auto *md = cast(d); + kind = md->isStatic() ? SymbolKind::StaticMethod : SymbolKind::Method; return Kind::Func; } case Decl::CXXConstructor: @@ -181,6 +192,10 @@ Kind GetKind(const Decl *D, SymbolKind &kind) { case Decl::CXXDestructor: kind = SymbolKind::Method; return Kind::Func; + case Decl::NonTypeTemplateParm: + // ccls extension + kind = SymbolKind::Parameter; + return Kind::Var; case Decl::Var: case Decl::Decomposition: kind = SymbolKind::Variable; @@ -204,13 +219,12 @@ Kind GetKind(const Decl *D, SymbolKind &kind) { return Kind::Invalid; default: - LOG_S(INFO) << "unhandled " << int(D->getKind()); return Kind::Invalid; } } -LanguageId GetDeclLanguage(const Decl *D) { - switch (D->getKind()) { +LanguageId getDeclLanguage(const Decl *d) { + switch (d->getKind()) { default: return LanguageId::C; case Decl::ImplicitParam: @@ -255,128 +269,128 @@ LanguageId GetDeclLanguage(const Decl *D) { } // clang/lib/AST/DeclPrinter.cpp -QualType GetBaseType(QualType T, bool deduce_auto) { - QualType BaseType = T; - while (!BaseType.isNull() && !BaseType->isSpecifierType()) { - if (const PointerType *PTy = BaseType->getAs()) - BaseType = PTy->getPointeeType(); - else if (const BlockPointerType *BPy = BaseType->getAs()) - BaseType = BPy->getPointeeType(); - else if (const ArrayType *ATy = dyn_cast(BaseType)) - BaseType = ATy->getElementType(); - else if (const VectorType *VTy = BaseType->getAs()) - BaseType = VTy->getElementType(); - else if (const ReferenceType *RTy = BaseType->getAs()) - BaseType = RTy->getPointeeType(); - else if (const ParenType *PTy = BaseType->getAs()) - BaseType = PTy->desugar(); +QualType getBaseType(QualType t, bool deduce_auto) { + QualType baseType = t; + while (!baseType.isNull() && !baseType->isSpecifierType()) { + if (const PointerType *pTy = baseType->getAs()) + baseType = pTy->getPointeeType(); + else if (const BlockPointerType *bPy = baseType->getAs()) + baseType = bPy->getPointeeType(); + else if (const ArrayType *aTy = dyn_cast(baseType)) + baseType = aTy->getElementType(); + else if (const VectorType *vTy = baseType->getAs()) + baseType = vTy->getElementType(); + else if (const ReferenceType *rTy = baseType->getAs()) + baseType = rTy->getPointeeType(); + else if (const ParenType *pTy = baseType->getAs()) + baseType = pTy->desugar(); else if (deduce_auto) { - if (const AutoType *ATy = BaseType->getAs()) - BaseType = ATy->getDeducedType(); + if (const AutoType *aTy = baseType->getAs()) + baseType = aTy->getDeducedType(); else break; } else break; } - return BaseType; + return baseType; } -const Decl *GetTypeDecl(QualType T, bool *specialization = nullptr) { - Decl *D = nullptr; - T = GetBaseType(T.getUnqualifiedType(), true); - const Type *TP = T.getTypePtrOrNull(); - if (!TP) +const Decl *getTypeDecl(QualType t, bool *specialization = nullptr) { + Decl *d = nullptr; + t = getBaseType(t.getUnqualifiedType(), true); + const Type *tp = t.getTypePtrOrNull(); + if (!tp) return nullptr; try_again: - switch (TP->getTypeClass()) { + switch (tp->getTypeClass()) { case Type::Typedef: - D = cast(TP)->getDecl(); + d = cast(tp)->getDecl(); break; case Type::ObjCObject: - D = cast(TP)->getInterface(); + d = cast(tp)->getInterface(); break; case Type::ObjCInterface: - D = cast(TP)->getDecl(); + d = cast(tp)->getDecl(); break; case Type::Record: case Type::Enum: - D = cast(TP)->getDecl(); + d = cast(tp)->getDecl(); break; case Type::TemplateTypeParm: - D = cast(TP)->getDecl(); + d = cast(tp)->getDecl(); break; case Type::TemplateSpecialization: if (specialization) *specialization = true; - if (const RecordType *Record = TP->getAs()) - D = Record->getDecl(); + if (const RecordType *record = tp->getAs()) + d = record->getDecl(); else - D = cast(TP) + d = cast(tp) ->getTemplateName() .getAsTemplateDecl(); break; case Type::Auto: case Type::DeducedTemplateSpecialization: - TP = cast(TP)->getDeducedType().getTypePtrOrNull(); - if (TP) + tp = cast(tp)->getDeducedType().getTypePtrOrNull(); + if (tp) goto try_again; break; case Type::InjectedClassName: - D = cast(TP)->getDecl(); + d = cast(tp)->getDecl(); break; // FIXME: Template type parameters! case Type::Elaborated: - TP = cast(TP)->getNamedType().getTypePtrOrNull(); + tp = cast(tp)->getNamedType().getTypePtrOrNull(); goto try_again; default: break; } - return D; + return d; } -const Decl *GetAdjustedDecl(const Decl *D) { - while (D) { - if (auto *R = dyn_cast(D)) { - if (auto *S = dyn_cast(R)) { - if (!S->getTypeAsWritten()) { +const Decl *getAdjustedDecl(const Decl *d) { + while (d) { + if (auto *r = dyn_cast(d)) { + if (auto *s = dyn_cast(r)) { + if (!s->isExplicitSpecialization()) { llvm::PointerUnion - Result = S->getSpecializedTemplateOrPartial(); - if (Result.is()) - D = Result.get(); + result = s->getSpecializedTemplateOrPartial(); + if (result.is()) + d = result.get(); else - D = Result.get(); + d = result.get(); continue; } - } else if (auto *D1 = R->getInstantiatedFromMemberClass()) { - D = D1; + } else if (auto *d1 = r->getInstantiatedFromMemberClass()) { + d = d1; continue; } - } else if (auto *ED = dyn_cast(D)) { - if (auto *D1 = ED->getInstantiatedFromMemberEnum()) { - D = D1; + } else if (auto *ed = dyn_cast(d)) { + if (auto *d1 = ed->getInstantiatedFromMemberEnum()) { + d = d1; continue; } } break; } - return D; + return d; } -bool ValidateRecord(const RecordDecl *RD) { - for (const auto *I : RD->fields()) { - QualType FQT = I->getType(); - if (FQT->isIncompleteType() || FQT->isDependentType()) +bool validateRecord(const RecordDecl *rd) { + for (const auto *i : rd->fields()) { + QualType fqt = i->getType(); + if (fqt->isIncompleteType() || fqt->isDependentType()) return false; - if (const RecordType *ChildType = I->getType()->getAs()) - if (const RecordDecl *Child = ChildType->getDecl()) - if (!ValidateRecord(Child)) + if (const RecordType *childType = i->getType()->getAs()) + if (const RecordDecl *child = childType->getDecl()) + if (!validateRecord(child)) return false; } return true; @@ -384,41 +398,41 @@ bool ValidateRecord(const RecordDecl *RD) { class IndexDataConsumer : public index::IndexDataConsumer { public: - ASTContext *Ctx; + ASTContext *ctx; IndexParam ¶m; - std::string GetComment(const Decl *D) { - SourceManager &SM = Ctx->getSourceManager(); - const RawComment *RC = Ctx->getRawCommentForAnyRedecl(D); - if (!RC) + std::string getComment(const Decl *d) { + SourceManager &sm = ctx->getSourceManager(); + const RawComment *rc = ctx->getRawCommentForAnyRedecl(d); + if (!rc) return ""; - StringRef Raw = RC->getRawText(Ctx->getSourceManager()); - SourceRange R = RC->getSourceRange(); - std::pair BInfo = SM.getDecomposedLoc(R.getBegin()); - unsigned start_column = SM.getLineNumber(BInfo.first, BInfo.second); + StringRef raw = rc->getRawText(ctx->getSourceManager()); + SourceRange sr = rc->getSourceRange(); + std::pair bInfo = sm.getDecomposedLoc(sr.getBegin()); + unsigned start_column = sm.getLineNumber(bInfo.first, bInfo.second); std::string ret; int pad = -1; - for (const char *p = Raw.data(), *E = Raw.end(); p < E;) { + for (const char *p = raw.data(), *e = raw.end(); p < e;) { // The first line starts with a comment marker, but the rest needs // un-indenting. unsigned skip = start_column - 1; - for (; skip > 0 && p < E && (*p == ' ' || *p == '\t'); p++) + for (; skip > 0 && p < e && (*p == ' ' || *p == '\t'); p++) skip--; const char *q = p; - while (q < E && *q != '\n') + while (q < e && *q != '\n') q++; - if (q < E) + if (q < e) q++; // A minimalist approach to skip Doxygen comment markers. // See https://www.stack.nl/~dimitri/doxygen/manual/docblocks.html if (pad < 0) { // First line, detect the length of comment marker and put into |pad| const char *begin = p; - while (p < E && (*p == '/' || *p == '*' || *p == '-' || *p == '=')) + while (p < e && (*p == '/' || *p == '*' || *p == '-' || *p == '=')) p++; - if (p < E && (*p == '<' || *p == '!')) + if (p < e && (*p == '<' || *p == '!')) p++; - if (p < E && *p == ' ') + if (p < e && *p == ' ') p++; if (p + 1 == q) p++; @@ -427,7 +441,7 @@ class IndexDataConsumer : public index::IndexDataConsumer { } else { // Other lines, skip |pad| bytes int prefix = pad; - while (prefix > 0 && p < E && + while (prefix > 0 && p < e && (*p == ' ' || *p == '/' || *p == '*' || *p == '<' || *p == '!')) prefix--, p++; } @@ -443,18 +457,19 @@ class IndexDataConsumer : public index::IndexDataConsumer { return ret; } - Usr GetUsr(const Decl *D, IndexParam::DeclInfo **info = nullptr) const { - D = D->getCanonicalDecl(); - auto [it, inserted] = param.Decl2Info.try_emplace(D); + Usr getUsr(const Decl *d, IndexParam::DeclInfo **info = nullptr) const { + d = d->getCanonicalDecl(); + auto [it, inserted] = param.decl2Info.try_emplace(d); if (inserted) { - SmallString<256> USR; - index::generateUSRForDecl(D, USR); + SmallString<256> usr; + index::generateUSRForDecl(d, usr); auto &info = it->second; - info.usr = HashUsr(USR); - if (auto *ND = dyn_cast(D)) { - info.short_name = ND->getNameAsString(); - info.qualified = ND->getQualifiedNameAsString(); - SimplifyAnonymous(info.qualified); + info.usr = hashUsr(usr); + if (auto *nd = dyn_cast(d)) { + info.short_name = nd->getNameAsString(); + llvm::raw_string_ostream os(info.qualified); + nd->printQualifiedName(os, getDefaultPolicy()); + simplifyAnonymous(info.qualified); } } if (info) @@ -462,19 +477,20 @@ class IndexDataConsumer : public index::IndexDataConsumer { return it->second.usr; } - PrintingPolicy GetDefaultPolicy() const { - PrintingPolicy PP(Ctx->getLangOpts()); - PP.AnonymousTagLocations = false; - PP.TerseOutput = true; - PP.PolishForDeclaration = true; - PP.ConstantsAsWritten = true; - PP.SuppressTagKeyword = true; - PP.SuppressInitializers = true; - PP.FullyQualifiedName = false; - return PP; + PrintingPolicy getDefaultPolicy() const { + PrintingPolicy pp(ctx->getLangOpts()); + pp.AnonymousTagLocations = false; + pp.TerseOutput = true; + pp.PolishForDeclaration = true; + pp.ConstantsAsWritten = true; + pp.SuppressTagKeyword = true; + pp.SuppressUnwrittenScope = g_config->index.name.suppressUnwrittenScope; + pp.SuppressInitializers = true; + pp.FullyQualifiedName = false; + return pp; } - static void SimplifyAnonymous(std::string &name) { + static void simplifyAnonymous(std::string &name) { for (std::string::size_type i = 0;;) { if ((i = name.find("(anonymous ", i)) == std::string::npos) break; @@ -487,14 +503,14 @@ class IndexDataConsumer : public index::IndexDataConsumer { } template - void SetName(const Decl *D, std::string_view short_name, + void setName(const Decl *d, std::string_view short_name, std::string_view qualified, Def &def) { - SmallString<256> Str; - llvm::raw_svector_ostream OS(Str); - D->print(OS, GetDefaultPolicy()); + SmallString<256> str; + llvm::raw_svector_ostream os(str); + d->print(os, getDefaultPolicy()); - std::string name = OS.str(); - SimplifyAnonymous(name); + std::string name(str.data(), str.size()); + simplifyAnonymous(name); // Remove \n in DeclPrinter.cpp "{\n" + if(!TerseOutput)something + "}" for (std::string::size_type i = 0;;) { if ((i = name.find("{\n}", i)) == std::string::npos) @@ -503,8 +519,9 @@ class IndexDataConsumer : public index::IndexDataConsumer { } auto i = name.find(short_name); if (short_name.size()) - while (i != std::string::npos && ((i && isIdentifierBody(name[i - 1])) || - isIdentifierBody(name[i + short_name.size()]))) + while (i != std::string::npos && + ((i && isIdentifierBody(name[i - 1])) || + isIdentifierBody(name[i + short_name.size()]))) i = name.find(short_name, i + short_name.size()); if (i == std::string::npos) { // e.g. operator type-parameter-1 @@ -517,7 +534,8 @@ class IndexDataConsumer : public index::IndexDataConsumer { name.replace(i, short_name.size(), qualified); def.short_name_offset = i + qualified.size() - short_name.size(); } - def.short_name_size = short_name.size(); + // name may be empty while short_name is not. + def.short_name_size = name.empty() ? 0 : short_name.size(); for (int paren = 0; i; i--) { // Skip parentheses in "(anon struct)::name" if (name[i - 1] == ')') @@ -529,137 +547,130 @@ class IndexDataConsumer : public index::IndexDataConsumer { break; } def.qual_name_offset = i; - def.detailed_name = Intern(name); + def.detailed_name = intern(name); } - void SetVarName(const Decl *D, std::string_view short_name, + void setVarName(const Decl *d, std::string_view short_name, std::string_view qualified, IndexVar::Def &def) { - QualType T; + QualType t; const Expr *init = nullptr; bool deduced = false; - if (auto *VD = dyn_cast(D)) { - T = VD->getType(); - init = VD->getAnyInitializer(); - def.storage = VD->getStorageClass(); - } else if (auto *FD = dyn_cast(D)) { - T = FD->getType(); - init = FD->getInClassInitializer(); - } else if (auto *BD = dyn_cast(D)) { - T = BD->getType(); + if (auto *vd = dyn_cast(d)) { + t = vd->getType(); + init = vd->getAnyInitializer(); + def.storage = vd->getStorageClass(); + } else if (auto *fd = dyn_cast(d)) { + t = fd->getType(); + init = fd->getInClassInitializer(); + } else if (auto *bd = dyn_cast(d)) { + t = bd->getType(); deduced = true; } - if (!T.isNull()) { - if (T->getContainedDeducedType()) { + if (!t.isNull()) { + if (t->getContainedDeducedType()) { deduced = true; - } else if (auto *DT = dyn_cast(T)) { + } else if (auto *dt = dyn_cast(t)) { // decltype(y) x; - while (DT && !DT->getUnderlyingType().isNull()) { - T = DT->getUnderlyingType(); - DT = dyn_cast(T); + while (dt && !dt->getUnderlyingType().isNull()) { + t = dt->getUnderlyingType(); + dt = dyn_cast(t); } deduced = true; } } - if (!T.isNull() && deduced) { - SmallString<256> Str; - llvm::raw_svector_ostream OS(Str); - PrintingPolicy PP = GetDefaultPolicy(); - T.print(OS, PP); - if (Str.size() && - (Str.back() != ' ' && Str.back() != '*' && Str.back() != '&')) - Str += ' '; - def.qual_name_offset = Str.size(); - def.short_name_offset = Str.size() + qualified.size() - short_name.size(); + if (!t.isNull() && deduced) { + SmallString<256> str; + llvm::raw_svector_ostream os(str); + PrintingPolicy pp = getDefaultPolicy(); + t.print(os, pp); + if (str.size() && + (str.back() != ' ' && str.back() != '*' && str.back() != '&')) + str += ' '; + def.qual_name_offset = str.size(); + def.short_name_offset = str.size() + qualified.size() - short_name.size(); def.short_name_size = short_name.size(); - Str += StringRef(qualified.data(), qualified.size()); - def.detailed_name = Intern(Str); + str += StringRef(qualified.data(), qualified.size()); + def.detailed_name = intern(str); } else { - SetName(D, short_name, qualified, def); + setName(d, short_name, qualified, def); } if (init) { - SourceManager &SM = Ctx->getSourceManager(); - const LangOptions &Lang = Ctx->getLangOpts(); - SourceRange R = SM.getExpansionRange(init->getSourceRange()) -#if LLVM_VERSION_MAJOR >= 7 - .getAsRange() -#endif - ; - SourceLocation L = D->getLocation(); - if (L.isMacroID() || !SM.isBeforeInTranslationUnit(L, R.getBegin())) + SourceManager &sm = ctx->getSourceManager(); + const LangOptions &lang = ctx->getLangOpts(); + SourceRange sr = + sm.getExpansionRange(init->getSourceRange()).getAsRange(); + SourceLocation l = d->getLocation(); + if (l.isMacroID() || !sm.isBeforeInTranslationUnit(l, sr.getBegin())) return; - StringRef Buf = GetSourceInRange(SM, Lang, R); - Twine Init = Buf.count('\n') <= kInitializerMaxLines - 1 - ? Buf.size() && Buf[0] == ':' ? Twine(" ", Buf) - : Twine(" = ", Buf) + StringRef buf = getSourceInRange(sm, lang, sr); + Twine init = buf.count('\n') <= g_config->index.maxInitializerLines - 1 + ? buf.size() && buf[0] == ':' ? Twine(" ", buf) + : Twine(" = ", buf) : Twine(); - Twine T = def.detailed_name + Init; + Twine t = def.detailed_name + init; def.hover = def.storage == SC_Static && strncmp(def.detailed_name, "static ", 7) - ? Intern(("static " + T).str()) - : Intern(T.str()); + ? intern(("static " + t).str()) + : intern(t.str()); } } - static int GetFileLID(IndexFile *db, SourceManager &SM, const FileEntry &FE) { - auto [it, inserted] = db->uid2lid_and_path.try_emplace(FE.getUniqueID()); + static int getFileLID(IndexFile *db, SourceManager &sm, FileID fid) { + auto [it, inserted] = db->uid2lid_and_path.try_emplace(fid); if (inserted) { - it->second.first = db->uid2lid_and_path.size() - 1; - SmallString<256> Path = FE.tryGetRealPathName(); - if (Path.empty()) - Path = FE.getName(); - if (!llvm::sys::path::is_absolute(Path) && - !SM.getFileManager().makeAbsolutePath(Path)) + const FileEntry *fe = sm.getFileEntryForID(fid); + if (!fe) { + it->second.first = -1; return -1; - it->second.second = llvm::sys::path::convert_to_slash(Path.str()); + } + it->second.first = db->uid2lid_and_path.size() - 1; + it->second.second = pathFromFileEntry(*fe); } return it->second.first; } - void AddMacroUse(IndexFile *db, SourceManager &SM, Usr usr, Kind kind, - SourceLocation Spell) const { - const FileEntry *FE = SM.getFileEntryForID(SM.getFileID(Spell)); - if (!FE) - return; - int lid = GetFileLID(db, SM, *FE); + void addMacroUse(IndexFile *db, SourceManager &sm, Usr usr, Kind kind, + SourceLocation sl) const { + FileID fid = sm.getFileID(sl); + int lid = getFileLID(db, sm, fid); if (lid < 0) return; - Range spell = - FromTokenRange(SM, Ctx->getLangOpts(), SourceRange(Spell, Spell)); + Range spell = fromTokenRange(sm, ctx->getLangOpts(), SourceRange(sl, sl)); Use use{{spell, Role::Dynamic}, lid}; switch (kind) { case Kind::Func: - db->ToFunc(usr).uses.push_back(use); + db->toFunc(usr).uses.push_back(use); break; case Kind::Type: - db->ToType(usr).uses.push_back(use); + db->toType(usr).uses.push_back(use); break; case Kind::Var: - db->ToVar(usr).uses.push_back(use); + db->toVar(usr).uses.push_back(use); break; default: llvm_unreachable(""); } } - void CollectRecordMembers(IndexType &type, const RecordDecl *RD) { - SmallVector, 2> Stack{{RD, 0}}; - llvm::DenseSet Seen; - Seen.insert(RD); - while (Stack.size()) { + void collectRecordMembers(IndexType &type, const RecordDecl *rd) { + SmallVector, 2> stack{{rd, 0}}; + llvm::DenseSet seen; + seen.insert(rd); + while (stack.size()) { int offset; - std::tie(RD, offset) = Stack.back(); - Stack.pop_back(); - if (!RD->isCompleteDefinition() || RD->isDependentType() || - RD->isInvalidDecl() || !ValidateRecord(RD)) + std::tie(rd, offset) = stack.back(); + stack.pop_back(); + if (!rd->isCompleteDefinition() || rd->isDependentType() || + rd->isInvalidDecl() || !validateRecord(rd)) offset = -1; - for (FieldDecl *FD : RD->fields()) { - int offset1 = offset < 0 ? -1 : offset + Ctx->getFieldOffset(FD); - if (FD->getIdentifier()) - type.def.vars.emplace_back(GetUsr(FD), offset1); - else if (const auto *RT1 = FD->getType()->getAs()) { - if (const RecordDecl *RD1 = RT1->getDecl()) - if (Seen.insert(RD1).second) - Stack.push_back({RD1, offset1}); + for (FieldDecl *fd : rd->fields()) { + int offset1 = offset < 0 ? -1 : int(offset + ctx->getFieldOffset(fd)); + if (fd->getIdentifier()) + type.def.vars.emplace_back(getUsr(fd), offset1); + else if (const auto *rt1 = fd->getType()->getAs()) { + if (const RecordDecl *rd1 = rt1->getDecl()) + if (seen.insert(rd1).second) + stack.push_back({rd1, offset1}); } } } @@ -667,105 +678,84 @@ class IndexDataConsumer : public index::IndexDataConsumer { public: IndexDataConsumer(IndexParam ¶m) : param(param) {} - void initialize(ASTContext &Ctx) override { - this->Ctx = param.Ctx = &Ctx; - SourceManager &SM = Ctx.getSourceManager(); - (void)param.ConsumeFile(*SM.getFileEntryForID(SM.getMainFileID())); - } - bool handleDeclOccurence(const Decl *D, index::SymbolRoleSet Roles, - ArrayRef Relations, -#if LLVM_VERSION_MAJOR >= 7 - SourceLocation Loc, -#else - FileID LocFID, unsigned LocOffset, + void initialize(ASTContext &ctx) override { this->ctx = param.ctx = &ctx; } +#if LLVM_VERSION_MAJOR < 10 // llvmorg-10-init-12036-g3b9715cb219 +# define handleDeclOccurrence handleDeclOccurence #endif - ASTNodeInfo ASTNode) override { - SourceManager &SM = Ctx->getSourceManager(); - const LangOptions &Lang = Ctx->getLangOpts(); -#if LLVM_VERSION_MAJOR < 7 - SourceLocation Loc; - { - const SrcMgr::SLocEntry &Entry = SM.getSLocEntry(LocFID); - unsigned off = Entry.getOffset() + LocOffset; - if (!Entry.isFile()) - off |= 1u << 31; - Loc = SourceLocation::getFromRawEncoding(off); + bool handleDeclOccurrence(const Decl *d, index::SymbolRoleSet roles, + ArrayRef relations, + SourceLocation src_loc, + ASTNodeInfo ast_node) override { + if (!param.no_linkage) { + if (auto *nd = dyn_cast(d); nd && nd->hasLinkage()) + ; + else + return true; } -#else - FileID LocFID; -#endif - SourceLocation Spell = SM.getSpellingLoc(Loc); - const FileEntry *FE; + SourceManager &sm = ctx->getSourceManager(); + const LangOptions &lang = ctx->getLangOpts(); + FileID fid; + SourceLocation spell = sm.getSpellingLoc(src_loc); Range loc; -#if LLVM_VERSION_MAJOR < 7 - CharSourceRange R; - if (SM.isMacroArgExpansion(Loc)) - R = CharSourceRange::getTokenRange(Spell); - else { - auto P = SM.getExpansionRange(Loc); - R = CharSourceRange::getTokenRange(P.first, P.second); - } -#else - auto R = SM.isMacroArgExpansion(Loc) ? CharSourceRange::getTokenRange(Spell) - : SM.getExpansionRange(Loc); -#endif - loc = FromCharSourceRange(SM, Lang, R); - LocFID = SM.getFileID(R.getBegin()); - FE = SM.getFileEntryForID(LocFID); - if (!FE) + auto r = sm.isMacroArgExpansion(src_loc) + ? CharSourceRange::getTokenRange(spell) + : sm.getExpansionRange(src_loc); + loc = fromCharSourceRange(sm, lang, r); + fid = sm.getFileID(r.getBegin()); + if (fid.isInvalid()) return true; int lid = -1; IndexFile *db; - if (g_config->index.multiVersion && param.UseMultiVersion(*FE)) { - db = param.ConsumeFile(*SM.getFileEntryForID(SM.getMainFileID())); + if (g_config->index.multiVersion && param.useMultiVersion(fid)) { + db = param.consumeFile(sm.getMainFileID()); if (!db) return true; - param.SeenFile(*FE); - if (!SM.isWrittenInMainFile(R.getBegin())) - lid = GetFileLID(db, SM, *FE); + param.seenFile(fid); + if (!sm.isWrittenInMainFile(r.getBegin())) + lid = getFileLID(db, sm, fid); } else { - db = param.ConsumeFile(*FE); + db = param.consumeFile(fid); if (!db) return true; } // spell, extent, comments use OrigD while most others use adjusted |D|. - const Decl *OrigD = ASTNode.OrigD; - const DeclContext *SemDC = OrigD->getDeclContext()->getRedeclContext(); - const DeclContext *LexDC = ASTNode.ContainerDC->getRedeclContext(); + const Decl *origD = ast_node.OrigD; + const DeclContext *sem_dc = origD->getDeclContext()->getRedeclContext(); + const DeclContext *lex_dc = ast_node.ContainerDC->getRedeclContext(); { - const NamespaceDecl *ND; - while ((ND = dyn_cast(cast(SemDC))) && - ND->isAnonymousNamespace()) - SemDC = ND->getDeclContext()->getRedeclContext(); - while ((ND = dyn_cast(cast(LexDC))) && - ND->isAnonymousNamespace()) - LexDC = ND->getDeclContext()->getRedeclContext(); + const NamespaceDecl *nd; + while ((nd = dyn_cast(cast(sem_dc))) && + nd->isAnonymousNamespace()) + sem_dc = nd->getDeclContext()->getRedeclContext(); + while ((nd = dyn_cast(cast(lex_dc))) && + nd->isAnonymousNamespace()) + lex_dc = nd->getDeclContext()->getRedeclContext(); } - Role role = static_cast(Roles); - db->language = LanguageId((int)db->language | (int)GetDeclLanguage(D)); + Role role = static_cast(roles); + db->language = LanguageId((int)db->language | (int)getDeclLanguage(d)); - bool is_decl = Roles & uint32_t(index::SymbolRole::Declaration); - bool is_def = Roles & uint32_t(index::SymbolRole::Definition); - if (is_decl && D->getKind() == Decl::Binding) + bool is_decl = roles & uint32_t(index::SymbolRole::Declaration); + bool is_def = roles & uint32_t(index::SymbolRole::Definition); + if (is_decl && d->getKind() == Decl::Binding) is_def = true; IndexFunc *func = nullptr; IndexType *type = nullptr; IndexVar *var = nullptr; - SymbolKind ls_kind; - Kind kind = GetKind(D, ls_kind); + SymbolKind ls_kind = SymbolKind::Unknown; + Kind kind = getKind(d, ls_kind); if (is_def) - switch (D->getKind()) { + switch (d->getKind()) { case Decl::CXXConversion: // *operator* int => *operator int* case Decl::CXXDestructor: // *~*A => *~A* - case Decl::CXXMethod: // *operator*= => *operator=* - case Decl::Function: // operator delete - if (Loc.isFileID()) { - SourceRange R = - cast(OrigD)->getNameInfo().getSourceRange(); - if (R.getEnd().isFileID()) - loc = FromTokenRange(SM, Lang, R); + case Decl::CXXMethod: // *operator*= => *operator=* + case Decl::Function: // operator delete + if (src_loc.isFileID()) { + SourceRange sr = + cast(origD)->getNameInfo().getSourceRange(); + if (sr.getEnd().isFileID()) + loc = fromTokenRange(sm, lang, sr); } break; default: @@ -773,117 +763,126 @@ class IndexDataConsumer : public index::IndexDataConsumer { } else { // e.g. typedef Foo gg; => Foo has an unadjusted `D` - const Decl *D1 = GetAdjustedDecl(D); - if (D1 && D1 != D) - D = D1; + const Decl *d1 = getAdjustedDecl(d); + if (d1 && d1 != d) + d = d1; } IndexParam::DeclInfo *info; - Usr usr = GetUsr(D, &info); + Usr usr = getUsr(d, &info); auto do_def_decl = [&](auto *entity) { Use use{{loc, role}, lid}; if (is_def) { - SourceRange R = OrigD->getSourceRange(); + SourceRange sr = origD->getSourceRange(); entity->def.spell = {use, - FromTokenRangeDefaulted(SM, Lang, R, FE, loc)}; + fromTokenRangeDefaulted(sm, lang, sr, fid, loc)}; entity->def.parent_kind = SymbolKind::File; - GetKind(cast(SemDC), entity->def.parent_kind); + getKind(cast(sem_dc), entity->def.parent_kind); } else if (is_decl) { - SourceRange R = OrigD->getSourceRange(); + SourceRange sr = origD->getSourceRange(); entity->declarations.push_back( - {use, FromTokenRangeDefaulted(SM, Lang, R, FE, loc)}); + {use, fromTokenRangeDefaulted(sm, lang, sr, fid, loc)}); } else { entity->uses.push_back(use); return; } if (entity->def.comments[0] == '\0' && g_config->index.comments) - entity->def.comments = Intern(GetComment(OrigD)); + entity->def.comments = intern(getComment(origD)); }; switch (kind) { case Kind::Invalid: - LOG_S(INFO) << "Unhandled " << int(D->getKind()) << " " << info->qualified - << " in " << db->path << ":" << loc.start.line + 1; + if (ls_kind == SymbolKind::Unknown) + LOG_S(INFO) << "Unhandled " << int(d->getKind()) << " " + << info->qualified << " in " << db->path << ":" + << (loc.start.line + 1) << ":" << (loc.start.column + 1); return true; case Kind::File: return true; case Kind::Func: - func = &db->ToFunc(usr); + func = &db->toFunc(usr); func->def.kind = ls_kind; // Mark as Role::Implicit to span one more column to the left/right. if (!is_def && !is_decl && - (D->getKind() == Decl::CXXConstructor || - D->getKind() == Decl::CXXConversion)) + (d->getKind() == Decl::CXXConstructor || + d->getKind() == Decl::CXXConversion)) role = Role(role | Role::Implicit); do_def_decl(func); - if (Spell != Loc) - AddMacroUse(db, SM, usr, Kind::Func, Spell); + if (spell != src_loc) + addMacroUse(db, sm, usr, Kind::Func, spell); if (func->def.detailed_name[0] == '\0') - SetName(D, info->short_name, info->qualified, func->def); + setName(d, info->short_name, info->qualified, func->def); if (is_def || is_decl) { - const Decl *DC = cast(SemDC); - if (GetKind(DC, ls_kind) == Kind::Type) - db->ToType(GetUsr(DC)).def.funcs.push_back(usr); + const Decl *dc = cast(sem_dc); + if (getKind(dc, ls_kind) == Kind::Type) + db->toType(getUsr(dc)).def.funcs.push_back(usr); } else { - const Decl *DC = cast(LexDC); - if (GetKind(DC, ls_kind) == Kind::Func) - db->ToFunc(GetUsr(DC)) + const Decl *dc = cast(lex_dc); + if (getKind(dc, ls_kind) == Kind::Func) + db->toFunc(getUsr(dc)) .def.callees.push_back({loc, usr, Kind::Func, role}); } break; case Kind::Type: - type = &db->ToType(usr); + type = &db->toType(usr); type->def.kind = ls_kind; do_def_decl(type); - if (Spell != Loc) - AddMacroUse(db, SM, usr, Kind::Type, Spell); - if (type->def.detailed_name[0] == '\0' && info->short_name.size()) - SetName(D, info->short_name, info->qualified, type->def); + if (spell != src_loc) + addMacroUse(db, sm, usr, Kind::Type, spell); + if ((is_def || type->def.detailed_name[0] == '\0') && + info->short_name.size()) { + if (d->getKind() == Decl::TemplateTypeParm) + type->def.detailed_name = intern(info->short_name); + else + // OrigD may be detailed, e.g. "struct D : B {}" + setName(origD, info->short_name, info->qualified, type->def); + } if (is_def || is_decl) { - const Decl *DC = cast(SemDC); - if (GetKind(DC, ls_kind) == Kind::Type) - db->ToType(GetUsr(DC)).def.types.push_back(usr); + const Decl *dc = cast(sem_dc); + if (getKind(dc, ls_kind) == Kind::Type) + db->toType(getUsr(dc)).def.types.push_back(usr); } break; case Kind::Var: - var = &db->ToVar(usr); + var = &db->toVar(usr); var->def.kind = ls_kind; do_def_decl(var); - if (Spell != Loc) - AddMacroUse(db, SM, usr, Kind::Var, Spell); + if (spell != src_loc) + addMacroUse(db, sm, usr, Kind::Var, spell); if (var->def.detailed_name[0] == '\0') - SetVarName(D, info->short_name, info->qualified, var->def); - QualType T; - if (auto *VD = dyn_cast(D)) - T = VD->getType(); + setVarName(d, info->short_name, info->qualified, var->def); + QualType t; + if (auto *vd = dyn_cast(d)) + t = vd->getType(); if (is_def || is_decl) { - const Decl *DC = cast(SemDC); - Kind kind = GetKind(DC, var->def.parent_kind); + const Decl *dc = cast(sem_dc); + Kind kind = getKind(dc, var->def.parent_kind); if (kind == Kind::Func) - db->ToFunc(GetUsr(DC)).def.vars.push_back(usr); - else if (kind == Kind::Type && !isa(SemDC)) - db->ToType(GetUsr(DC)).def.vars.emplace_back(usr, -1); - if (!T.isNull()) { - if (auto *BT = T->getAs()) { - Usr usr1 = static_cast(BT->getKind()); + db->toFunc(getUsr(dc)).def.vars.push_back(usr); + else if (kind == Kind::Type && !isa(sem_dc)) + db->toType(getUsr(dc)).def.vars.emplace_back(usr, -1); + if (!t.isNull()) { + if (auto *bt = t->getAs()) { + Usr usr1 = static_cast(bt->getKind()); var->def.type = usr1; - if (!isa(D)) - db->ToType(usr1).instances.push_back(usr); - } else if (const Decl *D1 = GetAdjustedDecl(GetTypeDecl(T))) { - if (isa(D1)) { + if (!isa(d)) + db->toType(usr1).instances.push_back(usr); + } else if (const Decl *d1 = getAdjustedDecl(getTypeDecl(t))) { +#if LLVM_VERSION_MAJOR < 9 + if (isa(d1)) { // e.g. TemplateTypeParmDecl is not handled by // handleDeclOccurence. - SourceRange R1 = D1->getSourceRange(); - if (SM.getFileID(R1.getBegin()) == LocFID) { + SourceRange sr1 = d1->getSourceRange(); + if (sm.getFileID(sr1.getBegin()) == fid) { IndexParam::DeclInfo *info1; - Usr usr1 = GetUsr(D1, &info1); - IndexType &type1 = db->ToType(usr1); - SourceLocation L1 = D1->getLocation(); + Usr usr1 = getUsr(d1, &info1); + IndexType &type1 = db->toType(usr1); + SourceLocation sl1 = d1->getLocation(); type1.def.spell = { - Use{{FromTokenRange(SM, Lang, {L1, L1}), Role::Definition}, + Use{{fromTokenRange(sm, lang, {sl1, sl1}), Role::Definition}, lid}, - FromTokenRange(SM, Lang, R1)}; - type1.def.detailed_name = Intern(info1->short_name); + fromTokenRange(sm, lang, sr1)}; + type1.def.detailed_name = intern(info1->short_name); type1.def.short_name_size = int16_t(info1->short_name.size()); type1.def.kind = SymbolKind::TypeParameter; type1.def.parent_kind = SymbolKind::Class; @@ -892,113 +891,125 @@ class IndexDataConsumer : public index::IndexDataConsumer { break; } } +#endif IndexParam::DeclInfo *info1; - Usr usr1 = GetUsr(D1, &info1); + Usr usr1 = getUsr(d1, &info1); var->def.type = usr1; - if (!isa(D)) - db->ToType(usr1).instances.push_back(usr); + if (!isa(d)) + db->toType(usr1).instances.push_back(usr); } } } else if (!var->def.spell && var->declarations.empty()) { // e.g. lambda parameter - SourceLocation L = D->getLocation(); - if (SM.getFileID(L) == LocFID) { + SourceLocation l = d->getLocation(); + if (sm.getFileID(l) == fid) { var->def.spell = { - Use{{FromTokenRange(SM, Lang, {L, L}), Role::Definition}, lid}, - FromTokenRange(SM, Lang, D->getSourceRange())}; + Use{{fromTokenRange(sm, lang, {l, l}), Role::Definition}, lid}, + fromTokenRange(sm, lang, d->getSourceRange())}; var->def.parent_kind = SymbolKind::Method; } } break; } - switch (D->getKind()) { + switch (d->getKind()) { case Decl::Namespace: - if (D->isFirstDecl()) { - auto *ND = cast(D); - auto *ND1 = cast(ND->getParent()); - if (isa(ND1)) { - Usr usr1 = GetUsr(ND1); + if (d->isFirstDecl()) { + auto *nd = cast(d); + auto *nd1 = cast(nd->getParent()); + if (isa(nd1)) { + Usr usr1 = getUsr(nd1); type->def.bases.push_back(usr1); - db->ToType(usr1).derived.push_back(usr); + db->toType(usr1).derived.push_back(usr); } } break; case Decl::NamespaceAlias: { - auto *NAD = cast(D); - if (const NamespaceDecl *ND = NAD->getNamespace()) { - Usr usr1 = GetUsr(ND); + auto *nad = cast(d); + if (const NamespaceDecl *nd = nad->getNamespace()) { + Usr usr1 = getUsr(nd); type->def.alias_of = usr1; - (void)db->ToType(usr1); + (void)db->toType(usr1); } break; } case Decl::CXXRecord: if (is_def) { - auto *RD = dyn_cast(D); - if (RD && RD->hasDefinition()) - for (const CXXBaseSpecifier &Base : RD->bases()) - if (const Decl *BaseD = - GetAdjustedDecl(GetTypeDecl(Base.getType()))) { - Usr usr1 = GetUsr(BaseD); + auto *rd = dyn_cast(d); + if (rd && rd->hasDefinition()) + for (const CXXBaseSpecifier &base : rd->bases()) + if (const Decl *baseD = + getAdjustedDecl(getTypeDecl(base.getType()))) { + Usr usr1 = getUsr(baseD); type->def.bases.push_back(usr1); - db->ToType(usr1).derived.push_back(usr); + db->toType(usr1).derived.push_back(usr); } } [[fallthrough]]; + case Decl::Enum: case Decl::Record: - if (auto *RD = dyn_cast(D)) { + if (auto *tag_d = dyn_cast(d)) { if (type->def.detailed_name[0] == '\0' && info->short_name.empty()) { - StringRef Tag; - switch (RD->getTagKind()) { - case TTK_Struct: Tag = "struct"; break; - case TTK_Interface: Tag = "__interface"; break; - case TTK_Union: Tag = "union"; break; - case TTK_Class: Tag = "class"; break; - case TTK_Enum: Tag = "enum"; break; + StringRef tag; + switch (tag_d->getTagKind()) { + case TTK_Struct: + tag = "struct"; + break; + case TTK_Interface: + tag = "__interface"; + break; + case TTK_Union: + tag = "union"; + break; + case TTK_Class: + tag = "class"; + break; + case TTK_Enum: + tag = "enum"; + break; } - if (TypedefNameDecl *TD = RD->getTypedefNameForAnonDecl()) { - StringRef Name = TD->getName(); - std::string name = ("anon " + Tag + " " + Name).str(); - type->def.detailed_name = Intern(name); - type->def.short_name_size = name.size(); + if (TypedefNameDecl *td = tag_d->getTypedefNameForAnonDecl()) { + StringRef name = td->getName(); + std::string detailed = ("anon " + tag + " " + name).str(); + type->def.detailed_name = intern(detailed); + type->def.short_name_size = detailed.size(); } else { - std::string name = ("anon " + Tag).str(); - type->def.detailed_name = Intern(name); + std::string name = ("anon " + tag).str(); + type->def.detailed_name = intern(name); type->def.short_name_size = name.size(); } } - if (is_def) - if (auto *ORD = dyn_cast(OrigD)) - CollectRecordMembers(*type, ORD); + if (is_def && !isa(d)) + if (auto *ord = dyn_cast(origD)) + collectRecordMembers(*type, ord); } break; case Decl::ClassTemplateSpecialization: case Decl::ClassTemplatePartialSpecialization: type->def.kind = SymbolKind::Class; if (is_def) { - if (auto *ORD = dyn_cast(OrigD)) - CollectRecordMembers(*type, ORD); - if (auto *RD = dyn_cast(D)) { - Decl *D1 = nullptr; - if (auto *SD = dyn_cast(RD)) - D1 = SD->getSpecializedTemplate(); - else if (auto *SD = dyn_cast(RD)) { + if (auto *ord = dyn_cast(origD)) + collectRecordMembers(*type, ord); + if (auto *rd = dyn_cast(d)) { + Decl *d1 = nullptr; + if (auto *sd = dyn_cast(rd)) + d1 = sd->getSpecializedTemplate(); + else if (auto *sd = dyn_cast(rd)) { llvm::PointerUnion - Result = SD->getSpecializedTemplateOrPartial(); - if (Result.is()) - D1 = Result.get(); + result = sd->getSpecializedTemplateOrPartial(); + if (result.is()) + d1 = result.get(); else - D1 = Result.get(); + d1 = result.get(); } else - D1 = RD->getInstantiatedFromMemberClass(); - if (D1) { - Usr usr1 = GetUsr(D1); + d1 = rd->getInstantiatedFromMemberClass(); + if (d1) { + Usr usr1 = getUsr(d1); type->def.bases.push_back(usr1); - db->ToType(usr1).derived.push_back(usr); + db->toType(usr1).derived.push_back(usr); } } } @@ -1006,45 +1017,45 @@ class IndexDataConsumer : public index::IndexDataConsumer { case Decl::TypeAlias: case Decl::Typedef: case Decl::UnresolvedUsingTypename: - if (auto *TD = dyn_cast(D)) { + if (auto *td = dyn_cast(d)) { bool specialization = false; - QualType T = TD->getUnderlyingType(); - if (const Decl *D1 = GetAdjustedDecl(GetTypeDecl(T, &specialization))) { - Usr usr1 = GetUsr(D1); - IndexType &type1 = db->ToType(usr1); + QualType t = td->getUnderlyingType(); + if (const Decl *d1 = getAdjustedDecl(getTypeDecl(t, &specialization))) { + Usr usr1 = getUsr(d1); + IndexType &type1 = db->toType(usr1); type->def.alias_of = usr1; // Not visited template struct B {typedef A t;}; if (specialization) { - const TypeSourceInfo *TSI = TD->getTypeSourceInfo(); - SourceLocation L1 = TSI->getTypeLoc().getBeginLoc(); - if (SM.getFileID(L1) == LocFID) + const TypeSourceInfo *tsi = td->getTypeSourceInfo(); + SourceLocation l1 = tsi->getTypeLoc().getBeginLoc(); + if (sm.getFileID(l1) == fid) type1.uses.push_back( - {{FromTokenRange(SM, Lang, {L1, L1}), Role::Reference}, lid}); + {{fromTokenRange(sm, lang, {l1, l1}), Role::Reference}, lid}); } } } break; case Decl::CXXMethod: if (is_def || is_decl) { - if (auto *ND = dyn_cast(D)) { - SmallVector OverDecls; - Ctx->getOverriddenMethods(ND, OverDecls); - for (const auto *ND1 : OverDecls) { - Usr usr1 = GetUsr(ND1); + if (auto *nd = dyn_cast(d)) { + SmallVector overDecls; + ctx->getOverriddenMethods(nd, overDecls); + for (const auto *nd1 : overDecls) { + Usr usr1 = getUsr(nd1); func->def.bases.push_back(usr1); - db->ToFunc(usr1).derived.push_back(usr); + db->toFunc(usr1).derived.push_back(usr); } } } break; case Decl::EnumConstant: if (is_def && strchr(var->def.detailed_name, '=') == nullptr) { - auto *ECD = cast(D); - const auto &Val = ECD->getInitVal(); + auto *ecd = cast(d); + const auto &val = ecd->getInitVal(); std::string init = - " = " + (Val.isSigned() ? std::to_string(Val.getSExtValue()) - : std::to_string(Val.getZExtValue())); - var->def.hover = Intern(var->def.detailed_name + init); + " = " + (val.isSigned() ? std::to_string(val.getSExtValue()) + : std::to_string(val.getZExtValue())); + var->def.hover = intern(var->def.detailed_name + init); } break; default: @@ -1055,152 +1066,171 @@ class IndexDataConsumer : public index::IndexDataConsumer { }; class IndexPPCallbacks : public PPCallbacks { - SourceManager &SM; + SourceManager &sm; IndexParam ¶m; - std::pair GetMacro(const Token &Tok) const { - StringRef Name = Tok.getIdentifierInfo()->getName(); - SmallString<256> USR("@macro@"); - USR += Name; - return {Name, HashUsr(USR)}; + std::pair getMacro(const Token &tok) const { + StringRef name = tok.getIdentifierInfo()->getName(); + SmallString<256> usr("@macro@"); + usr += name; + return {name, hashUsr(usr)}; } public: - IndexPPCallbacks(SourceManager &SM, IndexParam ¶m) - : SM(SM), param(param) {} - void InclusionDirective(SourceLocation HashLoc, const Token &Tok, - StringRef Included, bool IsAngled, - CharSourceRange FilenameRange, const FileEntry *File, - StringRef SearchPath, StringRef RelativePath, - const Module *Imported -#if LLVM_VERSION_MAJOR >= 7 - , - SrcMgr::CharacteristicKind FileType -#endif - ) override { - if (!File) - return; - llvm::sys::fs::UniqueID UniqueID; - auto spell = FromCharSourceRange(SM, param.Ctx->getLangOpts(), - FilenameRange, &UniqueID); - const FileEntry *FE = - SM.getFileEntryForID(SM.getFileID(FilenameRange.getBegin())); - if (!FE) + IndexPPCallbacks(SourceManager &sm, IndexParam ¶m) + : sm(sm), param(param) {} + void FileChanged(SourceLocation sl, FileChangeReason reason, + SrcMgr::CharacteristicKind, FileID) override { + if (reason == FileChangeReason::EnterFile) + (void)param.consumeFile(sm.getFileID(sl)); + } + void InclusionDirective(SourceLocation hashLoc, const Token &tok, + StringRef included, bool isAngled, + CharSourceRange filenameRange, const FileEntry *file, + StringRef searchPath, StringRef relativePath, + const Module *imported, + SrcMgr::CharacteristicKind fileType) override { + if (!file) return; - if (IndexFile *db = param.ConsumeFile(*FE)) { - std::string path = PathFromFileEntry(*File); + auto spell = fromCharSourceRange(sm, param.ctx->getLangOpts(), + filenameRange, nullptr); + FileID fid = sm.getFileID(filenameRange.getBegin()); + if (IndexFile *db = param.consumeFile(fid)) { + std::string path = pathFromFileEntry(*file); if (path.size()) - db->includes.push_back({spell.start.line, Intern(path)}); + db->includes.push_back({spell.start.line, intern(path)}); } } - void MacroDefined(const Token &Tok, const MacroDirective *MD) override { - llvm::sys::fs::UniqueID UniqueID; - const LangOptions &Lang = param.Ctx->getLangOpts(); - SourceLocation L = MD->getLocation(); - const FileEntry *FE = SM.getFileEntryForID(SM.getFileID(L)); - if (!FE) - return; - if (IndexFile *db = param.ConsumeFile(*FE)) { - auto [Name, usr] = GetMacro(Tok); - IndexVar &var = db->ToVar(usr); - Range range = FromTokenRange(SM, Lang, {L, L}, &UniqueID); + void MacroDefined(const Token &tok, const MacroDirective *md) override { + const LangOptions &lang = param.ctx->getLangOpts(); + SourceLocation sl = md->getLocation(); + FileID fid = sm.getFileID(sl); + if (IndexFile *db = param.consumeFile(fid)) { + auto [name, usr] = getMacro(tok); + IndexVar &var = db->toVar(usr); + Range range = fromTokenRange(sm, lang, {sl, sl}, nullptr); var.def.kind = SymbolKind::Macro; var.def.parent_kind = SymbolKind::File; if (var.def.spell) var.declarations.push_back(*var.def.spell); - const MacroInfo *MI = MD->getMacroInfo(); - SourceRange R(MI->getDefinitionLoc(), MI->getDefinitionEndLoc()); - Range extent = FromTokenRange(SM, param.Ctx->getLangOpts(), R); + const MacroInfo *mi = md->getMacroInfo(); + SourceRange sr(mi->getDefinitionLoc(), mi->getDefinitionEndLoc()); + Range extent = fromTokenRange(sm, param.ctx->getLangOpts(), sr); var.def.spell = {Use{{range, Role::Definition}}, extent}; if (var.def.detailed_name[0] == '\0') { - var.def.detailed_name = Intern(Name); - var.def.short_name_size = Name.size(); - StringRef Buf = GetSourceInRange(SM, Lang, R); + var.def.detailed_name = intern(name); + var.def.short_name_size = name.size(); + StringRef buf = getSourceInRange(sm, lang, sr); var.def.hover = - Intern(Buf.count('\n') <= kInitializerMaxLines - 1 - ? Twine("#define ", GetSourceInRange(SM, Lang, R)).str() - : Twine("#define ", Name).str()); + intern(buf.count('\n') <= g_config->index.maxInitializerLines - 1 + ? Twine("#define ", getSourceInRange(sm, lang, sr)).str() + : Twine("#define ", name).str()); } } } - void MacroExpands(const Token &Tok, const MacroDefinition &MD, SourceRange R, - const MacroArgs *Args) override { - llvm::sys::fs::UniqueID UniqueID; - SourceLocation L = SM.getSpellingLoc(R.getBegin()); - const FileEntry *FE = SM.getFileEntryForID(SM.getFileID(L)); - if (!FE) - return; - if (IndexFile *db = param.ConsumeFile(*FE)) { - IndexVar &var = db->ToVar(GetMacro(Tok).second); + void MacroExpands(const Token &tok, const MacroDefinition &, SourceRange sr, + const MacroArgs *) override { + SourceLocation sl = sm.getSpellingLoc(sr.getBegin()); + FileID fid = sm.getFileID(sl); + if (IndexFile *db = param.consumeFile(fid)) { + IndexVar &var = db->toVar(getMacro(tok).second); var.uses.push_back( - {{FromTokenRange(SM, param.Ctx->getLangOpts(), {L, L}, &UniqueID), + {{fromTokenRange(sm, param.ctx->getLangOpts(), {sl, sl}, nullptr), Role::Dynamic}}); } } - void MacroUndefined(const Token &Tok, const MacroDefinition &MD, - const MacroDirective *UD) override { - if (UD) { - SourceLocation L = UD->getLocation(); - MacroExpands(Tok, MD, {L, L}, nullptr); + void MacroUndefined(const Token &tok, const MacroDefinition &md, + const MacroDirective *ud) override { + if (ud) { + SourceLocation sl = ud->getLocation(); + MacroExpands(tok, md, {sl, sl}, nullptr); } } - void SourceRangeSkipped(SourceRange Range, SourceLocation EndifLoc) override { - llvm::sys::fs::UniqueID UniqueID; - auto range = FromCharRange(SM, param.Ctx->getLangOpts(), Range, &UniqueID); - if (const FileEntry *FE = - SM.getFileEntryForID(SM.getFileID(Range.getBegin()))) - if (IndexFile *db = param.ConsumeFile(*FE)) + void SourceRangeSkipped(SourceRange sr, SourceLocation) override { + Range range = fromCharSourceRange(sm, param.ctx->getLangOpts(), + CharSourceRange::getCharRange(sr)); + FileID fid = sm.getFileID(sr.getBegin()); + if (fid.isValid()) + if (IndexFile *db = param.consumeFile(fid)) db->skipped_ranges.push_back(range); } }; class IndexFrontendAction : public ASTFrontendAction { + std::shared_ptr dataConsumer; + const index::IndexingOptions &indexOpts; IndexParam ¶m; public: - IndexFrontendAction(IndexParam ¶m) : param(param) {} - std::unique_ptr CreateASTConsumer(CompilerInstance &CI, - StringRef InFile) override { - Preprocessor &PP = CI.getPreprocessor(); - PP.addPPCallbacks( - std::make_unique(PP.getSourceManager(), param)); - return std::make_unique(); + IndexFrontendAction(std::shared_ptr dataConsumer, + const index::IndexingOptions &indexOpts, + IndexParam ¶m) + : dataConsumer(std::move(dataConsumer)), indexOpts(indexOpts), + param(param) {} + std::unique_ptr CreateASTConsumer(CompilerInstance &ci, + StringRef inFile) override { + class SkipProcessed : public ASTConsumer { + IndexParam ¶m; + const ASTContext *ctx = nullptr; + + public: + SkipProcessed(IndexParam ¶m) : param(param) {} + void Initialize(ASTContext &ctx) override { this->ctx = &ctx; } + bool shouldSkipFunctionBody(Decl *d) override { + const SourceManager &sm = ctx->getSourceManager(); + FileID fid = sm.getFileID(sm.getExpansionLoc(d->getLocation())); + return !(g_config->index.multiVersion && param.useMultiVersion(fid)) && + !param.consumeFile(fid); + } + }; + + std::shared_ptr pp = ci.getPreprocessorPtr(); + pp->addPPCallbacks( + std::make_unique(pp->getSourceManager(), param)); + std::vector> consumers; + consumers.push_back(std::make_unique(param)); +#if LLVM_VERSION_MAJOR >= 10 // rC370337 + consumers.push_back(index::createIndexingASTConsumer( + dataConsumer, indexOpts, std::move(pp))); +#endif + return std::make_unique(std::move(consumers)); } }; } // namespace -const int IndexFile::kMajorVersion = 19; -const int IndexFile::kMinorVersion = 1; +const int IndexFile::kMajorVersion = 21; +const int IndexFile::kMinorVersion = 0; -IndexFile::IndexFile(const std::string &path, const std::string &contents) - : path(path), file_contents(contents) {} +IndexFile::IndexFile(const std::string &path, const std::string &contents, + bool no_linkage) + : path(path), no_linkage(no_linkage), file_contents(contents) {} -IndexFunc &IndexFile::ToFunc(Usr usr) { +IndexFunc &IndexFile::toFunc(Usr usr) { auto [it, inserted] = usr2func.try_emplace(usr); if (inserted) it->second.usr = usr; return it->second; } -IndexType &IndexFile::ToType(Usr usr) { +IndexType &IndexFile::toType(Usr usr) { auto [it, inserted] = usr2type.try_emplace(usr); if (inserted) it->second.usr = usr; return it->second; } -IndexVar &IndexFile::ToVar(Usr usr) { +IndexVar &IndexFile::toVar(Usr usr) { auto [it, inserted] = usr2var.try_emplace(usr); if (inserted) it->second.usr = usr; return it->second; } -std::string IndexFile::ToString() { - return ccls::Serialize(SerializeFormat::Json, *this); +std::string IndexFile::toString() { + return ccls::serialize(SerializeFormat::Json, *this); } -template void Uniquify(std::vector &a) { +template void uniquify(std::vector &a) { std::unordered_set seen; size_t n = 0; for (size_t i = 0; i < a.size(); i++) @@ -1210,117 +1240,149 @@ template void Uniquify(std::vector &a) { } namespace idx { -void Init() { +void init() { multiVersionMatcher = new GroupMatch(g_config->index.multiVersionWhitelist, g_config->index.multiVersionBlacklist); } std::vector> -Index(SemaManager *manager, WorkingFiles *wfiles, VFS *vfs, +index(SemaManager *manager, WorkingFiles *wfiles, VFS *vfs, const std::string &opt_wdir, const std::string &main, const std::vector &args, const std::vector> &remapped, - bool &ok) { + bool no_linkage, bool &ok) { ok = true; - auto PCH = std::make_shared(); - llvm::IntrusiveRefCntPtr FS = llvm::vfs::getRealFileSystem(); - std::shared_ptr CI = - BuildCompilerInvocation(main, args, FS); + auto pch = std::make_shared(); + llvm::IntrusiveRefCntPtr fs = + llvm::vfs::getRealFileSystem(); + std::shared_ptr ci = + buildCompilerInvocation(main, args, fs); // e.g. .s - if (!CI) + if (!ci) return {}; ok = false; // -fparse-all-comments enables documentation in the indexer and in // code completion. - CI->getLangOpts()->CommentOpts.ParseAllComments = + ci->getLangOpts()->CommentOpts.ParseAllComments = g_config->index.comments > 1; - CI->getLangOpts()->RetainCommentsFromSystemHeaders = true; - std::string buf = wfiles->GetContent(main); - std::vector> Bufs; + ci->getLangOpts()->RetainCommentsFromSystemHeaders = true; + std::string buf = wfiles->getContent(main); + std::vector> bufs; if (buf.size()) for (auto &[filename, content] : remapped) { - Bufs.push_back(llvm::MemoryBuffer::getMemBuffer(content)); - CI->getPreprocessorOpts().addRemappedFile(filename, Bufs.back().get()); + bufs.push_back(llvm::MemoryBuffer::getMemBuffer(content)); + ci->getPreprocessorOpts().addRemappedFile(filename, bufs.back().get()); } - DiagnosticConsumer DC; - auto Clang = std::make_unique(PCH); - Clang->setInvocation(std::move(CI)); - Clang->setVirtualFileSystem(FS); - Clang->createDiagnostics(&DC, false); - Clang->setTarget(TargetInfo::CreateTargetInfo( - Clang->getDiagnostics(), Clang->getInvocation().TargetOpts)); - if (!Clang->hasTarget()) + DiagnosticConsumer dc; + auto clang = std::make_unique(pch); + clang->setInvocation(std::move(ci)); + clang->createDiagnostics(&dc, false); + clang->getDiagnostics().setIgnoreAllWarnings(true); + clang->setTarget(TargetInfo::CreateTargetInfo( + clang->getDiagnostics(), clang->getInvocation().TargetOpts)); + if (!clang->hasTarget()) return {}; + clang->getPreprocessorOpts().RetainRemappedFileBuffers = true; +#if LLVM_VERSION_MAJOR >= 9 // rC357037 + clang->createFileManager(fs); +#else + clang->setVirtualFileSystem(fs); + clang->createFileManager(); +#endif + clang->setSourceManager(new SourceManager(clang->getDiagnostics(), + clang->getFileManager(), true)); - IndexParam param(*vfs); - auto DataConsumer = std::make_shared(param); + IndexParam param(*vfs, no_linkage); - index::IndexingOptions IndexOpts; - IndexOpts.SystemSymbolFilter = + index::IndexingOptions indexOpts; + indexOpts.SystemSymbolFilter = index::IndexingOptions::SystemSymbolFilterKind::All; - IndexOpts.IndexFunctionLocals = true; -#if LLVM_VERSION_MAJOR >= 7 - IndexOpts.IndexImplicitInstantiation = true; + if (no_linkage) { + indexOpts.IndexFunctionLocals = true; + indexOpts.IndexImplicitInstantiation = true; +#if LLVM_VERSION_MAJOR >= 9 + + indexOpts.IndexParametersInDeclarations = + g_config->index.parametersInDeclarations; + indexOpts.IndexTemplateParameters = true; #endif + } - std::unique_ptr Action = createIndexingAction( - DataConsumer, IndexOpts, std::make_unique(param)); +#if LLVM_VERSION_MAJOR >= 10 // rC370337 + auto action = std::make_unique( + std::make_shared(param), indexOpts, param); +#else + auto dataConsumer = std::make_shared(param); + auto action = createIndexingAction( + dataConsumer, indexOpts, + std::make_unique(dataConsumer, indexOpts, param)); +#endif + std::string reason; { - llvm::CrashRecoveryContext CRC; + llvm::CrashRecoveryContext crc; auto parse = [&]() { - if (!Action->BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0])) + if (!action->BeginSourceFile(*clang, clang->getFrontendOpts().Inputs[0])) + return; +#if LLVM_VERSION_MAJOR >= 9 // rL364464 + if (llvm::Error e = action->Execute()) { + reason = llvm::toString(std::move(e)); return; - if (!Action->Execute()) + } +#else + if (!action->Execute()) return; - Action->EndSourceFile(); +#endif + action->EndSourceFile(); ok = true; }; - if (!CRC.RunSafely(parse)) { + if (!crc.RunSafely(parse)) { LOG_S(ERROR) << "clang crashed for " << main; return {}; } } if (!ok) { - LOG_S(ERROR) << "failed to index " << main; + LOG_S(ERROR) << "failed to index " << main + << (reason.empty() ? "" : ": " + reason); return {}; } - for (auto &Buf : Bufs) - Buf.release(); std::vector> result; - for (auto &it : param.UID2File) { + for (auto &it : param.uid2file) { if (!it.second.db) continue; std::unique_ptr &entry = it.second.db; entry->import_file = main; entry->args = args; for (auto &[_, it] : entry->uid2lid_and_path) - entry->lid2path.emplace_back(it.first, std::move(it.second)); + if (it.first >= 0) + entry->lid2path.emplace_back(it.first, std::move(it.second)); entry->uid2lid_and_path.clear(); for (auto &it : entry->usr2func) { // e.g. declaration + out-of-line definition - Uniquify(it.second.derived); - Uniquify(it.second.uses); + uniquify(it.second.derived); + uniquify(it.second.uses); } for (auto &it : entry->usr2type) { - Uniquify(it.second.derived); - Uniquify(it.second.uses); + uniquify(it.second.derived); + uniquify(it.second.uses); // e.g. declaration + out-of-line definition - Uniquify(it.second.def.bases); - Uniquify(it.second.def.funcs); + uniquify(it.second.def.bases); + uniquify(it.second.def.funcs); } for (auto &it : entry->usr2var) - Uniquify(it.second.uses); + uniquify(it.second.uses); // Update dependencies for the file. - for (auto &[_, file] : param.UID2File) { + for (auto &[_, file] : param.uid2file) { const std::string &path = file.path; + if (path.empty()) + continue; if (path == entry->path) entry->mtime = file.mtime; else if (path != entry->import_file) - entry->dependencies[llvm::CachedHashStringRef(Intern(path))] = + entry->dependencies[llvm::CachedHashStringRef(intern(path))] = file.mtime; } result.push_back(std::move(entry)); @@ -1330,85 +1392,85 @@ Index(SemaManager *manager, WorkingFiles *wfiles, VFS *vfs, } } // namespace idx -void Reflect(JsonReader &vis, SymbolRef &v) { - std::string t = vis.GetString(); +void reflect(JsonReader &vis, SymbolRef &v) { + std::string t = vis.getString(); char *s = const_cast(t.c_str()); - v.range = Range::FromString(s); + v.range = Range::fromString(s); s = strchr(s, '|'); v.usr = strtoull(s + 1, &s, 10); v.kind = static_cast(strtol(s + 1, &s, 10)); v.role = static_cast(strtol(s + 1, &s, 10)); } -void Reflect(JsonReader &vis, Use &v) { - std::string t = vis.GetString(); +void reflect(JsonReader &vis, Use &v) { + std::string t = vis.getString(); char *s = const_cast(t.c_str()); - v.range = Range::FromString(s); + v.range = Range::fromString(s); s = strchr(s, '|'); v.role = static_cast(strtol(s + 1, &s, 10)); v.file_id = static_cast(strtol(s + 1, &s, 10)); } -void Reflect(JsonReader &vis, DeclRef &v) { - std::string t = vis.GetString(); +void reflect(JsonReader &vis, DeclRef &v) { + std::string t = vis.getString(); char *s = const_cast(t.c_str()); - v.range = Range::FromString(s); + v.range = Range::fromString(s); s = strchr(s, '|') + 1; - v.extent = Range::FromString(s); + v.extent = Range::fromString(s); s = strchr(s, '|'); v.role = static_cast(strtol(s + 1, &s, 10)); v.file_id = static_cast(strtol(s + 1, &s, 10)); } -void Reflect(JsonWriter &vis, SymbolRef &v) { +void reflect(JsonWriter &vis, SymbolRef &v) { char buf[99]; - snprintf(buf, sizeof buf, "%s|%" PRIu64 "|%d|%d", v.range.ToString().c_str(), + snprintf(buf, sizeof buf, "%s|%" PRIu64 "|%d|%d", v.range.toString().c_str(), v.usr, int(v.kind), int(v.role)); std::string s(buf); - Reflect(vis, s); + reflect(vis, s); } -void Reflect(JsonWriter &vis, Use &v) { +void reflect(JsonWriter &vis, Use &v) { char buf[99]; - snprintf(buf, sizeof buf, "%s|%d|%d", v.range.ToString().c_str(), int(v.role), + snprintf(buf, sizeof buf, "%s|%d|%d", v.range.toString().c_str(), int(v.role), v.file_id); std::string s(buf); - Reflect(vis, s); + reflect(vis, s); } -void Reflect(JsonWriter &vis, DeclRef &v) { +void reflect(JsonWriter &vis, DeclRef &v) { char buf[99]; - snprintf(buf, sizeof buf, "%s|%s|%d|%d", v.range.ToString().c_str(), - v.extent.ToString().c_str(), int(v.role), v.file_id); + snprintf(buf, sizeof buf, "%s|%s|%d|%d", v.range.toString().c_str(), + v.extent.toString().c_str(), int(v.role), v.file_id); std::string s(buf); - Reflect(vis, s); + reflect(vis, s); } -void Reflect(BinaryReader &vis, SymbolRef &v) { - Reflect(vis, v.range); - Reflect(vis, v.usr); - Reflect(vis, v.kind); - Reflect(vis, v.role); +void reflect(BinaryReader &vis, SymbolRef &v) { + reflect(vis, v.range); + reflect(vis, v.usr); + reflect(vis, v.kind); + reflect(vis, v.role); } -void Reflect(BinaryReader &vis, Use &v) { - Reflect(vis, v.range); - Reflect(vis, v.role); - Reflect(vis, v.file_id); +void reflect(BinaryReader &vis, Use &v) { + reflect(vis, v.range); + reflect(vis, v.role); + reflect(vis, v.file_id); } -void Reflect(BinaryReader &vis, DeclRef &v) { - Reflect(vis, static_cast(v)); - Reflect(vis, v.extent); +void reflect(BinaryReader &vis, DeclRef &v) { + reflect(vis, static_cast(v)); + reflect(vis, v.extent); } -void Reflect(BinaryWriter &vis, SymbolRef &v) { - Reflect(vis, v.range); - Reflect(vis, v.usr); - Reflect(vis, v.kind); - Reflect(vis, v.role); +void reflect(BinaryWriter &vis, SymbolRef &v) { + reflect(vis, v.range); + reflect(vis, v.usr); + reflect(vis, v.kind); + reflect(vis, v.role); } -void Reflect(BinaryWriter &vis, Use &v) { - Reflect(vis, v.range); - Reflect(vis, v.role); - Reflect(vis, v.file_id); +void reflect(BinaryWriter &vis, Use &v) { + reflect(vis, v.range); + reflect(vis, v.role); + reflect(vis, v.file_id); } -void Reflect(BinaryWriter &vis, DeclRef &v) { - Reflect(vis, static_cast(v)); - Reflect(vis, v.extent); +void reflect(BinaryWriter &vis, DeclRef &v) { + reflect(vis, static_cast(v)); + reflect(vis, v.extent); } } // namespace ccls diff --git a/src/indexer.hh b/src/indexer.hh index cc127b1d9..cd4669a99 100644 --- a/src/indexer.hh +++ b/src/indexer.hh @@ -8,7 +8,7 @@ #include "serializer.hh" #include "utils.hh" -#include +#include #include #include #include @@ -19,19 +19,15 @@ #include namespace std { -template <> struct hash { - std::size_t operator()(llvm::sys::fs::UniqueID ID) const { - size_t ret = ID.getDevice(); - ccls::hash_combine(ret, ID.getFile()); - return ret; - } +template <> struct hash { + std::size_t operator()(clang::FileID fid) const { return fid.getHashValue(); } }; } // namespace std namespace ccls { using Usr = uint64_t; -// The order matters. In FindSymbolsAtLocation, we want Var/Func ordered in +// The order matters. In findSymbolsAtLocation, we want Var/Func ordered in // front of others. enum class Kind : uint8_t { Invalid, File, Type, Func, Var }; REFLECT_UNDERLYING_B(Kind); @@ -76,31 +72,31 @@ struct SymbolRef { Kind kind; Role role; operator SymbolIdx() const { return {usr, kind}; } - std::tuple ToTuple() const { + std::tuple toTuple() const { return std::make_tuple(range, usr, kind, role); } - bool operator==(const SymbolRef &o) const { return ToTuple() == o.ToTuple(); } - bool Valid() const { return range.Valid(); } + bool operator==(const SymbolRef &o) const { return toTuple() == o.toTuple(); } + bool valid() const { return range.valid(); } }; struct ExtentRef : SymbolRef { Range extent; - std::tuple ToTuple() const { + std::tuple toTuple() const { return std::make_tuple(range, usr, kind, role, extent); } - bool operator==(const ExtentRef &o) const { return ToTuple() == o.ToTuple(); } + bool operator==(const ExtentRef &o) const { return toTuple() == o.toTuple(); } }; struct Ref { Range range; Role role; - bool Valid() const { return range.Valid(); } - std::tuple ToTuple() const { + bool valid() const { return range.valid(); } + std::tuple toTuple() const { return std::make_tuple(range, role); } - bool operator==(const Ref &o) const { return ToTuple() == o.ToTuple(); } - bool operator<(const Ref &o) const { return ToTuple() < o.ToTuple(); } + bool operator==(const Ref &o) const { return toTuple() == o.toTuple(); } + bool operator<(const Ref &o) const { return toTuple() < o.toTuple(); } }; // Represents an occurrence of a variable/type, |usr,kind| refer to the lexical @@ -118,21 +114,23 @@ struct DeclRef : Use { Range extent; }; -void Reflect(JsonReader &visitor, SymbolRef &value); -void Reflect(JsonReader &visitor, Use &value); -void Reflect(JsonReader &visitor, DeclRef &value); -void Reflect(JsonWriter &visitor, SymbolRef &value); -void Reflect(JsonWriter &visitor, Use &value); -void Reflect(JsonWriter &visitor, DeclRef &value); -void Reflect(BinaryReader &visitor, SymbolRef &value); -void Reflect(BinaryReader &visitor, Use &value); -void Reflect(BinaryReader &visitor, DeclRef &value); -void Reflect(BinaryWriter &visitor, SymbolRef &value); -void Reflect(BinaryWriter &visitor, Use &value); -void Reflect(BinaryWriter &visitor, DeclRef &value); +void reflect(JsonReader &visitor, SymbolRef &value); +void reflect(JsonReader &visitor, Use &value); +void reflect(JsonReader &visitor, DeclRef &value); +void reflect(JsonWriter &visitor, SymbolRef &value); +void reflect(JsonWriter &visitor, Use &value); +void reflect(JsonWriter &visitor, DeclRef &value); +void reflect(BinaryReader &visitor, SymbolRef &value); +void reflect(BinaryReader &visitor, Use &value); +void reflect(BinaryReader &visitor, DeclRef &value); +void reflect(BinaryWriter &visitor, SymbolRef &value); +void reflect(BinaryWriter &visitor, Use &value); +void reflect(BinaryWriter &visitor, DeclRef &value); + +template using VectorAdapter = std::vector>; template struct NameMixin { - std::string_view Name(bool qualified) const { + std::string_view name(bool qualified) const { auto self = static_cast(this); return qualified ? std::string_view(self->detailed_name + self->qual_name_offset, @@ -144,7 +142,8 @@ template struct NameMixin { } }; -struct FuncDef : NameMixin { +template