diff --git a/.appveyor.yml b/.appveyor.yml index 0b89cb118..42a7a4c03 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -1,49 +1,22 @@ version: "{build}" -image: Visual Studio 2017 -platform: x64 -clone_folder: C:\projects\cquery +os: Visual Studio 2017 -install: - - git submodule update --init - -environment: - CLICOLOR_FORCE: 1 - matrix: - - MSYSTEM: MINGW64 - - MSYSTEM: MSVC +platform: + - x64 -matrix: - fast_finish: true # set this flag to immediately finish build once one of the jobs fails. - allow_failures: - - platform: x64 +build: + parallel: true # enable MSBuild parallel builds + verbosity: minimal +install: + - if not exist llvm.tar.xz appveyor DownloadFile "https://ziglang.org/deps/llvm+clang-7.0.0-win64-msvc-release.tar.xz" -FileName llvm.tar.xz + - 7z e -txz llvm.tar.xz + - 7z x llvm.tar + - git submodule update --init --recursive build_script: - - ps: | - If ($Env:MSYSTEM -Eq "MSVC") { - $dir = "cquery-${env:appveyor_build_version}-win64" - cd C:\projects\cquery - python waf configure --msvc_version="msvc 15.0" - python waf build - mkdir "${dir}\build\release\bin" -ea 0 - mkdir "${dir}\build\release\lib\LLVM-6.0.0-win64\lib\clang\6.0.0\" - copy "build\release\bin\*" "${dir}\build\release\bin" - copy -recurse "build\LLVM-6.0.0-win64\lib\clang\6.0.0\include" "${dir}\build\release\lib\LLVM-6.0.0-win64\lib\clang\6.0.0\" - 7z a -tzip "C:\projects\cquery\${dir}.zip" "${dir}" - } Else { - C:\msys64\usr\bin\bash -lc @' - pacman -S --needed --noconfirm mingw-w64-x86_64-clang python - cd /c/projects/cquery - CXXFLAGS=-Wall /usr/bin/python waf configure build --llvm-config llvm-config 2>&1 - '@ - } - - set PATH=%PATH%;C:\msys64\%MSYSTEM%\bin - - build\release\bin\cquery --ci --log-all-to-stderr --test-unit - - IF "%MSYSTEM%"=="MSVC" build\release\bin\cquery --ci --log-all-to-stderr --test-index + - 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: - - path: 'cquery-*.zip' - -cache: - - C:\projects\cquery\build\LLVM-6.0.0-win64.exe - - C:\projects\cquery\build\LLVM-6.0.0-win64\ + - path: build\Release diff --git a/.clang-format b/.clang-format index 3f19e6161..9b3aa8b72 100644 --- a/.clang-format +++ b/.clang-format @@ -1 +1 @@ -BasedOnStyle: Chromium +BasedOnStyle: LLVM diff --git a/.clang_complete b/.clang_complete deleted file mode 100644 index d0dac3366..000000000 --- a/.clang_complete +++ /dev/null @@ -1,6 +0,0 @@ --std=c++11 --Ithird_party/rapidjson/include --IC:/Program Files/LLVM/include --std=c++11 --fms-compatibility --fdelayed-template-parsing \ No newline at end of file diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 5fa46b3e6..000000000 --- a/.gitattributes +++ /dev/null @@ -1,7 +0,0 @@ -# By default, use platform specific endings. -*.h text eol=auto -*.cpp text eol=auto -*.cc text eol=auto - -# Tests must always be crlf -index_tests/** text eol=crlf 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 4ba98ad52..6c3c5f19e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,26 +1,6 @@ -.cquery_cached_index -.DS_Store -.lock-waf* -.vs -.vscode/ -.vscode/.ropeproject -.waf* -*.cquery -*.sln -*.swp -*.vcxproj -*.vcxproj.filters -*.vcxproj.user -**/*.pyc +.* build -cquery_diagnostics.log -cquery_log.txt -Debug -e2e_cache -foo -libcxx -vscode-extension.vsix -waf-* -waf2* -waf3* -x64 +debug +release +/compile_commands.json +!.github/ diff --git a/.gitmodules b/.gitmodules index e2c0733dd..ff65b1dbb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,15 +1,3 @@ [submodule "third_party/rapidjson"] path = third_party/rapidjson - url = https://github.com/miloyip/rapidjson -[submodule "third_party/doctest"] - path = third_party/doctest - url = https://github.com/onqtam/doctest -[submodule "third_party/sparsepp"] - path = third_party/sparsepp - url = https://github.com/greg7mdp/sparsepp -[submodule "third_party/loguru"] - path = third_party/loguru - url = https://github.com/emilk/loguru -[submodule "third_party/msgpack-c"] - path = third_party/msgpack-c - url = https://github.com/msgpack/msgpack-c + url = https://github.com/Tencent/rapidjson diff --git a/.pep8 b/.pep8 deleted file mode 100644 index 1fe66c12e..000000000 --- a/.pep8 +++ /dev/null @@ -1,2 +0,0 @@ -[pep8] -indent-size=2 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index c9ac2e618..000000000 --- a/.travis.yml +++ /dev/null @@ -1,166 +0,0 @@ -dist: trusty -sudo: false -language: c++ - -env: - global: - - COMPILER=g++ - -# Default --recursive (rapidjson/thirdparty/gtest) is unnecessary -git: - submodules: false - depth: 1 - -before_install: - - git submodule update --init - -addons: - apt: - sources: &apt_sources - - ubuntu-toolchain-r-test - - llvm-toolchain-precise-3.5 - - llvm-toolchain-trusty-5.0 - -compiler: clang -os: linux - -cache: - directories: - - build/clang+llvm-6.0.0-x86_64-linux-gnu-ubuntu-14.04/ - - build/clang+llvm-6.0.0-x86_64-apple-darwin/ - -matrix: - fast_finish: true - include: - - env: COMPILER=g++-5 - compiler: gcc - addons: &gcc5 - apt: - packages: ["g++-5"] - sources: *apt_sources - - - env: COMPILER=g++-7 - compiler: gcc - addons: &gcc7 - apt: - packages: ["g++-7"] - sources: *apt_sources - - - env: COMPILER=clang++-3.5 - addons: &clang35 - apt: - packages: ["clang-3.5", "g++-7"] - sources: *apt_sources - - - env: COMPILER=clang++-5.0 - addons: &clang50 - apt: - packages: ["clang-5.0", "g++-7"] - sources: *apt_sources - - - env: COMPILER=clang++ - osx_image: xcode9.1 - os: osx - - - env: COMPILER=g++-7 - compiler: gcc - osx_image: xcode9.1 - os: osx - - - allow_failures: - - # macOS takes too long. - - #- env: COMPILER=clang++ - # osx_image: xcode9.1 - # os: osx - - #- env: COMPILER=g++-7 - # compiler: gcc - # osx_image: xcode9.1 - # os: osx - - # gcc builds that should be fixed at some point - - #- env: COMPILER=g++-5 - # compiler: gcc - - #- env: COMPILER=g++-6 - # compiler: gcc - - #- env: COMPILER=g++-7 - # compiler: gcc - - #- env: COMPILER=g++-5 - # compiler: gcc - # osx_image: xcode9.1 - # os: osx - - #- env: COMPILER=g++-6 - # compiler: gcc - # osx_image: xcode9.1 - # os: osx - - #- env: COMPILER=g++-7 - # compiler: gcc - # osx_image: xcode9.1 - # os: osx - -install: - - | - if [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then - if [[ "${COMPILER}" == "g++-5" ]]; then - brew install gcc@5 - brew link --overwrite gcc@5 - fi - if [[ "${COMPILER}" == "g++-6" ]]; then - brew install gcc@6 - brew link --overwrite gcc@6 - fi - if [[ "${COMPILER}" == "g++-7" ]]; then - brew install gcc@7 - brew link --overwrite gcc@7 - fi - fi - - - | - if [[ "${COMPILER}" == g++* ]]; then - export J="-j1" - fi - - - export CXX="${COMPILER}" - -before_script: - - ${CXX} --version - -script: - - travis_retry ./waf configure - - ./waf build ${J} - - ./build/release/bin/cquery --ci --log-all-to-stderr --test-unit - - ./build/release/bin/cquery --ci --log-all-to-stderr --test-index - -notifications: - email: false - irc: - channels: - - "ircs://chat.freenode.net:6697/#cquery" - template: - - "[ %{repository_slug}#%{commit}] %{result} on %{branch} by %{author} (%{build_url} )" - -before_deploy: - #- zip -r build/cquery-$TRAVIS_TAG-$TRAVIS_OS_NAME.zip build/release/bin/ build/release/lib/clang+llvm-*/lib/libclang.* build/release/lib/clang+llvm-*/lib/clang/5.0.1/include/ - - ci/before_deploy.sh - -deploy: - provider: releases - api_key: - secure: Ahv4Wp1wveWILqp6HB8UmsXwwfZ103fuJV/u6W4oJFRpnbIXRCGFKaDR1Ql0hsHduKFd/76nNQGSVvNNuTXlWaK2n0bTu1EZ4VYmXk7Q7gn4ROP9XFrIZu0c9XKJ/bzehCLj3t6KT0R5MK5gQe+cBmx4S5uGsGG5/nM+GZpE1N4craRCh64UNXMvIx20sW4VQcgj1Ccrc/6Skb3HET7PKbY+IB/LXnaF3nM6V71LxKW2wlakBmzzaNatQ46QOcOCduY4edE8FqBs7yZ0eFktNZusmjiaZT12t0r1hVe0O8e0ER3u9/c3t+hbPUplMR2FAPBZXojgLVhSfFtBaj45T74oCIi0eUaDeS+Oxl6IzgyVho9RurOtaru3hLOVoaD9wR6lGhj6Nz/2Na3lOIorxHfAZ4OgUmluoFLCynO4ylMD03fMBGBshChnmYbrxLw0xLZP2005WUAj8JN64QOmFmLt3gV7TfVldSFHuwoZyESfkXPRM1Xn8RtgFi/89p4jtPtyBFLSaeDggCwfWEMfADCfJ/j8lXtAPdyEINoaKrxkH8qCPoMLNPXE7JhkP8L0Smdq4cFUEXg3wKWM2hXmWmh2Y25BAyh4qu9CrDPd5qqFcXMtyix4ZjmThLFs/oKYbbMUo4FQ5xT5dpt/VZOi4NpcAj0G/M3jWhu85tMdtTc= - #file: build/cquery-$TRAVIS_TAG-$TRAVIS_OS_NAME.zip - file: build/cquery-$TRAVIS_TAG-x86_64-apple-darwin.tar.xz - file: build/cquery-$TRAVIS_TAG-x86_64-unknown-linux-gnu.tar.xz - skip_cleanup: true - on: - repo: cquery-project/cquery - tags: true - diff --git a/.ycm_extra_conf.py b/.ycm_extra_conf.py deleted file mode 100644 index 385f768c1..000000000 --- a/.ycm_extra_conf.py +++ /dev/null @@ -1,15 +0,0 @@ -def FlagsForFile( filename, **kwargs ): - return { - 'flags': [ - '-xc++', - '-std=c++11', - '-DLOGURU_WITH_STREAMS=1', - '-Isrc/', - '-Ithird_party/', - '-Ithird_party/doctest', - '-Ithird_party/rapidjson/include', - '-Ithird_party/sparsepp', - '-Ithird_party/loguru', - '-Ibuild/clang+llvm-4.0.0-x86_64-linux-gnu-ubuntu-14.04/include' - ] - } diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 000000000..278e07359 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,238 @@ +cmake_minimum_required(VERSION 3.8) +project(ccls LANGUAGES CXX) + +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 "") + +### Default build type + +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() + +### Compile options + +# Enable C++17 (Required) +set_property(TARGET ccls PROPERTY CXX_STANDARD 17) +set_property(TARGET ccls PROPERTY CXX_STANDARD_REQUIRED ON) +set_property(TARGET ccls PROPERTY CXX_EXTENSIONS OFF) + +# CMake sets MSVC for both MSVC and Clang(Windows) +if(MSVC) + # Common MSVC/Clang(Windows) options + target_compile_options(ccls PRIVATE + /nologo + /EHsc + /D_CRT_SECURE_NO_WARNINGS # don't try to use MSVC std replacements + /W3 # roughly -Wall + /wd4996 # ignore deprecated declaration + /wd4267 # ignores warning C4267 + # (conversion from 'size_t' to 'type'), + # roughly -Wno-sign-compare + /wd4800 + /wd4068 # Disable unknown pragma warning + /std:c++17 + $<$:/FS> + ) + # relink system libs + target_link_libraries(ccls PRIVATE Mincore.lib) +else() + # Common GCC/Clang(Linux) options + target_compile_options(ccls PRIVATE + -Wall + -Wno-sign-compare + ) + + if(${CMAKE_CXX_COMPILER_ID} STREQUAL GNU) + target_compile_options(ccls PRIVATE -Wno-return-type -Wno-unused-result) + endif() + + if(${CMAKE_CXX_COMPILER_ID} STREQUAL Clang) + target_compile_options(ccls PRIVATE + $<$:-fno-limit-debug-info>) + endif() +endif() + +### Libraries + +find_package(Clang REQUIRED) + +target_link_libraries(ccls PRIVATE + clangIndex + clangFormat + clangTooling + clangToolingInclusions + clangToolingCore + clangFrontend + clangParse + clangSerialization + clangSema + clangAST + clangLex + clangDriver + clangBasic +) + +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() + +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) +find_package(Threads REQUIRED) +target_link_libraries(ccls PRIVATE Threads::Threads) + +if(${CMAKE_SYSTEM_NAME} STREQUAL FreeBSD) + find_package(Backtrace REQUIRED) + target_link_libraries(ccls PRIVATE ${Backtrace_LIBRARIES}) + # src/platform_posix.cc uses libthr + target_link_libraries(ccls PRIVATE thr) +endif() + +### Definitions + +# Find Clang resource directory with Clang executable. + +if(NOT CLANG_RESOURCE_DIR) + find_program(CLANG_EXECUTABLE clang) + if(NOT CLANG_EXECUTABLE) + message(FATAL_ERROR "clang executable not found.") + endif() + + 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() +endif() + +set_property(SOURCE src/utils.cc APPEND PROPERTY COMPILE_DEFINITIONS + CLANG_RESOURCE_DIRECTORY=R"\(${CLANG_RESOURCE_DIR}\)") + +### Includes + +if(USE_SYSTEM_RAPIDJSON) + find_package(RapidJSON QUIET) +endif() +if(NOT RapidJSON_FOUND) + set(RapidJSON_INCLUDE_DIRS third_party/rapidjson/include) +endif() + +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() + +### Install + +install(TARGETS ccls RUNTIME DESTINATION bin) + +### Sources + +target_sources(ccls PRIVATE third_party/siphash.cc) + +target_sources(ccls PRIVATE + src/clang_tu.cc + src/config.cc + src/filesystem.cc + src/fuzzy_match.cc + src/main.cc + src/include_complete.cc + src/indexer.cc + src/log.cc + src/lsp.cc + src/message_handler.cc + src/pipeline.cc + src/platform_posix.cc + src/platform_win.cc + src/position.cc + src/project.cc + src/query.cc + src/sema_manager.cc + src/serializer.cc + src/test.cc + src/utils.cc + src/working_files.cc +) + +target_sources(ccls PRIVATE + src/messages/ccls_call.cc + src/messages/ccls_info.cc + src/messages/ccls_inheritance.cc + src/messages/ccls_member.cc + src/messages/ccls_navigate.cc + src/messages/ccls_reload.cc + src/messages/ccls_vars.cc + src/messages/initialize.cc + src/messages/textDocument_code.cc + src/messages/textDocument_completion.cc + src/messages/textDocument_definition.cc + src/messages/textDocument_did.cc + src/messages/textDocument_foldingRange.cc + src/messages/textDocument_formatting.cc + src/messages/textDocument_document.cc + src/messages/textDocument_hover.cc + src/messages/textDocument_references.cc + src/messages/textDocument_rename.cc + 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}\") diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md index 22648ec2e..e02351f5a 100644 --- a/README.md +++ b/README.md @@ -1,42 +1,46 @@ -# cquery +# ccls -[![Join the chat at https://gitter.im/cquery-project/Lobby](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/cquery-project/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![Telegram](https://img.shields.io/badge/telegram-@cclsp-blue.svg)](https://telegram.me/cclsp) +[![Gitter](https://img.shields.io/badge/gitter-ccls--project-blue.svg?logo=gitter-white)](https://gitter.im/ccls-project/ccls) -cquery is a highly-scalable, low-latency language server for C/C++/Objective-C. It is tested -and designed for large code bases like -[Chromium](https://chromium.googlesource.com/chromium/src/). cquery provides -accurate and fast semantic analysis without interrupting workflow. - -![Demo](https://ptpb.pw/GlSQ.png?raw=true) - -cquery implements almost the entire language server protocol and provides -some extra features to boot: +ccls, which originates from [cquery](https://github.com/cquery-project/cquery), is a C/C++/Objective-C language server. * code completion (with both signature help and snippets) - * finding definition/references - * type hierarchy (parent type, derived types, expandable tree view) - * finding base/derived methods/classes, call tree - * symbol rename - * document and global symbol search - * hover tooltips showing symbol type - * diagnostics - * code actions (clang FixIts) - * darken/fade code disabled by preprocessor - * #include auto-complete, undefined type include insertion, include quick-jump - (goto definition, document links) - * auto-implement functions without a definition - * semantic highlighting, including support for [rainbow semantic highlighting](https://medium.com/@evnbr/coding-in-color-3a6db2743a1e) - -# >>> [Getting started](https://github.com/jacobdufault/cquery/wiki/Getting-started) (CLICK HERE) <<< - -# Limitations - -cquery is able to respond to queries quickly because it caches a huge amount of -information. When a request comes in, cquery just looks it up in the cache -without running many computations. As a result, there's a large memory overhead. -For example, a full index of Chrome will take about 10gb of memory. If you -exclude v8, webkit, and third_party, it goes down to about 6.5gb. - -# License - -MIT + * [definition](src/messages/textDocument_definition.cc)/[references](src/messages/textDocument_references.cc), and other cross references + * cross reference extensions: `$ccls/call` `$ccls/inheritance` `$ccls/member` `$ccls/vars` ... + * 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) + * [hover information](src/messages/textDocument_hover.cc) + * diagnostics and code actions (clang FixIts) + * semantic highlighting and preprocessor skipped regions + * semantic navigation: `$ccls/navigate` + +It has a global view of the code base and support a lot of cross reference features, see [wiki/FAQ](../../wiki/FAQ). +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/Home) (CLICK HERE) <<< + +* [Build](../../wiki/Build) +* [FAQ](../../wiki/FAQ) + +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/compile_commands.json](../../wiki/compile_commands.json) for examples. diff --git a/ci/before_deploy.sh b/ci/before_deploy.sh index 25fa14e05..e954d7f5c 100755 --- a/ci/before_deploy.sh +++ b/ci/before_deploy.sh @@ -7,15 +7,15 @@ case $(uname -s) in Darwin) libclang=(lib/clang+llvm-*/lib/libclang.dylib) strip_option="-x" - name=cquery-$version-x86_64-apple-darwin ;; + name=ccls-$version-x86_64-apple-darwin ;; FreeBSD) libclang=(lib/clang+llvm-*/lib/libclang.so.?) strip_option="-s" - name=cquery-$version-x86_64-unknown-freebsd10 ;; + name=ccls-$version-x86_64-unknown-freebsd10 ;; Linux) libclang=(lib/clang+llvm-*/lib/libclang.so.?) strip_option="-s" - name=cquery-$version-x86_64-unknown-linux-gnu ;; + name=ccls-$version-x86_64-unknown-linux-gnu ;; *) echo Unsupported >&2 exit 1 ;; @@ -26,7 +26,7 @@ mkdir "$pkg/$name" rsync -rtLR bin "./${libclang[-1]}" ./lib/clang+llvm-*/lib/clang/*/include "$pkg/$name" cd "$pkg" -strip "$strip_option" "$name/bin/cquery" "$name/${libclang[-1]}" +strip "$strip_option" "$name/bin/ccls" "$name/${libclang[-1]}" case $(uname -s) in Darwin) # https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man1/tar.1.html diff --git a/compile_commands.json b/compile_commands.json deleted file mode 120000 index 4595a4b07..000000000 --- a/compile_commands.json +++ /dev/null @@ -1 +0,0 @@ -build/release/compile_commands.json \ No newline at end of file diff --git a/index_tests/_empty_test.cc b/index_tests/_empty_test.cc new file mode 100644 index 000000000..228b26748 --- /dev/null +++ b/index_tests/_empty_test.cc @@ -0,0 +1,4 @@ +/* +OUTPUT: +{} +*/ diff --git a/index_tests/class_forward_declaration.cc b/index_tests/class_forward_declaration.cc new file mode 100644 index 000000000..7c5e23a23 --- /dev/null +++ b/index_tests/class_forward_declaration.cc @@ -0,0 +1,32 @@ +class Foo; +class Foo; +class Foo {}; +class Foo; + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [], + "usr2type": [{ + "usr": 15041163540773201510, + "detailed_name": "class Foo", + "qual_name_offset": 6, + "short_name": "Foo", + "spell": "3:7-3:10|3:1-3:13|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": ["1:7-1:10|1:1-1:10|1|-1", "2:7-2:10|2:1-2:10|1|-1", "4:7-4:10|4:1-4:10|1|-1"], + "derived": [], + "instances": [], + "uses": [] + }], + "usr2var": [] +} +*/ diff --git a/index_tests/constructors/constructor.cc b/index_tests/constructors/constructor.cc new file mode 100644 index 000000000..7901e0fb2 --- /dev/null +++ b/index_tests/constructors/constructor.cc @@ -0,0 +1,92 @@ +class Foo { +public: + Foo() {} +}; + +void foo() { + Foo f; + Foo* f2 = new Foo(); +} + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 3385168158331140247, + "detailed_name": "Foo::Foo()", + "qual_name_offset": 0, + "short_name": "Foo", + "spell": "3:3-3:6|3:3-3:11|1026|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 9, + "parent_kind": 5, + "storage": 0, + "declarations": [], + "derived": [], + "uses": ["7:7-7:8|16676|-1", "8:17-8:20|16676|-1"] + }, { + "usr": 4259594751088586730, + "detailed_name": "void foo()", + "qual_name_offset": 5, + "short_name": "foo", + "spell": "6:6-6:9|6:1-9:2|2|-1", + "bases": [], + "vars": [10983126130596230582, 17165811951126099095], + "callees": ["7:7-7:8|3385168158331140247|3|16676", "7:7-7:8|3385168158331140247|3|16676", "8:17-8:20|3385168158331140247|3|16676", "8:17-8:20|3385168158331140247|3|16676"], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 15041163540773201510, + "detailed_name": "class Foo {}", + "qual_name_offset": 6, + "short_name": "Foo", + "spell": "1:7-1:10|1:1-4:2|2|-1", + "bases": [], + "funcs": [3385168158331140247], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [10983126130596230582, 17165811951126099095], + "uses": ["3:3-3:6|4|-1", "7:3-7:6|4|-1", "8:3-8:6|4|-1", "8:17-8:20|4|-1"] + }], + "usr2var": [{ + "usr": 10983126130596230582, + "detailed_name": "Foo f", + "qual_name_offset": 4, + "short_name": "f", + "spell": "7:7-7:8|7:3-7:8|2|-1", + "type": 15041163540773201510, + "kind": 13, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 17165811951126099095, + "detailed_name": "Foo *f2", + "qual_name_offset": 5, + "short_name": "f2", + "hover": "Foo *f2 = new Foo()", + "spell": "8:8-8:10|8:3-8:22|2|-1", + "type": 15041163540773201510, + "kind": 13, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": [] + }] +} +*/ diff --git a/index_tests/constructors/destructor.cc b/index_tests/constructors/destructor.cc new file mode 100644 index 000000000..c8ad82b71 --- /dev/null +++ b/index_tests/constructors/destructor.cc @@ -0,0 +1,99 @@ +class Foo { +public: + Foo() {} + ~Foo() {}; +}; + +void foo() { + Foo f; +} + +// TODO: Support destructors (notice how the dtor has no usages listed). +// - check if variable is a pointer. if so, do *not* insert dtor +// - check if variable is normal type. if so, insert dtor +// - scan for statements that look like dtors in function def handler +// - figure out some way to support w/ unique_ptrs? +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 3385168158331140247, + "detailed_name": "Foo::Foo()", + "qual_name_offset": 0, + "short_name": "Foo", + "spell": "3:3-3:6|3:3-3:11|1026|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 9, + "parent_kind": 5, + "storage": 0, + "declarations": [], + "derived": [], + "uses": ["8:7-8:8|16676|-1"] + }, { + "usr": 4259594751088586730, + "detailed_name": "void foo()", + "qual_name_offset": 5, + "short_name": "foo", + "spell": "7:6-7:9|7:1-9:2|2|-1", + "bases": [], + "vars": [1893354193220338759], + "callees": ["8:7-8:8|3385168158331140247|3|16676", "8:7-8:8|3385168158331140247|3|16676"], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }, { + "usr": 7440261702884428359, + "detailed_name": "Foo::~Foo() noexcept", + "qual_name_offset": 0, + "short_name": "~Foo", + "spell": "4:3-4:7|4:3-4:12|1026|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 6, + "parent_kind": 5, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 15041163540773201510, + "detailed_name": "class Foo {}", + "qual_name_offset": 6, + "short_name": "Foo", + "spell": "1:7-1:10|1:1-5:2|2|-1", + "bases": [], + "funcs": [3385168158331140247, 7440261702884428359], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [1893354193220338759], + "uses": ["3:3-3:6|4|-1", "4:4-4:7|4|-1", "8:3-8:6|4|-1"] + }], + "usr2var": [{ + "usr": 1893354193220338759, + "detailed_name": "Foo f", + "qual_name_offset": 4, + "short_name": "f", + "spell": "8:7-8:8|8:3-8:8|2|-1", + "type": 15041163540773201510, + "kind": 13, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": [] + }] +} +*/ diff --git a/index_tests/constructors/implicit_constructor.cc b/index_tests/constructors/implicit_constructor.cc new file mode 100644 index 000000000..31c7c8ce9 --- /dev/null +++ b/index_tests/constructors/implicit_constructor.cc @@ -0,0 +1,91 @@ +struct Type { + Type() {} +}; + +void Make() { + Type foo0; + auto foo1 = Type(); +} + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 3957104924306079513, + "detailed_name": "void Make()", + "qual_name_offset": 5, + "short_name": "Make", + "spell": "5:6-5:10|5:1-8:2|2|-1", + "bases": [], + "vars": [449111627548814328, 17097499197730163115], + "callees": ["6:8-6:12|10530961286677896857|3|16676", "6:8-6:12|10530961286677896857|3|16676", "7:15-7:19|10530961286677896857|3|16676", "7:15-7:19|10530961286677896857|3|16676"], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }, { + "usr": 10530961286677896857, + "detailed_name": "Type::Type()", + "qual_name_offset": 0, + "short_name": "Type", + "spell": "2:3-2:7|2:3-2:12|1026|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 9, + "parent_kind": 23, + "storage": 0, + "declarations": [], + "derived": [], + "uses": ["6:8-6:12|16676|-1", "7:15-7:19|16676|-1"] + }], + "usr2type": [{ + "usr": 13487927231218873822, + "detailed_name": "struct Type {}", + "qual_name_offset": 7, + "short_name": "Type", + "spell": "1:8-1:12|1:1-3:2|2|-1", + "bases": [], + "funcs": [10530961286677896857], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [449111627548814328, 17097499197730163115], + "uses": ["2:3-2:7|4|-1", "6:3-6:7|4|-1", "7:15-7:19|4|-1"] + }], + "usr2var": [{ + "usr": 449111627548814328, + "detailed_name": "Type foo0", + "qual_name_offset": 5, + "short_name": "foo0", + "spell": "6:8-6:12|6:3-6:12|2|-1", + "type": 13487927231218873822, + "kind": 13, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 17097499197730163115, + "detailed_name": "Type foo1", + "qual_name_offset": 5, + "short_name": "foo1", + "hover": "Type foo1 = Type()", + "spell": "7:8-7:12|7:3-7:21|2|-1", + "type": 13487927231218873822, + "kind": 13, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": [] + }] +} +*/ diff --git a/index_tests/constructors/invalid_reference.cc b/index_tests/constructors/invalid_reference.cc new file mode 100644 index 000000000..3b960a163 --- /dev/null +++ b/index_tests/constructors/invalid_reference.cc @@ -0,0 +1,51 @@ +struct Foo {}; + +template +Foo::Foo() {} + +/* +EXTRA_FLAGS: +-fms-compatibility +-fdelayed-template-parsing + +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 17319723337446061757, + "detailed_name": "Foo::Foo::Foo()", + "qual_name_offset": 0, + "short_name": "Foo", + "spell": "4:6-4:9|4:1-4:11|1026|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 9, + "parent_kind": 23, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 15041163540773201510, + "detailed_name": "struct Foo {}", + "qual_name_offset": 7, + "short_name": "Foo", + "spell": "1:8-1:11|1:1-1:14|2|-1", + "bases": [], + "funcs": [17319723337446061757], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": ["4:1-4:4|4|-1", "4:6-4:9|4|-1"] + }], + "usr2var": [] +} +*/ diff --git a/index_tests/constructors/make_functions.cc b/index_tests/constructors/make_functions.cc new file mode 100644 index 000000000..5bc5004b4 --- /dev/null +++ b/index_tests/constructors/make_functions.cc @@ -0,0 +1,351 @@ +#include "make_functions.h" + +template +T* MakeUnique(Args&&... args) { + return nullptr; +} + +template +T* maKE_NoRefs(Args... args) { + return nullptr; +} + +void caller22() { + MakeUnique(); + MakeUnique(1); + MakeUnique(1, new Bar(), nullptr); + maKE_NoRefs(1, new Bar(), nullptr); +} + +// TODO: Eliminate the extra entries in the "types" array here. They come from +// the template function definitions. + +// Foobar is defined in a separate file to ensure that we can attribute +// MakeUnique calls across translation units. + +/* +OUTPUT: make_functions.h +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 3765833212244435302, + "detailed_name": "Foobar::Foobar(int &&, Bar *, bool *)", + "qual_name_offset": 0, + "short_name": "Foobar", + "spell": "7:3-7:9|7:3-7:32|1026|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 9, + "parent_kind": 5, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }, { + "usr": 13028995015627606181, + "detailed_name": "Foobar::Foobar(int)", + "qual_name_offset": 0, + "short_name": "Foobar", + "spell": "6:3-6:9|6:3-6:17|1026|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 9, + "parent_kind": 5, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }, { + "usr": 13131778807733950299, + "detailed_name": "Foobar::Foobar()", + "qual_name_offset": 0, + "short_name": "Foobar", + "spell": "5:3-5:9|5:3-5:14|1026|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 9, + "parent_kind": 5, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }, { + "usr": 17321436359755983845, + "detailed_name": "Foobar::Foobar(int, Bar *, bool *)", + "qual_name_offset": 0, + "short_name": "Foobar", + "spell": "8:3-8:9|8:3-8:30|1026|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 9, + "parent_kind": 5, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 12993848456528750350, + "detailed_name": "struct Bar {}", + "qual_name_offset": 7, + "short_name": "Bar", + "spell": "1:8-1:11|1:1-1:14|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": ["7:17-7:20|4|-1", "8:15-8:18|4|-1"] + }, { + "usr": 14935975554338052500, + "detailed_name": "class Foobar {}", + "qual_name_offset": 6, + "short_name": "Foobar", + "spell": "3:7-3:13|3:1-9:2|2|-1", + "bases": [], + "funcs": [13131778807733950299, 13028995015627606181, 3765833212244435302, 17321436359755983845], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": ["5:3-5:9|4|-1", "6:3-6:9|4|-1", "7:3-7:9|4|-1", "8:3-8:9|4|-1"] + }], + "usr2var": [] +} +OUTPUT: make_functions.cc +{ + "includes": [{ + "line": 0, + "resolved_path": "&make_functions.h" + }], + "skipped_ranges": [], + "usr2func": [{ + "usr": 768523651983844320, + "detailed_name": "", + "qual_name_offset": 0, + "short_name": "", + "bases": [], + "vars": [2555873744476712860, 2555873744476712860, 2555873744476712860], + "callees": [], + "kind": 0, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }, { + "usr": 2532818908869373467, + "detailed_name": "T *maKE_NoRefs(Args ...args)", + "qual_name_offset": 3, + "short_name": "maKE_NoRefs", + "spell": "9:4-9:15|9:1-11:2|2|-1", + "bases": [], + "vars": [3908732770590594660], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": ["17:3-17:14|16420|-1"] + }, { + "usr": 2816883305867289955, + "detailed_name": "void caller22()", + "qual_name_offset": 5, + "short_name": "caller22", + "spell": "13:6-13:14|13:1-18:2|2|-1", + "bases": [], + "vars": [], + "callees": ["14:3-14:13|15793662558620604611|3|16420", "15:3-15:13|15793662558620604611|3|16420", "16:3-16:13|15793662558620604611|3|16420", "17:3-17:14|2532818908869373467|3|16420"], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }, { + "usr": 11138976705878544996, + "detailed_name": "", + "qual_name_offset": 0, + "short_name": "", + "bases": [], + "vars": [16395392342608151399], + "callees": [], + "kind": 0, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }, { + "usr": 11363675606380070883, + "detailed_name": "", + "qual_name_offset": 0, + "short_name": "", + "bases": [], + "vars": [180270746871803062, 180270746871803062, 180270746871803062], + "callees": [], + "kind": 0, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }, { + "usr": 15793662558620604611, + "detailed_name": "T *MakeUnique(Args &&...args)", + "qual_name_offset": 3, + "short_name": "MakeUnique", + "spell": "4:4-4:14|4:1-6:2|2|-1", + "bases": [], + "vars": [8463700030555379526], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": ["14:3-14:13|16420|-1", "15:3-15:13|16420|-1", "16:3-16:13|16420|-1"] + }], + "usr2type": [{ + "usr": 53, + "detailed_name": "", + "qual_name_offset": 0, + "short_name": "", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 0, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [180270746871803062], + "uses": [] + }, { + "usr": 87, + "detailed_name": "", + "qual_name_offset": 0, + "short_name": "", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 0, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [180270746871803062], + "uses": [] + }, { + "usr": 12993848456528750350, + "detailed_name": "struct Bar {}", + "qual_name_offset": 7, + "short_name": "Bar", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": ["16:29-16:32|4|-1", "17:30-17:33|4|-1"] + }, { + "usr": 14935975554338052500, + "detailed_name": "class Foobar {}", + "qual_name_offset": 6, + "short_name": "Foobar", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": ["14:14-14:20|4|-1", "15:14-15:20|4|-1", "16:14-16:20|4|-1", "17:15-17:21|4|-1"] + }], + "usr2var": [{ + "usr": 180270746871803062, + "detailed_name": "int args", + "qual_name_offset": 4, + "short_name": "args", + "spell": "9:24-9:28|9:16-9:28|1026|-1", + "type": 87, + "kind": 253, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 2555873744476712860, + "detailed_name": "int &&args", + "qual_name_offset": 6, + "short_name": "args", + "spell": "4:25-4:29|4:15-4:29|1026|-1", + "type": 0, + "kind": 253, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 3908732770590594660, + "detailed_name": "Args ...args", + "qual_name_offset": 8, + "short_name": "args", + "spell": "9:24-9:28|9:16-9:28|1026|-1", + "type": 0, + "kind": 253, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 8463700030555379526, + "detailed_name": "Args &&...args", + "qual_name_offset": 10, + "short_name": "args", + "spell": "4:25-4:29|4:15-4:29|1026|-1", + "type": 0, + "kind": 253, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 16395392342608151399, + "detailed_name": "int &&args", + "qual_name_offset": 6, + "short_name": "args", + "spell": "4:25-4:29|4:15-4:29|1026|-1", + "type": 0, + "kind": 253, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": [] + }] +} +*/ diff --git a/index_tests/constructors/make_functions.h b/index_tests/constructors/make_functions.h new file mode 100644 index 000000000..f119cfb8e --- /dev/null +++ b/index_tests/constructors/make_functions.h @@ -0,0 +1,10 @@ +struct Bar {}; + +class Foobar { + public: + Foobar() {} + Foobar(int) {} + Foobar(int&&, Bar*, bool*) {} + Foobar(int, Bar*, bool*) {} +}; + diff --git a/index_tests/declaration_vs_definition/class.cc b/index_tests/declaration_vs_definition/class.cc new file mode 100644 index 000000000..ee90a78bf --- /dev/null +++ b/index_tests/declaration_vs_definition/class.cc @@ -0,0 +1,34 @@ +class Foo; +class Foo; +class Foo {}; +class Foo; + +/* +// NOTE: Separate decl/definition are not supported for classes. See source +// for comments. +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [], + "usr2type": [{ + "usr": 15041163540773201510, + "detailed_name": "class Foo", + "qual_name_offset": 6, + "short_name": "Foo", + "spell": "3:7-3:10|3:1-3:13|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": ["1:7-1:10|1:1-1:10|1|-1", "2:7-2:10|2:1-2:10|1|-1", "4:7-4:10|4:1-4:10|1|-1"], + "derived": [], + "instances": [], + "uses": [] + }], + "usr2var": [] +} +*/ diff --git a/index_tests/declaration_vs_definition/class_member.cc b/index_tests/declaration_vs_definition/class_member.cc new file mode 100644 index 000000000..0099e070e --- /dev/null +++ b/index_tests/declaration_vs_definition/class_member.cc @@ -0,0 +1,62 @@ +class Foo { + int foo; +}; + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [], + "usr2type": [{ + "usr": 53, + "detailed_name": "", + "qual_name_offset": 0, + "short_name": "", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 0, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [9736582033442720743], + "uses": [] + }, { + "usr": 15041163540773201510, + "detailed_name": "class Foo {}", + "qual_name_offset": 6, + "short_name": "Foo", + "spell": "1:7-1:10|1:1-3:2|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [{ + "L": 9736582033442720743, + "R": 0 + }], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": [] + }], + "usr2var": [{ + "usr": 9736582033442720743, + "detailed_name": "int Foo::foo", + "qual_name_offset": 4, + "short_name": "foo", + "spell": "2:7-2:10|2:3-2:10|1026|-1", + "type": 53, + "kind": 8, + "parent_kind": 5, + "storage": 0, + "declarations": [], + "uses": [] + }] +} +*/ diff --git a/index_tests/declaration_vs_definition/class_member_static.cc b/index_tests/declaration_vs_definition/class_member_static.cc new file mode 100644 index 000000000..6e7951e91 --- /dev/null +++ b/index_tests/declaration_vs_definition/class_member_static.cc @@ -0,0 +1,61 @@ +class Foo { + static int foo; +}; + +int Foo::foo; + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [], + "usr2type": [{ + "usr": 53, + "detailed_name": "", + "qual_name_offset": 0, + "short_name": "", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 0, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [8942920329766232482, 8942920329766232482], + "uses": [] + }, { + "usr": 15041163540773201510, + "detailed_name": "class Foo {}", + "qual_name_offset": 6, + "short_name": "Foo", + "spell": "1:7-1:10|1:1-3:2|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": ["5:5-5:8|4|-1"] + }], + "usr2var": [{ + "usr": 8942920329766232482, + "detailed_name": "static int Foo::foo", + "qual_name_offset": 11, + "short_name": "foo", + "spell": "5:10-5:13|5:1-5:13|1026|-1", + "type": 53, + "kind": 13, + "parent_kind": 5, + "storage": 2, + "declarations": ["2:14-2:17|2:3-2:17|1025|-1"], + "uses": [] + }] +} +*/ diff --git a/index_tests/declaration_vs_definition/func.cc b/index_tests/declaration_vs_definition/func.cc new file mode 100644 index 000000000..f1ad37498 --- /dev/null +++ b/index_tests/declaration_vs_definition/func.cc @@ -0,0 +1,31 @@ +void foo(); +void foo(); +void foo() {} +void foo(); + +/* +// Note: we always use the latest seen ("most local") definition/declaration. +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 4259594751088586730, + "detailed_name": "void foo()", + "qual_name_offset": 5, + "short_name": "foo", + "spell": "3:6-3:9|3:1-3:14|2|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": ["1:6-1:9|1:1-1:11|1|-1", "2:6-2:9|2:1-2:11|1|-1", "4:6-4:9|4:1-4:11|1|-1"], + "derived": [], + "uses": [] + }], + "usr2type": [], + "usr2var": [] +} +*/ diff --git a/index_tests/declaration_vs_definition/func_associated_function_params.cc b/index_tests/declaration_vs_definition/func_associated_function_params.cc new file mode 100644 index 000000000..fbf17f3a8 --- /dev/null +++ b/index_tests/declaration_vs_definition/func_associated_function_params.cc @@ -0,0 +1,71 @@ +int foo(int, int); +int foo(int aa, + int bb); +int foo(int aaa, int bbb); +int foo(int a, int b) { return 0; } + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 2747674671862363334, + "detailed_name": "int foo(int, int)", + "qual_name_offset": 4, + "short_name": "foo", + "spell": "5:5-5:8|5:1-5:36|2|-1", + "bases": [], + "vars": [14555488990109936920, 10963664335057337329], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": ["1:5-1:8|1:1-1:18|1|-1", "2:5-2:8|2:1-3:16|1|-1", "4:5-4:8|4:1-4:26|1|-1"], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 53, + "detailed_name": "", + "qual_name_offset": 0, + "short_name": "", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 0, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [14555488990109936920, 10963664335057337329], + "uses": [] + }], + "usr2var": [{ + "usr": 10963664335057337329, + "detailed_name": "int b", + "qual_name_offset": 4, + "short_name": "b", + "spell": "5:20-5:21|5:16-5:21|1026|-1", + "type": 53, + "kind": 253, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 14555488990109936920, + "detailed_name": "int a", + "qual_name_offset": 4, + "short_name": "a", + "spell": "5:13-5:14|5:9-5:14|1026|-1", + "type": 53, + "kind": 253, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": [] + }] +} +*/ diff --git a/index_tests/declaration_vs_definition/method.cc b/index_tests/declaration_vs_definition/method.cc new file mode 100644 index 000000000..ef280fdbd --- /dev/null +++ b/index_tests/declaration_vs_definition/method.cc @@ -0,0 +1,78 @@ +class Foo { + void declonly(); + virtual void purevirtual() = 0; + void def(); +}; + +void Foo::def() {} + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 4012226004228259562, + "detailed_name": "void Foo::declonly()", + "qual_name_offset": 5, + "short_name": "declonly", + "bases": [], + "vars": [], + "callees": [], + "kind": 6, + "parent_kind": 0, + "storage": 0, + "declarations": ["2:8-2:16|2:3-2:18|1025|-1"], + "derived": [], + "uses": [] + }, { + "usr": 10939323144126021546, + "detailed_name": "virtual void Foo::purevirtual() = 0", + "qual_name_offset": 13, + "short_name": "purevirtual", + "bases": [], + "vars": [], + "callees": [], + "kind": 6, + "parent_kind": 0, + "storage": 0, + "declarations": ["3:16-3:27|3:3-3:33|1089|-1"], + "derived": [], + "uses": [] + }, { + "usr": 15416083548883122431, + "detailed_name": "void Foo::def()", + "qual_name_offset": 5, + "short_name": "def", + "spell": "7:11-7:14|7:1-7:19|1026|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 6, + "parent_kind": 5, + "storage": 0, + "declarations": ["4:8-4:11|4:3-4:13|1025|-1"], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 15041163540773201510, + "detailed_name": "class Foo {}", + "qual_name_offset": 6, + "short_name": "Foo", + "spell": "1:7-1:10|1:1-5:2|2|-1", + "bases": [], + "funcs": [4012226004228259562, 10939323144126021546, 15416083548883122431], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": ["7:6-7:9|4|-1"] + }], + "usr2var": [] +} +*/ diff --git a/index_tests/enums/enum_class_decl.cc b/index_tests/enums/enum_class_decl.cc new file mode 100644 index 000000000..f1b8c10d3 --- /dev/null +++ b/index_tests/enums/enum_class_decl.cc @@ -0,0 +1,81 @@ +typedef unsigned char uint8_t; +enum class Foo : uint8_t { + A, + B = 20 +}; + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [], + "usr2type": [{ + "usr": 2010430204259339553, + "detailed_name": "typedef unsigned char uint8_t", + "qual_name_offset": 22, + "short_name": "uint8_t", + "spell": "1:23-1:30|1:1-1:30|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 252, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": [] + }, { + "usr": 16985894625255407295, + "detailed_name": "enum class Foo : uint8_t {}", + "qual_name_offset": 11, + "short_name": "Foo", + "spell": "2:12-2:15|2:1-5:2|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [{ + "L": 439339022761937396, + "R": -1 + }, { + "L": 15962370213938840720, + "R": -1 + }], + "alias_of": 0, + "kind": 10, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": [] + }], + "usr2var": [{ + "usr": 439339022761937396, + "detailed_name": "Foo::A", + "qual_name_offset": 0, + "short_name": "A", + "hover": "Foo::A = 0", + "spell": "3:3-3:4|3:3-3:4|1026|-1", + "type": 16985894625255407295, + "kind": 22, + "parent_kind": 10, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 15962370213938840720, + "detailed_name": "Foo::B = 20", + "qual_name_offset": 0, + "short_name": "B", + "spell": "4:3-4:4|4:3-4:9|1026|-1", + "type": 16985894625255407295, + "kind": 22, + "parent_kind": 10, + "storage": 0, + "declarations": [], + "uses": [] + }] +} +*/ diff --git a/index_tests/enums/enum_decl.cc b/index_tests/enums/enum_decl.cc new file mode 100644 index 000000000..fd84c0b6a --- /dev/null +++ b/index_tests/enums/enum_decl.cc @@ -0,0 +1,57 @@ +enum Foo { + A, + B = 20 +}; + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [], + "usr2type": [{ + "usr": 16985894625255407295, + "detailed_name": "enum Foo {}", + "qual_name_offset": 5, + "short_name": "Foo", + "spell": "1:6-1:9|1:1-4:2|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 10, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": [] + }], + "usr2var": [{ + "usr": 439339022761937396, + "detailed_name": "A", + "qual_name_offset": 0, + "short_name": "A", + "hover": "A = 0", + "spell": "2:3-2:4|2:3-2:4|1026|-1", + "type": 16985894625255407295, + "kind": 22, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 15962370213938840720, + "detailed_name": "B = 20", + "qual_name_offset": 0, + "short_name": "B", + "spell": "3:3-3:4|3:3-3:9|1026|-1", + "type": 16985894625255407295, + "kind": 22, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "uses": [] + }] +} +*/ diff --git a/index_tests/enums/enum_inherit.cc b/index_tests/enums/enum_inherit.cc new file mode 100644 index 000000000..9459dcfec --- /dev/null +++ b/index_tests/enums/enum_inherit.cc @@ -0,0 +1,129 @@ +enum Foo : int { + A, + B = 20 +}; + +typedef int int32_t; + +enum class E : int32_t { + E0, + E20 = 20 +}; + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [], + "usr2type": [{ + "usr": 2986879766914123941, + "detailed_name": "enum class E : int32_t {}", + "qual_name_offset": 11, + "short_name": "E", + "spell": "8:12-8:13|8:1-11:2|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [{ + "L": 16614320383091394267, + "R": -1 + }, { + "L": 16847439761518576294, + "R": -1 + }], + "alias_of": 0, + "kind": 10, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": [] + }, { + "usr": 14939241684006947339, + "detailed_name": "typedef int int32_t", + "qual_name_offset": 12, + "short_name": "int32_t", + "spell": "6:13-6:20|6:1-6:20|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 252, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": [] + }, { + "usr": 16985894625255407295, + "detailed_name": "enum Foo : int {}", + "qual_name_offset": 5, + "short_name": "Foo", + "spell": "1:6-1:9|1:1-4:2|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 10, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": [] + }], + "usr2var": [{ + "usr": 439339022761937396, + "detailed_name": "A", + "qual_name_offset": 0, + "short_name": "A", + "hover": "A = 0", + "spell": "2:3-2:4|2:3-2:4|1026|-1", + "type": 16985894625255407295, + "kind": 22, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 15962370213938840720, + "detailed_name": "B = 20", + "qual_name_offset": 0, + "short_name": "B", + "spell": "3:3-3:4|3:3-3:9|1026|-1", + "type": 16985894625255407295, + "kind": 22, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 16614320383091394267, + "detailed_name": "E::E0", + "qual_name_offset": 0, + "short_name": "E0", + "hover": "E::E0 = 0", + "spell": "9:3-9:5|9:3-9:5|1026|-1", + "type": 2986879766914123941, + "kind": 22, + "parent_kind": 10, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 16847439761518576294, + "detailed_name": "E::E20 = 20", + "qual_name_offset": 0, + "short_name": "E20", + "spell": "10:3-10:6|10:3-10:11|1026|-1", + "type": 2986879766914123941, + "kind": 22, + "parent_kind": 10, + "storage": 0, + "declarations": [], + "uses": [] + }] +} +*/ diff --git a/index_tests/enums/enum_usage.cc b/index_tests/enums/enum_usage.cc new file mode 100644 index 000000000..c3a9a6416 --- /dev/null +++ b/index_tests/enums/enum_usage.cc @@ -0,0 +1,78 @@ +enum class Foo { + A, + B = 20 +}; + +Foo x = Foo::A; + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [], + "usr2type": [{ + "usr": 16985894625255407295, + "detailed_name": "enum class Foo : int {}", + "qual_name_offset": 11, + "short_name": "Foo", + "spell": "1:12-1:15|1:1-4:2|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [{ + "L": 439339022761937396, + "R": -1 + }, { + "L": 15962370213938840720, + "R": -1 + }], + "alias_of": 0, + "kind": 10, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [10677751717622394455], + "uses": ["6:1-6:4|4|-1", "6:9-6:12|4|-1"] + }], + "usr2var": [{ + "usr": 439339022761937396, + "detailed_name": "Foo::A", + "qual_name_offset": 0, + "short_name": "A", + "hover": "Foo::A = 0", + "spell": "2:3-2:4|2:3-2:4|1026|-1", + "type": 16985894625255407295, + "kind": 22, + "parent_kind": 10, + "storage": 0, + "declarations": [], + "uses": ["6:14-6:15|4|-1"] + }, { + "usr": 10677751717622394455, + "detailed_name": "Foo x", + "qual_name_offset": 4, + "short_name": "x", + "hover": "Foo x = Foo::A", + "spell": "6:5-6:6|6:1-6:15|2|-1", + "type": 16985894625255407295, + "kind": 13, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 15962370213938840720, + "detailed_name": "Foo::B = 20", + "qual_name_offset": 0, + "short_name": "B", + "spell": "3:3-3:4|3:3-3:9|1026|-1", + "type": 16985894625255407295, + "kind": 22, + "parent_kind": 10, + "storage": 0, + "declarations": [], + "uses": [] + }] +} +*/ diff --git a/index_tests/foobar.cc b/index_tests/foobar.cc new file mode 100644 index 000000000..6337f1eff --- /dev/null +++ b/index_tests/foobar.cc @@ -0,0 +1,112 @@ +enum A {}; +enum B {}; + +template +struct Foo { + struct Inner {}; +}; + +Foo::Inner a; +Foo b; +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [], + "usr2type": [{ + "usr": 6697181287623958829, + "detailed_name": "enum A {}", + "qual_name_offset": 5, + "short_name": "A", + "spell": "1:6-1:7|1:1-1:10|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 10, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": ["9:5-9:6|4|-1"] + }, { + "usr": 10528472276654770367, + "detailed_name": "struct Foo {}", + "qual_name_offset": 7, + "short_name": "Foo", + "spell": "5:8-5:11|5:1-7:2|2|-1", + "bases": [], + "funcs": [], + "types": [13938528237873543349], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [12028309045033782423], + "uses": ["9:1-9:4|4|-1", "10:1-10:4|4|-1"] + }, { + "usr": 13892793056005362145, + "detailed_name": "enum B {}", + "qual_name_offset": 5, + "short_name": "B", + "spell": "2:6-2:7|2:1-2:10|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 10, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": ["10:5-10:6|4|-1"] + }, { + "usr": 13938528237873543349, + "detailed_name": "struct Foo::Inner {}", + "qual_name_offset": 7, + "short_name": "Inner", + "spell": "6:10-6:15|6:3-6:18|1026|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 23, + "declarations": [], + "derived": [], + "instances": [16721564935990383768], + "uses": ["9:9-9:14|4|-1"] + }], + "usr2var": [{ + "usr": 12028309045033782423, + "detailed_name": "Foo b", + "qual_name_offset": 7, + "short_name": "b", + "spell": "10:8-10:9|10:1-10:9|2|-1", + "type": 10528472276654770367, + "kind": 13, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 16721564935990383768, + "detailed_name": "Foo::Inner a", + "qual_name_offset": 14, + "short_name": "a", + "spell": "9:15-9:16|9:1-9:16|2|-1", + "type": 13938528237873543349, + "kind": 13, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "uses": [] + }] +} +*/ diff --git a/index_tests/function_declaration.cc b/index_tests/function_declaration.cc new file mode 100644 index 000000000..49225d2f3 --- /dev/null +++ b/index_tests/function_declaration.cc @@ -0,0 +1,26 @@ +void foo(int a, int b); + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 2747674671862363334, + "detailed_name": "void foo(int a, int b)", + "qual_name_offset": 5, + "short_name": "foo", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": ["1:6-1:9|1:1-1:23|1|-1"], + "derived": [], + "uses": [] + }], + "usr2type": [], + "usr2var": [] +} +*/ diff --git a/index_tests/function_declaration_definition.cc b/index_tests/function_declaration_definition.cc new file mode 100644 index 000000000..5350dbc2b --- /dev/null +++ b/index_tests/function_declaration_definition.cc @@ -0,0 +1,29 @@ +void foo(); + +void foo() {} + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 4259594751088586730, + "detailed_name": "void foo()", + "qual_name_offset": 5, + "short_name": "foo", + "spell": "3:6-3:9|3:1-3:14|2|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": ["1:6-1:9|1:1-1:11|1|-1"], + "derived": [], + "uses": [] + }], + "usr2type": [], + "usr2var": [] +} +*/ diff --git a/index_tests/function_definition.cc b/index_tests/function_definition.cc new file mode 100644 index 000000000..2176a6584 --- /dev/null +++ b/index_tests/function_definition.cc @@ -0,0 +1,27 @@ +void foo() {} + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 4259594751088586730, + "detailed_name": "void foo()", + "qual_name_offset": 5, + "short_name": "foo", + "spell": "1:6-1:9|1:1-1:14|2|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }], + "usr2type": [], + "usr2var": [] +} +*/ diff --git a/index_tests/inheritance/class_inherit.cc b/index_tests/inheritance/class_inherit.cc new file mode 100644 index 000000000..791d98654 --- /dev/null +++ b/index_tests/inheritance/class_inherit.cc @@ -0,0 +1,47 @@ +class Parent {}; +class Derived : public Parent {}; + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [], + "usr2type": [{ + "usr": 3866412049634585509, + "detailed_name": "class Parent {}", + "qual_name_offset": 6, + "short_name": "Parent", + "spell": "1:7-1:13|1:1-1:16|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": [], + "derived": [10963370434658308541], + "instances": [], + "uses": ["2:24-2:30|2052|-1"] + }, { + "usr": 10963370434658308541, + "detailed_name": "class Derived : public Parent {}", + "qual_name_offset": 6, + "short_name": "Derived", + "spell": "2:7-2:14|2:1-2:33|2|-1", + "bases": [3866412049634585509], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": [] + }], + "usr2var": [] +} +*/ diff --git a/index_tests/inheritance/class_inherit_templated_parent.cc b/index_tests/inheritance/class_inherit_templated_parent.cc new file mode 100644 index 000000000..4e3f5bded --- /dev/null +++ b/index_tests/inheritance/class_inherit_templated_parent.cc @@ -0,0 +1,109 @@ +template +class Base1 {}; + +template +class Base2 {}; + +template +class Derived1 : Base1 {}; + +template +class Derived2 : Base2 {}; + +class Derived : Base1<3>, Base2, Derived1<4>, Derived2 {}; + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [], + "usr2type": [{ + "usr": 5863733211528032190, + "detailed_name": "class Derived1 : Base1 {}", + "qual_name_offset": 6, + "short_name": "Derived1", + "spell": "8:7-8:15|8:1-8:29|2|-1", + "bases": [11930058224338108382], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": [], + "derived": [10963370434658308541], + "instances": [], + "uses": ["13:43-13:51|2052|-1"] + }, { + "usr": 10651399730831737929, + "detailed_name": "class Derived2 : Base2 {}", + "qual_name_offset": 6, + "short_name": "Derived2", + "spell": "11:7-11:15|11:1-11:29|2|-1", + "bases": [11118288764693061434], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": [], + "derived": [10963370434658308541], + "instances": [], + "uses": ["13:56-13:64|2052|-1"] + }, { + "usr": 10963370434658308541, + "detailed_name": "class Derived : Base1<3>, Base2, Derived1<4>, Derived2 {}", + "qual_name_offset": 6, + "short_name": "Derived", + "spell": "13:7-13:14|13:1-13:76|2|-1", + "bases": [11930058224338108382, 11118288764693061434, 5863733211528032190, 10651399730831737929], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": ["13:33-13:40|2052|-1", "13:65-13:72|2052|-1"] + }, { + "usr": 11118288764693061434, + "detailed_name": "class Base2 {}", + "qual_name_offset": 6, + "short_name": "Base2", + "spell": "5:7-5:12|5:1-5:15|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": [], + "derived": [10651399730831737929, 10963370434658308541], + "instances": [], + "uses": ["11:18-11:23|2052|-1", "13:27-13:32|2052|-1"] + }, { + "usr": 11930058224338108382, + "detailed_name": "class Base1 {}", + "qual_name_offset": 6, + "short_name": "Base1", + "spell": "2:7-2:12|2:1-2:15|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": [], + "derived": [5863733211528032190, 10963370434658308541], + "instances": [], + "uses": ["8:18-8:23|2052|-1", "13:17-13:22|2052|-1"] + }], + "usr2var": [] +} +*/ diff --git a/index_tests/inheritance/class_multiple_inherit.cc b/index_tests/inheritance/class_multiple_inherit.cc new file mode 100644 index 000000000..49b09501f --- /dev/null +++ b/index_tests/inheritance/class_multiple_inherit.cc @@ -0,0 +1,83 @@ +class Root {}; +class MiddleA : public Root {}; +class MiddleB : public Root {}; +class Derived : public MiddleA, public MiddleB {}; + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [], + "usr2type": [{ + "usr": 3897841498936210886, + "detailed_name": "class Root {}", + "qual_name_offset": 6, + "short_name": "Root", + "spell": "1:7-1:11|1:1-1:14|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": [], + "derived": [11863524815063131483, 14022569716337624303], + "instances": [], + "uses": ["2:24-2:28|2052|-1", "3:24-3:28|2052|-1"] + }, { + "usr": 10963370434658308541, + "detailed_name": "class Derived : public MiddleA, public MiddleB {}", + "qual_name_offset": 6, + "short_name": "Derived", + "spell": "4:7-4:14|4:1-4:50|2|-1", + "bases": [11863524815063131483, 14022569716337624303], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": [] + }, { + "usr": 11863524815063131483, + "detailed_name": "class MiddleA : public Root {}", + "qual_name_offset": 6, + "short_name": "MiddleA", + "spell": "2:7-2:14|2:1-2:31|2|-1", + "bases": [3897841498936210886], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": [], + "derived": [10963370434658308541], + "instances": [], + "uses": ["4:24-4:31|2052|-1"] + }, { + "usr": 14022569716337624303, + "detailed_name": "class MiddleB : public Root {}", + "qual_name_offset": 6, + "short_name": "MiddleB", + "spell": "3:7-3:14|3:1-3:31|2|-1", + "bases": [3897841498936210886], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": [], + "derived": [10963370434658308541], + "instances": [], + "uses": ["4:40-4:47|2052|-1"] + }], + "usr2var": [] +} +*/ diff --git a/index_tests/inheritance/function_override.cc b/index_tests/inheritance/function_override.cc new file mode 100644 index 000000000..319b43946 --- /dev/null +++ b/index_tests/inheritance/function_override.cc @@ -0,0 +1,80 @@ +class Root { + virtual void foo(); +}; +class Derived : public Root { + void foo() override {} +}; + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 6666242542855173890, + "detailed_name": "void Derived::foo()", + "qual_name_offset": 5, + "short_name": "foo", + "spell": "5:8-5:11|5:3-5:25|5186|-1", + "bases": [9948027785633571339], + "vars": [], + "callees": [], + "kind": 6, + "parent_kind": 5, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }, { + "usr": 9948027785633571339, + "detailed_name": "virtual void Root::foo()", + "qual_name_offset": 13, + "short_name": "foo", + "bases": [], + "vars": [], + "callees": [], + "kind": 6, + "parent_kind": 0, + "storage": 0, + "declarations": ["2:16-2:19|2:3-2:21|1089|-1"], + "derived": [6666242542855173890], + "uses": [] + }], + "usr2type": [{ + "usr": 3897841498936210886, + "detailed_name": "class Root {}", + "qual_name_offset": 6, + "short_name": "Root", + "spell": "1:7-1:11|1:1-3:2|2|-1", + "bases": [], + "funcs": [9948027785633571339], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": [], + "derived": [10963370434658308541], + "instances": [], + "uses": ["4:24-4:28|2052|-1"] + }, { + "usr": 10963370434658308541, + "detailed_name": "class Derived : public Root {}", + "qual_name_offset": 6, + "short_name": "Derived", + "spell": "4:7-4:14|4:1-6:2|2|-1", + "bases": [3897841498936210886], + "funcs": [6666242542855173890], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": [] + }], + "usr2var": [] +} +*/ diff --git a/index_tests/inheritance/interface_pure_virtual.cc b/index_tests/inheritance/interface_pure_virtual.cc new file mode 100644 index 000000000..fcf9841f5 --- /dev/null +++ b/index_tests/inheritance/interface_pure_virtual.cc @@ -0,0 +1,45 @@ +class IFoo { + virtual void foo() = 0; +}; + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 3277829753446788562, + "detailed_name": "virtual void IFoo::foo() = 0", + "qual_name_offset": 13, + "short_name": "foo", + "bases": [], + "vars": [], + "callees": [], + "kind": 6, + "parent_kind": 0, + "storage": 0, + "declarations": ["2:16-2:19|2:3-2:25|1089|-1"], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 9949214233977131946, + "detailed_name": "class IFoo {}", + "qual_name_offset": 6, + "short_name": "IFoo", + "spell": "1:7-1:11|1:1-3:2|2|-1", + "bases": [], + "funcs": [3277829753446788562], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": [] + }], + "usr2var": [] +} +*/ diff --git a/index_tests/inheritance/multiple_base_functions.cc b/index_tests/inheritance/multiple_base_functions.cc new file mode 100644 index 000000000..f4ef3c4a2 --- /dev/null +++ b/index_tests/inheritance/multiple_base_functions.cc @@ -0,0 +1,116 @@ +struct Base0 { + virtual ~Base0() { } +}; +struct Base1 { + virtual ~Base1() { } +}; +struct Derived : Base0, Base1 { + ~Derived() override { } +}; + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 8401779086123965305, + "detailed_name": "virtual Base1::~Base1() noexcept", + "qual_name_offset": 8, + "short_name": "~Base1", + "spell": "5:11-5:17|5:3-5:23|1090|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 6, + "parent_kind": 23, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }, { + "usr": 13164726294460837993, + "detailed_name": "Derived::~Derived() noexcept", + "qual_name_offset": 0, + "short_name": "~Derived", + "spell": "8:3-8:11|8:3-8:26|5186|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 6, + "parent_kind": 23, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }, { + "usr": 16347272523198263017, + "detailed_name": "virtual Base0::~Base0() noexcept", + "qual_name_offset": 8, + "short_name": "~Base0", + "spell": "2:11-2:17|2:3-2:23|1090|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 6, + "parent_kind": 23, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 10963370434658308541, + "detailed_name": "struct Derived : Base0, Base1 {}", + "qual_name_offset": 7, + "short_name": "Derived", + "spell": "7:8-7:15|7:1-9:2|2|-1", + "bases": [11628904180681204356, 15826803741381445676], + "funcs": [13164726294460837993], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": ["8:4-8:11|4|-1"] + }, { + "usr": 11628904180681204356, + "detailed_name": "struct Base0 {}", + "qual_name_offset": 7, + "short_name": "Base0", + "spell": "1:8-1:13|1:1-3:2|2|-1", + "bases": [], + "funcs": [16347272523198263017], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": [], + "derived": [10963370434658308541], + "instances": [], + "uses": ["2:12-2:17|4|-1", "7:18-7:23|2052|-1"] + }, { + "usr": 15826803741381445676, + "detailed_name": "struct Base1 {}", + "qual_name_offset": 7, + "short_name": "Base1", + "spell": "4:8-4:13|4:1-6:2|2|-1", + "bases": [], + "funcs": [8401779086123965305], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": [], + "derived": [10963370434658308541], + "instances": [], + "uses": ["5:12-5:17|4|-1", "7:25-7:30|2052|-1"] + }], + "usr2var": [] +} +*/ \ No newline at end of file diff --git a/index_tests/lambdas/lambda.cc b/index_tests/lambdas/lambda.cc new file mode 100644 index 000000000..059d148b8 --- /dev/null +++ b/index_tests/lambdas/lambda.cc @@ -0,0 +1,121 @@ +void foo() { + int x; + + auto dosomething = [&x](int y) { + ++x; + ++y; + }; + + dosomething(1); + dosomething(1); + dosomething(1); +} + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 4259594751088586730, + "detailed_name": "void foo()", + "qual_name_offset": 5, + "short_name": "foo", + "spell": "1:6-1:9|1:1-12:2|2|-1", + "bases": [], + "vars": [12666114896600231317, 2981279427664991319], + "callees": ["9:14-9:15|17926497908620168464|3|16420", "10:14-10:15|17926497908620168464|3|16420", "11:14-11:15|17926497908620168464|3|16420"], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }, { + "usr": 17926497908620168464, + "detailed_name": "inline void foo()::(anon class)::operator()(int y) const", + "qual_name_offset": 12, + "short_name": "operator()", + "bases": [], + "vars": [], + "callees": [], + "kind": 6, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": ["9:14-9:15|16420|-1", "10:14-10:15|16420|-1", "11:14-11:15|16420|-1"] + }], + "usr2type": [{ + "usr": 53, + "detailed_name": "", + "qual_name_offset": 0, + "short_name": "", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 0, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [12666114896600231317], + "uses": [] + }, { + "usr": 14635009347499519042, + "detailed_name": "", + "qual_name_offset": 0, + "short_name": "", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 0, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [2981279427664991319], + "uses": [] + }], + "usr2var": [{ + "usr": 2981279427664991319, + "detailed_name": "(lambda) dosomething", + "qual_name_offset": 9, + "short_name": "dosomething", + "hover": "(lambda) dosomething", + "spell": "4:8-4:19|4:3-7:4|2|-1", + "type": 14635009347499519042, + "kind": 13, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": ["9:3-9:14|4|-1", "10:3-10:14|4|-1", "11:3-11:14|4|-1"] + }, { + "usr": 12666114896600231317, + "detailed_name": "int x", + "qual_name_offset": 4, + "short_name": "x", + "spell": "2:7-2:8|2:3-2:8|2|-1", + "type": 53, + "kind": 13, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": ["4:24-4:25|4|-1", "5:7-5:8|28|-1"] + }, { + "usr": 12879188959314906706, + "detailed_name": "int y", + "qual_name_offset": 4, + "short_name": "y", + "spell": "4:31-4:32|4:27-4:32|2|-1", + "type": 0, + "kind": 253, + "parent_kind": 6, + "storage": 0, + "declarations": [], + "uses": ["6:7-6:8|28|-1"] + }] +} +*/ diff --git a/index_tests/macros/complex.cc b/index_tests/macros/complex.cc new file mode 100644 index 000000000..55ac9624c --- /dev/null +++ b/index_tests/macros/complex.cc @@ -0,0 +1,95 @@ +#define FOO(aaa, bbb) \ + int a();\ + int a() { return aaa + bbb; } + + +int make1() { + return 3; +} +const int make2 = 5; + + +FOO(make1(), make2); + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 9720930732776154610, + "detailed_name": "int a()", + "qual_name_offset": 4, + "short_name": "a", + "spell": "12:1-12:20|12:1-12:4|2|-1", + "bases": [], + "vars": [], + "callees": ["12:5-12:10|14400399977994209582|3|16420"], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": ["12:1-12:20|12:1-12:4|1|-1"], + "derived": [], + "uses": ["2:7-2:8|64|0", "3:7-3:8|64|0"] + }, { + "usr": 14400399977994209582, + "detailed_name": "int make1()", + "qual_name_offset": 4, + "short_name": "make1", + "spell": "6:5-6:10|6:1-8:2|2|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": ["12:5-12:10|16420|-1", "12:5-12:10|64|0"] + }], + "usr2type": [{ + "usr": 53, + "detailed_name": "", + "qual_name_offset": 0, + "short_name": "", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 0, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [2878407290385495202], + "uses": [] + }], + "usr2var": [{ + "usr": 2878407290385495202, + "detailed_name": "const int make2", + "qual_name_offset": 10, + "short_name": "make2", + "hover": "const int make2 = 5", + "spell": "9:11-9:16|9:1-9:20|2|-1", + "type": 53, + "kind": 13, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "uses": ["12:14-12:19|12|-1", "12:14-12:19|64|0"] + }, { + "usr": 14219599523415845943, + "detailed_name": "FOO", + "qual_name_offset": 0, + "short_name": "FOO", + "hover": "#define FOO(aaa, bbb) \\\n int a();\\\n int a() { return aaa + bbb; }", + "spell": "1:9-1:12|1:9-3:32|2|-1", + "type": 0, + "kind": 255, + "parent_kind": 1, + "storage": 0, + "declarations": [], + "uses": ["12:1-12:4|64|-1"] + }] +} +*/ \ No newline at end of file diff --git a/index_tests/macros/foo.cc b/index_tests/macros/foo.cc new file mode 100644 index 000000000..a75078311 --- /dev/null +++ b/index_tests/macros/foo.cc @@ -0,0 +1,106 @@ +#define A 5 +#define DISALLOW(type) type(type&&) = delete; + +struct Foo { + DISALLOW(Foo); +}; + +int x = A; + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 13788753348312146871, + "detailed_name": "Foo::Foo(Foo &&) = delete", + "qual_name_offset": 0, + "short_name": "Foo", + "spell": "5:12-5:15|5:3-5:11|1026|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 9, + "parent_kind": 23, + "storage": 0, + "declarations": [], + "derived": [], + "uses": ["5:12-5:15|64|0"] + }], + "usr2type": [{ + "usr": 53, + "detailed_name": "", + "qual_name_offset": 0, + "short_name": "", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 0, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [10677751717622394455], + "uses": [] + }, { + "usr": 15041163540773201510, + "detailed_name": "struct Foo {}", + "qual_name_offset": 7, + "short_name": "Foo", + "spell": "4:8-4:11|4:1-6:2|2|-1", + "bases": [], + "funcs": [13788753348312146871], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": ["5:12-5:15|4|-1", "5:12-5:15|64|0"] + }], + "usr2var": [{ + "usr": 1569772797058982873, + "detailed_name": "A", + "qual_name_offset": 0, + "short_name": "A", + "hover": "#define A 5", + "spell": "1:9-1:10|1:9-1:12|2|-1", + "type": 0, + "kind": 255, + "parent_kind": 1, + "storage": 0, + "declarations": [], + "uses": ["8:9-8:10|64|-1"] + }, { + "usr": 4904139678698066671, + "detailed_name": "DISALLOW", + "qual_name_offset": 0, + "short_name": "DISALLOW", + "hover": "#define DISALLOW(type) type(type&&) = delete;", + "spell": "2:9-2:17|2:9-2:46|2|-1", + "type": 0, + "kind": 255, + "parent_kind": 1, + "storage": 0, + "declarations": [], + "uses": ["5:3-5:11|64|-1"] + }, { + "usr": 10677751717622394455, + "detailed_name": "int x", + "qual_name_offset": 4, + "short_name": "x", + "hover": "int x = A", + "spell": "8:5-8:6|8:1-8:10|2|-1", + "type": 53, + "kind": 13, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "uses": [] + }] +} +*/ diff --git a/index_tests/method_declaration.cc b/index_tests/method_declaration.cc new file mode 100644 index 000000000..c777fa497 --- /dev/null +++ b/index_tests/method_declaration.cc @@ -0,0 +1,49 @@ +class Foo { + void foo(); +}; + +/* +// NOTE: Lack of declaring_type in functions and funcs in Foo is okay, because +// those are processed when we find the definition for Foo::foo. Pure +// virtuals are treated specially and get added to the type immediately. + +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 17922201480358737771, + "detailed_name": "void Foo::foo()", + "qual_name_offset": 5, + "short_name": "foo", + "bases": [], + "vars": [], + "callees": [], + "kind": 6, + "parent_kind": 0, + "storage": 0, + "declarations": ["2:8-2:11|2:3-2:13|1025|-1"], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 15041163540773201510, + "detailed_name": "class Foo {}", + "qual_name_offset": 6, + "short_name": "Foo", + "spell": "1:7-1:10|1:1-3:2|2|-1", + "bases": [], + "funcs": [17922201480358737771], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": [] + }], + "usr2var": [] +} +*/ diff --git a/index_tests/method_definition.cc b/index_tests/method_definition.cc new file mode 100644 index 000000000..94ff8e564 --- /dev/null +++ b/index_tests/method_definition.cc @@ -0,0 +1,48 @@ +class Foo { + void foo() const; +}; + +void Foo::foo() const {} + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 6446764306530590711, + "detailed_name": "void Foo::foo() const", + "qual_name_offset": 5, + "short_name": "foo", + "spell": "5:11-5:14|5:1-5:25|1026|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 6, + "parent_kind": 5, + "storage": 0, + "declarations": ["2:8-2:11|2:3-2:19|1025|-1"], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 15041163540773201510, + "detailed_name": "class Foo {}", + "qual_name_offset": 6, + "short_name": "Foo", + "spell": "1:7-1:10|1:1-3:2|2|-1", + "bases": [], + "funcs": [6446764306530590711], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": ["5:6-5:9|4|-1"] + }], + "usr2var": [] +} +*/ diff --git a/index_tests/method_inline_declaration.cc b/index_tests/method_inline_declaration.cc new file mode 100644 index 000000000..774bac0c0 --- /dev/null +++ b/index_tests/method_inline_declaration.cc @@ -0,0 +1,46 @@ +class Foo { + void foo() {} +}; + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 17922201480358737771, + "detailed_name": "void Foo::foo()", + "qual_name_offset": 5, + "short_name": "foo", + "spell": "2:8-2:11|2:3-2:16|1026|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 6, + "parent_kind": 5, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 15041163540773201510, + "detailed_name": "class Foo {}", + "qual_name_offset": 6, + "short_name": "Foo", + "spell": "1:7-1:10|1:1-3:2|2|-1", + "bases": [], + "funcs": [17922201480358737771], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": [] + }], + "usr2var": [] +} +*/ diff --git a/index_tests/multi_file/funky_enum.cc b/index_tests/multi_file/funky_enum.cc new file mode 100644 index 000000000..b30c456d7 --- /dev/null +++ b/index_tests/multi_file/funky_enum.cc @@ -0,0 +1,87 @@ +enum Foo { +#include "funky_enum.h" +}; + +/* +// TODO: In the future try to have better support for types defined across +// multiple files. + +OUTPUT: funky_enum.h +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [], + "usr2type": [], + "usr2var": [{ + "usr": 439339022761937396, + "detailed_name": "A", + "qual_name_offset": 0, + "short_name": "A", + "hover": "A = 0", + "comments": "This file cannot be built directory. It is included in an enum definition of\nanother file.", + "spell": "4:1-4:2|4:1-4:2|1026|-1", + "type": 16985894625255407295, + "kind": 22, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 8524995777615948802, + "detailed_name": "C", + "qual_name_offset": 0, + "short_name": "C", + "hover": "C = 2", + "comments": "This file cannot be built directory. It is included in an enum definition of\nanother file.", + "spell": "6:1-6:2|6:1-6:2|1026|-1", + "type": 16985894625255407295, + "kind": 22, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 15962370213938840720, + "detailed_name": "B", + "qual_name_offset": 0, + "short_name": "B", + "hover": "B = 1", + "comments": "This file cannot be built directory. It is included in an enum definition of\nanother file.", + "spell": "5:1-5:2|5:1-5:2|1026|-1", + "type": 16985894625255407295, + "kind": 22, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "uses": [] + }] +} +OUTPUT: funky_enum.cc +{ + "includes": [{ + "line": 1, + "resolved_path": "&funky_enum.h" + }], + "skipped_ranges": [], + "usr2func": [], + "usr2type": [{ + "usr": 16985894625255407295, + "detailed_name": "enum Foo {}", + "qual_name_offset": 5, + "short_name": "Foo", + "spell": "1:6-1:9|1:1-3:2|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 10, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": [] + }], + "usr2var": [] +} +*/ \ No newline at end of file diff --git a/index_tests/multi_file/funky_enum.h b/index_tests/multi_file/funky_enum.h new file mode 100644 index 000000000..1acdf0566 --- /dev/null +++ b/index_tests/multi_file/funky_enum.h @@ -0,0 +1,6 @@ +// This file cannot be built directory. It is included in an enum definition of +// another file. + +A, +B, +C diff --git a/index_tests/multi_file/header.h b/index_tests/multi_file/header.h new file mode 100644 index 000000000..717ef8213 --- /dev/null +++ b/index_tests/multi_file/header.h @@ -0,0 +1,18 @@ +#pragma once + +struct Base {}; + +struct SameFileDerived : Base {}; + +using Foo0 = SameFileDerived; + +template +void Foo1() {} + +template +struct Foo2 {}; + +enum Foo3 { A, B, C }; + +int Foo4; +static int Foo5; \ No newline at end of file diff --git a/index_tests/multi_file/impl.cc b/index_tests/multi_file/impl.cc new file mode 100644 index 000000000..9e6e477ae --- /dev/null +++ b/index_tests/multi_file/impl.cc @@ -0,0 +1,235 @@ +#include "header.h" + +void Impl() { + Foo1(); +} + +/* +OUTPUT: header.h +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 11650481237659640387, + "detailed_name": "void Foo1()", + "qual_name_offset": 5, + "short_name": "Foo1", + "spell": "10:6-10:10|10:1-10:15|2|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 53, + "detailed_name": "", + "qual_name_offset": 0, + "short_name": "", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 0, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [2638219001294786365, 8395885290297540138], + "uses": [] + }, { + "usr": 529393482671181129, + "detailed_name": "struct Foo2 {}", + "qual_name_offset": 7, + "short_name": "Foo2", + "spell": "13:8-13:12|13:1-13:15|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": [] + }, { + "usr": 619345544228965342, + "detailed_name": "using Foo0 = SameFileDerived", + "qual_name_offset": 6, + "short_name": "Foo0", + "spell": "7:7-7:11|7:1-7:29|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 16750616846959666305, + "kind": 252, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": [] + }, { + "usr": 4481210672785600703, + "detailed_name": "enum Foo3 {}", + "qual_name_offset": 5, + "short_name": "Foo3", + "spell": "15:6-15:10|15:1-15:22|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 10, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": [] + }, { + "usr": 8420119006782424779, + "detailed_name": "struct Base {}", + "qual_name_offset": 7, + "short_name": "Base", + "spell": "3:8-3:12|3:1-3:15|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": [], + "derived": [16750616846959666305], + "instances": [], + "uses": ["5:26-5:30|2052|-1"] + }, { + "usr": 16750616846959666305, + "detailed_name": "struct SameFileDerived : Base {}", + "qual_name_offset": 7, + "short_name": "SameFileDerived", + "spell": "5:8-5:23|5:1-5:33|2|-1", + "bases": [8420119006782424779], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": ["7:14-7:29|4|-1"] + }], + "usr2var": [{ + "usr": 2638219001294786365, + "detailed_name": "int Foo4", + "qual_name_offset": 4, + "short_name": "Foo4", + "spell": "17:5-17:9|17:1-17:9|2|-1", + "type": 53, + "kind": 13, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 6141718166919284735, + "detailed_name": "A", + "qual_name_offset": 0, + "short_name": "A", + "hover": "A = 0", + "spell": "15:13-15:14|15:13-15:14|1026|-1", + "type": 4481210672785600703, + "kind": 22, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 7285646116511901840, + "detailed_name": "C", + "qual_name_offset": 0, + "short_name": "C", + "hover": "C = 2", + "spell": "15:19-15:20|15:19-15:20|1026|-1", + "type": 4481210672785600703, + "kind": 22, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 8395885290297540138, + "detailed_name": "static int Foo5", + "qual_name_offset": 11, + "short_name": "Foo5", + "spell": "18:12-18:16|18:1-18:16|2|-1", + "type": 53, + "kind": 13, + "parent_kind": 0, + "storage": 2, + "declarations": [], + "uses": [] + }, { + "usr": 17716334512218775320, + "detailed_name": "B", + "qual_name_offset": 0, + "short_name": "B", + "hover": "B = 1", + "spell": "15:16-15:17|15:16-15:17|1026|-1", + "type": 4481210672785600703, + "kind": 22, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "uses": [] + }] +} +OUTPUT: impl.cc +{ + "includes": [{ + "line": 0, + "resolved_path": "&header.h" + }], + "skipped_ranges": [], + "usr2func": [{ + "usr": 5817708529036841195, + "detailed_name": "void Impl()", + "qual_name_offset": 5, + "short_name": "Impl", + "spell": "3:6-3:10|3:1-5:2|2|-1", + "bases": [], + "vars": [], + "callees": ["4:3-4:7|11650481237659640387|3|16420"], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }, { + "usr": 11650481237659640387, + "detailed_name": "void Foo1()", + "qual_name_offset": 5, + "short_name": "Foo1", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": ["4:3-4:7|16420|-1"] + }], + "usr2type": [], + "usr2var": [] +} +*/ diff --git a/index_tests/multi_file/simple_header.h b/index_tests/multi_file/simple_header.h new file mode 100644 index 000000000..3bdf008ae --- /dev/null +++ b/index_tests/multi_file/simple_header.h @@ -0,0 +1,3 @@ +#pragma once + +void header(); \ No newline at end of file diff --git a/index_tests/multi_file/simple_impl.cc b/index_tests/multi_file/simple_impl.cc new file mode 100644 index 000000000..e97af2376 --- /dev/null +++ b/index_tests/multi_file/simple_impl.cc @@ -0,0 +1,70 @@ +#include "simple_header.h" + +void impl() { + header(); +} + +/* +OUTPUT: simple_header.h +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 16236105532929924676, + "detailed_name": "void header()", + "qual_name_offset": 5, + "short_name": "header", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": ["3:6-3:12|3:1-3:14|1|-1"], + "derived": [], + "uses": [] + }], + "usr2type": [], + "usr2var": [] +} +OUTPUT: simple_impl.cc +{ + "includes": [{ + "line": 0, + "resolved_path": "&simple_header.h" + }], + "skipped_ranges": [], + "usr2func": [{ + "usr": 3373269392705484958, + "detailed_name": "void impl()", + "qual_name_offset": 5, + "short_name": "impl", + "spell": "3:6-3:10|3:1-5:2|2|-1", + "bases": [], + "vars": [], + "callees": ["4:3-4:9|16236105532929924676|3|16420"], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }, { + "usr": 16236105532929924676, + "detailed_name": "void header()", + "qual_name_offset": 5, + "short_name": "header", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": ["4:3-4:9|16420|-1"] + }], + "usr2type": [], + "usr2var": [] +} +*/ diff --git a/index_tests/multi_file/static.cc b/index_tests/multi_file/static.cc new file mode 100644 index 000000000..d344cb7e5 --- /dev/null +++ b/index_tests/multi_file/static.cc @@ -0,0 +1,87 @@ +#include "static.h" + +void Buffer::CreateSharedBuffer() {} + +/* +OUTPUT: static.h +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 14576076421851654759, + "detailed_name": "static void Buffer::CreateSharedBuffer()", + "qual_name_offset": 12, + "short_name": "CreateSharedBuffer", + "bases": [], + "vars": [], + "callees": [], + "kind": 254, + "parent_kind": 0, + "storage": 0, + "declarations": ["4:15-4:33|4:3-4:35|1025|-1"], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 9411323049603567600, + "detailed_name": "struct Buffer {}", + "qual_name_offset": 7, + "short_name": "Buffer", + "spell": "3:8-3:14|3:1-5:2|2|-1", + "bases": [], + "funcs": [14576076421851654759], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": [] + }], + "usr2var": [] +} +OUTPUT: static.cc +{ + "includes": [{ + "line": 0, + "resolved_path": "&static.h" + }], + "skipped_ranges": [], + "usr2func": [{ + "usr": 14576076421851654759, + "detailed_name": "static void Buffer::CreateSharedBuffer()", + "qual_name_offset": 12, + "short_name": "CreateSharedBuffer", + "spell": "3:14-3:32|3:1-3:37|1026|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 254, + "parent_kind": 23, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 9411323049603567600, + "detailed_name": "struct Buffer {}", + "qual_name_offset": 7, + "short_name": "Buffer", + "bases": [], + "funcs": [14576076421851654759], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": ["3:6-3:12|4|-1"] + }], + "usr2var": [] +} +*/ \ No newline at end of file diff --git a/index_tests/multi_file/static.h b/index_tests/multi_file/static.h new file mode 100644 index 000000000..4204f7859 --- /dev/null +++ b/index_tests/multi_file/static.h @@ -0,0 +1,5 @@ +#pragma once + +struct Buffer { + static void CreateSharedBuffer(); +}; diff --git a/index_tests/namespaces/anonymous_function.cc b/index_tests/namespaces/anonymous_function.cc new file mode 100644 index 000000000..e8bebc888 --- /dev/null +++ b/index_tests/namespaces/anonymous_function.cc @@ -0,0 +1,28 @@ +namespace { +void foo(); +} + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 5010253035933134245, + "detailed_name": "void (anon ns)::foo()", + "qual_name_offset": 5, + "short_name": "foo", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": ["2:6-2:9|2:1-2:11|1|-1"], + "derived": [], + "uses": [] + }], + "usr2type": [], + "usr2var": [] +} +*/ diff --git a/index_tests/namespaces/function_declaration.cc b/index_tests/namespaces/function_declaration.cc new file mode 100644 index 000000000..fd16717e5 --- /dev/null +++ b/index_tests/namespaces/function_declaration.cc @@ -0,0 +1,44 @@ +namespace hello { +void foo(int a, int b); +} + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 18343102288837190527, + "detailed_name": "void hello::foo(int a, int b)", + "qual_name_offset": 5, + "short_name": "foo", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": ["2:6-2:9|2:1-2:23|1025|-1"], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 2029211996748007610, + "detailed_name": "namespace hello {}", + "qual_name_offset": 10, + "short_name": "hello", + "bases": [], + "funcs": [18343102288837190527], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 3, + "parent_kind": 0, + "declarations": ["1:11-1:16|1:1-3:2|1|-1"], + "derived": [], + "instances": [], + "uses": [] + }], + "usr2var": [] +} +*/ diff --git a/index_tests/namespaces/function_definition.cc b/index_tests/namespaces/function_definition.cc new file mode 100644 index 000000000..afede23d8 --- /dev/null +++ b/index_tests/namespaces/function_definition.cc @@ -0,0 +1,45 @@ +namespace hello { +void foo() {} +} + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 243328841292951622, + "detailed_name": "void hello::foo()", + "qual_name_offset": 5, + "short_name": "foo", + "spell": "2:6-2:9|2:1-2:14|1026|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 3, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 2029211996748007610, + "detailed_name": "namespace hello {}", + "qual_name_offset": 10, + "short_name": "hello", + "bases": [], + "funcs": [243328841292951622], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 3, + "parent_kind": 0, + "declarations": ["1:11-1:16|1:1-3:2|1|-1"], + "derived": [], + "instances": [], + "uses": [] + }], + "usr2var": [] +} +*/ diff --git a/index_tests/namespaces/method_declaration.cc b/index_tests/namespaces/method_declaration.cc new file mode 100644 index 000000000..236328cd5 --- /dev/null +++ b/index_tests/namespaces/method_declaration.cc @@ -0,0 +1,63 @@ +namespace hello { +class Foo { + void foo(); +}; +} + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 10487325150128053272, + "detailed_name": "void hello::Foo::foo()", + "qual_name_offset": 5, + "short_name": "foo", + "bases": [], + "vars": [], + "callees": [], + "kind": 6, + "parent_kind": 0, + "storage": 0, + "declarations": ["3:8-3:11|3:3-3:13|1025|-1"], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 2029211996748007610, + "detailed_name": "namespace hello {}", + "qual_name_offset": 10, + "short_name": "hello", + "bases": [], + "funcs": [], + "types": [4508214972876735896], + "vars": [], + "alias_of": 0, + "kind": 3, + "parent_kind": 0, + "declarations": ["1:11-1:16|1:1-5:2|1|-1"], + "derived": [], + "instances": [], + "uses": [] + }, { + "usr": 4508214972876735896, + "detailed_name": "class hello::Foo {}", + "qual_name_offset": 6, + "short_name": "Foo", + "spell": "2:7-2:10|2:1-4:2|1026|-1", + "bases": [], + "funcs": [10487325150128053272], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 3, + "declarations": [], + "derived": [], + "instances": [], + "uses": [] + }], + "usr2var": [] +} +*/ diff --git a/index_tests/namespaces/method_definition.cc b/index_tests/namespaces/method_definition.cc new file mode 100644 index 000000000..f2f47121c --- /dev/null +++ b/index_tests/namespaces/method_definition.cc @@ -0,0 +1,66 @@ +namespace hello { +class Foo { + void foo(); +}; + +void Foo::foo() {} +} + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 10487325150128053272, + "detailed_name": "void hello::Foo::foo()", + "qual_name_offset": 5, + "short_name": "foo", + "spell": "6:11-6:14|6:1-6:19|1026|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 6, + "parent_kind": 5, + "storage": 0, + "declarations": ["3:8-3:11|3:3-3:13|1025|-1"], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 2029211996748007610, + "detailed_name": "namespace hello {}", + "qual_name_offset": 10, + "short_name": "hello", + "bases": [], + "funcs": [], + "types": [4508214972876735896], + "vars": [], + "alias_of": 0, + "kind": 3, + "parent_kind": 0, + "declarations": ["1:11-1:16|1:1-7:2|1|-1"], + "derived": [], + "instances": [], + "uses": [] + }, { + "usr": 4508214972876735896, + "detailed_name": "class hello::Foo {}", + "qual_name_offset": 6, + "short_name": "Foo", + "spell": "2:7-2:10|2:1-4:2|1026|-1", + "bases": [], + "funcs": [10487325150128053272], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 3, + "declarations": [], + "derived": [], + "instances": [], + "uses": ["6:6-6:9|4|-1"] + }], + "usr2var": [] +} +*/ diff --git a/index_tests/namespaces/method_inline_declaration.cc b/index_tests/namespaces/method_inline_declaration.cc new file mode 100644 index 000000000..445bf61e9 --- /dev/null +++ b/index_tests/namespaces/method_inline_declaration.cc @@ -0,0 +1,64 @@ +namespace hello { +class Foo { + void foo() {} +}; +} + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 10487325150128053272, + "detailed_name": "void hello::Foo::foo()", + "qual_name_offset": 5, + "short_name": "foo", + "spell": "3:8-3:11|3:3-3:16|1026|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 6, + "parent_kind": 5, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 2029211996748007610, + "detailed_name": "namespace hello {}", + "qual_name_offset": 10, + "short_name": "hello", + "bases": [], + "funcs": [], + "types": [4508214972876735896], + "vars": [], + "alias_of": 0, + "kind": 3, + "parent_kind": 0, + "declarations": ["1:11-1:16|1:1-5:2|1|-1"], + "derived": [], + "instances": [], + "uses": [] + }, { + "usr": 4508214972876735896, + "detailed_name": "class hello::Foo {}", + "qual_name_offset": 6, + "short_name": "Foo", + "spell": "2:7-2:10|2:1-4:2|1026|-1", + "bases": [], + "funcs": [10487325150128053272], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 3, + "declarations": [], + "derived": [], + "instances": [], + "uses": [] + }], + "usr2var": [] +} +*/ diff --git a/index_tests/namespaces/namespace_alias.cc b/index_tests/namespaces/namespace_alias.cc new file mode 100644 index 000000000..f74a7c798 --- /dev/null +++ b/index_tests/namespaces/namespace_alias.cc @@ -0,0 +1,162 @@ +namespace foo { + namespace bar { + namespace baz { + int qux = 42; + } + } +} + +namespace fbz = foo::bar::baz; + +void func() { + int a = foo::bar::baz::qux; + int b = fbz::qux; +} + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 10818727483146447186, + "detailed_name": "void func()", + "qual_name_offset": 5, + "short_name": "func", + "spell": "11:6-11:10|11:1-14:2|2|-1", + "bases": [], + "vars": [6030927277961448585, 7657277353101371136], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 53, + "detailed_name": "", + "qual_name_offset": 0, + "short_name": "", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 0, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [15042442838933090518, 6030927277961448585, 7657277353101371136], + "uses": [] + }, { + "usr": 926793467007732869, + "detailed_name": "namespace foo {}", + "qual_name_offset": 10, + "short_name": "foo", + "bases": [], + "funcs": [], + "types": [17805385787823406700], + "vars": [], + "alias_of": 0, + "kind": 3, + "parent_kind": 0, + "declarations": ["1:11-1:14|1:1-7:2|1|-1"], + "derived": [17805385787823406700], + "instances": [], + "uses": ["9:17-9:20|4|-1", "12:11-12:14|4|-1"] + }, { + "usr": 11879713791858506216, + "detailed_name": "namespace fbz = foo::bar::baz", + "qual_name_offset": 10, + "short_name": "fbz", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 14450849931009540802, + "kind": 252, + "parent_kind": 0, + "declarations": ["9:11-9:14|9:1-9:30|1|-1"], + "derived": [], + "instances": [], + "uses": ["13:11-13:14|4|-1"] + }, { + "usr": 14450849931009540802, + "detailed_name": "namespace foo::bar::baz {}", + "qual_name_offset": 10, + "short_name": "baz", + "bases": [17805385787823406700], + "funcs": [], + "types": [], + "vars": [{ + "L": 15042442838933090518, + "R": -1 + }], + "alias_of": 0, + "kind": 3, + "parent_kind": 0, + "declarations": ["3:20-3:23|3:10-5:11|1025|-1"], + "derived": [], + "instances": [], + "uses": ["9:27-9:30|4|-1", "12:21-12:24|4|-1"] + }, { + "usr": 17805385787823406700, + "detailed_name": "namespace foo::bar {}", + "qual_name_offset": 10, + "short_name": "bar", + "bases": [926793467007732869], + "funcs": [], + "types": [14450849931009540802], + "vars": [], + "alias_of": 0, + "kind": 3, + "parent_kind": 0, + "declarations": ["2:15-2:18|2:5-6:6|1025|-1"], + "derived": [14450849931009540802], + "instances": [], + "uses": ["9:22-9:25|4|-1", "12:16-12:19|4|-1"] + }], + "usr2var": [{ + "usr": 6030927277961448585, + "detailed_name": "int a", + "qual_name_offset": 4, + "short_name": "a", + "hover": "int a = foo::bar::baz::qux", + "spell": "12:7-12:8|12:3-12:29|2|-1", + "type": 53, + "kind": 13, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 7657277353101371136, + "detailed_name": "int b", + "qual_name_offset": 4, + "short_name": "b", + "hover": "int b = fbz::qux", + "spell": "13:7-13:8|13:3-13:19|2|-1", + "type": 53, + "kind": 13, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 15042442838933090518, + "detailed_name": "int foo::bar::baz::qux", + "qual_name_offset": 4, + "short_name": "qux", + "hover": "int foo::bar::baz::qux = 42", + "spell": "4:18-4:21|4:14-4:26|1026|-1", + "type": 53, + "kind": 13, + "parent_kind": 3, + "storage": 0, + "declarations": [], + "uses": ["12:26-12:29|12|-1", "13:16-13:19|12|-1"] + }] +} +*/ diff --git a/index_tests/namespaces/namespace_reference.cc b/index_tests/namespaces/namespace_reference.cc new file mode 100644 index 000000000..f90fd4b0d --- /dev/null +++ b/index_tests/namespaces/namespace_reference.cc @@ -0,0 +1,113 @@ +namespace ns { + int Foo; + void Accept(int a) {} +} + +void Runner() { + ns::Accept(ns::Foo); + using namespace ns; + Accept(Foo); +} + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 631910859630953711, + "detailed_name": "void Runner()", + "qual_name_offset": 5, + "short_name": "Runner", + "spell": "6:6-6:12|6:1-10:2|2|-1", + "bases": [], + "vars": [], + "callees": ["7:7-7:13|17328473273923617489|3|16420", "9:3-9:9|17328473273923617489|3|16420"], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }, { + "usr": 17328473273923617489, + "detailed_name": "void ns::Accept(int a)", + "qual_name_offset": 5, + "short_name": "Accept", + "spell": "3:8-3:14|3:3-3:24|1026|-1", + "bases": [], + "vars": [3649375698083002347], + "callees": [], + "kind": 12, + "parent_kind": 3, + "storage": 0, + "declarations": [], + "derived": [], + "uses": ["7:7-7:13|16420|-1", "9:3-9:9|16420|-1"] + }], + "usr2type": [{ + "usr": 53, + "detailed_name": "", + "qual_name_offset": 0, + "short_name": "", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 0, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [12898699035586282159, 3649375698083002347], + "uses": [] + }, { + "usr": 11072669167287398027, + "detailed_name": "namespace ns {}", + "qual_name_offset": 10, + "short_name": "ns", + "bases": [], + "funcs": [17328473273923617489], + "types": [], + "vars": [{ + "L": 12898699035586282159, + "R": -1 + }], + "alias_of": 0, + "kind": 3, + "parent_kind": 0, + "declarations": ["1:11-1:13|1:1-4:2|1|-1"], + "derived": [], + "instances": [], + "uses": ["7:3-7:5|4|-1", "7:14-7:16|4|-1", "8:19-8:21|4|-1"] + }], + "usr2var": [{ + "usr": 3649375698083002347, + "detailed_name": "int a", + "qual_name_offset": 4, + "short_name": "a", + "spell": "3:19-3:20|3:15-3:20|1026|-1", + "type": 53, + "kind": 253, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 12898699035586282159, + "detailed_name": "int ns::Foo", + "qual_name_offset": 4, + "short_name": "Foo", + "spell": "2:7-2:10|2:3-2:10|1026|-1", + "type": 53, + "kind": 13, + "parent_kind": 3, + "storage": 0, + "declarations": [], + "uses": ["7:18-7:21|12|-1", "9:10-9:13|12|-1"] + }] +} +*/ + + + diff --git a/index_tests/objective-c/class.m b/index_tests/objective-c/class.m new file mode 100644 index 000000000..4736cadef --- /dev/null +++ b/index_tests/objective-c/class.m @@ -0,0 +1,184 @@ +@interface AClass + + (void)test; + - (void)anInstanceMethod; + @property (nonatomic) int aProp; +@end + +@implementation AClass ++ (void)test {} +- (void)anInstanceMethod {} +@end + +int main(void) +{ + AClass *instance = [AClass init]; + [instance anInstanceMethod]; + instance.aProp = 12; +} + +/* +OUTPUT: +{ + "includes": [], + "skipped_by_preprocessor": [], + "types": [{ + "id": 0, + "usr": 11832280568361305387, + "detailed_name": "AClass", + "short_name": "AClass", + "kind": 7, + "spell": "7:17-7:23|-1|1|2", + "extent": "7:1-10:2|-1|1|0", + "parents": [], + "derived": [], + "types": [], + "funcs": [], + "vars": [], + "instances": [2], + "uses": ["14:3-14:9|-1|1|4", "14:23-14:29|-1|1|4"] + }, { + "id": 1, + "usr": 17, + "detailed_name": "", + "short_name": "", + "kind": 0, + "parents": [], + "derived": [], + "types": [], + "funcs": [], + "vars": [], + "instances": [0, 1], + "uses": [] + }], + "funcs": [{ + "id": 0, + "usr": 12775970426728664910, + "detailed_name": "AClass::test", + "short_name": "test", + "kind": 17, + "storage": 0, + "declarations": [{ + "spelling": "2:11-2:15", + "extent": "2:3-2:16", + "content": "+ (void)test;", + "param_spellings": [] + }], + "spell": "8:9-8:13|-1|1|2", + "extent": "8:1-8:16|-1|1|0", + "base": [], + "derived": [], + "locals": [], + "uses": [], + "callees": [] + }, { + "id": 1, + "usr": 4096877434426330804, + "detailed_name": "AClass::anInstanceMethod", + "short_name": "anInstanceMethod", + "kind": 16, + "storage": 0, + "declarations": [{ + "spelling": "3:11-3:27", + "extent": "3:3-3:28", + "content": "- (void)anInstanceMethod;", + "param_spellings": [] + }], + "spell": "9:9-9:25|-1|1|2", + "extent": "9:1-9:28|-1|1|0", + "base": [], + "derived": [], + "locals": [], + "uses": ["15:13-15:29|4|3|64"], + "callees": [] + }, { + "id": 2, + "usr": 12774569141855220778, + "detailed_name": "AClass::aProp", + "short_name": "aProp", + "kind": 16, + "storage": 0, + "declarations": [{ + "spelling": "0:0-0:0", + "extent": "4:29-4:34", + "content": "aProp", + "param_spellings": [] + }], + "extent": "4:29-4:34|-1|1|0", + "base": [], + "derived": [], + "locals": [], + "uses": [], + "callees": [] + }, { + "id": 3, + "usr": 17992064398538597892, + "detailed_name": "AClass::setAProp:", + "short_name": "setAProp:", + "kind": 16, + "storage": 0, + "declarations": [{ + "spelling": "0:0-0:0", + "extent": "4:29-4:34", + "content": "aProp", + "param_spellings": ["4:29-4:34"] + }], + "extent": "4:29-4:34|-1|1|0", + "base": [], + "derived": [], + "locals": [], + "uses": ["0:0-0:0|4|3|64"], + "callees": [] + }, { + "id": 4, + "usr": 7033269674615638282, + "detailed_name": "int main()", + "short_name": "main", + "kind": 12, + "storage": 1, + "declarations": [], + "spell": "12:5-12:9|-1|1|2", + "extent": "12:1-17:2|-1|1|0", + "base": [], + "derived": [], + "locals": [], + "uses": [], + "callees": ["15:13-15:29|1|3|64", "0:0-0:0|3|3|64"] + }], + "vars": [{ + "id": 0, + "usr": 14842397373703114213, + "detailed_name": "int AClass::aProp", + "short_name": "aProp", + "declarations": ["4:29-4:34|-1|1|1"], + "type": 1, + "uses": ["16:12-16:17|4|3|4"], + "kind": 19, + "storage": 0 + }, { + "id": 1, + "usr": 17112602610366149042, + "detailed_name": "int AClass::_aProp", + "short_name": "_aProp", + "declarations": [], + "spell": "4:29-4:34|-1|1|2", + "extent": "4:29-4:34|-1|1|0", + "type": 1, + "uses": [], + "kind": 14, + "storage": 0 + }, { + "id": 2, + "usr": 6849095699869081177, + "detailed_name": "AClass *instance", + "short_name": "instance", + "hover": "AClass *instance = [AClass init]", + "declarations": [], + "spell": "14:11-14:19|4|3|2", + "extent": "14:3-14:35|4|3|2", + "type": 0, + "uses": ["15:4-15:12|4|3|4", "16:3-16:11|4|3|4"], + "kind": 13, + "storage": 1 + }] +} +*/ diff --git a/index_tests/operators/operator.cc b/index_tests/operators/operator.cc new file mode 100644 index 000000000..0640a220e --- /dev/null +++ b/index_tests/operators/operator.cc @@ -0,0 +1,92 @@ +class Foo { + void operator()(int) { } + void operator()(bool); + int operator()(int a, int b); +}; + +Foo &operator += (const Foo&, const int&); + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 3545323327609582678, + "detailed_name": "void Foo::operator()(bool)", + "qual_name_offset": 5, + "short_name": "operator()", + "bases": [], + "vars": [], + "callees": [], + "kind": 6, + "parent_kind": 0, + "storage": 0, + "declarations": ["3:8-3:16|3:3-3:24|1025|-1"], + "derived": [], + "uses": [] + }, { + "usr": 3986818119971932909, + "detailed_name": "int Foo::operator()(int a, int b)", + "qual_name_offset": 4, + "short_name": "operator()", + "bases": [], + "vars": [], + "callees": [], + "kind": 6, + "parent_kind": 0, + "storage": 0, + "declarations": ["4:7-4:15|4:3-4:31|1025|-1"], + "derived": [], + "uses": [] + }, { + "usr": 7874436189163837815, + "detailed_name": "void Foo::operator()(int)", + "qual_name_offset": 5, + "short_name": "operator()", + "spell": "2:8-2:18|2:3-2:27|1026|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 6, + "parent_kind": 5, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }, { + "usr": 8288368475529136092, + "detailed_name": "Foo &operator+=(const Foo &, const int &)", + "qual_name_offset": 5, + "short_name": "operator+=", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": ["7:6-7:14|7:1-7:42|1|-1"], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 15041163540773201510, + "detailed_name": "class Foo {}", + "qual_name_offset": 6, + "short_name": "Foo", + "spell": "1:7-1:10|1:1-5:2|2|-1", + "bases": [], + "funcs": [7874436189163837815, 3545323327609582678, 3986818119971932909], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": ["7:1-7:4|4|-1", "7:25-7:28|4|-1"] + }], + "usr2var": [] +} +*/ diff --git a/index_tests/outline/static_function_in_type.cc b/index_tests/outline/static_function_in_type.cc new file mode 100644 index 000000000..7120de031 --- /dev/null +++ b/index_tests/outline/static_function_in_type.cc @@ -0,0 +1,168 @@ +#include "static_function_in_type.h" + +namespace ns { +// static +void Foo::Register(Manager* m) { +} +} + +/* +OUTPUT: static_function_in_type.h +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 17019747379608639279, + "detailed_name": "static void ns::Foo::Register(ns::Manager *)", + "qual_name_offset": 12, + "short_name": "Register", + "bases": [], + "vars": [], + "callees": [], + "kind": 254, + "parent_kind": 0, + "storage": 0, + "declarations": ["6:15-6:23|6:3-6:33|1025|-1"], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 1972401196751872203, + "detailed_name": "class ns::Manager", + "qual_name_offset": 6, + "short_name": "Manager", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": ["3:7-3:14|3:1-3:14|1025|-1"], + "derived": [], + "instances": [], + "uses": ["6:24-6:31|4|-1"] + }, { + "usr": 11072669167287398027, + "detailed_name": "namespace ns {}", + "qual_name_offset": 10, + "short_name": "ns", + "bases": [], + "funcs": [], + "types": [1972401196751872203, 17262466801709381811], + "vars": [], + "alias_of": 0, + "kind": 3, + "parent_kind": 0, + "declarations": ["1:11-1:13|1:1-9:2|1|-1"], + "derived": [], + "instances": [], + "uses": [] + }, { + "usr": 17262466801709381811, + "detailed_name": "struct ns::Foo {}", + "qual_name_offset": 7, + "short_name": "Foo", + "spell": "5:8-5:11|5:1-7:2|1026|-1", + "bases": [], + "funcs": [17019747379608639279], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 3, + "declarations": [], + "derived": [], + "instances": [], + "uses": [] + }], + "usr2var": [] +} +OUTPUT: static_function_in_type.cc +{ + "includes": [{ + "line": 0, + "resolved_path": "&static_function_in_type.h" + }], + "skipped_ranges": [], + "usr2func": [{ + "usr": 17019747379608639279, + "detailed_name": "static void ns::Foo::Register(ns::Manager *)", + "qual_name_offset": 12, + "short_name": "Register", + "spell": "5:11-5:19|5:1-6:2|1026|-1", + "comments": "static", + "bases": [], + "vars": [13569879755236306838], + "callees": [], + "kind": 254, + "parent_kind": 23, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 1972401196751872203, + "detailed_name": "class ns::Manager", + "qual_name_offset": 6, + "short_name": "Manager", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [13569879755236306838], + "uses": ["5:20-5:27|4|-1"] + }, { + "usr": 11072669167287398027, + "detailed_name": "namespace ns {}", + "qual_name_offset": 10, + "short_name": "ns", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 3, + "parent_kind": 0, + "declarations": ["3:11-3:13|3:1-7:2|1|-1"], + "derived": [], + "instances": [], + "uses": [] + }, { + "usr": 17262466801709381811, + "detailed_name": "struct ns::Foo {}", + "qual_name_offset": 7, + "short_name": "Foo", + "bases": [], + "funcs": [17019747379608639279], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": ["5:6-5:9|4|-1"] + }], + "usr2var": [{ + "usr": 13569879755236306838, + "detailed_name": "ns::Manager *m", + "qual_name_offset": 13, + "short_name": "m", + "spell": "5:29-5:30|5:20-5:30|1026|-1", + "type": 1972401196751872203, + "kind": 253, + "parent_kind": 254, + "storage": 0, + "declarations": [], + "uses": [] + }] +} +*/ \ No newline at end of file diff --git a/index_tests/outline/static_function_in_type.h b/index_tests/outline/static_function_in_type.h new file mode 100644 index 000000000..013aa70e5 --- /dev/null +++ b/index_tests/outline/static_function_in_type.h @@ -0,0 +1,9 @@ +namespace ns { + +class Manager; + +struct Foo { + static void Register(Manager*); +}; + +} // namespace ns \ No newline at end of file diff --git a/index_tests/preprocessor/include_guard.cc b/index_tests/preprocessor/include_guard.cc new file mode 100644 index 000000000..efee4de53 --- /dev/null +++ b/index_tests/preprocessor/include_guard.cc @@ -0,0 +1,28 @@ +#ifndef FOO +#define FOO + +#endif + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [], + "usr2type": [], + "usr2var": [{ + "usr": 14219599523415845943, + "detailed_name": "FOO", + "qual_name_offset": 0, + "short_name": "FOO", + "hover": "#define FOO", + "spell": "2:9-2:12|2:9-2:12|2|-1", + "type": 0, + "kind": 255, + "parent_kind": 1, + "storage": 0, + "declarations": [], + "uses": [] + }] +} +*/ diff --git a/index_tests/preprocessor/skipped.cc b/index_tests/preprocessor/skipped.cc new file mode 100644 index 000000000..74577e1f9 --- /dev/null +++ b/index_tests/preprocessor/skipped.cc @@ -0,0 +1,25 @@ + +#ifdef FOOBAR +void hello(); +#endif + +#if false + + + +#endif + +#if defined(OS_FOO) + +#endif + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": ["2:1-5:1", "6:1-11:1", "12:1-15:1"], + "usr2func": [], + "usr2type": [], + "usr2var": [] +} +*/ diff --git a/index_tests/templates/func_specialized_template_param.cc b/index_tests/templates/func_specialized_template_param.cc new file mode 100644 index 000000000..0cb68a030 --- /dev/null +++ b/index_tests/templates/func_specialized_template_param.cc @@ -0,0 +1,68 @@ +template +class Template {}; + +struct Foo { + void Bar(Template&); +}; + +void Foo::Bar(Template&) {} + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 8412238651648388423, + "detailed_name": "void Foo::Bar(Template &)", + "qual_name_offset": 5, + "short_name": "Bar", + "spell": "8:11-8:14|8:1-8:36|1026|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 6, + "parent_kind": 23, + "storage": 0, + "declarations": ["5:8-5:11|5:3-5:30|1025|-1"], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 15041163540773201510, + "detailed_name": "struct Foo {}", + "qual_name_offset": 7, + "short_name": "Foo", + "spell": "4:8-4:11|4:1-6:2|2|-1", + "bases": [], + "funcs": [8412238651648388423], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": ["8:6-8:9|4|-1"] + }, { + "usr": 17107291254533526269, + "detailed_name": "class Template {}", + "qual_name_offset": 6, + "short_name": "Template", + "spell": "2:7-2:15|2:1-2:18|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": ["5:12-5:20|4|-1", "8:15-8:23|4|-1"] + }], + "usr2var": [] +} +*/ diff --git a/index_tests/templates/implicit_variable_instantiation.cc b/index_tests/templates/implicit_variable_instantiation.cc new file mode 100644 index 000000000..b02443587 --- /dev/null +++ b/index_tests/templates/implicit_variable_instantiation.cc @@ -0,0 +1,137 @@ +namespace ns { + enum VarType {}; + + template + struct Holder { + static constexpr VarType static_var = (VarType)0x0; + }; + + template + const typename VarType Holder<_>::static_var; + + + int Foo = Holder::static_var; + int Foo2 = Holder::static_var; +} + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [], + "usr2type": [{ + "usr": 53, + "detailed_name": "", + "qual_name_offset": 0, + "short_name": "", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 0, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [12898699035586282159, 9008550860229740818], + "uses": [] + }, { + "usr": 1532099849728741556, + "detailed_name": "enum ns::VarType {}", + "qual_name_offset": 5, + "short_name": "VarType", + "spell": "2:8-2:15|2:3-2:18|1026|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 10, + "parent_kind": 3, + "declarations": [], + "derived": [], + "instances": [4731849186641714451, 4731849186641714451], + "uses": ["6:22-6:29|4|-1", "6:44-6:51|4|-1", "10:18-10:25|4|-1"] + }, { + "usr": 11072669167287398027, + "detailed_name": "namespace ns {}", + "qual_name_offset": 10, + "short_name": "ns", + "bases": [], + "funcs": [], + "types": [1532099849728741556, 12688716854043726585], + "vars": [{ + "L": 12898699035586282159, + "R": -1 + }, { + "L": 9008550860229740818, + "R": -1 + }], + "alias_of": 0, + "kind": 3, + "parent_kind": 0, + "declarations": ["1:11-1:13|1:1-15:2|1|-1"], + "derived": [], + "instances": [], + "uses": [] + }, { + "usr": 12688716854043726585, + "detailed_name": "struct ns::Holder {}", + "qual_name_offset": 7, + "short_name": "Holder", + "spell": "5:10-5:16|5:3-7:4|1026|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 3, + "declarations": [], + "derived": [], + "instances": [], + "uses": ["10:26-10:32|4|-1", "13:13-13:19|4|-1", "14:14-14:20|4|-1"] + }], + "usr2var": [{ + "usr": 4731849186641714451, + "detailed_name": "static constexpr ns::VarType ns::Holder::static_var", + "qual_name_offset": 29, + "short_name": "static_var", + "hover": "static constexpr ns::VarType ns::Holder::static_var = (VarType)0x0", + "spell": "10:37-10:47|9:3-10:47|1026|-1", + "type": 1532099849728741556, + "kind": 13, + "parent_kind": 23, + "storage": 2, + "declarations": ["6:30-6:40|6:5-6:55|1025|-1"], + "uses": ["13:26-13:36|12|-1", "14:27-14:37|12|-1"] + }, { + "usr": 9008550860229740818, + "detailed_name": "int ns::Foo2", + "qual_name_offset": 4, + "short_name": "Foo2", + "hover": "int ns::Foo2 = Holder::static_var", + "spell": "14:7-14:11|14:3-14:37|1026|-1", + "type": 53, + "kind": 13, + "parent_kind": 3, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 12898699035586282159, + "detailed_name": "int ns::Foo", + "qual_name_offset": 4, + "short_name": "Foo", + "hover": "int ns::Foo = Holder::static_var", + "spell": "13:7-13:10|13:3-13:36|1026|-1", + "type": 53, + "kind": 13, + "parent_kind": 3, + "storage": 0, + "declarations": [], + "uses": [] + }] +} +*/ diff --git a/index_tests/templates/member_ref_in_template.cc b/index_tests/templates/member_ref_in_template.cc new file mode 100644 index 000000000..c9cc268e8 --- /dev/null +++ b/index_tests/templates/member_ref_in_template.cc @@ -0,0 +1,110 @@ +template +struct C { + T x; + void bar(); +}; + +template +void foo() { + C d; + d.x; // spelling range is empty, use cursor extent for range + d.bar(); // spelling range is empty, use cursor extent for range + + auto e = new C; + e->x; // `x` seems not exposed by libclang + e->bar(); // `bar` seems not exposed by libclang +} + +/* +EXTRA_FLAGS: +-fms-extensions +-fms-compatibility +-fdelayed-template-parsing + +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 6875364467121018690, + "detailed_name": "void foo()", + "qual_name_offset": 5, + "short_name": "foo", + "spell": "8:6-8:9|8:1-8:11|2|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }, { + "usr": 8905286151237717330, + "detailed_name": "void C::bar()", + "qual_name_offset": 5, + "short_name": "bar", + "bases": [], + "vars": [], + "callees": [], + "kind": 6, + "parent_kind": 0, + "storage": 0, + "declarations": ["4:8-4:11|4:3-4:13|1025|-1"], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 8402783583255987702, + "detailed_name": "struct C {}", + "qual_name_offset": 7, + "short_name": "C", + "spell": "2:8-2:9|2:1-5:2|2|-1", + "bases": [], + "funcs": [8905286151237717330], + "types": [], + "vars": [{ + "L": 5866801090710377175, + "R": -1 + }], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": [] + }, { + "usr": 14750650276757822712, + "detailed_name": "T", + "qual_name_offset": 0, + "short_name": "T", + "spell": "1:17-1:18|1:11-1:18|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 26, + "parent_kind": 5, + "declarations": [], + "derived": [], + "instances": [5866801090710377175], + "uses": [] + }], + "usr2var": [{ + "usr": 5866801090710377175, + "detailed_name": "T C::x", + "qual_name_offset": 2, + "short_name": "x", + "spell": "3:5-3:6|3:3-3:6|1026|-1", + "type": 14750650276757822712, + "kind": 8, + "parent_kind": 23, + "storage": 0, + "declarations": [], + "uses": [] + }] +} +*/ diff --git a/index_tests/templates/namespace_template_class_template_func_usage_folded_into_one.cc b/index_tests/templates/namespace_template_class_template_func_usage_folded_into_one.cc new file mode 100644 index 000000000..de9c3143c --- /dev/null +++ b/index_tests/templates/namespace_template_class_template_func_usage_folded_into_one.cc @@ -0,0 +1,119 @@ +namespace ns { + template + struct Foo { + template + static int foo() { + return 3; + } + }; + + int a = Foo::foo(); + int b = Foo::foo(); +} + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 8221803074608342407, + "detailed_name": "static int ns::Foo::foo()", + "qual_name_offset": 11, + "short_name": "foo", + "spell": "5:16-5:19|5:5-7:6|1026|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 254, + "parent_kind": 23, + "storage": 0, + "declarations": [], + "derived": [], + "uses": ["10:21-10:24|36|-1", "11:22-11:25|36|-1"] + }], + "usr2type": [{ + "usr": 53, + "detailed_name": "", + "qual_name_offset": 0, + "short_name": "", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 0, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [15768138241775955040, 3182917058194750998], + "uses": [] + }, { + "usr": 11072669167287398027, + "detailed_name": "namespace ns {}", + "qual_name_offset": 10, + "short_name": "ns", + "bases": [], + "funcs": [], + "types": [14042997404480181958], + "vars": [{ + "L": 15768138241775955040, + "R": -1 + }, { + "L": 3182917058194750998, + "R": -1 + }], + "alias_of": 0, + "kind": 3, + "parent_kind": 0, + "declarations": ["1:11-1:13|1:1-12:2|1|-1"], + "derived": [], + "instances": [], + "uses": [] + }, { + "usr": 14042997404480181958, + "detailed_name": "struct ns::Foo {}", + "qual_name_offset": 7, + "short_name": "Foo", + "spell": "3:10-3:13|3:3-8:4|1026|-1", + "bases": [], + "funcs": [8221803074608342407], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 3, + "declarations": [], + "derived": [], + "instances": [], + "uses": ["10:11-10:14|4|-1", "11:11-11:14|4|-1"] + }], + "usr2var": [{ + "usr": 3182917058194750998, + "detailed_name": "int ns::b", + "qual_name_offset": 4, + "short_name": "b", + "hover": "int ns::b = Foo::foo()", + "spell": "11:7-11:8|11:3-11:35|1026|-1", + "type": 53, + "kind": 13, + "parent_kind": 3, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 15768138241775955040, + "detailed_name": "int ns::a", + "qual_name_offset": 4, + "short_name": "a", + "hover": "int ns::a = Foo::foo()", + "spell": "10:7-10:8|10:3-10:33|1026|-1", + "type": 53, + "kind": 13, + "parent_kind": 3, + "storage": 0, + "declarations": [], + "uses": [] + }] +} +*/ diff --git a/index_tests/templates/namespace_template_type_usage_folded_into_one.cc b/index_tests/templates/namespace_template_type_usage_folded_into_one.cc new file mode 100644 index 000000000..e8c8367c3 --- /dev/null +++ b/index_tests/templates/namespace_template_type_usage_folded_into_one.cc @@ -0,0 +1,81 @@ +namespace ns { + template + class Foo {}; + + Foo a; + Foo b; +} + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [], + "usr2type": [{ + "usr": 11072669167287398027, + "detailed_name": "namespace ns {}", + "qual_name_offset": 10, + "short_name": "ns", + "bases": [], + "funcs": [], + "types": [14042997404480181958], + "vars": [{ + "L": 15768138241775955040, + "R": -1 + }, { + "L": 3182917058194750998, + "R": -1 + }], + "alias_of": 0, + "kind": 3, + "parent_kind": 0, + "declarations": ["1:11-1:13|1:1-7:2|1|-1"], + "derived": [], + "instances": [], + "uses": [] + }, { + "usr": 14042997404480181958, + "detailed_name": "class ns::Foo {}", + "qual_name_offset": 6, + "short_name": "Foo", + "spell": "3:9-3:12|3:3-3:15|1026|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 3, + "declarations": [], + "derived": [], + "instances": [15768138241775955040, 3182917058194750998], + "uses": ["5:3-5:6|4|-1", "6:3-6:6|4|-1"] + }], + "usr2var": [{ + "usr": 3182917058194750998, + "detailed_name": "Foo ns::b", + "qual_name_offset": 10, + "short_name": "b", + "spell": "6:13-6:14|6:3-6:14|1026|-1", + "type": 14042997404480181958, + "kind": 13, + "parent_kind": 3, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 15768138241775955040, + "detailed_name": "Foo ns::a", + "qual_name_offset": 9, + "short_name": "a", + "spell": "5:12-5:13|5:3-5:13|1026|-1", + "type": 14042997404480181958, + "kind": 13, + "parent_kind": 3, + "storage": 0, + "declarations": [], + "uses": [] + }] +} +*/ diff --git a/index_tests/templates/specialization.cc b/index_tests/templates/specialization.cc new file mode 100644 index 000000000..b943c207e --- /dev/null +++ b/index_tests/templates/specialization.cc @@ -0,0 +1,422 @@ +template +class function; + +template +class function {}; + +function f; + +template class allocator; + +template > +class vector { + void clear(); +}; + +template +class vector {}; + +struct Z1 {}; + +template class vector; + +struct Z2 {}; + +template<> +class vector { + void clear(); +}; + +vector vc; +vector vip; +vector vz1; +vector vz2; + +enum Enum { + Enum0, Enum1 +}; +template +void foo(T Value) {} + +static const int kOnst = 7; +template <> +void foo(float Value); + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 3861597222587452538, + "detailed_name": "template<> void foo(float Value)", + "qual_name_offset": 16, + "short_name": "foo", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": ["43:6-43:9|42:1-43:50|1|-1"], + "derived": [], + "uses": [] + }, { + "usr": 6113470698424012876, + "detailed_name": "void vector >::clear()", + "qual_name_offset": 5, + "short_name": "clear", + "bases": [], + "vars": [], + "callees": [], + "kind": 6, + "parent_kind": 0, + "storage": 0, + "declarations": ["27:8-27:13|27:3-27:15|1025|-1"], + "derived": [], + "uses": [] + }, { + "usr": 17498190318698490707, + "detailed_name": "void foo(T Value)", + "qual_name_offset": 5, + "short_name": "foo", + "spell": "39:6-39:9|39:1-39:21|2|-1", + "bases": [], + "vars": [17826688417349629938], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }, { + "usr": 18107614608385228556, + "detailed_name": "void vector::clear()", + "qual_name_offset": 5, + "short_name": "clear", + "bases": [], + "vars": [], + "callees": [], + "kind": 6, + "parent_kind": 0, + "storage": 0, + "declarations": ["13:8-13:13|13:3-13:15|1025|-1"], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 53, + "detailed_name": "", + "qual_name_offset": 0, + "short_name": "", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 0, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [13914496963221806870], + "uses": [] + }, { + "usr": 218068462278884837, + "detailed_name": "template class function {}", + "qual_name_offset": 46, + "short_name": "function", + "spell": "5:7-5:15|4:1-5:30|2|-1", + "bases": [15019211479263750068], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [2933643612409209903], + "uses": ["7:1-7:9|4|-1"] + }, { + "usr": 1663022413889915338, + "detailed_name": "template<> class vector> {}", + "qual_name_offset": 17, + "short_name": "vector", + "spell": "26:7-26:13|25:1-28:2|2|-1", + "bases": [7440942986741176606], + "funcs": [6113470698424012876], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [15931696253641284761], + "uses": ["26:7-26:13|4|-1", "33:1-33:7|4|-1"] + }, { + "usr": 5760043510674081814, + "detailed_name": "struct Z1 {}", + "qual_name_offset": 7, + "short_name": "Z1", + "spell": "19:8-19:10|19:1-19:13|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": ["21:23-21:25|4|-1", "32:8-32:10|4|-1"] + }, { + "usr": 7440942986741176606, + "detailed_name": "class vector {}", + "qual_name_offset": 6, + "short_name": "vector", + "spell": "12:7-12:13|12:1-14:2|2|-1", + "bases": [], + "funcs": [18107614608385228556], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": [], + "derived": [16155717907537731864, 1663022413889915338], + "instances": [5792869548777559988], + "uses": ["17:7-17:13|4|-1", "21:16-21:22|4|-1", "30:1-30:7|4|-1", "32:1-32:7|4|-1"] + }, { + "usr": 9201299975592934124, + "detailed_name": "enum Enum {}", + "qual_name_offset": 5, + "short_name": "Enum", + "spell": "35:6-35:10|35:1-37:2|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 10, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": [] + }, { + "usr": 10124869160135436852, + "detailed_name": "struct Z2 {}", + "qual_name_offset": 7, + "short_name": "Z2", + "spell": "23:8-23:10|23:1-23:13|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": ["26:14-26:16|4|-1", "33:8-33:10|4|-1"] + }, { + "usr": 14111105212951082474, + "detailed_name": "T", + "qual_name_offset": 0, + "short_name": "T", + "spell": "38:20-38:21|38:11-38:21|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 26, + "parent_kind": 5, + "declarations": [], + "derived": [], + "instances": [17826688417349629938], + "uses": [] + }, { + "usr": 15019211479263750068, + "detailed_name": "class function", + "qual_name_offset": 6, + "short_name": "function", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": ["2:7-2:15|2:1-2:15|1|-1"], + "derived": [218068462278884837], + "instances": [], + "uses": ["5:7-5:15|4|-1"] + }, { + "usr": 15440970074034693939, + "detailed_name": "", + "qual_name_offset": 0, + "short_name": "", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 0, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [3566687051827176322], + "uses": [] + }, { + "usr": 15695704394170757108, + "detailed_name": "class allocator", + "qual_name_offset": 6, + "short_name": "allocator", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": ["9:28-9:37|9:22-9:37|1|-1"], + "derived": [], + "instances": [], + "uses": ["11:39-11:48|4|-1"] + }, { + "usr": 16155717907537731864, + "detailed_name": "template class vector> {}", + "qual_name_offset": 28, + "short_name": "vector", + "spell": "17:7-17:13|16:1-17:20|2|-1", + "bases": [7440942986741176606], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [86949563628772958], + "uses": ["31:1-31:7|4|-1"] + }], + "usr2var": [{ + "usr": 86949563628772958, + "detailed_name": "vector vip", + "qual_name_offset": 14, + "short_name": "vip", + "spell": "31:14-31:17|31:1-31:17|2|-1", + "type": 16155717907537731864, + "kind": 13, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 2933643612409209903, + "detailed_name": "function f", + "qual_name_offset": 21, + "short_name": "f", + "spell": "7:21-7:22|7:1-7:22|2|-1", + "type": 218068462278884837, + "kind": 13, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 3566687051827176322, + "detailed_name": "vector vz1", + "qual_name_offset": 11, + "short_name": "vz1", + "spell": "32:12-32:15|32:1-32:15|2|-1", + "type": 15440970074034693939, + "kind": 13, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 4917621020431490070, + "detailed_name": "Enum1", + "qual_name_offset": 0, + "short_name": "Enum1", + "hover": "Enum1 = 1", + "spell": "36:10-36:15|36:10-36:15|1026|-1", + "type": 9201299975592934124, + "kind": 22, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 5792869548777559988, + "detailed_name": "vector vc", + "qual_name_offset": 13, + "short_name": "vc", + "spell": "30:14-30:16|30:1-30:16|2|-1", + "type": 7440942986741176606, + "kind": 13, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 13914496963221806870, + "detailed_name": "static const int kOnst", + "qual_name_offset": 17, + "short_name": "kOnst", + "hover": "static const int kOnst = 7", + "spell": "41:18-41:23|41:1-41:27|2|-1", + "type": 53, + "kind": 13, + "parent_kind": 0, + "storage": 2, + "declarations": [], + "uses": ["43:27-43:32|12|-1"] + }, { + "usr": 15477793821005285152, + "detailed_name": "Enum0", + "qual_name_offset": 0, + "short_name": "Enum0", + "hover": "Enum0 = 0", + "spell": "36:3-36:8|36:3-36:8|1026|-1", + "type": 9201299975592934124, + "kind": 22, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "uses": ["43:20-43:25|4|-1"] + }, { + "usr": 15931696253641284761, + "detailed_name": "vector vz2", + "qual_name_offset": 11, + "short_name": "vz2", + "spell": "33:12-33:15|33:1-33:15|2|-1", + "type": 1663022413889915338, + "kind": 13, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 17826688417349629938, + "detailed_name": "T Value", + "qual_name_offset": 2, + "short_name": "Value", + "spell": "39:12-39:17|39:10-39:17|1026|-1", + "type": 14111105212951082474, + "kind": 253, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": [] + }] +} +*/ diff --git a/index_tests/templates/specialized_func_definition.cc b/index_tests/templates/specialized_func_definition.cc new file mode 100644 index 000000000..c57b35ffe --- /dev/null +++ b/index_tests/templates/specialized_func_definition.cc @@ -0,0 +1,92 @@ +template +class Template { + void Foo(); +}; + +template +void Template::Foo() {} + +template<> +void Template::Foo() {} + + +/* +// TODO: usage information on Template is bad. +// TODO: Foo() should have multiple definitions. + +EXTRA_FLAGS: +-fms-compatibility +-fdelayed-template-parsing + +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 6995843774014807426, + "detailed_name": "void Template::Foo()", + "qual_name_offset": 5, + "short_name": "Foo", + "spell": "10:22-10:25|9:1-10:30|1026|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 6, + "parent_kind": 5, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }, { + "usr": 11994188353303124840, + "detailed_name": "void Template::Foo()", + "qual_name_offset": 5, + "short_name": "Foo", + "spell": "7:19-7:22|6:1-7:24|1026|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 6, + "parent_kind": 5, + "storage": 0, + "declarations": ["3:8-3:11|3:3-3:13|1025|-1"], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 17107291254533526269, + "detailed_name": "class Template {}", + "qual_name_offset": 6, + "short_name": "Template", + "spell": "2:7-2:15|2:1-4:2|2|-1", + "bases": [], + "funcs": [11994188353303124840], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": ["7:6-7:14|4|-1", "10:6-10:14|4|-1"] + }, { + "usr": 17649312483543982122, + "detailed_name": "", + "qual_name_offset": 0, + "short_name": "", + "bases": [], + "funcs": [6995843774014807426], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 0, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": [] + }], + "usr2var": [] +} +*/ diff --git a/index_tests/templates/template_class_func_usage_folded_into_one.cc b/index_tests/templates/template_class_func_usage_folded_into_one.cc new file mode 100644 index 000000000..6efb299e3 --- /dev/null +++ b/index_tests/templates/template_class_func_usage_folded_into_one.cc @@ -0,0 +1,94 @@ +template +struct Foo { + static int foo() { + return 3; + } +}; + +int a = Foo::foo(); +int b = Foo::foo(); + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 8340731781048851399, + "detailed_name": "static int Foo::foo()", + "qual_name_offset": 11, + "short_name": "foo", + "spell": "3:14-3:17|3:3-5:4|1026|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 254, + "parent_kind": 23, + "storage": 0, + "declarations": [], + "derived": [], + "uses": ["8:19-8:22|36|-1", "9:20-9:23|36|-1"] + }], + "usr2type": [{ + "usr": 53, + "detailed_name": "", + "qual_name_offset": 0, + "short_name": "", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 0, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [16721564935990383768, 12028309045033782423], + "uses": [] + }, { + "usr": 10528472276654770367, + "detailed_name": "struct Foo {}", + "qual_name_offset": 7, + "short_name": "Foo", + "spell": "2:8-2:11|2:1-6:2|2|-1", + "bases": [], + "funcs": [8340731781048851399], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": ["8:9-8:12|4|-1", "9:9-9:12|4|-1"] + }], + "usr2var": [{ + "usr": 12028309045033782423, + "detailed_name": "int b", + "qual_name_offset": 4, + "short_name": "b", + "hover": "int b = Foo::foo()", + "spell": "9:5-9:6|9:1-9:25|2|-1", + "type": 53, + "kind": 13, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 16721564935990383768, + "detailed_name": "int a", + "qual_name_offset": 4, + "short_name": "a", + "hover": "int a = Foo::foo()", + "spell": "8:5-8:6|8:1-8:24|2|-1", + "type": 53, + "kind": 13, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "uses": [] + }] +} +*/ diff --git a/index_tests/templates/template_class_template_func_usage_folded_into_one.cc b/index_tests/templates/template_class_template_func_usage_folded_into_one.cc new file mode 100644 index 000000000..c364718f1 --- /dev/null +++ b/index_tests/templates/template_class_template_func_usage_folded_into_one.cc @@ -0,0 +1,95 @@ +template +struct Foo { + template + static int foo() { + return 3; + } +}; + +int a = Foo::foo(); +int b = Foo::foo(); + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 9034026360701857235, + "detailed_name": "static int Foo::foo()", + "qual_name_offset": 11, + "short_name": "foo", + "spell": "4:14-4:17|4:3-6:4|1026|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 254, + "parent_kind": 23, + "storage": 0, + "declarations": [], + "derived": [], + "uses": ["9:19-9:22|36|-1", "10:20-10:23|36|-1"] + }], + "usr2type": [{ + "usr": 53, + "detailed_name": "", + "qual_name_offset": 0, + "short_name": "", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 0, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [16721564935990383768, 12028309045033782423], + "uses": [] + }, { + "usr": 10528472276654770367, + "detailed_name": "struct Foo {}", + "qual_name_offset": 7, + "short_name": "Foo", + "spell": "2:8-2:11|2:1-7:2|2|-1", + "bases": [], + "funcs": [9034026360701857235], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": ["9:9-9:12|4|-1", "10:9-10:12|4|-1"] + }], + "usr2var": [{ + "usr": 12028309045033782423, + "detailed_name": "int b", + "qual_name_offset": 4, + "short_name": "b", + "hover": "int b = Foo::foo()", + "spell": "10:5-10:6|10:1-10:33|2|-1", + "type": 53, + "kind": 13, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 16721564935990383768, + "detailed_name": "int a", + "qual_name_offset": 4, + "short_name": "a", + "hover": "int a = Foo::foo()", + "spell": "9:5-9:6|9:1-9:31|2|-1", + "type": 53, + "kind": 13, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "uses": [] + }] +} +*/ diff --git a/index_tests/templates/template_class_type_usage_folded_into_one.cc b/index_tests/templates/template_class_type_usage_folded_into_one.cc new file mode 100644 index 000000000..b15eb530a --- /dev/null +++ b/index_tests/templates/template_class_type_usage_folded_into_one.cc @@ -0,0 +1,131 @@ +enum A {}; +enum B {}; + +template +struct Foo { + struct Inner {}; +}; + +Foo::Inner a; +Foo::Inner b; + +#if false +EnumDecl A +EnumDecl B +ClassTemplate Foo + TemplateTypeParameter T + StructDecl Inner +VarDecl a + TemplateRef Foo + TypeRef enum A + TypeRef struct Foo::Inner + CallExpr Inner +VarDecl b + TemplateRef Foo + TypeRef enum B + TypeRef struct Foo::Inner + CallExpr Inner +#endif + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": ["12:1-29:1"], + "usr2func": [], + "usr2type": [{ + "usr": 6697181287623958829, + "detailed_name": "enum A {}", + "qual_name_offset": 5, + "short_name": "A", + "spell": "1:6-1:7|1:1-1:10|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 10, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": ["9:5-9:6|4|-1"] + }, { + "usr": 10528472276654770367, + "detailed_name": "struct Foo {}", + "qual_name_offset": 7, + "short_name": "Foo", + "spell": "5:8-5:11|5:1-7:2|2|-1", + "bases": [], + "funcs": [], + "types": [13938528237873543349], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": ["9:1-9:4|4|-1", "10:1-10:4|4|-1"] + }, { + "usr": 13892793056005362145, + "detailed_name": "enum B {}", + "qual_name_offset": 5, + "short_name": "B", + "spell": "2:6-2:7|2:1-2:10|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 10, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": ["10:5-10:6|4|-1"] + }, { + "usr": 13938528237873543349, + "detailed_name": "struct Foo::Inner {}", + "qual_name_offset": 7, + "short_name": "Inner", + "spell": "6:10-6:15|6:3-6:18|1026|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 23, + "declarations": [], + "derived": [], + "instances": [16721564935990383768, 12028309045033782423], + "uses": ["9:9-9:14|4|-1", "10:9-10:14|4|-1"] + }], + "usr2var": [{ + "usr": 12028309045033782423, + "detailed_name": "Foo::Inner b", + "qual_name_offset": 14, + "short_name": "b", + "spell": "10:15-10:16|10:1-10:16|2|-1", + "type": 13938528237873543349, + "kind": 13, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 16721564935990383768, + "detailed_name": "Foo::Inner a", + "qual_name_offset": 14, + "short_name": "a", + "spell": "9:15-9:16|9:1-9:16|2|-1", + "type": 13938528237873543349, + "kind": 13, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "uses": [] + }] +} +*/ diff --git a/index_tests/templates/template_class_var_usage_folded_into_one.cc b/index_tests/templates/template_class_var_usage_folded_into_one.cc new file mode 100644 index 000000000..f2c4390ca --- /dev/null +++ b/index_tests/templates/template_class_var_usage_folded_into_one.cc @@ -0,0 +1,89 @@ +template +struct Foo { + static constexpr int var = 3; +}; + +int a = Foo::var; +int b = Foo::var; + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [], + "usr2type": [{ + "usr": 53, + "detailed_name": "", + "qual_name_offset": 0, + "short_name": "", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 0, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [13545144895171991916, 16721564935990383768, 12028309045033782423], + "uses": [] + }, { + "usr": 10528472276654770367, + "detailed_name": "struct Foo {}", + "qual_name_offset": 7, + "short_name": "Foo", + "spell": "2:8-2:11|2:1-4:2|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": ["6:9-6:12|4|-1", "7:9-7:12|4|-1"] + }], + "usr2var": [{ + "usr": 12028309045033782423, + "detailed_name": "int b", + "qual_name_offset": 4, + "short_name": "b", + "hover": "int b = Foo::var", + "spell": "7:5-7:6|7:1-7:23|2|-1", + "type": 53, + "kind": 13, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 13545144895171991916, + "detailed_name": "static constexpr int Foo::var", + "qual_name_offset": 21, + "short_name": "var", + "hover": "static constexpr int Foo::var = 3", + "type": 53, + "kind": 13, + "parent_kind": 23, + "storage": 2, + "declarations": ["3:24-3:27|3:3-3:31|1025|-1"], + "uses": ["6:19-6:22|12|-1", "7:20-7:23|12|-1"] + }, { + "usr": 16721564935990383768, + "detailed_name": "int a", + "qual_name_offset": 4, + "short_name": "a", + "hover": "int a = Foo::var", + "spell": "6:5-6:6|6:1-6:22|2|-1", + "type": 53, + "kind": 13, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "uses": [] + }] +} +*/ diff --git a/index_tests/templates/template_func_usage_folded_into_one.cc b/index_tests/templates/template_func_usage_folded_into_one.cc new file mode 100644 index 000000000..9c2538940 --- /dev/null +++ b/index_tests/templates/template_func_usage_folded_into_one.cc @@ -0,0 +1,78 @@ +template +static int foo() { + return 3; +} + +int a = foo(); +int b = foo(); + +// TODO: put template foo inside a namespace +// TODO: put template foo inside a template class inside a namespace + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 326583651986177228, + "detailed_name": "static int foo()", + "qual_name_offset": 11, + "short_name": "foo", + "spell": "2:12-2:15|2:1-4:2|2|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": ["6:9-6:12|36|-1", "7:9-7:12|36|-1"] + }], + "usr2type": [{ + "usr": 53, + "detailed_name": "", + "qual_name_offset": 0, + "short_name": "", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 0, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [16721564935990383768, 12028309045033782423], + "uses": [] + }], + "usr2var": [{ + "usr": 12028309045033782423, + "detailed_name": "int b", + "qual_name_offset": 4, + "short_name": "b", + "hover": "int b = foo()", + "spell": "7:5-7:6|7:1-7:20|2|-1", + "type": 53, + "kind": 13, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 16721564935990383768, + "detailed_name": "int a", + "qual_name_offset": 4, + "short_name": "a", + "hover": "int a = foo()", + "spell": "6:5-6:6|6:1-6:19|2|-1", + "type": 53, + "kind": 13, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "uses": [] + }] +} +*/ diff --git a/index_tests/templates/template_type_usage_folded_into_one.cc b/index_tests/templates/template_type_usage_folded_into_one.cc new file mode 100644 index 000000000..05dfa00e7 --- /dev/null +++ b/index_tests/templates/template_type_usage_folded_into_one.cc @@ -0,0 +1,57 @@ +template +class Foo {}; + +Foo a; +Foo b; + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [], + "usr2type": [{ + "usr": 10528472276654770367, + "detailed_name": "class Foo {}", + "qual_name_offset": 6, + "short_name": "Foo", + "spell": "2:7-2:10|2:1-2:13|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [16721564935990383768, 12028309045033782423], + "uses": ["4:1-4:4|4|-1", "5:1-5:4|4|-1"] + }], + "usr2var": [{ + "usr": 12028309045033782423, + "detailed_name": "Foo b", + "qual_name_offset": 10, + "short_name": "b", + "spell": "5:11-5:12|5:1-5:12|2|-1", + "type": 10528472276654770367, + "kind": 13, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 16721564935990383768, + "detailed_name": "Foo a", + "qual_name_offset": 9, + "short_name": "a", + "spell": "4:10-4:11|4:1-4:11|2|-1", + "type": 10528472276654770367, + "kind": 13, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "uses": [] + }] +} +*/ diff --git a/index_tests/templates/template_var_usage_folded_into_one.cc b/index_tests/templates/template_var_usage_folded_into_one.cc new file mode 100644 index 000000000..7359e0dd7 --- /dev/null +++ b/index_tests/templates/template_var_usage_folded_into_one.cc @@ -0,0 +1,132 @@ +enum A {}; +enum B {}; + +template +T var = T(); + +A a = var; +B b = var; + +// NOTE: libclang before 4.0 doesn't expose template usage on |var|. + +#if false +EnumDecl A +EnumDecl B +UnexposedDecl var +VarDecl a + UnexposedExpr var + UnexposedExpr var + DeclRefExpr var + TypeRef enum A +UnexposedDecl var +VarDecl b + UnexposedExpr var + UnexposedExpr var + DeclRefExpr var + TypeRef enum B +UnexposedDecl var +#endif + +/* +EXTRA_FLAGS: +-std=c++14 + +OUTPUT: +{ + "includes": [], + "skipped_ranges": ["12:1-29:1"], + "usr2func": [], + "usr2type": [{ + "usr": 6697181287623958829, + "detailed_name": "enum A {}", + "qual_name_offset": 5, + "short_name": "A", + "spell": "1:6-1:7|1:1-1:10|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 10, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [16721564935990383768], + "uses": ["7:1-7:2|4|-1", "7:11-7:12|4|-1"] + }, { + "usr": 11919899838872947844, + "detailed_name": "T", + "qual_name_offset": 0, + "short_name": "T", + "spell": "4:19-4:20|4:10-4:20|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 26, + "parent_kind": 5, + "declarations": [], + "derived": [], + "instances": [8096973118640070624], + "uses": [] + }, { + "usr": 13892793056005362145, + "detailed_name": "enum B {}", + "qual_name_offset": 5, + "short_name": "B", + "spell": "2:6-2:7|2:1-2:10|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 10, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [12028309045033782423], + "uses": ["8:1-8:2|4|-1", "8:11-8:12|4|-1"] + }], + "usr2var": [{ + "usr": 8096973118640070624, + "detailed_name": "T var", + "qual_name_offset": 2, + "short_name": "var", + "hover": "T var = T()", + "spell": "5:3-5:6|5:1-5:12|2|-1", + "type": 11919899838872947844, + "kind": 13, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "uses": ["7:7-7:10|12|-1", "8:7-8:10|12|-1"] + }, { + "usr": 12028309045033782423, + "detailed_name": "B b", + "qual_name_offset": 2, + "short_name": "b", + "hover": "B b = var", + "spell": "8:3-8:4|8:1-8:13|2|-1", + "type": 13892793056005362145, + "kind": 13, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 16721564935990383768, + "detailed_name": "A a", + "qual_name_offset": 2, + "short_name": "a", + "hover": "A a = var", + "spell": "7:3-7:4|7:1-7:13|2|-1", + "type": 6697181287623958829, + "kind": 13, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "uses": [] + }] +} +*/ diff --git a/index_tests/types/anonymous_struct.cc b/index_tests/types/anonymous_struct.cc new file mode 100644 index 000000000..baa5c2dc8 --- /dev/null +++ b/index_tests/types/anonymous_struct.cc @@ -0,0 +1,134 @@ +union vector3 { + struct { float x, y, z; }; + float v[3]; +}; + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [], + "usr2type": [{ + "usr": 82, + "detailed_name": "", + "qual_name_offset": 0, + "short_name": "", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 0, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [3348817847649945564, 4821094820988543895, 15292551660437765731], + "uses": [] + }, { + "usr": 1428566502523368801, + "detailed_name": "anon struct", + "qual_name_offset": 0, + "short_name": "anon struct", + "spell": "2:3-2:9|2:3-2:28|1026|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [{ + "L": 3348817847649945564, + "R": 0 + }, { + "L": 4821094820988543895, + "R": 32 + }, { + "L": 15292551660437765731, + "R": 64 + }], + "alias_of": 0, + "kind": 23, + "parent_kind": 5, + "declarations": [], + "derived": [], + "instances": [], + "uses": [] + }, { + "usr": 17937907487590875128, + "detailed_name": "union vector3 {}", + "qual_name_offset": 6, + "short_name": "vector3", + "spell": "1:7-1:14|1:1-4:2|2|-1", + "bases": [], + "funcs": [], + "types": [1428566502523368801], + "vars": [{ + "L": 1963212417280098348, + "R": 0 + }, { + "L": 3348817847649945564, + "R": 0 + }, { + "L": 4821094820988543895, + "R": 32 + }, { + "L": 15292551660437765731, + "R": 64 + }], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": [] + }], + "usr2var": [{ + "usr": 1963212417280098348, + "detailed_name": "float vector3::v[3]", + "qual_name_offset": 6, + "short_name": "v", + "spell": "3:9-3:10|3:3-3:13|1026|-1", + "type": 0, + "kind": 8, + "parent_kind": 5, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 3348817847649945564, + "detailed_name": "float vector3::(anon struct)::x", + "qual_name_offset": 6, + "short_name": "x", + "spell": "2:18-2:19|2:12-2:19|1026|-1", + "type": 82, + "kind": 8, + "parent_kind": 23, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 4821094820988543895, + "detailed_name": "float vector3::(anon struct)::y", + "qual_name_offset": 6, + "short_name": "y", + "spell": "2:21-2:22|2:12-2:22|1026|-1", + "type": 82, + "kind": 8, + "parent_kind": 23, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 15292551660437765731, + "detailed_name": "float vector3::(anon struct)::z", + "qual_name_offset": 6, + "short_name": "z", + "spell": "2:24-2:25|2:12-2:25|1026|-1", + "type": 82, + "kind": 8, + "parent_kind": 23, + "storage": 0, + "declarations": [], + "uses": [] + }] +} +*/ diff --git a/index_tests/types/typedefs.cc b/index_tests/types/typedefs.cc new file mode 100644 index 000000000..4a2844b70 --- /dev/null +++ b/index_tests/types/typedefs.cc @@ -0,0 +1,44 @@ +typedef int (func)(const int *a, const int *b); +static func g; + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 8105378401105136463, + "detailed_name": "static int g(const int *, const int *)", + "qual_name_offset": 11, + "short_name": "g", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": ["2:13-2:14|2:1-2:14|1|-1"], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 10383876566159302459, + "detailed_name": "typedef int (func)(const int *, const int *)", + "qual_name_offset": 12, + "short_name": "func", + "spell": "1:14-1:18|1:1-1:47|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 252, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": ["2:8-2:12|4|-1"] + }], + "usr2var": [] +} +*/ \ No newline at end of file diff --git a/index_tests/unions/union_decl.cc b/index_tests/unions/union_decl.cc new file mode 100644 index 000000000..313c201e7 --- /dev/null +++ b/index_tests/unions/union_decl.cc @@ -0,0 +1,94 @@ +union Foo { + int a; + bool b; +}; + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [], + "usr2type": [{ + "usr": 37, + "detailed_name": "", + "qual_name_offset": 0, + "short_name": "", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 0, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [8804696910588009104], + "uses": [] + }, { + "usr": 53, + "detailed_name": "", + "qual_name_offset": 0, + "short_name": "", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 0, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [9529311430721959843], + "uses": [] + }, { + "usr": 8501689086387244262, + "detailed_name": "union Foo {}", + "qual_name_offset": 6, + "short_name": "Foo", + "spell": "1:7-1:10|1:1-4:2|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [{ + "L": 9529311430721959843, + "R": 0 + }, { + "L": 8804696910588009104, + "R": 0 + }], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": [] + }], + "usr2var": [{ + "usr": 8804696910588009104, + "detailed_name": "bool Foo::b", + "qual_name_offset": 5, + "short_name": "b", + "spell": "3:8-3:9|3:3-3:9|1026|-1", + "type": 37, + "kind": 8, + "parent_kind": 5, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 9529311430721959843, + "detailed_name": "int Foo::a", + "qual_name_offset": 4, + "short_name": "a", + "spell": "2:7-2:8|2:3-2:8|1026|-1", + "type": 53, + "kind": 8, + "parent_kind": 5, + "storage": 0, + "declarations": [], + "uses": [] + }] +} +*/ diff --git a/index_tests/unions/union_usage.cc b/index_tests/unions/union_usage.cc new file mode 100644 index 000000000..57bb2ebf4 --- /dev/null +++ b/index_tests/unions/union_usage.cc @@ -0,0 +1,129 @@ +union Foo { + int a : 5; + bool b : 3; +}; + +Foo f; + +void act(Foo*) { + f.a = 3; +} + +/* +// TODO: instantiations on Foo should include parameter? + +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 13982179977217945200, + "detailed_name": "void act(Foo *)", + "qual_name_offset": 5, + "short_name": "act", + "spell": "8:6-8:9|8:1-10:2|2|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 37, + "detailed_name": "", + "qual_name_offset": 0, + "short_name": "", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 0, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [8804696910588009104], + "uses": [] + }, { + "usr": 53, + "detailed_name": "", + "qual_name_offset": 0, + "short_name": "", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 0, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [9529311430721959843], + "uses": [] + }, { + "usr": 8501689086387244262, + "detailed_name": "union Foo {}", + "qual_name_offset": 6, + "short_name": "Foo", + "spell": "1:7-1:10|1:1-4:2|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [{ + "L": 9529311430721959843, + "R": 0 + }, { + "L": 8804696910588009104, + "R": 0 + }], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [2933643612409209903], + "uses": ["6:1-6:4|4|-1", "8:10-8:13|4|-1"] + }], + "usr2var": [{ + "usr": 2933643612409209903, + "detailed_name": "Foo f", + "qual_name_offset": 4, + "short_name": "f", + "spell": "6:5-6:6|6:1-6:6|2|-1", + "type": 8501689086387244262, + "kind": 13, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "uses": ["9:3-9:4|4|-1"] + }, { + "usr": 8804696910588009104, + "detailed_name": "bool Foo::b : 3", + "qual_name_offset": 5, + "short_name": "b", + "spell": "3:8-3:9|3:3-3:13|1026|-1", + "type": 37, + "kind": 8, + "parent_kind": 5, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 9529311430721959843, + "detailed_name": "int Foo::a : 5", + "qual_name_offset": 4, + "short_name": "a", + "spell": "2:7-2:8|2:3-2:12|1026|-1", + "type": 53, + "kind": 8, + "parent_kind": 5, + "storage": 0, + "declarations": [], + "uses": ["9:5-9:6|20|-1"] + }] +} +*/ diff --git a/index_tests/usage/func_called_from_constructor.cc b/index_tests/usage/func_called_from_constructor.cc new file mode 100644 index 000000000..d002eb453 --- /dev/null +++ b/index_tests/usage/func_called_from_constructor.cc @@ -0,0 +1,67 @@ +void called() {} + +struct Foo { + Foo(); +}; + +Foo::Foo() { + called(); +} + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 468307235068920063, + "detailed_name": "void called()", + "qual_name_offset": 5, + "short_name": "called", + "spell": "1:6-1:12|1:1-1:17|2|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": ["8:3-8:9|16420|-1"] + }, { + "usr": 3385168158331140247, + "detailed_name": "Foo::Foo()", + "qual_name_offset": 0, + "short_name": "Foo", + "spell": "7:6-7:9|7:1-9:2|1026|-1", + "bases": [], + "vars": [], + "callees": ["8:3-8:9|468307235068920063|3|16420"], + "kind": 9, + "parent_kind": 23, + "storage": 0, + "declarations": ["4:3-4:6|4:3-4:8|1025|-1"], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 15041163540773201510, + "detailed_name": "struct Foo {}", + "qual_name_offset": 7, + "short_name": "Foo", + "spell": "3:8-3:11|3:1-5:2|2|-1", + "bases": [], + "funcs": [3385168158331140247], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": ["4:3-4:6|4|-1", "7:1-7:4|4|-1", "7:6-7:9|4|-1"] + }], + "usr2var": [] +} +*/ diff --git a/index_tests/usage/func_called_from_macro_argument.cc b/index_tests/usage/func_called_from_macro_argument.cc new file mode 100644 index 000000000..2c7f43050 --- /dev/null +++ b/index_tests/usage/func_called_from_macro_argument.cc @@ -0,0 +1,60 @@ +#define MACRO_CALL(e) e + +bool called(bool a, bool b); + +void caller() { + MACRO_CALL(called(true, true)); +} + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 3787803219955606747, + "detailed_name": "bool called(bool a, bool b)", + "qual_name_offset": 5, + "short_name": "called", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": ["3:6-3:12|3:1-3:28|1|-1"], + "derived": [], + "uses": ["6:14-6:20|16420|-1", "6:14-6:20|64|0"] + }, { + "usr": 11404881820527069090, + "detailed_name": "void caller()", + "qual_name_offset": 5, + "short_name": "caller", + "spell": "5:6-5:12|5:1-7:2|2|-1", + "bases": [], + "vars": [], + "callees": ["6:14-6:20|3787803219955606747|3|16420"], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }], + "usr2type": [], + "usr2var": [{ + "usr": 16326993795872073150, + "detailed_name": "MACRO_CALL", + "qual_name_offset": 0, + "short_name": "MACRO_CALL", + "hover": "#define MACRO_CALL(e) e", + "spell": "1:9-1:19|1:9-1:24|2|-1", + "type": 0, + "kind": 255, + "parent_kind": 1, + "storage": 0, + "declarations": [], + "uses": ["6:3-6:13|64|-1"] + }] +} +*/ \ No newline at end of file diff --git a/index_tests/usage/func_called_from_template.cc b/index_tests/usage/func_called_from_template.cc new file mode 100644 index 000000000..9ff9b66ce --- /dev/null +++ b/index_tests/usage/func_called_from_template.cc @@ -0,0 +1,81 @@ +void called(); + +template +void caller() { + called(); +} + +void foo() { + caller(); +} + +/* +// NOTE: without caller() instantation caller() is never visited so +// called() is never referenced. +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 468307235068920063, + "detailed_name": "void called()", + "qual_name_offset": 5, + "short_name": "called", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": ["1:6-1:12|1:1-1:14|1|-1"], + "derived": [], + "uses": ["5:3-5:9|16420|-1"] + }, { + "usr": 2459767597003442547, + "detailed_name": "", + "qual_name_offset": 0, + "short_name": "", + "bases": [], + "vars": [], + "callees": ["5:3-5:9|468307235068920063|3|16420"], + "kind": 0, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }, { + "usr": 4259594751088586730, + "detailed_name": "void foo()", + "qual_name_offset": 5, + "short_name": "foo", + "spell": "8:6-8:9|8:1-10:2|2|-1", + "bases": [], + "vars": [], + "callees": ["9:3-9:9|10177235824697315808|3|16420"], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }, { + "usr": 10177235824697315808, + "detailed_name": "void caller()", + "qual_name_offset": 5, + "short_name": "caller", + "spell": "4:6-4:12|4:1-6:2|2|-1", + "bases": [], + "vars": [], + "callees": ["5:3-5:9|468307235068920063|3|16420"], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": ["9:3-9:9|16420|-1"] + }], + "usr2type": [], + "usr2var": [] +} +*/ \ No newline at end of file diff --git a/index_tests/usage/func_called_implicit_ctor.cc b/index_tests/usage/func_called_implicit_ctor.cc new file mode 100644 index 000000000..771ab3ce4 --- /dev/null +++ b/index_tests/usage/func_called_implicit_ctor.cc @@ -0,0 +1,81 @@ +struct Wrapper { + Wrapper(int i); +}; + +int called() { return 1; } + +Wrapper caller() { + return called(); +} + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 468307235068920063, + "detailed_name": "int called()", + "qual_name_offset": 4, + "short_name": "called", + "spell": "5:5-5:11|5:1-5:27|2|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": ["8:10-8:16|16420|-1"] + }, { + "usr": 10544127002917214589, + "detailed_name": "Wrapper::Wrapper(int i)", + "qual_name_offset": 0, + "short_name": "Wrapper", + "bases": [], + "vars": [], + "callees": [], + "kind": 9, + "parent_kind": 0, + "storage": 0, + "declarations": ["2:3-2:10|2:3-2:17|1025|-1"], + "derived": [], + "uses": ["8:10-8:16|16676|-1"] + }, { + "usr": 11404881820527069090, + "detailed_name": "Wrapper caller()", + "qual_name_offset": 8, + "short_name": "caller", + "spell": "7:9-7:15|7:1-9:2|2|-1", + "bases": [], + "vars": [], + "callees": ["8:10-8:16|10544127002917214589|3|16676", "8:10-8:16|468307235068920063|3|16420"], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 13611487872560323389, + "detailed_name": "struct Wrapper {}", + "qual_name_offset": 7, + "short_name": "Wrapper", + "spell": "1:8-1:15|1:1-3:2|2|-1", + "bases": [], + "funcs": [10544127002917214589], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": ["2:3-2:10|4|-1", "7:1-7:8|4|-1"] + }], + "usr2var": [] +} +*/ diff --git a/index_tests/usage/func_usage_addr_func.cc b/index_tests/usage/func_usage_addr_func.cc new file mode 100644 index 000000000..b0c5fb4f2 --- /dev/null +++ b/index_tests/usage/func_usage_addr_func.cc @@ -0,0 +1,77 @@ +void consume(void (*)()) {} + +void used() {} + +void user() { + void (*x)() = &used; + consume(&used); +} + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 5264867802674151787, + "detailed_name": "void used()", + "qual_name_offset": 5, + "short_name": "used", + "spell": "3:6-3:10|3:1-3:15|2|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": ["6:18-6:22|132|-1", "7:12-7:16|132|-1"] + }, { + "usr": 9376923949268137283, + "detailed_name": "void user()", + "qual_name_offset": 5, + "short_name": "user", + "spell": "5:6-5:10|5:1-8:2|2|-1", + "bases": [], + "vars": [16088407831770615719], + "callees": ["6:18-6:22|5264867802674151787|3|132", "6:18-6:22|5264867802674151787|3|132", "7:3-7:10|12924914488846929470|3|16420", "7:12-7:16|5264867802674151787|3|132"], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }, { + "usr": 12924914488846929470, + "detailed_name": "void consume(void (*)())", + "qual_name_offset": 5, + "short_name": "consume", + "spell": "1:6-1:13|1:1-1:28|2|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": ["7:3-7:10|16420|-1"] + }], + "usr2type": [], + "usr2var": [{ + "usr": 16088407831770615719, + "detailed_name": "void (*x)()", + "qual_name_offset": 7, + "short_name": "x", + "hover": "void (*x)() = &used", + "spell": "6:10-6:11|6:3-6:22|2|-1", + "type": 0, + "kind": 13, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": [] + }] +} +*/ diff --git a/index_tests/usage/func_usage_addr_method.cc b/index_tests/usage/func_usage_addr_method.cc new file mode 100644 index 000000000..3e0582daf --- /dev/null +++ b/index_tests/usage/func_usage_addr_method.cc @@ -0,0 +1,78 @@ +struct Foo { + void Used(); +}; + +void user() { + auto x = &Foo::Used; +} + + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 9376923949268137283, + "detailed_name": "void user()", + "qual_name_offset": 5, + "short_name": "user", + "spell": "5:6-5:10|5:1-7:2|2|-1", + "bases": [], + "vars": [4636142131003982569], + "callees": ["6:18-6:22|18417145003926999463|3|132", "6:18-6:22|18417145003926999463|3|132"], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }, { + "usr": 18417145003926999463, + "detailed_name": "void Foo::Used()", + "qual_name_offset": 5, + "short_name": "Used", + "bases": [], + "vars": [], + "callees": [], + "kind": 6, + "parent_kind": 0, + "storage": 0, + "declarations": ["2:8-2:12|2:3-2:14|1025|-1"], + "derived": [], + "uses": ["6:18-6:22|132|-1"] + }], + "usr2type": [{ + "usr": 15041163540773201510, + "detailed_name": "struct Foo {}", + "qual_name_offset": 7, + "short_name": "Foo", + "spell": "1:8-1:11|1:1-3:2|2|-1", + "bases": [], + "funcs": [18417145003926999463], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": ["6:13-6:16|4|-1"] + }], + "usr2var": [{ + "usr": 4636142131003982569, + "detailed_name": "void (Foo::*)() x", + "qual_name_offset": 16, + "short_name": "x", + "hover": "void (Foo::*)() x = &Foo::Used", + "spell": "6:8-6:9|6:3-6:22|2|-1", + "type": 0, + "kind": 13, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": [] + }] +} +*/ diff --git a/index_tests/usage/func_usage_call_func.cc b/index_tests/usage/func_usage_call_func.cc new file mode 100644 index 000000000..298411347 --- /dev/null +++ b/index_tests/usage/func_usage_call_func.cc @@ -0,0 +1,45 @@ +void called() {} +void caller() { + called(); +} + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 468307235068920063, + "detailed_name": "void called()", + "qual_name_offset": 5, + "short_name": "called", + "spell": "1:6-1:12|1:1-1:17|2|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": ["3:3-3:9|16420|-1"] + }, { + "usr": 11404881820527069090, + "detailed_name": "void caller()", + "qual_name_offset": 5, + "short_name": "caller", + "spell": "2:6-2:12|2:1-4:2|2|-1", + "bases": [], + "vars": [], + "callees": ["3:3-3:9|468307235068920063|3|16420"], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }], + "usr2type": [], + "usr2var": [] +} +*/ diff --git a/index_tests/usage/func_usage_call_method.cc b/index_tests/usage/func_usage_call_method.cc new file mode 100644 index 000000000..6e86be512 --- /dev/null +++ b/index_tests/usage/func_usage_call_method.cc @@ -0,0 +1,78 @@ +struct Foo { + void Used(); +}; + +void user() { + Foo* f = nullptr; + f->Used(); +} + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 9376923949268137283, + "detailed_name": "void user()", + "qual_name_offset": 5, + "short_name": "user", + "spell": "5:6-5:10|5:1-8:2|2|-1", + "bases": [], + "vars": [14045150712868309451], + "callees": ["7:6-7:10|18417145003926999463|3|16420"], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }, { + "usr": 18417145003926999463, + "detailed_name": "void Foo::Used()", + "qual_name_offset": 5, + "short_name": "Used", + "bases": [], + "vars": [], + "callees": [], + "kind": 6, + "parent_kind": 0, + "storage": 0, + "declarations": ["2:8-2:12|2:3-2:14|1025|-1"], + "derived": [], + "uses": ["7:6-7:10|16420|-1"] + }], + "usr2type": [{ + "usr": 15041163540773201510, + "detailed_name": "struct Foo {}", + "qual_name_offset": 7, + "short_name": "Foo", + "spell": "1:8-1:11|1:1-3:2|2|-1", + "bases": [], + "funcs": [18417145003926999463], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [14045150712868309451], + "uses": ["6:3-6:6|4|-1"] + }], + "usr2var": [{ + "usr": 14045150712868309451, + "detailed_name": "Foo *f", + "qual_name_offset": 5, + "short_name": "f", + "hover": "Foo *f = nullptr", + "spell": "6:8-6:9|6:3-6:19|2|-1", + "type": 15041163540773201510, + "kind": 13, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": ["7:3-7:4|12|-1"] + }] +} +*/ diff --git a/index_tests/usage/func_usage_class_inline_var_def.cc b/index_tests/usage/func_usage_class_inline_var_def.cc new file mode 100644 index 000000000..f62581eae --- /dev/null +++ b/index_tests/usage/func_usage_class_inline_var_def.cc @@ -0,0 +1,82 @@ +static int helper() { + return 5; +} + +class Foo { + int x = helper(); +}; + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 9630503130605430498, + "detailed_name": "static int helper()", + "qual_name_offset": 11, + "short_name": "helper", + "spell": "1:12-1:18|1:1-3:2|2|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": ["6:11-6:17|36|-1"] + }], + "usr2type": [{ + "usr": 53, + "detailed_name": "", + "qual_name_offset": 0, + "short_name": "", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 0, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [4220150017963593039], + "uses": [] + }, { + "usr": 15041163540773201510, + "detailed_name": "class Foo {}", + "qual_name_offset": 6, + "short_name": "Foo", + "spell": "5:7-5:10|5:1-7:2|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [{ + "L": 4220150017963593039, + "R": 0 + }], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": [] + }], + "usr2var": [{ + "usr": 4220150017963593039, + "detailed_name": "int Foo::x", + "qual_name_offset": 4, + "short_name": "x", + "hover": "int Foo::x = helper()", + "spell": "6:7-6:8|6:3-6:19|1026|-1", + "type": 53, + "kind": 8, + "parent_kind": 5, + "storage": 0, + "declarations": [], + "uses": [] + }] +} +*/ diff --git a/index_tests/usage/func_usage_forward_decl_func.cc b/index_tests/usage/func_usage_forward_decl_func.cc new file mode 100644 index 000000000..06a1e767b --- /dev/null +++ b/index_tests/usage/func_usage_forward_decl_func.cc @@ -0,0 +1,44 @@ +void foo(); + +void usage() { + foo(); +} +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 4259594751088586730, + "detailed_name": "void foo()", + "qual_name_offset": 5, + "short_name": "foo", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": ["1:6-1:9|1:1-1:11|1|-1"], + "derived": [], + "uses": ["4:3-4:6|16420|-1"] + }, { + "usr": 6767773193109753523, + "detailed_name": "void usage()", + "qual_name_offset": 5, + "short_name": "usage", + "spell": "3:6-3:11|3:1-5:2|2|-1", + "bases": [], + "vars": [], + "callees": ["4:3-4:6|4259594751088586730|3|16420"], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }], + "usr2type": [], + "usr2var": [] +} +*/ diff --git a/index_tests/usage/func_usage_forward_decl_method.cc b/index_tests/usage/func_usage_forward_decl_method.cc new file mode 100644 index 000000000..7da093ea0 --- /dev/null +++ b/index_tests/usage/func_usage_forward_decl_method.cc @@ -0,0 +1,77 @@ +struct Foo { + void foo(); +}; + +void usage() { + Foo* f = nullptr; + f->foo(); +} +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 6767773193109753523, + "detailed_name": "void usage()", + "qual_name_offset": 5, + "short_name": "usage", + "spell": "5:6-5:11|5:1-8:2|2|-1", + "bases": [], + "vars": [16229832321010999607], + "callees": ["7:6-7:9|17922201480358737771|3|16420"], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }, { + "usr": 17922201480358737771, + "detailed_name": "void Foo::foo()", + "qual_name_offset": 5, + "short_name": "foo", + "bases": [], + "vars": [], + "callees": [], + "kind": 6, + "parent_kind": 0, + "storage": 0, + "declarations": ["2:8-2:11|2:3-2:13|1025|-1"], + "derived": [], + "uses": ["7:6-7:9|16420|-1"] + }], + "usr2type": [{ + "usr": 15041163540773201510, + "detailed_name": "struct Foo {}", + "qual_name_offset": 7, + "short_name": "Foo", + "spell": "1:8-1:11|1:1-3:2|2|-1", + "bases": [], + "funcs": [17922201480358737771], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [16229832321010999607], + "uses": ["6:3-6:6|4|-1"] + }], + "usr2var": [{ + "usr": 16229832321010999607, + "detailed_name": "Foo *f", + "qual_name_offset": 5, + "short_name": "f", + "hover": "Foo *f = nullptr", + "spell": "6:8-6:9|6:3-6:19|2|-1", + "type": 15041163540773201510, + "kind": 13, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": ["7:3-7:4|12|-1"] + }] +} +*/ diff --git a/index_tests/usage/func_usage_template_func.cc b/index_tests/usage/func_usage_template_func.cc new file mode 100644 index 000000000..6a812a46c --- /dev/null +++ b/index_tests/usage/func_usage_template_func.cc @@ -0,0 +1,47 @@ +template +void accept(T); + +void foo() { + accept(1); + accept(true); +} + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 4259594751088586730, + "detailed_name": "void foo()", + "qual_name_offset": 5, + "short_name": "foo", + "spell": "4:6-4:9|4:1-7:2|2|-1", + "bases": [], + "vars": [], + "callees": ["5:3-5:9|10585861037135727329|3|16420", "6:3-6:9|10585861037135727329|3|16420"], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }, { + "usr": 10585861037135727329, + "detailed_name": "void accept(T)", + "qual_name_offset": 5, + "short_name": "accept", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": ["2:6-2:12|2:1-2:15|1|-1"], + "derived": [], + "uses": ["5:3-5:9|16420|-1", "6:3-6:9|16420|-1"] + }], + "usr2type": [], + "usr2var": [] +} +*/ diff --git a/index_tests/usage/type_usage_as_template_parameter.cc b/index_tests/usage/type_usage_as_template_parameter.cc new file mode 100644 index 000000000..f13a593f3 --- /dev/null +++ b/index_tests/usage/type_usage_as_template_parameter.cc @@ -0,0 +1,107 @@ +template +class unique_ptr {}; + +struct S {}; + +static unique_ptr f0; +static unique_ptr f1; + +unique_ptr* return_type() { + unique_ptr* local; + return nullptr; +} +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 16359708726068806331, + "detailed_name": "unique_ptr *return_type()", + "qual_name_offset": 15, + "short_name": "return_type", + "spell": "9:16-9:27|9:1-12:2|2|-1", + "bases": [], + "vars": [3364438781074774169], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 3286534761799572592, + "detailed_name": "class unique_ptr {}", + "qual_name_offset": 6, + "short_name": "unique_ptr", + "spell": "2:7-2:17|2:1-2:20|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [12857919739649552168, 18075066956054788088, 3364438781074774169], + "uses": ["6:8-6:18|4|-1", "7:8-7:18|4|-1", "9:1-9:11|4|-1", "10:3-10:13|4|-1"] + }, { + "usr": 4750332761459066907, + "detailed_name": "struct S {}", + "qual_name_offset": 7, + "short_name": "S", + "spell": "4:8-4:9|4:1-4:12|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": ["7:19-7:20|4|-1", "9:12-9:13|4|-1", "10:14-10:15|4|-1"] + }], + "usr2var": [{ + "usr": 3364438781074774169, + "detailed_name": "unique_ptr *local", + "qual_name_offset": 15, + "short_name": "local", + "spell": "10:18-10:23|10:3-10:23|2|-1", + "type": 3286534761799572592, + "kind": 13, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 12857919739649552168, + "detailed_name": "static unique_ptr f0", + "qual_name_offset": 24, + "short_name": "f0", + "spell": "6:25-6:27|6:1-6:27|2|-1", + "type": 3286534761799572592, + "kind": 13, + "parent_kind": 0, + "storage": 2, + "declarations": [], + "uses": [] + }, { + "usr": 18075066956054788088, + "detailed_name": "static unique_ptr f1", + "qual_name_offset": 21, + "short_name": "f1", + "spell": "7:22-7:24|7:1-7:24|2|-1", + "type": 3286534761799572592, + "kind": 13, + "parent_kind": 0, + "storage": 2, + "declarations": [], + "uses": [] + }] +} +*/ diff --git a/index_tests/usage/type_usage_as_template_parameter_complex.cc b/index_tests/usage/type_usage_as_template_parameter_complex.cc new file mode 100644 index 000000000..4dedd1317 --- /dev/null +++ b/index_tests/usage/type_usage_as_template_parameter_complex.cc @@ -0,0 +1,238 @@ +template +class unique_ptr; + +struct S1; +struct S2; + +#if false +VarDecl f + TemplateRef unique_ptr + TemplateRef unique_ptr + TypeRef struct S1 + TypeRef struct S2 + TypeRef struct S2 +#endif +extern unique_ptr, S2> f; + +#if false +FunctionDecl as_return_type + TemplateRef unique_ptr + TemplateRef unique_ptr + TypeRef struct S1 + TypeRef struct S2 + TypeRef struct S2 + ParmDecl + TemplateRef unique_ptr + TypeRef struct S1 + TypeRef struct S2 + CompoundStmt + ReturnStmt + UnexposedExpr + CXXNullPtrLiteralExpr +#endif +unique_ptr, S2>* as_return_type(unique_ptr*) { return nullptr; } + +#if false +FunctionDecl no_return_type + ParmDecl + CompoundStmt +#endif +void no_return_type(int) {} + +#if false +FunctionDecl empty + CompoundStmt + DeclStmt + VarDecl local + TemplateRef unique_ptr + TemplateRef unique_ptr + TypeRef struct S1 + TypeRef struct S2 + TypeRef struct S2 +#endif +void empty() { + unique_ptr, S2>* local; +} + +#if false +ClassDecl Foo + CXXMethod foo + TemplateRef unique_ptr + TypeRef struct S1 + TypeRef struct S2 +#endif +class Foo { + unique_ptr* foo(); +}; + +#if false +CXXMethod foo + TemplateRef unique_ptr + TypeRef struct S1 + TypeRef struct S2 + TypeRef class Foo + CompoundStmt + ReturnStmt + UnexposedExpr + CXXNullPtrLiteralExpr +#endif +unique_ptr* Foo::foo() { return nullptr; } + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": ["7:1-15:1", "17:1-33:1", "35:1-40:1", "42:1-53:1", "57:1-64:1", "68:1-79:1"], + "usr2func": [{ + "usr": 1246637699196435450, + "detailed_name": "unique_ptr, S2> *as_return_type(unique_ptr *)", + "qual_name_offset": 36, + "short_name": "as_return_type", + "spell": "33:37-33:51|33:1-33:92|2|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }, { + "usr": 13067214284561914253, + "detailed_name": "void no_return_type(int)", + "qual_name_offset": 5, + "short_name": "no_return_type", + "spell": "40:6-40:20|40:1-40:28|2|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }, { + "usr": 17922201480358737771, + "detailed_name": "unique_ptr *Foo::foo()", + "qual_name_offset": 20, + "short_name": "foo", + "spell": "79:26-79:29|79:1-79:51|1026|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 6, + "parent_kind": 5, + "storage": 0, + "declarations": ["65:23-65:26|65:3-65:28|1025|-1"], + "derived": [], + "uses": [] + }, { + "usr": 18320186404467436976, + "detailed_name": "void empty()", + "qual_name_offset": 5, + "short_name": "empty", + "spell": "53:6-53:11|53:1-55:2|2|-1", + "bases": [], + "vars": [500112618220246], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 4310164820010458371, + "detailed_name": "struct S1", + "qual_name_offset": 7, + "short_name": "S1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": ["4:8-4:10|4:1-4:10|1|-1"], + "derived": [], + "instances": [], + "uses": ["15:30-15:32|4|-1", "33:23-33:25|4|-1", "33:63-33:65|4|-1", "54:25-54:27|4|-1", "65:14-65:16|4|-1", "79:12-79:14|4|-1"] + }, { + "usr": 12728490517004312484, + "detailed_name": "struct S2", + "qual_name_offset": 7, + "short_name": "S2", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": ["5:8-5:10|5:1-5:10|1|-1"], + "derived": [], + "instances": [], + "uses": ["15:34-15:36|4|-1", "15:39-15:41|4|-1", "33:27-33:29|4|-1", "33:32-33:34|4|-1", "33:67-33:69|4|-1", "54:29-54:31|4|-1", "54:34-54:36|4|-1", "65:18-65:20|4|-1", "79:16-79:18|4|-1"] + }, { + "usr": 14209198335088845323, + "detailed_name": "class unique_ptr", + "qual_name_offset": 6, + "short_name": "unique_ptr", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": ["2:7-2:17|2:1-2:17|1|-1"], + "derived": [], + "instances": [2933643612409209903, 500112618220246], + "uses": ["15:8-15:18|4|-1", "15:19-15:29|4|-1", "33:1-33:11|4|-1", "33:12-33:22|4|-1", "33:52-33:62|4|-1", "54:3-54:13|4|-1", "54:14-54:24|4|-1", "65:3-65:13|4|-1", "79:1-79:11|4|-1"] + }, { + "usr": 15041163540773201510, + "detailed_name": "class Foo {}", + "qual_name_offset": 6, + "short_name": "Foo", + "spell": "64:7-64:10|64:1-66:2|2|-1", + "bases": [], + "funcs": [17922201480358737771], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": ["79:21-79:24|4|-1"] + }], + "usr2var": [{ + "usr": 500112618220246, + "detailed_name": "unique_ptr, S2> *local", + "qual_name_offset": 36, + "short_name": "local", + "spell": "54:39-54:44|54:3-54:44|2|-1", + "type": 14209198335088845323, + "kind": 13, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 2933643612409209903, + "detailed_name": "extern unique_ptr, S2> f", + "qual_name_offset": 42, + "short_name": "f", + "type": 14209198335088845323, + "kind": 13, + "parent_kind": 0, + "storage": 1, + "declarations": ["15:43-15:44|15:1-15:44|1|-1"], + "uses": [] + }] +} +*/ diff --git a/index_tests/usage/type_usage_as_template_parameter_simple.cc b/index_tests/usage/type_usage_as_template_parameter_simple.cc new file mode 100644 index 000000000..75c9d63ed --- /dev/null +++ b/index_tests/usage/type_usage_as_template_parameter_simple.cc @@ -0,0 +1,62 @@ +template +class unique_ptr {}; + +struct S; + +static unique_ptr foo; + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [], + "usr2type": [{ + "usr": 3286534761799572592, + "detailed_name": "class unique_ptr {}", + "qual_name_offset": 6, + "short_name": "unique_ptr", + "spell": "2:7-2:17|2:1-2:20|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [3398408600781120939], + "uses": ["6:8-6:18|4|-1"] + }, { + "usr": 4750332761459066907, + "detailed_name": "struct S", + "qual_name_offset": 7, + "short_name": "S", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": ["4:8-4:9|4:1-4:9|1|-1"], + "derived": [], + "instances": [], + "uses": ["6:19-6:20|4|-1"] + }], + "usr2var": [{ + "usr": 3398408600781120939, + "detailed_name": "static unique_ptr foo", + "qual_name_offset": 21, + "short_name": "foo", + "spell": "6:22-6:25|6:1-6:25|2|-1", + "type": 3286534761799572592, + "kind": 13, + "parent_kind": 0, + "storage": 2, + "declarations": [], + "uses": [] + }] +} +*/ diff --git a/index_tests/usage/type_usage_declare_extern.cc b/index_tests/usage/type_usage_declare_extern.cc new file mode 100644 index 000000000..afe6962f1 --- /dev/null +++ b/index_tests/usage/type_usage_declare_extern.cc @@ -0,0 +1,41 @@ +struct T {}; + +extern T t; +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [], + "usr2type": [{ + "usr": 5673439900521455039, + "detailed_name": "struct T {}", + "qual_name_offset": 7, + "short_name": "T", + "spell": "1:8-1:9|1:1-1:12|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [1346710425945444872], + "uses": ["3:8-3:9|4|-1"] + }], + "usr2var": [{ + "usr": 1346710425945444872, + "detailed_name": "extern T t", + "qual_name_offset": 9, + "short_name": "t", + "type": 5673439900521455039, + "kind": 13, + "parent_kind": 0, + "storage": 1, + "declarations": ["3:10-3:11|3:1-3:11|1|-1"], + "uses": [] + }] +} +*/ diff --git a/index_tests/usage/type_usage_declare_field.cc b/index_tests/usage/type_usage_declare_field.cc new file mode 100644 index 000000000..fe6f1afb9 --- /dev/null +++ b/index_tests/usage/type_usage_declare_field.cc @@ -0,0 +1,98 @@ +struct ForwardType; +struct ImplementedType {}; + +struct Foo { + ForwardType* a; + ImplementedType b; +}; + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [], + "usr2type": [{ + "usr": 8508299082070213750, + "detailed_name": "struct ImplementedType {}", + "qual_name_offset": 7, + "short_name": "ImplementedType", + "spell": "2:8-2:23|2:1-2:26|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [14727441168849658842], + "uses": ["6:3-6:18|4|-1"] + }, { + "usr": 13749354388332789217, + "detailed_name": "struct ForwardType", + "qual_name_offset": 7, + "short_name": "ForwardType", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": ["1:8-1:19|1:1-1:19|1|-1"], + "derived": [], + "instances": [14314859014962085433], + "uses": ["5:3-5:14|4|-1"] + }, { + "usr": 15041163540773201510, + "detailed_name": "struct Foo {}", + "qual_name_offset": 7, + "short_name": "Foo", + "spell": "4:8-4:11|4:1-7:2|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [{ + "L": 14314859014962085433, + "R": 0 + }, { + "L": 14727441168849658842, + "R": 64 + }], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": [] + }], + "usr2var": [{ + "usr": 14314859014962085433, + "detailed_name": "ForwardType *Foo::a", + "qual_name_offset": 13, + "short_name": "a", + "spell": "5:16-5:17|5:3-5:17|1026|-1", + "type": 13749354388332789217, + "kind": 8, + "parent_kind": 23, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 14727441168849658842, + "detailed_name": "ImplementedType Foo::b", + "qual_name_offset": 16, + "short_name": "b", + "spell": "6:19-6:20|6:3-6:20|1026|-1", + "type": 8508299082070213750, + "kind": 8, + "parent_kind": 23, + "storage": 0, + "declarations": [], + "uses": [] + }] +} +*/ diff --git a/index_tests/usage/type_usage_declare_local.cc b/index_tests/usage/type_usage_declare_local.cc new file mode 100644 index 000000000..82415f552 --- /dev/null +++ b/index_tests/usage/type_usage_declare_local.cc @@ -0,0 +1,90 @@ +struct ForwardType; +struct ImplementedType {}; + +void Foo() { + ForwardType* a; + ImplementedType b; +} + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 4654328188330986029, + "detailed_name": "void Foo()", + "qual_name_offset": 5, + "short_name": "Foo", + "spell": "4:6-4:9|4:1-7:2|2|-1", + "bases": [], + "vars": [16374832544037266261, 2580122838476012357], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 8508299082070213750, + "detailed_name": "struct ImplementedType {}", + "qual_name_offset": 7, + "short_name": "ImplementedType", + "spell": "2:8-2:23|2:1-2:26|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [2580122838476012357], + "uses": ["6:3-6:18|4|-1"] + }, { + "usr": 13749354388332789217, + "detailed_name": "struct ForwardType", + "qual_name_offset": 7, + "short_name": "ForwardType", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": ["1:8-1:19|1:1-1:19|1|-1"], + "derived": [], + "instances": [16374832544037266261], + "uses": ["5:3-5:14|4|-1"] + }], + "usr2var": [{ + "usr": 2580122838476012357, + "detailed_name": "ImplementedType b", + "qual_name_offset": 16, + "short_name": "b", + "spell": "6:19-6:20|6:3-6:20|2|-1", + "type": 8508299082070213750, + "kind": 13, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 16374832544037266261, + "detailed_name": "ForwardType *a", + "qual_name_offset": 13, + "short_name": "a", + "spell": "5:16-5:17|5:3-5:17|2|-1", + "type": 13749354388332789217, + "kind": 13, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": [] + }] +} +*/ diff --git a/index_tests/usage/type_usage_declare_param.cc b/index_tests/usage/type_usage_declare_param.cc new file mode 100644 index 000000000..ae7c00063 --- /dev/null +++ b/index_tests/usage/type_usage_declare_param.cc @@ -0,0 +1,87 @@ +struct ForwardType; +struct ImplementedType {}; + +void foo(ForwardType* f, ImplementedType a) {} + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 1699390678058422036, + "detailed_name": "void foo(ForwardType *f, ImplementedType a)", + "qual_name_offset": 5, + "short_name": "foo", + "spell": "4:6-4:9|4:1-4:47|2|-1", + "bases": [], + "vars": [13058491096576226774, 11055777568039014776], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 8508299082070213750, + "detailed_name": "struct ImplementedType {}", + "qual_name_offset": 7, + "short_name": "ImplementedType", + "spell": "2:8-2:23|2:1-2:26|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [11055777568039014776], + "uses": ["4:26-4:41|4|-1"] + }, { + "usr": 13749354388332789217, + "detailed_name": "struct ForwardType", + "qual_name_offset": 7, + "short_name": "ForwardType", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": ["1:8-1:19|1:1-1:19|1|-1"], + "derived": [], + "instances": [13058491096576226774], + "uses": ["4:10-4:21|4|-1"] + }], + "usr2var": [{ + "usr": 11055777568039014776, + "detailed_name": "ImplementedType a", + "qual_name_offset": 16, + "short_name": "a", + "spell": "4:42-4:43|4:26-4:43|1026|-1", + "type": 8508299082070213750, + "kind": 253, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 13058491096576226774, + "detailed_name": "ForwardType *f", + "qual_name_offset": 13, + "short_name": "f", + "spell": "4:23-4:24|4:10-4:24|1026|-1", + "type": 13749354388332789217, + "kind": 253, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": [] + }] +} +*/ diff --git a/index_tests/usage/type_usage_declare_param_prototype.cc b/index_tests/usage/type_usage_declare_param_prototype.cc new file mode 100644 index 000000000..7c33416a7 --- /dev/null +++ b/index_tests/usage/type_usage_declare_param_prototype.cc @@ -0,0 +1,63 @@ +struct Foo; + +void foo(Foo* f, Foo*); +void foo(Foo* f, Foo*) {} + +/* +// TODO: No interesting usage on prototype. But maybe that's ok! +// TODO: We should have the same variable declared for both prototype and +// declaration. So it should have a usage marker on both. Then we could +// rename parameters! + +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 8908726657907936744, + "detailed_name": "void foo(Foo *f, Foo *)", + "qual_name_offset": 5, + "short_name": "foo", + "spell": "4:6-4:9|4:1-4:26|2|-1", + "bases": [], + "vars": [13823260660189154978], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": ["3:6-3:9|3:1-3:23|1|-1"], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 15041163540773201510, + "detailed_name": "struct Foo", + "qual_name_offset": 7, + "short_name": "Foo", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": ["1:8-1:11|1:1-1:11|1|-1"], + "derived": [], + "instances": [13823260660189154978], + "uses": ["3:10-3:13|4|-1", "3:18-3:21|4|-1", "4:10-4:13|4|-1", "4:18-4:21|4|-1"] + }], + "usr2var": [{ + "usr": 13823260660189154978, + "detailed_name": "Foo *f", + "qual_name_offset": 5, + "short_name": "f", + "spell": "4:15-4:16|4:10-4:16|1026|-1", + "type": 15041163540773201510, + "kind": 253, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": [] + }] +} +*/ diff --git a/index_tests/usage/type_usage_declare_param_unnamed.cc b/index_tests/usage/type_usage_declare_param_unnamed.cc new file mode 100644 index 000000000..292087394 --- /dev/null +++ b/index_tests/usage/type_usage_declare_param_unnamed.cc @@ -0,0 +1,43 @@ +struct ForwardType; +void foo(ForwardType*) {} +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 15327735280790448926, + "detailed_name": "void foo(ForwardType *)", + "qual_name_offset": 5, + "short_name": "foo", + "spell": "2:6-2:9|2:1-2:26|2|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 13749354388332789217, + "detailed_name": "struct ForwardType", + "qual_name_offset": 7, + "short_name": "ForwardType", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": ["1:8-1:19|1:1-1:19|1|-1"], + "derived": [], + "instances": [], + "uses": ["2:10-2:21|4|-1"] + }], + "usr2var": [] +} +*/ diff --git a/index_tests/usage/type_usage_declare_qualifiers.cc b/index_tests/usage/type_usage_declare_qualifiers.cc new file mode 100644 index 000000000..b6131373b --- /dev/null +++ b/index_tests/usage/type_usage_declare_qualifiers.cc @@ -0,0 +1,123 @@ +struct Type {}; + +void foo(Type& a0, const Type& a1) { + Type a2; + Type* a3; + const Type* a4; + const Type* const a5 = nullptr; +} +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 16858540520096802573, + "detailed_name": "void foo(Type &a0, const Type &a1)", + "qual_name_offset": 5, + "short_name": "foo", + "spell": "3:6-3:9|3:1-8:2|2|-1", + "bases": [], + "vars": [7997456978847868736, 17228576662112939520, 15429032129697337561, 6081981442495435784, 5004072032239834773, 14939253431683105646], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 13487927231218873822, + "detailed_name": "struct Type {}", + "qual_name_offset": 7, + "short_name": "Type", + "spell": "1:8-1:12|1:1-1:15|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [7997456978847868736, 17228576662112939520, 15429032129697337561, 6081981442495435784, 5004072032239834773, 14939253431683105646], + "uses": ["3:10-3:14|4|-1", "3:26-3:30|4|-1", "4:3-4:7|4|-1", "5:3-5:7|4|-1", "6:9-6:13|4|-1", "7:9-7:13|4|-1"] + }], + "usr2var": [{ + "usr": 5004072032239834773, + "detailed_name": "const Type *a4", + "qual_name_offset": 12, + "short_name": "a4", + "spell": "6:15-6:17|6:3-6:17|2|-1", + "type": 13487927231218873822, + "kind": 13, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 6081981442495435784, + "detailed_name": "Type *a3", + "qual_name_offset": 6, + "short_name": "a3", + "spell": "5:9-5:11|5:3-5:11|2|-1", + "type": 13487927231218873822, + "kind": 13, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 7997456978847868736, + "detailed_name": "Type &a0", + "qual_name_offset": 6, + "short_name": "a0", + "spell": "3:16-3:18|3:10-3:18|1026|-1", + "type": 13487927231218873822, + "kind": 253, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 14939253431683105646, + "detailed_name": "const Type *const a5", + "qual_name_offset": 18, + "short_name": "a5", + "hover": "const Type *const a5 = nullptr", + "spell": "7:21-7:23|7:3-7:33|2|-1", + "type": 13487927231218873822, + "kind": 13, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 15429032129697337561, + "detailed_name": "Type a2", + "qual_name_offset": 5, + "short_name": "a2", + "spell": "4:8-4:10|4:3-4:10|2|-1", + "type": 13487927231218873822, + "kind": 13, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 17228576662112939520, + "detailed_name": "const Type &a1", + "qual_name_offset": 12, + "short_name": "a1", + "spell": "3:32-3:34|3:20-3:34|1026|-1", + "type": 13487927231218873822, + "kind": 253, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": [] + }] +} +*/ diff --git a/index_tests/usage/type_usage_declare_static.cc b/index_tests/usage/type_usage_declare_static.cc new file mode 100644 index 000000000..3957ceb21 --- /dev/null +++ b/index_tests/usage/type_usage_declare_static.cc @@ -0,0 +1,41 @@ +struct Type {}; +static Type t; +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [], + "usr2type": [{ + "usr": 13487927231218873822, + "detailed_name": "struct Type {}", + "qual_name_offset": 7, + "short_name": "Type", + "spell": "1:8-1:12|1:1-1:15|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [6601831367240627080], + "uses": ["2:8-2:12|4|-1"] + }], + "usr2var": [{ + "usr": 6601831367240627080, + "detailed_name": "static Type t", + "qual_name_offset": 12, + "short_name": "t", + "spell": "2:13-2:14|2:1-2:14|2|-1", + "type": 13487927231218873822, + "kind": 13, + "parent_kind": 0, + "storage": 2, + "declarations": [], + "uses": [] + }] +} +*/ diff --git a/index_tests/usage/type_usage_on_return_type.cc b/index_tests/usage/type_usage_on_return_type.cc new file mode 100644 index 000000000..cb6ef08e6 --- /dev/null +++ b/index_tests/usage/type_usage_on_return_type.cc @@ -0,0 +1,136 @@ +struct Type; + +Type* foo(); +Type* foo(); +Type* foo() { return nullptr; } + +class Foo { + Type* Get(int); + void Empty(); +}; + +Type* Foo::Get(int) { return nullptr; } +void Foo::Empty() {} + +extern const Type& external(); + +static Type* bar(); +static Type* bar() { return nullptr; } + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 4240751906910175539, + "detailed_name": "void Foo::Empty()", + "qual_name_offset": 5, + "short_name": "Empty", + "spell": "13:11-13:16|13:1-13:21|1026|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 6, + "parent_kind": 5, + "storage": 0, + "declarations": ["9:8-9:13|9:3-9:15|1025|-1"], + "derived": [], + "uses": [] + }, { + "usr": 4259594751088586730, + "detailed_name": "Type *foo()", + "qual_name_offset": 6, + "short_name": "foo", + "spell": "5:7-5:10|5:1-5:32|2|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": ["3:7-3:10|3:1-3:12|1|-1", "4:7-4:10|4:1-4:12|1|-1"], + "derived": [], + "uses": [] + }, { + "usr": 7746867874366499515, + "detailed_name": "extern const Type &external()", + "qual_name_offset": 19, + "short_name": "external", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": ["15:20-15:28|15:1-15:30|1|-1"], + "derived": [], + "uses": [] + }, { + "usr": 13402221340333431092, + "detailed_name": "Type *Foo::Get(int)", + "qual_name_offset": 6, + "short_name": "Get", + "spell": "12:12-12:15|12:1-12:40|1026|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 6, + "parent_kind": 5, + "storage": 0, + "declarations": ["8:9-8:12|8:3-8:17|1025|-1"], + "derived": [], + "uses": [] + }, { + "usr": 18408440185620243373, + "detailed_name": "static Type *bar()", + "qual_name_offset": 13, + "short_name": "bar", + "spell": "18:14-18:17|18:1-18:39|2|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": ["17:14-17:17|17:1-17:19|1|-1"], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 13487927231218873822, + "detailed_name": "struct Type", + "qual_name_offset": 7, + "short_name": "Type", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": ["1:8-1:12|1:1-1:12|1|-1"], + "derived": [], + "instances": [], + "uses": ["3:1-3:5|4|-1", "4:1-4:5|4|-1", "5:1-5:5|4|-1", "8:3-8:7|4|-1", "12:1-12:5|4|-1", "15:14-15:18|4|-1", "17:8-17:12|4|-1", "18:8-18:12|4|-1"] + }, { + "usr": 15041163540773201510, + "detailed_name": "class Foo {}", + "qual_name_offset": 6, + "short_name": "Foo", + "spell": "7:7-7:10|7:1-10:2|2|-1", + "bases": [], + "funcs": [13402221340333431092, 4240751906910175539], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": ["12:7-12:10|4|-1", "13:6-13:9|4|-1"] + }], + "usr2var": [] +} +*/ diff --git a/index_tests/usage/type_usage_typedef_and_using.cc b/index_tests/usage/type_usage_typedef_and_using.cc new file mode 100644 index 000000000..58c82325d --- /dev/null +++ b/index_tests/usage/type_usage_typedef_and_using.cc @@ -0,0 +1,165 @@ +struct Foo; +using Foo1 = Foo*; +typedef Foo Foo2; +using Foo3 = Foo1; +using Foo4 = int; + +void accept(Foo*) {} +void accept1(Foo1*) {} +void accept2(Foo2*) {} +void accept3(Foo3*) {} + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 558620830317390922, + "detailed_name": "void accept1(Foo1 *)", + "qual_name_offset": 5, + "short_name": "accept1", + "spell": "8:6-8:13|8:1-8:23|2|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }, { + "usr": 9119341505144503905, + "detailed_name": "void accept(Foo *)", + "qual_name_offset": 5, + "short_name": "accept", + "spell": "7:6-7:12|7:1-7:21|2|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }, { + "usr": 10523262907746124479, + "detailed_name": "void accept2(Foo2 *)", + "qual_name_offset": 5, + "short_name": "accept2", + "spell": "9:6-9:13|9:1-9:23|2|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }, { + "usr": 14986366321326974406, + "detailed_name": "void accept3(Foo3 *)", + "qual_name_offset": 5, + "short_name": "accept3", + "spell": "10:6-10:13|10:1-10:23|2|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 1544499294580512394, + "detailed_name": "using Foo1 = Foo *", + "qual_name_offset": 6, + "short_name": "Foo1", + "spell": "2:7-2:11|2:1-2:18|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 15041163540773201510, + "kind": 252, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": ["4:14-4:18|4|-1", "8:14-8:18|4|-1"] + }, { + "usr": 2638219001294786365, + "detailed_name": "using Foo4 = int", + "qual_name_offset": 6, + "short_name": "Foo4", + "spell": "5:7-5:11|5:1-5:17|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 252, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": [] + }, { + "usr": 15041163540773201510, + "detailed_name": "struct Foo", + "qual_name_offset": 7, + "short_name": "Foo", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": ["1:8-1:11|1:1-1:11|1|-1"], + "derived": [], + "instances": [], + "uses": ["2:14-2:17|4|-1", "3:9-3:12|4|-1", "7:13-7:16|4|-1"] + }, { + "usr": 15466821155413653804, + "detailed_name": "typedef Foo Foo2", + "qual_name_offset": 12, + "short_name": "Foo2", + "spell": "3:13-3:17|3:1-3:17|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 15041163540773201510, + "kind": 252, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": ["9:14-9:18|4|-1"] + }, { + "usr": 17897026942631673064, + "detailed_name": "using Foo3 = Foo1", + "qual_name_offset": 6, + "short_name": "Foo3", + "spell": "4:7-4:11|4:1-4:18|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 1544499294580512394, + "kind": 252, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": ["10:14-10:18|4|-1"] + }], + "usr2var": [] +} +*/ diff --git a/index_tests/usage/type_usage_typedef_and_using_template.cc b/index_tests/usage/type_usage_typedef_and_using_template.cc new file mode 100644 index 000000000..008484833 --- /dev/null +++ b/index_tests/usage/type_usage_typedef_and_using_template.cc @@ -0,0 +1,66 @@ +template +struct Foo; + +using Foo1 = Foo; +typedef Foo Foo2; + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [], + "usr2type": [{ + "usr": 1544499294580512394, + "detailed_name": "using Foo1 = Foo", + "qual_name_offset": 6, + "short_name": "Foo1", + "spell": "4:7-4:11|4:1-4:22|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 10528472276654770367, + "kind": 252, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": ["5:13-5:17|4|-1"] + }, { + "usr": 10528472276654770367, + "detailed_name": "struct Foo", + "qual_name_offset": 7, + "short_name": "Foo", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": ["2:8-2:11|2:1-2:11|1|-1"], + "derived": [], + "instances": [], + "uses": ["4:14-4:17|4|-1", "5:9-5:12|4|-1"] + }, { + "usr": 15933698173231330933, + "detailed_name": "typedef Foo Foo2", + "qual_name_offset": 18, + "short_name": "Foo2", + "spell": "5:19-5:23|5:1-5:23|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 10528472276654770367, + "kind": 252, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": [] + }], + "usr2var": [] +} +*/ diff --git a/index_tests/usage/type_usage_various.cc b/index_tests/usage/type_usage_various.cc new file mode 100644 index 000000000..d3c8959a6 --- /dev/null +++ b/index_tests/usage/type_usage_various.cc @@ -0,0 +1,76 @@ +class Foo { + Foo* make(); +}; + +Foo* Foo::make() { + Foo f; + return nullptr; +} + +extern Foo foo; + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 9488177941273031343, + "detailed_name": "Foo *Foo::make()", + "qual_name_offset": 5, + "short_name": "make", + "spell": "5:11-5:15|5:1-8:2|1026|-1", + "bases": [], + "vars": [16380484338511689669], + "callees": [], + "kind": 6, + "parent_kind": 5, + "storage": 0, + "declarations": ["2:8-2:12|2:3-2:14|1025|-1"], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 15041163540773201510, + "detailed_name": "class Foo {}", + "qual_name_offset": 6, + "short_name": "Foo", + "spell": "1:7-1:10|1:1-3:2|2|-1", + "bases": [], + "funcs": [9488177941273031343], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [16380484338511689669, 14455976355866885943], + "uses": ["2:3-2:6|4|-1", "5:1-5:4|4|-1", "5:6-5:9|4|-1", "6:3-6:6|4|-1", "10:8-10:11|4|-1"] + }], + "usr2var": [{ + "usr": 14455976355866885943, + "detailed_name": "extern Foo foo", + "qual_name_offset": 11, + "short_name": "foo", + "type": 15041163540773201510, + "kind": 13, + "parent_kind": 0, + "storage": 1, + "declarations": ["10:12-10:15|10:1-10:15|1|-1"], + "uses": [] + }, { + "usr": 16380484338511689669, + "detailed_name": "Foo f", + "qual_name_offset": 4, + "short_name": "f", + "spell": "6:7-6:8|6:3-6:8|2|-1", + "type": 15041163540773201510, + "kind": 13, + "parent_kind": 6, + "storage": 0, + "declarations": [], + "uses": [] + }] +} +*/ diff --git a/index_tests/usage/usage_inside_of_call.cc b/index_tests/usage/usage_inside_of_call.cc new file mode 100644 index 000000000..44d654160 --- /dev/null +++ b/index_tests/usage/usage_inside_of_call.cc @@ -0,0 +1,142 @@ +void called(int a); + +int gen(); + +struct Foo { + static int static_var; + int field_var; +}; + +int Foo::static_var = 0; + +void foo() { + int a = 5; + called(a + gen() + Foo().field_var + Foo::static_var); +} + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 4259594751088586730, + "detailed_name": "void foo()", + "qual_name_offset": 5, + "short_name": "foo", + "spell": "12:6-12:9|12:1-15:2|2|-1", + "bases": [], + "vars": [8039186520399841081], + "callees": ["14:3-14:9|18319417758892371313|3|16420", "14:14-14:17|11404602816585117695|3|16420"], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }, { + "usr": 11404602816585117695, + "detailed_name": "int gen()", + "qual_name_offset": 4, + "short_name": "gen", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": ["3:5-3:8|3:1-3:10|1|-1"], + "derived": [], + "uses": ["14:14-14:17|16420|-1"] + }, { + "usr": 18319417758892371313, + "detailed_name": "void called(int a)", + "qual_name_offset": 5, + "short_name": "called", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": ["1:6-1:12|1:1-1:19|1|-1"], + "derived": [], + "uses": ["14:3-14:9|16420|-1"] + }], + "usr2type": [{ + "usr": 53, + "detailed_name": "", + "qual_name_offset": 0, + "short_name": "", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 0, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [11489549839875479478, 9648311402855509901, 11489549839875479478, 8039186520399841081], + "uses": [] + }, { + "usr": 15041163540773201510, + "detailed_name": "struct Foo {}", + "qual_name_offset": 7, + "short_name": "Foo", + "spell": "5:8-5:11|5:1-8:2|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [{ + "L": 9648311402855509901, + "R": 0 + }], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": ["10:5-10:8|4|-1", "14:22-14:25|4|-1", "14:40-14:43|4|-1"] + }], + "usr2var": [{ + "usr": 8039186520399841081, + "detailed_name": "int a", + "qual_name_offset": 4, + "short_name": "a", + "hover": "int a = 5", + "spell": "13:7-13:8|13:3-13:12|2|-1", + "type": 53, + "kind": 13, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": ["14:10-14:11|12|-1"] + }, { + "usr": 9648311402855509901, + "detailed_name": "int Foo::field_var", + "qual_name_offset": 4, + "short_name": "field_var", + "spell": "7:7-7:16|7:3-7:16|1026|-1", + "type": 53, + "kind": 8, + "parent_kind": 23, + "storage": 0, + "declarations": [], + "uses": ["14:28-14:37|12|-1"] + }, { + "usr": 11489549839875479478, + "detailed_name": "static int Foo::static_var", + "qual_name_offset": 11, + "short_name": "static_var", + "spell": "10:10-10:20|10:1-10:24|1026|-1", + "type": 53, + "kind": 13, + "parent_kind": 23, + "storage": 2, + "declarations": ["6:14-6:24|6:3-6:24|1025|-1"], + "uses": ["14:45-14:55|12|-1"] + }] +} +*/ diff --git a/index_tests/usage/usage_inside_of_call_simple.cc b/index_tests/usage/usage_inside_of_call_simple.cc new file mode 100644 index 000000000..c2320556c --- /dev/null +++ b/index_tests/usage/usage_inside_of_call_simple.cc @@ -0,0 +1,62 @@ +void called(int a); + +int gen() { return 1; } + +void foo() { + called(gen() * gen()); +} + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 4259594751088586730, + "detailed_name": "void foo()", + "qual_name_offset": 5, + "short_name": "foo", + "spell": "5:6-5:9|5:1-7:2|2|-1", + "bases": [], + "vars": [], + "callees": ["6:3-6:9|18319417758892371313|3|16420", "6:10-6:13|11404602816585117695|3|16420", "6:18-6:21|11404602816585117695|3|16420"], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }, { + "usr": 11404602816585117695, + "detailed_name": "int gen()", + "qual_name_offset": 4, + "short_name": "gen", + "spell": "3:5-3:8|3:1-3:24|2|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": ["6:10-6:13|16420|-1", "6:18-6:21|16420|-1"] + }, { + "usr": 18319417758892371313, + "detailed_name": "void called(int a)", + "qual_name_offset": 5, + "short_name": "called", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": ["1:6-1:12|1:1-1:19|1|-1"], + "derived": [], + "uses": ["6:3-6:9|16420|-1"] + }], + "usr2type": [], + "usr2var": [] +} +*/ diff --git a/index_tests/usage/var_usage_call_function.cc b/index_tests/usage/var_usage_call_function.cc new file mode 100644 index 000000000..0a0df74fb --- /dev/null +++ b/index_tests/usage/var_usage_call_function.cc @@ -0,0 +1,62 @@ +void called() {} + +void caller() { + auto x = &called; + x(); + + called(); +} + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 468307235068920063, + "detailed_name": "void called()", + "qual_name_offset": 5, + "short_name": "called", + "spell": "1:6-1:12|1:1-1:17|2|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": ["4:13-4:19|132|-1", "7:3-7:9|16420|-1"] + }, { + "usr": 11404881820527069090, + "detailed_name": "void caller()", + "qual_name_offset": 5, + "short_name": "caller", + "spell": "3:6-3:12|3:1-8:2|2|-1", + "bases": [], + "vars": [9121974011454213596], + "callees": ["4:13-4:19|468307235068920063|3|132", "4:13-4:19|468307235068920063|3|132", "7:3-7:9|468307235068920063|3|16420"], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }], + "usr2type": [], + "usr2var": [{ + "usr": 9121974011454213596, + "detailed_name": "void (*)() x", + "qual_name_offset": 11, + "short_name": "x", + "hover": "void (*)() x = &called", + "spell": "4:8-4:9|4:3-4:19|2|-1", + "type": 0, + "kind": 13, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": ["5:3-5:4|16428|-1"] + }] +} +*/ diff --git a/index_tests/usage/var_usage_class_member.cc b/index_tests/usage/var_usage_class_member.cc new file mode 100644 index 000000000..9f230cd5d --- /dev/null +++ b/index_tests/usage/var_usage_class_member.cc @@ -0,0 +1,147 @@ +class Foo { +public: + int x; + int y; +}; + +void accept(int); +void accept(int*); + +void foo() { + Foo f; + f.x = 3; + f.x += 5; + accept(f.x); + accept(f.x + 20); + accept(&f.x); + accept(f.y); +} + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 4259594751088586730, + "detailed_name": "void foo()", + "qual_name_offset": 5, + "short_name": "foo", + "spell": "10:6-10:9|10:1-18:2|2|-1", + "bases": [], + "vars": [14669930844300034456], + "callees": ["14:3-14:9|17175780305784503374|3|16420", "15:3-15:9|17175780305784503374|3|16420", "16:3-16:9|12086644540399881766|3|16420", "17:3-17:9|17175780305784503374|3|16420"], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }, { + "usr": 12086644540399881766, + "detailed_name": "void accept(int *)", + "qual_name_offset": 5, + "short_name": "accept", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": ["8:6-8:12|8:1-8:18|1|-1"], + "derived": [], + "uses": ["16:3-16:9|16420|-1"] + }, { + "usr": 17175780305784503374, + "detailed_name": "void accept(int)", + "qual_name_offset": 5, + "short_name": "accept", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": ["7:6-7:12|7:1-7:17|1|-1"], + "derived": [], + "uses": ["14:3-14:9|16420|-1", "15:3-15:9|16420|-1", "17:3-17:9|16420|-1"] + }], + "usr2type": [{ + "usr": 53, + "detailed_name": "", + "qual_name_offset": 0, + "short_name": "", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 0, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [4220150017963593039, 3873837747174060388], + "uses": [] + }, { + "usr": 15041163540773201510, + "detailed_name": "class Foo {}", + "qual_name_offset": 6, + "short_name": "Foo", + "spell": "1:7-1:10|1:1-5:2|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [{ + "L": 4220150017963593039, + "R": 0 + }, { + "L": 3873837747174060388, + "R": 32 + }], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [14669930844300034456], + "uses": ["11:3-11:6|4|-1"] + }], + "usr2var": [{ + "usr": 3873837747174060388, + "detailed_name": "int Foo::y", + "qual_name_offset": 4, + "short_name": "y", + "spell": "4:7-4:8|4:3-4:8|1026|-1", + "type": 53, + "kind": 8, + "parent_kind": 5, + "storage": 0, + "declarations": [], + "uses": ["17:12-17:13|12|-1"] + }, { + "usr": 4220150017963593039, + "detailed_name": "int Foo::x", + "qual_name_offset": 4, + "short_name": "x", + "spell": "3:7-3:8|3:3-3:8|1026|-1", + "type": 53, + "kind": 8, + "parent_kind": 5, + "storage": 0, + "declarations": [], + "uses": ["12:5-12:6|20|-1", "13:5-13:6|4|-1", "14:12-14:13|12|-1", "15:12-15:13|12|-1", "16:13-16:14|132|-1"] + }, { + "usr": 14669930844300034456, + "detailed_name": "Foo f", + "qual_name_offset": 4, + "short_name": "f", + "spell": "11:7-11:8|11:3-11:8|2|-1", + "type": 15041163540773201510, + "kind": 13, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": ["12:3-12:4|4|-1", "13:3-13:4|4|-1", "14:10-14:11|4|-1", "15:10-15:11|4|-1", "16:11-16:12|4|-1", "17:10-17:11|4|-1"] + }] +} +*/ diff --git a/index_tests/usage/var_usage_class_member_static.cc b/index_tests/usage/var_usage_class_member_static.cc new file mode 100644 index 000000000..1206c2d8d --- /dev/null +++ b/index_tests/usage/var_usage_class_member_static.cc @@ -0,0 +1,93 @@ +struct Foo { + static int x; +}; + +void accept(int); + +void foo() { + accept(Foo::x); +} + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 4259594751088586730, + "detailed_name": "void foo()", + "qual_name_offset": 5, + "short_name": "foo", + "spell": "7:6-7:9|7:1-9:2|2|-1", + "bases": [], + "vars": [], + "callees": ["8:3-8:9|17175780305784503374|3|16420"], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }, { + "usr": 17175780305784503374, + "detailed_name": "void accept(int)", + "qual_name_offset": 5, + "short_name": "accept", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": ["5:6-5:12|5:1-5:17|1|-1"], + "derived": [], + "uses": ["8:3-8:9|16420|-1"] + }], + "usr2type": [{ + "usr": 53, + "detailed_name": "", + "qual_name_offset": 0, + "short_name": "", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 0, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [8599782646965457351], + "uses": [] + }, { + "usr": 15041163540773201510, + "detailed_name": "struct Foo {}", + "qual_name_offset": 7, + "short_name": "Foo", + "spell": "1:8-1:11|1:1-3:2|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": ["8:10-8:13|4|-1"] + }], + "usr2var": [{ + "usr": 8599782646965457351, + "detailed_name": "static int Foo::x", + "qual_name_offset": 11, + "short_name": "x", + "type": 53, + "kind": 13, + "parent_kind": 23, + "storage": 2, + "declarations": ["2:14-2:15|2:3-2:15|1025|-1"], + "uses": ["8:15-8:16|12|-1"] + }] +} +*/ diff --git a/index_tests/usage/var_usage_cstyle_cast.cc b/index_tests/usage/var_usage_cstyle_cast.cc new file mode 100644 index 000000000..e041ffba4 --- /dev/null +++ b/index_tests/usage/var_usage_cstyle_cast.cc @@ -0,0 +1,135 @@ +enum VarType {}; + +struct Holder { + static constexpr VarType static_var = (VarType)0x0; +}; + +const VarType Holder::static_var; + + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [], + "usr2type": [{ + "usr": 5792006888140599735, + "detailed_name": "enum VarType {}", + "qual_name_offset": 5, + "short_name": "VarType", + "spell": "1:6-1:13|1:1-1:16|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 10, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [7057400933868440116, 7057400933868440116], + "uses": ["4:20-4:27|4|-1", "4:42-4:49|4|-1", "7:7-7:14|4|-1"] + }, { + "usr": 10028537921178202800, + "detailed_name": "struct Holder {}", + "qual_name_offset": 7, + "short_name": "Holder", + "spell": "3:8-3:14|3:1-5:2|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": ["7:15-7:21|4|-1"] + }], + "usr2var": [{ + "usr": 7057400933868440116, + "detailed_name": "static constexpr VarType Holder::static_var", + "qual_name_offset": 25, + "short_name": "static_var", + "hover": "static constexpr VarType Holder::static_var = (VarType)0x0", + "spell": "7:23-7:33|7:1-7:33|1026|-1", + "type": 5792006888140599735, + "kind": 13, + "parent_kind": 23, + "storage": 2, + "declarations": ["4:28-4:38|4:3-4:53|1025|-1"], + "uses": [] + }] +} +*/ + + + + + + + + + + + + + + + + + + + + + + + + + +//#include +//#include + +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include diff --git a/index_tests/usage/var_usage_extern.cc b/index_tests/usage/var_usage_extern.cc new file mode 100644 index 000000000..14cb93e7f --- /dev/null +++ b/index_tests/usage/var_usage_extern.cc @@ -0,0 +1,57 @@ +extern int a; + +void foo() { + a = 5; +} +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 4259594751088586730, + "detailed_name": "void foo()", + "qual_name_offset": 5, + "short_name": "foo", + "spell": "3:6-3:9|3:1-5:2|2|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 53, + "detailed_name": "", + "qual_name_offset": 0, + "short_name": "", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 0, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [16721564935990383768], + "uses": [] + }], + "usr2var": [{ + "usr": 16721564935990383768, + "detailed_name": "extern int a", + "qual_name_offset": 11, + "short_name": "a", + "type": 53, + "kind": 13, + "parent_kind": 0, + "storage": 1, + "declarations": ["1:12-1:13|1:1-1:13|1|-1"], + "uses": ["4:3-4:4|20|-1"] + }] +} +*/ diff --git a/index_tests/usage/var_usage_func_parameter.cc b/index_tests/usage/var_usage_func_parameter.cc new file mode 100644 index 000000000..069c72628 --- /dev/null +++ b/index_tests/usage/var_usage_func_parameter.cc @@ -0,0 +1,56 @@ +void foo(int a) { + a += 10; +} +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 11998306017310352355, + "detailed_name": "void foo(int a)", + "qual_name_offset": 5, + "short_name": "foo", + "spell": "1:6-1:9|1:1-3:2|2|-1", + "bases": [], + "vars": [10063793875496522529], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 53, + "detailed_name": "", + "qual_name_offset": 0, + "short_name": "", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 0, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [10063793875496522529], + "uses": [] + }], + "usr2var": [{ + "usr": 10063793875496522529, + "detailed_name": "int a", + "qual_name_offset": 4, + "short_name": "a", + "spell": "1:14-1:15|1:10-1:15|1026|-1", + "type": 53, + "kind": 253, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": ["2:3-2:4|4|-1"] + }] +} +*/ diff --git a/index_tests/usage/var_usage_local.cc b/index_tests/usage/var_usage_local.cc new file mode 100644 index 000000000..577d810c5 --- /dev/null +++ b/index_tests/usage/var_usage_local.cc @@ -0,0 +1,57 @@ +void foo() { + int x; + x = 3; +} +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 4259594751088586730, + "detailed_name": "void foo()", + "qual_name_offset": 5, + "short_name": "foo", + "spell": "1:6-1:9|1:1-4:2|2|-1", + "bases": [], + "vars": [14014650769929566957], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 53, + "detailed_name": "", + "qual_name_offset": 0, + "short_name": "", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 0, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [14014650769929566957], + "uses": [] + }], + "usr2var": [{ + "usr": 14014650769929566957, + "detailed_name": "int x", + "qual_name_offset": 4, + "short_name": "x", + "spell": "2:7-2:8|2:3-2:8|2|-1", + "type": 53, + "kind": 13, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": ["3:3-3:4|20|-1"] + }] +} +*/ diff --git a/index_tests/usage/var_usage_shadowed_local.cc b/index_tests/usage/var_usage_shadowed_local.cc new file mode 100644 index 000000000..efe2d4604 --- /dev/null +++ b/index_tests/usage/var_usage_shadowed_local.cc @@ -0,0 +1,74 @@ +void foo() { + int a; + a = 1; + { + int a; + a = 2; + } + a = 3; +} +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 4259594751088586730, + "detailed_name": "void foo()", + "qual_name_offset": 5, + "short_name": "foo", + "spell": "1:6-1:9|1:1-9:2|2|-1", + "bases": [], + "vars": [13311055950748663970, 14036425367303419504], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 53, + "detailed_name": "", + "qual_name_offset": 0, + "short_name": "", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 0, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [13311055950748663970, 14036425367303419504], + "uses": [] + }], + "usr2var": [{ + "usr": 13311055950748663970, + "detailed_name": "int a", + "qual_name_offset": 4, + "short_name": "a", + "spell": "2:7-2:8|2:3-2:8|2|-1", + "type": 53, + "kind": 13, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": ["3:3-3:4|20|-1", "8:3-8:4|20|-1"] + }, { + "usr": 14036425367303419504, + "detailed_name": "int a", + "qual_name_offset": 4, + "short_name": "a", + "spell": "5:9-5:10|5:5-5:10|2|-1", + "type": 53, + "kind": 13, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": ["6:5-6:6|20|-1"] + }] +} +*/ diff --git a/index_tests/usage/var_usage_shadowed_parameter.cc b/index_tests/usage/var_usage_shadowed_parameter.cc new file mode 100644 index 000000000..dfa4a6ca0 --- /dev/null +++ b/index_tests/usage/var_usage_shadowed_parameter.cc @@ -0,0 +1,74 @@ +void foo(int a) { + a = 1; + { + int a; + a = 2; + } + a = 3; +} + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 11998306017310352355, + "detailed_name": "void foo(int a)", + "qual_name_offset": 5, + "short_name": "foo", + "spell": "1:6-1:9|1:1-8:2|2|-1", + "bases": [], + "vars": [11608231465452906059, 6997229590862003559], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 53, + "detailed_name": "", + "qual_name_offset": 0, + "short_name": "", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 0, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [11608231465452906059, 6997229590862003559], + "uses": [] + }], + "usr2var": [{ + "usr": 6997229590862003559, + "detailed_name": "int a", + "qual_name_offset": 4, + "short_name": "a", + "spell": "4:9-4:10|4:5-4:10|2|-1", + "type": 53, + "kind": 13, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": ["5:5-5:6|20|-1"] + }, { + "usr": 11608231465452906059, + "detailed_name": "int a", + "qual_name_offset": 4, + "short_name": "a", + "spell": "1:14-1:15|1:10-1:15|1026|-1", + "type": 53, + "kind": 253, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": ["2:3-2:4|20|-1", "7:3-7:4|20|-1"] + }] +} +*/ diff --git a/index_tests/usage/var_usage_static.cc b/index_tests/usage/var_usage_static.cc new file mode 100644 index 000000000..705fddff9 --- /dev/null +++ b/index_tests/usage/var_usage_static.cc @@ -0,0 +1,59 @@ +static int a; + +void foo() { + a = 3; +} + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 4259594751088586730, + "detailed_name": "void foo()", + "qual_name_offset": 5, + "short_name": "foo", + "spell": "3:6-3:9|3:1-5:2|2|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 53, + "detailed_name": "", + "qual_name_offset": 0, + "short_name": "", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 0, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [11823161916242867318], + "uses": [] + }], + "usr2var": [{ + "usr": 11823161916242867318, + "detailed_name": "static int a", + "qual_name_offset": 11, + "short_name": "a", + "spell": "1:12-1:13|1:1-1:13|2|-1", + "type": 53, + "kind": 13, + "parent_kind": 0, + "storage": 2, + "declarations": [], + "uses": ["4:3-4:4|20|-1"] + }] +} +*/ diff --git a/index_tests/vars/class_member.cc b/index_tests/vars/class_member.cc new file mode 100644 index 000000000..9b62aec7e --- /dev/null +++ b/index_tests/vars/class_member.cc @@ -0,0 +1,45 @@ +class Foo { + Foo* member; +}; +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [], + "usr2type": [{ + "usr": 15041163540773201510, + "detailed_name": "class Foo {}", + "qual_name_offset": 6, + "short_name": "Foo", + "spell": "1:7-1:10|1:1-3:2|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [{ + "L": 13799811842374292251, + "R": 0 + }], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [13799811842374292251], + "uses": ["2:3-2:6|4|-1"] + }], + "usr2var": [{ + "usr": 13799811842374292251, + "detailed_name": "Foo *Foo::member", + "qual_name_offset": 5, + "short_name": "member", + "spell": "2:8-2:14|2:3-2:14|1026|-1", + "type": 15041163540773201510, + "kind": 8, + "parent_kind": 5, + "storage": 0, + "declarations": [], + "uses": [] + }] +} +*/ diff --git a/index_tests/vars/class_static_member.cc b/index_tests/vars/class_static_member.cc new file mode 100644 index 000000000..3c67ac6ec --- /dev/null +++ b/index_tests/vars/class_static_member.cc @@ -0,0 +1,44 @@ +class Foo { + static Foo* member; +}; +Foo* Foo::member = nullptr; + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [], + "usr2type": [{ + "usr": 15041163540773201510, + "detailed_name": "class Foo {}", + "qual_name_offset": 6, + "short_name": "Foo", + "spell": "1:7-1:10|1:1-3:2|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [5844987037615239736, 5844987037615239736], + "uses": ["2:10-2:13|4|-1", "4:1-4:4|4|-1", "4:6-4:9|4|-1"] + }], + "usr2var": [{ + "usr": 5844987037615239736, + "detailed_name": "static Foo *Foo::member", + "qual_name_offset": 12, + "short_name": "member", + "spell": "4:11-4:17|4:1-4:27|1026|-1", + "type": 15041163540773201510, + "kind": 13, + "parent_kind": 5, + "storage": 2, + "declarations": ["2:15-2:21|2:3-2:21|1025|-1"], + "uses": [] + }] +} +*/ diff --git a/index_tests/vars/class_static_member_decl_only.cc b/index_tests/vars/class_static_member_decl_only.cc new file mode 100644 index 000000000..0cf61b2c6 --- /dev/null +++ b/index_tests/vars/class_static_member_decl_only.cc @@ -0,0 +1,57 @@ +class Foo { + static int member; +}; +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [], + "usr2type": [{ + "usr": 53, + "detailed_name": "", + "qual_name_offset": 0, + "short_name": "", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 0, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [5844987037615239736], + "uses": [] + }, { + "usr": 15041163540773201510, + "detailed_name": "class Foo {}", + "qual_name_offset": 6, + "short_name": "Foo", + "spell": "1:7-1:10|1:1-3:2|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": [] + }], + "usr2var": [{ + "usr": 5844987037615239736, + "detailed_name": "static int Foo::member", + "qual_name_offset": 11, + "short_name": "member", + "type": 53, + "kind": 13, + "parent_kind": 5, + "storage": 2, + "declarations": ["2:14-2:20|2:3-2:20|1025|-1"], + "uses": [] + }] +} +*/ diff --git a/index_tests/vars/deduce_auto_type.cc b/index_tests/vars/deduce_auto_type.cc new file mode 100644 index 000000000..7e9c0cbc2 --- /dev/null +++ b/index_tests/vars/deduce_auto_type.cc @@ -0,0 +1,74 @@ +class Foo {}; +void f() { + auto x = new Foo(); + auto* y = new Foo(); +} + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 880549676430489861, + "detailed_name": "void f()", + "qual_name_offset": 5, + "short_name": "f", + "spell": "2:6-2:7|2:1-5:2|2|-1", + "bases": [], + "vars": [10601729374837386290, 18422884837902130475], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 15041163540773201510, + "detailed_name": "class Foo {}", + "qual_name_offset": 6, + "short_name": "Foo", + "spell": "1:7-1:10|1:1-1:13|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 5, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [10601729374837386290, 18422884837902130475], + "uses": ["3:16-3:19|4|-1", "4:17-4:20|4|-1"] + }], + "usr2var": [{ + "usr": 10601729374837386290, + "detailed_name": "Foo *x", + "qual_name_offset": 5, + "short_name": "x", + "hover": "Foo *x = new Foo()", + "spell": "3:8-3:9|3:3-3:21|2|-1", + "type": 15041163540773201510, + "kind": 13, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 18422884837902130475, + "detailed_name": "Foo *y", + "qual_name_offset": 5, + "short_name": "y", + "hover": "Foo *y = new Foo()", + "spell": "4:9-4:10|4:3-4:22|2|-1", + "type": 15041163540773201510, + "kind": 13, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": [] + }] +} +*/ diff --git a/index_tests/vars/function_local.cc b/index_tests/vars/function_local.cc new file mode 100644 index 000000000..70a985bab --- /dev/null +++ b/index_tests/vars/function_local.cc @@ -0,0 +1,59 @@ +struct Foo; + +void foo() { + Foo* a; +} + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 4259594751088586730, + "detailed_name": "void foo()", + "qual_name_offset": 5, + "short_name": "foo", + "spell": "3:6-3:9|3:1-5:2|2|-1", + "bases": [], + "vars": [13198746475679542317], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 15041163540773201510, + "detailed_name": "struct Foo", + "qual_name_offset": 7, + "short_name": "Foo", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": ["1:8-1:11|1:1-1:11|1|-1"], + "derived": [], + "instances": [13198746475679542317], + "uses": ["4:3-4:6|4|-1"] + }], + "usr2var": [{ + "usr": 13198746475679542317, + "detailed_name": "Foo *a", + "qual_name_offset": 5, + "short_name": "a", + "spell": "4:8-4:9|4:3-4:9|2|-1", + "type": 15041163540773201510, + "kind": 13, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": [] + }] +} +*/ diff --git a/index_tests/vars/function_param.cc b/index_tests/vars/function_param.cc new file mode 100644 index 000000000..751fbd651 --- /dev/null +++ b/index_tests/vars/function_param.cc @@ -0,0 +1,69 @@ +struct Foo; + +void foo(Foo* p0, Foo* p1) {} + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 8908726657907936744, + "detailed_name": "void foo(Foo *p0, Foo *p1)", + "qual_name_offset": 5, + "short_name": "foo", + "spell": "3:6-3:9|3:1-3:30|2|-1", + "bases": [], + "vars": [8730439006497971620, 2525014371090380500], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 15041163540773201510, + "detailed_name": "struct Foo", + "qual_name_offset": 7, + "short_name": "Foo", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": ["1:8-1:11|1:1-1:11|1|-1"], + "derived": [], + "instances": [8730439006497971620, 2525014371090380500], + "uses": ["3:10-3:13|4|-1", "3:19-3:22|4|-1"] + }], + "usr2var": [{ + "usr": 2525014371090380500, + "detailed_name": "Foo *p1", + "qual_name_offset": 5, + "short_name": "p1", + "spell": "3:24-3:26|3:19-3:26|1026|-1", + "type": 15041163540773201510, + "kind": 253, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 8730439006497971620, + "detailed_name": "Foo *p0", + "qual_name_offset": 5, + "short_name": "p0", + "spell": "3:15-3:17|3:10-3:17|1026|-1", + "type": 15041163540773201510, + "kind": 253, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": [] + }] +} +*/ diff --git a/index_tests/vars/function_param_unnamed.cc b/index_tests/vars/function_param_unnamed.cc new file mode 100644 index 000000000..bd8c9d84b --- /dev/null +++ b/index_tests/vars/function_param_unnamed.cc @@ -0,0 +1,26 @@ +void foo(int, int) {} +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 2747674671862363334, + "detailed_name": "void foo(int, int)", + "qual_name_offset": 5, + "short_name": "foo", + "spell": "1:6-1:9|1:1-1:22|2|-1", + "bases": [], + "vars": [], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }], + "usr2type": [], + "usr2var": [] +} +*/ diff --git a/index_tests/vars/function_shadow_local.cc b/index_tests/vars/function_shadow_local.cc new file mode 100644 index 000000000..0ccc91e30 --- /dev/null +++ b/index_tests/vars/function_shadow_local.cc @@ -0,0 +1,74 @@ +void foo() { + int a; + a = 1; + { + int a; + a = 2; + } + a = 3; +} +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 4259594751088586730, + "detailed_name": "void foo()", + "qual_name_offset": 5, + "short_name": "foo", + "spell": "1:6-1:9|1:1-9:2|2|-1", + "bases": [], + "vars": [1894874819807168345, 4508045017817092115], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 53, + "detailed_name": "", + "qual_name_offset": 0, + "short_name": "", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 0, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [1894874819807168345, 4508045017817092115], + "uses": [] + }], + "usr2var": [{ + "usr": 1894874819807168345, + "detailed_name": "int a", + "qual_name_offset": 4, + "short_name": "a", + "spell": "2:7-2:8|2:3-2:8|2|-1", + "type": 53, + "kind": 13, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": ["3:3-3:4|20|-1", "8:3-8:4|20|-1"] + }, { + "usr": 4508045017817092115, + "detailed_name": "int a", + "qual_name_offset": 4, + "short_name": "a", + "spell": "5:9-5:10|5:5-5:10|2|-1", + "type": 53, + "kind": 13, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": ["6:5-6:6|20|-1"] + }] +} +*/ diff --git a/index_tests/vars/function_shadow_param.cc b/index_tests/vars/function_shadow_param.cc new file mode 100644 index 000000000..80f8d4f1c --- /dev/null +++ b/index_tests/vars/function_shadow_param.cc @@ -0,0 +1,69 @@ +void foo(int p) { + { int p = 0; } +} +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 11998306017310352355, + "detailed_name": "void foo(int p)", + "qual_name_offset": 5, + "short_name": "foo", + "spell": "1:6-1:9|1:1-3:2|2|-1", + "bases": [], + "vars": [5875271969926422921, 11404600766177939811], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 53, + "detailed_name": "", + "qual_name_offset": 0, + "short_name": "", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 0, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [5875271969926422921, 11404600766177939811], + "uses": [] + }], + "usr2var": [{ + "usr": 5875271969926422921, + "detailed_name": "int p", + "qual_name_offset": 4, + "short_name": "p", + "spell": "1:14-1:15|1:10-1:15|1026|-1", + "type": 53, + "kind": 253, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": [] + }, { + "usr": 11404600766177939811, + "detailed_name": "int p", + "qual_name_offset": 4, + "short_name": "p", + "hover": "int p = 0", + "spell": "2:9-2:10|2:5-2:14|2|-1", + "type": 53, + "kind": 13, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": [] + }] +} +*/ diff --git a/index_tests/vars/global_variable.cc b/index_tests/vars/global_variable.cc new file mode 100644 index 000000000..3ce2d1192 --- /dev/null +++ b/index_tests/vars/global_variable.cc @@ -0,0 +1,40 @@ +static int global = 0; +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [], + "usr2type": [{ + "usr": 53, + "detailed_name": "", + "qual_name_offset": 0, + "short_name": "", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 0, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [6834525061342585382], + "uses": [] + }], + "usr2var": [{ + "usr": 6834525061342585382, + "detailed_name": "static int global", + "qual_name_offset": 11, + "short_name": "global", + "hover": "static int global = 0", + "spell": "1:12-1:18|1:1-1:22|2|-1", + "type": 53, + "kind": 13, + "parent_kind": 0, + "storage": 2, + "declarations": [], + "uses": [] + }] +} +*/ diff --git a/index_tests/vars/global_variable_decl_only.cc b/index_tests/vars/global_variable_decl_only.cc new file mode 100644 index 000000000..1ba9f2e4c --- /dev/null +++ b/index_tests/vars/global_variable_decl_only.cc @@ -0,0 +1,38 @@ +extern int global; +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [], + "usr2type": [{ + "usr": 53, + "detailed_name": "", + "qual_name_offset": 0, + "short_name": "", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 0, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [9937941849651546906], + "uses": [] + }], + "usr2var": [{ + "usr": 9937941849651546906, + "detailed_name": "extern int global", + "qual_name_offset": 11, + "short_name": "global", + "type": 53, + "kind": 13, + "parent_kind": 0, + "storage": 1, + "declarations": ["1:12-1:18|1:1-1:18|1|-1"], + "uses": [] + }] +} +*/ diff --git a/index_tests/vars/type_instance_on_using_type.cc b/index_tests/vars/type_instance_on_using_type.cc new file mode 100644 index 000000000..ce3607eb2 --- /dev/null +++ b/index_tests/vars/type_instance_on_using_type.cc @@ -0,0 +1,79 @@ +struct S {}; +using F = S; +void Foo() { + F a; +} + +// TODO: Should we also add a usage to |S|? + +/* +OUTPUT: +{ + "includes": [], + "skipped_ranges": [], + "usr2func": [{ + "usr": 4654328188330986029, + "detailed_name": "void Foo()", + "qual_name_offset": 5, + "short_name": "Foo", + "spell": "3:6-3:9|3:1-5:2|2|-1", + "bases": [], + "vars": [6975456769752895964], + "callees": [], + "kind": 12, + "parent_kind": 0, + "storage": 0, + "declarations": [], + "derived": [], + "uses": [] + }], + "usr2type": [{ + "usr": 4750332761459066907, + "detailed_name": "struct S {}", + "qual_name_offset": 7, + "short_name": "S", + "spell": "1:8-1:9|1:1-1:12|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 0, + "kind": 23, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [], + "uses": ["2:11-2:12|4|-1"] + }, { + "usr": 7434820806199665424, + "detailed_name": "using F = S", + "qual_name_offset": 6, + "short_name": "F", + "spell": "2:7-2:8|2:1-2:12|2|-1", + "bases": [], + "funcs": [], + "types": [], + "vars": [], + "alias_of": 4750332761459066907, + "kind": 252, + "parent_kind": 0, + "declarations": [], + "derived": [], + "instances": [6975456769752895964], + "uses": ["4:3-4:4|4|-1"] + }], + "usr2var": [{ + "usr": 6975456769752895964, + "detailed_name": "F a", + "qual_name_offset": 2, + "short_name": "a", + "spell": "4:5-4:6|4:3-4:6|2|-1", + "type": 7434820806199665424, + "kind": 13, + "parent_kind": 12, + "storage": 0, + "declarations": [], + "uses": [] + }] +} +*/ diff --git a/src/cache_manager.cc b/src/cache_manager.cc deleted file mode 100644 index 520e9dcf8..000000000 --- a/src/cache_manager.cc +++ /dev/null @@ -1,156 +0,0 @@ -#include "cache_manager.h" - -#include "config.h" -#include "indexer.h" -#include "lsp.h" -#include "platform.h" - -#include - -#include -#include - -namespace { - -// Manages loading caches from file paths for the indexer process. -struct RealCacheManager : ICacheManager { - explicit RealCacheManager(Config* config) : config_(config) {} - ~RealCacheManager() override = default; - - void WriteToCache(IndexFile& file) override { - std::string cache_path = GetCachePath(file.path); - WriteToFile(cache_path, file.file_contents); - - std::string indexed_content = Serialize(config_->cacheFormat, file); - WriteToFile(AppendSerializationFormat(cache_path), indexed_content); - } - - optional LoadCachedFileContents( - const std::string& path) override { - return ReadContent(GetCachePath(path)); - } - - std::unique_ptr RawCacheLoad(const std::string& path) override { - std::string cache_path = GetCachePath(path); - optional file_content = ReadContent(cache_path); - optional serialized_indexed_content = - ReadContent(AppendSerializationFormat(cache_path)); - if (!file_content || !serialized_indexed_content) - return nullptr; - - return Deserialize(config_->cacheFormat, path, *serialized_indexed_content, - *file_content, IndexFile::kMajorVersion); - } - - std::string GetCachePath(const std::string& source_file) { - assert(!config_->cacheDirectory.empty()); - std::string cache_file; - size_t len = config_->projectRoot.size(); - if (StartsWith(source_file, config_->projectRoot)) { - cache_file = EscapeFileName(config_->projectRoot) + '/' + - EscapeFileName(source_file.substr(len)); - } else { - cache_file = '@' + EscapeFileName(config_->projectRoot) + '/' + - EscapeFileName(source_file); - } - - return config_->cacheDirectory + cache_file; - } - - std::string AppendSerializationFormat(const std::string& base) { - switch (config_->cacheFormat) { - case SerializeFormat::Json: - return base + ".json"; - case SerializeFormat::MessagePack: - return base + ".mpack"; - } - assert(false); - return ".json"; - } - - Config* config_; -}; - -struct FakeCacheManager : ICacheManager { - explicit FakeCacheManager(const std::vector& entries) - : entries_(entries) {} - - void WriteToCache(IndexFile& file) override { assert(false); } - - optional LoadCachedFileContents( - const std::string& path) override { - for (const FakeCacheEntry& entry : entries_) { - if (entry.path == path) { - return entry.content; - } - } - - return nullopt; - } - - std::unique_ptr RawCacheLoad(const std::string& path) override { - for (const FakeCacheEntry& entry : entries_) { - if (entry.path == path) { - return Deserialize(SerializeFormat::Json, path, entry.json, "", - nullopt); - } - } - - return nullptr; - } - - std::vector entries_; -}; - -} // namespace - -// static -std::shared_ptr ICacheManager::Make(Config* config) { - return std::make_shared(config); -} - -// static -std::shared_ptr ICacheManager::MakeFake( - const std::vector& entries) { - return std::make_shared(entries); -} - -ICacheManager::~ICacheManager() = default; - -IndexFile* ICacheManager::TryLoad(const std::string& path) { - auto it = caches_.find(path); - if (it != caches_.end()) - return it->second.get(); - - std::unique_ptr cache = RawCacheLoad(path); - if (!cache) - return nullptr; - - caches_[path] = std::move(cache); - return caches_[path].get(); -} - -std::unique_ptr ICacheManager::TryTakeOrLoad( - const std::string& path) { - auto it = caches_.find(path); - if (it != caches_.end()) { - auto result = std::move(it->second); - caches_.erase(it); - return result; - } - - return RawCacheLoad(path); -} - -std::unique_ptr ICacheManager::TakeOrLoad(const std::string& path) { - auto result = TryTakeOrLoad(path); - assert(result); - return result; -} - -void ICacheManager::IterateLoadedCaches(std::function fn) { - for (const auto& cache : caches_) { - assert(cache.second); - fn(cache.second.get()); - } -} diff --git a/src/cache_manager.h b/src/cache_manager.h deleted file mode 100644 index e5fd96720..000000000 --- a/src/cache_manager.h +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once - -#include - -#include -#include -#include -#include -#include - -struct Config; -struct IndexFile; - -struct ICacheManager { - struct FakeCacheEntry { - std::string path; - std::string content; - std::string json; - }; - - static std::shared_ptr Make(Config* config); - static std::shared_ptr MakeFake( - const std::vector& entries); - - virtual ~ICacheManager(); - - // Tries to load a cache for |path|, returning null if there is none. The - // cache loader still owns the cache. - IndexFile* TryLoad(const std::string& path); - - // Takes the existing cache or loads the cache at |path|. May return null if - // the cache does not exist. - std::unique_ptr TryTakeOrLoad(const std::string& path); - - // Takes the existing cache or loads the cache at |path|. Asserts the cache - // exists. - std::unique_ptr TakeOrLoad(const std::string& path); - - virtual void WriteToCache(IndexFile& file) = 0; - - virtual optional LoadCachedFileContents( - const std::string& path) = 0; - - // Iterate over all loaded caches. - void IterateLoadedCaches(std::function fn); - - protected: - virtual std::unique_ptr RawCacheLoad(const std::string& path) = 0; - std::unordered_map> caches_; -}; diff --git a/src/clang_complete.cc b/src/clang_complete.cc deleted file mode 100644 index 0eec9a710..000000000 --- a/src/clang_complete.cc +++ /dev/null @@ -1,804 +0,0 @@ -#include "clang_complete.h" - -#include "clang_utils.h" -#include "platform.h" -#include "timer.h" - -#include - -#include -#include - -namespace { - -unsigned Flags() { - // TODO: use clang_defaultEditingTranslationUnitOptions()? - return CXTranslationUnit_Incomplete | CXTranslationUnit_KeepGoing | - CXTranslationUnit_CacheCompletionResults | - CXTranslationUnit_PrecompiledPreamble | - CXTranslationUnit_IncludeBriefCommentsInCodeCompletion | - CXTranslationUnit_DetailedPreprocessingRecord -#if !defined(_WIN32) - // For whatever reason, CreatePreambleOnFirstParse causes clang to - // become very crashy on windows. - // TODO: do more investigation, submit fixes to clang. - | CXTranslationUnit_CreatePreambleOnFirstParse -#endif - ; -} - -unsigned GetCompletionPriority(const CXCompletionString& str, - CXCursorKind result_kind, - const optional& typedText) { - unsigned priority = clang_getCompletionPriority(str); - - // XXX: What happens if priority overflows? - if (result_kind == CXCursor_Destructor) { - priority *= 100; - } - if (result_kind == CXCursor_ConversionFunction || - (result_kind == CXCursor_CXXMethod && typedText && - StartsWith(*typedText, "operator"))) { - priority *= 100; - } - if (clang_getCompletionAvailability(str) != CXAvailability_Available) { - priority *= 100; - } - return priority; -} - -/* -bool IsCallKind(CXCursorKind kind) { - switch (kind) { - case CXCursor_ObjCInstanceMethodDecl: - case CXCursor_CXXMethod: - case CXCursor_FunctionTemplate: - case CXCursor_FunctionDecl: - case CXCursor_Constructor: - case CXCursor_Destructor: - case CXCursor_ConversionFunction: - return true; - default: - return false; - } -} -*/ - -lsCompletionItemKind GetCompletionKind(CXCursorKind cursor_kind) { - switch (cursor_kind) { - case CXCursor_UnexposedDecl: - return lsCompletionItemKind::Text; - - case CXCursor_StructDecl: - case CXCursor_UnionDecl: - return lsCompletionItemKind::Struct; - case CXCursor_ClassDecl: - return lsCompletionItemKind::Class; - case CXCursor_EnumDecl: - return lsCompletionItemKind::Enum; - case CXCursor_FieldDecl: - return lsCompletionItemKind::Field; - case CXCursor_EnumConstantDecl: - return lsCompletionItemKind::EnumMember; - case CXCursor_FunctionDecl: - return lsCompletionItemKind::Function; - case CXCursor_VarDecl: - case CXCursor_ParmDecl: - return lsCompletionItemKind::Variable; - case CXCursor_ObjCInterfaceDecl: - return lsCompletionItemKind::Interface; - - case CXCursor_ObjCInstanceMethodDecl: - case CXCursor_CXXMethod: - case CXCursor_ObjCClassMethodDecl: - return lsCompletionItemKind::Method; - - case CXCursor_FunctionTemplate: - return lsCompletionItemKind::Function; - - case CXCursor_Constructor: - case CXCursor_Destructor: - case CXCursor_ConversionFunction: - return lsCompletionItemKind::Constructor; - - case CXCursor_ObjCIvarDecl: - return lsCompletionItemKind::Variable; - - case CXCursor_ClassTemplate: - case CXCursor_ClassTemplatePartialSpecialization: - case CXCursor_UsingDeclaration: - case CXCursor_TypedefDecl: - case CXCursor_TypeAliasDecl: - case CXCursor_TypeAliasTemplateDecl: - case CXCursor_ObjCCategoryDecl: - case CXCursor_ObjCProtocolDecl: - case CXCursor_ObjCImplementationDecl: - case CXCursor_ObjCCategoryImplDecl: - return lsCompletionItemKind::Class; - - case CXCursor_ObjCPropertyDecl: - return lsCompletionItemKind::Property; - - case CXCursor_MacroInstantiation: - case CXCursor_MacroDefinition: - return lsCompletionItemKind::Interface; - - case CXCursor_Namespace: - case CXCursor_NamespaceAlias: - case CXCursor_NamespaceRef: - return lsCompletionItemKind::Module; - - case CXCursor_MemberRef: - case CXCursor_TypeRef: - case CXCursor_ObjCSuperClassRef: - case CXCursor_ObjCProtocolRef: - case CXCursor_ObjCClassRef: - return lsCompletionItemKind::Reference; - - // return lsCompletionItemKind::Unit; - // return lsCompletionItemKind::Value; - // return lsCompletionItemKind::Keyword; - // return lsCompletionItemKind::Snippet; - // return lsCompletionItemKind::Color; - // return lsCompletionItemKind::File; - - case CXCursor_NotImplemented: - case CXCursor_OverloadCandidate: - return lsCompletionItemKind::Text; - - case CXCursor_TemplateTypeParameter: - case CXCursor_TemplateTemplateParameter: - return lsCompletionItemKind::TypeParameter; - - default: - LOG_S(WARNING) << "Unhandled completion kind " << cursor_kind; - return lsCompletionItemKind::Text; - } -} - -void BuildCompletionItemTexts(std::vector& out, - CXCompletionString completion_string, - bool include_snippets) { - assert(!out.empty()); - auto out_first = out.size() - 1; - - std::string result_type; - - int num_chunks = clang_getNumCompletionChunks(completion_string); - for (int i = 0; i < num_chunks; ++i) { - CXCompletionChunkKind kind = - clang_getCompletionChunkKind(completion_string, i); - - std::string text; - switch (kind) { - // clang-format off - case CXCompletionChunk_LeftParen: text = '('; break; - case CXCompletionChunk_RightParen: text = ')'; break; - case CXCompletionChunk_LeftBracket: text = '['; break; - case CXCompletionChunk_RightBracket: text = ']'; break; - case CXCompletionChunk_LeftBrace: text = '{'; break; - case CXCompletionChunk_RightBrace: text = '}'; break; - case CXCompletionChunk_LeftAngle: text = '<'; break; - case CXCompletionChunk_RightAngle: text = '>'; break; - case CXCompletionChunk_Comma: text = ", "; break; - case CXCompletionChunk_Colon: text = ':'; break; - case CXCompletionChunk_SemiColon: text = ';'; break; - case CXCompletionChunk_Equal: text = '='; break; - case CXCompletionChunk_HorizontalSpace: text = ' '; break; - case CXCompletionChunk_VerticalSpace: text = ' '; break; - // clang-format on - - case CXCompletionChunk_ResultType: - result_type = - ToString(clang_getCompletionChunkText(completion_string, i)); - continue; - - case CXCompletionChunk_TypedText: - case CXCompletionChunk_Placeholder: - case CXCompletionChunk_Text: - case CXCompletionChunk_Informative: - text = ToString(clang_getCompletionChunkText(completion_string, i)); - - for (auto i = out_first; i < out.size(); ++i) { - // first typed text is used for filtering - if (kind == CXCompletionChunk_TypedText && !out[i].filterText) - out[i].filterText = text; - - if (kind == CXCompletionChunk_Placeholder) - out[i].parameters_.push_back(text); - } - break; - - case CXCompletionChunk_CurrentParameter: - // We have our own parsing logic for active parameter. This doesn't seem - // to be very reliable. - continue; - - case CXCompletionChunk_Optional: { - CXCompletionString nested = - clang_getCompletionChunkCompletionString(completion_string, i); - // duplicate last element, the recursive call will complete it - out.push_back(out.back()); - BuildCompletionItemTexts(out, nested, include_snippets); - continue; - } - } - - for (auto i = out_first; i < out.size(); ++i) - out[i].label += text; - - if (kind == CXCompletionChunk_Informative) - continue; - - for (auto i = out_first; i < out.size(); ++i) { - if (!include_snippets && !out[i].parameters_.empty()) - continue; - - if (kind == CXCompletionChunk_Placeholder) { - out[i].insertText += - "${" + std::to_string(out[i].parameters_.size()) + ":" + text + "}"; - out[i].insertTextFormat = lsInsertTextFormat::Snippet; - } else { - out[i].insertText += text; - } - } - } - - if (result_type.empty()) - return; - - for (auto i = out_first; i < out.size(); ++i) { - // ' : ' for variables, - // ' -> ' (trailing return type-like) for functions - out[i].label += (out[i].label == out[i].filterText ? " : " : " -> "); - out[i].label += result_type; - } -} - -// |do_insert|: if |!do_insert|, do not append strings to |insert| after -// a placeholder. -void BuildDetailString(CXCompletionString completion_string, - std::string& label, - std::string& detail, - std::string& insert, - bool& do_insert, - lsInsertTextFormat& format, - std::vector* parameters, - bool include_snippets) { - int num_chunks = clang_getNumCompletionChunks(completion_string); - auto append = [&](const char* text) { - detail += text; - if (do_insert) - insert += text; - }; - for (int i = 0; i < num_chunks; ++i) { - CXCompletionChunkKind kind = - clang_getCompletionChunkKind(completion_string, i); - - switch (kind) { - case CXCompletionChunk_Optional: { - CXCompletionString nested = - clang_getCompletionChunkCompletionString(completion_string, i); - BuildDetailString(nested, label, detail, insert, do_insert, format, - parameters, include_snippets); - break; - } - - case CXCompletionChunk_Placeholder: { - std::string text = - ToString(clang_getCompletionChunkText(completion_string, i)); - parameters->push_back(text); - detail += text; - // Add parameter declarations as snippets if enabled - if (include_snippets) { - insert += - "${" + std::to_string(parameters->size()) + ":" + text + "}"; - format = lsInsertTextFormat::Snippet; - } else - do_insert = false; - break; - } - - case CXCompletionChunk_CurrentParameter: - // We have our own parsing logic for active parameter. This doesn't seem - // to be very reliable. - break; - - case CXCompletionChunk_TypedText: { - std::string text = - ToString(clang_getCompletionChunkText(completion_string, i)); - label = text; - detail += text; - if (do_insert) - insert += text; - break; - } - - case CXCompletionChunk_Text: { - std::string text = - ToString(clang_getCompletionChunkText(completion_string, i)); - detail += text; - if (do_insert) - insert += text; - break; - } - - case CXCompletionChunk_Informative: { - detail += ToString(clang_getCompletionChunkText(completion_string, i)); - break; - } - - case CXCompletionChunk_ResultType: { - CXString text = clang_getCompletionChunkText(completion_string, i); - std::string new_detail = ToString(text) + detail + " "; - detail = new_detail; - break; - } - - // clang-format off - case CXCompletionChunk_LeftParen: append("("); break; - case CXCompletionChunk_RightParen: append(")"); break; - case CXCompletionChunk_LeftBracket: append("["); break; - case CXCompletionChunk_RightBracket: append("]"); break; - case CXCompletionChunk_LeftBrace: append("{"); break; - case CXCompletionChunk_RightBrace: append("}"); break; - case CXCompletionChunk_LeftAngle: append("<"); break; - case CXCompletionChunk_RightAngle: append(">"); break; - case CXCompletionChunk_Comma: append(", "); break; - case CXCompletionChunk_Colon: append(":"); break; - case CXCompletionChunk_SemiColon: append(";"); break; - case CXCompletionChunk_Equal: append("="); break; - // clang-format on - case CXCompletionChunk_HorizontalSpace: - case CXCompletionChunk_VerticalSpace: - append(" "); - break; - } - } -} - -void TryEnsureDocumentParsed(ClangCompleteManager* manager, - std::shared_ptr session, - std::unique_ptr* tu, - ClangIndex* index) { - // Nothing to do. We already have a translation unit. - if (*tu) - return; - - std::vector args = session->file.args; - - // -fspell-checking enables FixIts for, ie, misspelled types. - if (!AnyStartsWith(args, "-fno-spell-checking") && - !AnyStartsWith(args, "-fspell-checking")) { - args.push_back("-fspell-checking"); - } - - WorkingFiles::Snapshot snapshot = session->working_files->AsSnapshot( - {StripFileType(session->file.filename)}); - std::vector unsaved = snapshot.AsUnsavedFiles(); - - LOG_S(INFO) << "Creating completion session with arguments " - << StringJoin(args, " "); - *tu = ClangTranslationUnit::Create(index, session->file.filename, args, - unsaved, Flags()); - - // Build diagnostics. - if (manager->config_->diagnostics.onParse && *tu) { - // If we're emitting diagnostics, do an immediate reparse, otherwise we will - // emit stale/bad diagnostics. - *tu = ClangTranslationUnit::Reparse(std::move(*tu), unsaved); - if (!*tu) { - LOG_S(ERROR) << "Reparsing translation unit for diagnostics failed for " - << session->file.filename; - return; - } - - std::vector ls_diagnostics; - unsigned num_diagnostics = clang_getNumDiagnostics((*tu)->cx_tu); - for (unsigned i = 0; i < num_diagnostics; ++i) { - optional diagnostic = BuildAndDisposeDiagnostic( - clang_getDiagnostic((*tu)->cx_tu, i), session->file.filename); - // Filter messages like "too many errors emitted, stopping now - // [-ferror-limit=]" which has line = 0 and got subtracted by 1 after - // conversion to lsDiagnostic - if (diagnostic && diagnostic->range.start.line >= 0) - ls_diagnostics.push_back(*diagnostic); - } - manager->on_diagnostic_(session->file.filename, ls_diagnostics); - } -} - -void CompletionParseMain(ClangCompleteManager* completion_manager) { - while (true) { - // Fetching the completion request blocks until we have a request. - ClangCompleteManager::ParseRequest request = - completion_manager->parse_requests_.Dequeue(); - - // If we don't get a session then that means we don't care about the file - // anymore - abandon the request. - std::shared_ptr session = - completion_manager->TryGetSession(request.path, - false /*mark_as_completion*/, - false /*create_if_needed*/); - if (!session) - continue; - - // If we've parsed it more recently than the request time, don't bother - // reparsing. - if (session->tu_last_parsed_at && - *session->tu_last_parsed_at > request.request_time) { - continue; - } - - std::unique_ptr parsing; - TryEnsureDocumentParsed(completion_manager, session, &parsing, - &session->index); - - // Activate new translation unit. - // tu_last_parsed_at is only read by this thread, so it doesn't need to be - // under the mutex. - session->tu_last_parsed_at = std::chrono::high_resolution_clock::now(); - std::lock_guard lock(session->tu_lock); - session->tu = std::move(parsing); - } -} - -void CompletionQueryMain(ClangCompleteManager* completion_manager) { - while (true) { - // Fetching the completion request blocks until we have a request. - std::unique_ptr request = - completion_manager->completion_request_.Dequeue(); - - // Drop older requests if we're not buffering. - while (completion_manager->config_->completion.dropOldRequests && - !completion_manager->completion_request_.IsEmpty()) { - completion_manager->on_dropped_(request->id); - request = completion_manager->completion_request_.Dequeue(); - } - - std::string path = request->document.uri.GetPath(); - - std::shared_ptr session = - completion_manager->TryGetSession(path, true /*mark_as_completion*/, - true /*create_if_needed*/); - - std::lock_guard lock(session->tu_lock); - Timer timer; - TryEnsureDocumentParsed(completion_manager, session, &session->tu, - &session->index); - timer.ResetAndPrint("[complete] TryEnsureDocumentParsed"); - - // It is possible we failed to create the document despite - // |TryEnsureDocumentParsed|. - if (!session->tu) - continue; - - timer.Reset(); - WorkingFiles::Snapshot snapshot = - completion_manager->working_files_->AsSnapshot({StripFileType(path)}); - std::vector unsaved = snapshot.AsUnsavedFiles(); - timer.ResetAndPrint("[complete] Creating WorkingFile snapshot"); - - // Emit code completion data. - if (request->position) { - // Language server is 0-based, clang is 1-based. - unsigned line = request->position->line + 1; - unsigned column = request->position->character + 1; - - timer.Reset(); - unsigned const kCompleteOptions = - CXCodeComplete_IncludeMacros | CXCodeComplete_IncludeBriefComments; - CXCodeCompleteResults* cx_results = clang_codeCompleteAt( - session->tu->cx_tu, session->file.filename.c_str(), line, column, - unsaved.data(), (unsigned)unsaved.size(), kCompleteOptions); - timer.ResetAndPrint("[complete] clangCodeCompleteAt"); - if (!cx_results) { - if (request->on_complete) - request->on_complete({}, false /*is_cached_result*/); - continue; - } - - { - if (request->on_complete) { - std::vector ls_result; - // this is a guess but can be larger in case of optional parameters, - // as they may be expanded into multiple items - ls_result.reserve(cx_results->NumResults); - - timer.Reset(); - for (unsigned i = 0; i < cx_results->NumResults; ++i) { - CXCompletionResult& result = cx_results->Results[i]; - - // TODO: Try to figure out how we can hide base method calls without - // also hiding method implementation assistance, ie, - // - // void Foo::* { - // } - // - - if (clang_getCompletionAvailability(result.CompletionString) == - CXAvailability_NotAvailable) - continue; - - // TODO: fill in more data - lsCompletionItem ls_completion_item; - - ls_completion_item.kind = GetCompletionKind(result.CursorKind); - ls_completion_item.documentation = ToString( - clang_getCompletionBriefComment(result.CompletionString)); - - // label/detail/filterText/insertText/priority - if (completion_manager->config_->completion.detailedLabel) { - ls_completion_item.detail = ToString( - clang_getCompletionParent(result.CompletionString, nullptr)); - - auto first_idx = ls_result.size(); - ls_result.push_back(ls_completion_item); - - // label/filterText/insertText - BuildCompletionItemTexts( - ls_result, result.CompletionString, - completion_manager->config_->client.snippetSupport); - - for (auto i = first_idx; i < ls_result.size(); ++i) { - if (completion_manager->config_->client.snippetSupport && - ls_result[i].insertTextFormat == - lsInsertTextFormat::Snippet) { - ls_result[i].insertText += "$0"; - } - - ls_result[i].priority_ = GetCompletionPriority( - result.CompletionString, result.CursorKind, - ls_result[i].filterText); - } - } else { - bool do_insert = true; - BuildDetailString( - result.CompletionString, ls_completion_item.label, - ls_completion_item.detail, ls_completion_item.insertText, - do_insert, ls_completion_item.insertTextFormat, - &ls_completion_item.parameters_, - completion_manager->config_->client.snippetSupport); - if (completion_manager->config_->client.snippetSupport && - ls_completion_item.insertTextFormat == - lsInsertTextFormat::Snippet) { - ls_completion_item.insertText += "$0"; - } - ls_completion_item.priority_ = GetCompletionPriority( - result.CompletionString, result.CursorKind, - ls_completion_item.label); - ls_result.push_back(ls_completion_item); - } - } - - timer.ResetAndPrint("[complete] Building " + - std::to_string(ls_result.size()) + - " completion results"); - - request->on_complete(ls_result, false /*is_cached_result*/); - } - } - - // Make sure |ls_results| is destroyed before clearing |cx_results|. - clang_disposeCodeCompleteResults(cx_results); - } - - // Emit diagnostics. - if (request->emit_diagnostics) { - // TODO: before emitting diagnostics check if we have another completion - // request and think about servicing that first, because it may be much - // faster than reparsing the document. - // TODO: have a separate thread for diagnostics? - - timer.Reset(); - session->tu = - ClangTranslationUnit::Reparse(std::move(session->tu), unsaved); - timer.ResetAndPrint("[complete] clang_reparseTranslationUnit"); - if (!session->tu) { - LOG_S(ERROR) << "Reparsing translation unit for diagnostics failed for " - << path; - continue; - } - - size_t num_diagnostics = clang_getNumDiagnostics(session->tu->cx_tu); - std::vector ls_diagnostics; - ls_diagnostics.reserve(num_diagnostics); - for (unsigned i = 0; i < num_diagnostics; ++i) { - CXDiagnostic cx_diag = clang_getDiagnostic(session->tu->cx_tu, i); - optional diagnostic = - BuildAndDisposeDiagnostic(cx_diag, path); - // Filter messages like "too many errors emitted, stopping now - // [-ferror-limit=]" which has line = 0 and got subtracted by 1 after - // conversion to lsDiagnostic - if (diagnostic && diagnostic->range.start.line >= 0) - ls_diagnostics.push_back(*diagnostic); - } - completion_manager->on_diagnostic_(session->file.filename, - ls_diagnostics); - - /* - timer.Reset(); - completion_manager->on_index_(session->tu.get(), unsaved, - session->file.filename, session->file.args); - timer.ResetAndPrint("[complete] Reindex file"); - */ - } - - continue; - } -} - -} // namespace - -CompletionSession::CompletionSession(const Project::Entry& file, - WorkingFiles* working_files) - : file(file), - working_files(working_files), - index(0 /*excludeDeclarationsFromPCH*/, 0 /*displayDiagnostics*/) {} - -CompletionSession::~CompletionSession() {} - -ClangCompleteManager::ParseRequest::ParseRequest(const std::string& path) - : request_time(std::chrono::high_resolution_clock::now()), path(path) {} - -ClangCompleteManager::CompletionRequest::CompletionRequest( - const lsRequestId& id, - const lsTextDocumentIdentifier& document, - bool emit_diagnostics) - : id(id), document(document), emit_diagnostics(emit_diagnostics) {} -ClangCompleteManager::CompletionRequest::CompletionRequest( - const lsRequestId& id, - const lsTextDocumentIdentifier& document, - const lsPosition& position, - const OnComplete& on_complete, - bool emit_diagnostics) - : id(id), - document(document), - position(position), - on_complete(on_complete), - emit_diagnostics(emit_diagnostics) {} - -ClangCompleteManager::ClangCompleteManager(Config* config, - Project* project, - WorkingFiles* working_files, - OnDiagnostic on_diagnostic, - OnIndex on_index, - OnDropped on_dropped) - : config_(config), - project_(project), - working_files_(working_files), - on_diagnostic_(on_diagnostic), - on_index_(on_index), - on_dropped_(on_dropped), - preloaded_sessions_(kMaxPreloadedSessions), - completion_sessions_(kMaxCompletionSessions) { - new std::thread([&]() { - SetCurrentThreadName("completequery"); - CompletionQueryMain(this); - }); - - new std::thread([&]() { - SetCurrentThreadName("completeparse"); - CompletionParseMain(this); - }); -} - -ClangCompleteManager::~ClangCompleteManager() {} - -void ClangCompleteManager::CodeComplete( - const lsRequestId& id, - const lsTextDocumentPositionParams& completion_location, - const OnComplete& on_complete) { - completion_request_.PushBack(std::make_unique( - id, completion_location.textDocument, completion_location.position, - on_complete, false)); -} - -void ClangCompleteManager::DiagnosticsUpdate( - const lsRequestId& id, - const lsTextDocumentIdentifier& document) { - completion_request_.PushBack( - std::make_unique(id, document, true)); -} - -void ClangCompleteManager::NotifyView(const std::string& filename) { - // - // On view, we reparse only if the file has not been parsed. The existence of - // a CompletionSession instance implies the file is already parsed or will be - // parsed soon. - // - - // Only reparse the file if we create a new CompletionSession. - if (EnsureCompletionOrCreatePreloadSession(filename)) - parse_requests_.PushBack(ParseRequest(filename), true); -} - -void ClangCompleteManager::NotifyEdit(const std::string& filename) { - // - // We treat an edit like a view, because the completion logic will handle - // moving the CompletionSession instance from preloaded to completion - // storage. - // - - NotifyView(filename); -} - -void ClangCompleteManager::NotifySave(const std::string& filename) { - // - // On save, always reparse. - // - - EnsureCompletionOrCreatePreloadSession(filename); - parse_requests_.PushBack(ParseRequest(filename), true); -} - -void ClangCompleteManager::NotifyClose(const std::string& filename) { - // - // On close, we clear any existing CompletionSession instance. - // - - std::lock_guard lock(sessions_lock_); - - // Take and drop. It's okay if we don't actually drop the file, it'll - // eventually get pushed out of the caches as the user opens other files. - auto preloaded_ptr = preloaded_sessions_.TryTake(filename); - LOG_IF_S(INFO, !!preloaded_ptr) - << "Dropped preloaded-based code completion session for " << filename; - auto completion_ptr = completion_sessions_.TryTake(filename); - LOG_IF_S(INFO, !!completion_ptr) - << "Dropped completion-based code completion session for " << filename; - - // We should never have both a preloaded and completion session. - assert((preloaded_ptr && completion_ptr) == false); -} - -bool ClangCompleteManager::EnsureCompletionOrCreatePreloadSession( - const std::string& filename) { - std::lock_guard lock(sessions_lock_); - - // Check for an existing CompletionSession. - if (preloaded_sessions_.TryGet(filename) || - completion_sessions_.TryGet(filename)) { - return false; - } - - // No CompletionSession, create new one. - auto session = std::make_shared( - project_->FindCompilationEntryForFile(filename), working_files_); - preloaded_sessions_.Insert(session->file.filename, session); - return true; -} - -std::shared_ptr ClangCompleteManager::TryGetSession( - const std::string& filename, - bool mark_as_completion, - bool create_if_needed) { - std::lock_guard lock(sessions_lock_); - - // Try to find a preloaded session. - std::shared_ptr preloaded_session = - preloaded_sessions_.TryGet(filename); - - if (preloaded_session) { - // If this request is for a completion, we should move it to - // |completion_sessions|. - if (mark_as_completion) { - assert(!completion_sessions_.TryGet(filename)); - preloaded_sessions_.TryTake(filename); - completion_sessions_.Insert(filename, preloaded_session); - } - - return preloaded_session; - } - - // Try to find a completion session. If none create one. - std::shared_ptr completion_session = - completion_sessions_.TryGet(filename); - if (!completion_session && create_if_needed) { - completion_session = std::make_shared( - project_->FindCompilationEntryForFile(filename), working_files_); - completion_sessions_.Insert(filename, completion_session); - } - - return completion_session; -} diff --git a/src/clang_complete.h b/src/clang_complete.h deleted file mode 100644 index cf7524178..000000000 --- a/src/clang_complete.h +++ /dev/null @@ -1,142 +0,0 @@ -#pragma once - -#include "clang_index.h" -#include "clang_translation_unit.h" -#include "lsp_completion.h" -#include "lsp_diagnostic.h" -#include "lru_cache.h" -#include "project.h" -#include "threaded_queue.h" -#include "working_files.h" - -#include - -#include -#include -#include -#include - -struct CompletionSession - : public std::enable_shared_from_this { - Project::Entry file; - WorkingFiles* working_files; - ClangIndex index; - - // When |tu| was last parsed. - optional> - tu_last_parsed_at; - - // Acquired when |tu| is being used. - std::mutex tu_lock; - - // The active translation unit. - std::unique_ptr tu; - - CompletionSession(const Project::Entry& file, WorkingFiles* working_files); - ~CompletionSession(); -}; - -struct ClangCompleteManager { - using OnDiagnostic = - std::function diagnostics)>; - using OnIndex = std::function& unsaved, - const std::string& path, - const std::vector& args)>; - using OnComplete = - std::function& results, - bool is_cached_result)>; - using OnDropped = std::function; - - struct ParseRequest { - ParseRequest(const std::string& path); - - std::chrono::time_point request_time; - std::string path; - }; - struct CompletionRequest { - CompletionRequest(const lsRequestId& id, - const lsTextDocumentIdentifier& document, - bool emit_diagnostics); - CompletionRequest(const lsRequestId& id, - const lsTextDocumentIdentifier& document, - const lsPosition& position, - const OnComplete& on_complete, - bool emit_diagnostics); - - lsRequestId id; - lsTextDocumentIdentifier document; - optional position; - OnComplete on_complete; // May be null/empty. - bool emit_diagnostics = false; - }; - - ClangCompleteManager(Config* config, - Project* project, - WorkingFiles* working_files, - OnDiagnostic on_diagnostic, - OnIndex on_index, - OnDropped on_dropped); - ~ClangCompleteManager(); - - // Start a code completion at the given location. |on_complete| will run when - // completion results are available. |on_complete| may run on any thread. - void CodeComplete(const lsRequestId& request_id, - const lsTextDocumentPositionParams& completion_location, - const OnComplete& on_complete); - // Request a diagnostics update. - void DiagnosticsUpdate(const lsRequestId& request_id, - const lsTextDocumentIdentifier& document); - - // Notify the completion manager that |filename| has been viewed and we - // should begin preloading completion data. - void NotifyView(const std::string& filename); - // Notify the completion manager that |filename| has been edited. - void NotifyEdit(const std::string& filename); - // Notify the completion manager that |filename| has been saved. This - // triggers a reparse. - void NotifySave(const std::string& filename); - // Notify the completion manager that |filename| has been closed. Any existing - // completion session will be dropped. - void NotifyClose(const std::string& filename); - - // Ensures there is a completion or preloaded session. Returns true if a new - // session was created. - bool EnsureCompletionOrCreatePreloadSession(const std::string& filename); - // Tries to find an edit session for |filename|. This will move the session - // from view to edit. - std::shared_ptr TryGetSession(const std::string& filename, - bool mark_as_completion, - bool create_if_needed); - - // TODO: make these configurable. - const int kMaxPreloadedSessions = 10; - const int kMaxCompletionSessions = 5; - - // Global state. - Config* config_; - Project* project_; - WorkingFiles* working_files_; - OnDiagnostic on_diagnostic_; - OnIndex on_index_; - OnDropped on_dropped_; - - using LruSessionCache = LruCache; - - // CompletionSession instances which are preloaded, ie, files which the user - // has viewed but not requested code completion for. - LruSessionCache preloaded_sessions_; - // CompletionSession instances which the user has actually performed - // completion on. This is more rare so these instances tend to stay alive - // much longer than the ones in |preloaded_sessions_|. - LruSessionCache completion_sessions_; - // Mutex which protects |view_sessions_| and |edit_sessions_|. - std::mutex sessions_lock_; - - // Request a code completion at the given location. - ThreadedQueue> completion_request_; - // Parse requests. The path may already be parsed, in which case it should be - // reparsed. - ThreadedQueue parse_requests_; -}; diff --git a/src/clang_cursor.cc b/src/clang_cursor.cc deleted file mode 100644 index 8ce4c2626..000000000 --- a/src/clang_cursor.cc +++ /dev/null @@ -1,286 +0,0 @@ -#include "clang_cursor.h" - -#include "clang_utils.h" - -#include - -#include -#include - -Range ResolveCXSourceRange(const CXSourceRange& range, CXFile* cx_file) { - CXSourceLocation start = clang_getRangeStart(range); - CXSourceLocation end = clang_getRangeEnd(range); - - unsigned int start_line, start_column; - clang_getSpellingLocation(start, cx_file, &start_line, &start_column, - nullptr); - unsigned int end_line, end_column; - clang_getSpellingLocation(end, nullptr, &end_line, &end_column, nullptr); - - return Range(Position((int16_t)start_line - 1, (int16_t)start_column - 1), - Position((int16_t)end_line - 1, (int16_t)end_column - 1)); -} - -ClangType::ClangType() : cx_type() {} - -ClangType::ClangType(const CXType& other) : cx_type(other) {} - -bool ClangType::operator==(const ClangType& rhs) const { - return clang_equalTypes(cx_type, rhs.cx_type); -} - -ClangCursor ClangType::get_declaration() const { - return clang_getTypeDeclaration(cx_type); -} - -std::string ClangType::get_usr() const { - return ClangCursor(clang_getTypeDeclaration(cx_type)).get_usr(); -} - -Usr ClangType::get_usr_hash() const { - if (is_builtin()) - return static_cast(cx_type.kind); - return ClangCursor(clang_getTypeDeclaration(cx_type)).get_usr_hash(); -} - -ClangType ClangType::get_canonical() const { - return clang_getCanonicalType(cx_type); -} - -ClangType ClangType::strip_qualifiers() const { - CXType cx = cx_type; - while (1) { - switch (cx.kind) { - default: - break; - case CXType_ConstantArray: - case CXType_DependentSizedArray: - case CXType_IncompleteArray: - case CXType_VariableArray: - cx = clang_getElementType(cx); - continue; - case CXType_BlockPointer: - case CXType_LValueReference: - case CXType_MemberPointer: - case CXType_ObjCObjectPointer: - case CXType_Pointer: - case CXType_RValueReference: - cx = clang_getPointeeType(cx); - continue; - } - break; - } - - return cx; -} - -std::string ClangType::get_spell_name() const { - return ToString(clang_getTypeSpelling(cx_type)); -} - -ClangType ClangType::get_return_type() const { - return ClangType(clang_getResultType(cx_type)); -} - -std::vector ClangType::get_arguments() const { - int size = clang_getNumArgTypes(cx_type); - if (size < 0) - return {}; - std::vector types(size); - for (int i = 0; i < size; ++i) - types.emplace_back(clang_getArgType(cx_type, i)); - return types; -} - -std::vector ClangType::get_template_arguments() const { - int size = clang_Type_getNumTemplateArguments(cx_type); - assert(size >= 0); - if (size < 0) - return std::vector(); - - std::vector types(size); - for (int i = 0; i < size; ++i) - types.emplace_back(clang_Type_getTemplateArgumentAsType(cx_type, i)); - return types; -} - -static_assert(sizeof(ClangCursor) == sizeof(CXCursor), - "Cursor must be the same size as CXCursor"); - -ClangCursor::ClangCursor() : cx_cursor(clang_getNullCursor()) {} - -ClangCursor::ClangCursor(const CXCursor& other) : cx_cursor(other) {} - -ClangCursor::operator bool() const { - return !clang_Cursor_isNull(cx_cursor); -} - -bool ClangCursor::operator==(const ClangCursor& rhs) const { - return clang_equalCursors(cx_cursor, rhs.cx_cursor); -} - -bool ClangCursor::operator!=(const ClangCursor& rhs) const { - return !(*this == rhs); -} - -CXCursorKind ClangCursor::get_kind() const { - return cx_cursor.kind; -} - -ClangType ClangCursor::get_type() const { - return ClangType(clang_getCursorType(cx_cursor)); -} - -std::string ClangCursor::get_spell_name() const { - return ::ToString(clang_getCursorSpelling(cx_cursor)); -} - -Range ClangCursor::get_spell(CXFile* cx_file) const { - // TODO for Objective-C methods and Objective-C message expressions, there are - // multiple pieces for each selector identifier. - CXSourceRange range = clang_Cursor_getSpellingNameRange(cx_cursor, 0, 0); - return ResolveCXSourceRange(range, cx_file); -} - -Range ClangCursor::get_extent() const { - CXSourceRange range = clang_getCursorExtent(cx_cursor); - return ResolveCXSourceRange(range, nullptr); -} - -std::string ClangCursor::get_display_name() const { - return ::ToString(clang_getCursorDisplayName(cx_cursor)); -} - -std::string ClangCursor::get_usr() const { - return ::ToString(clang_getCursorUSR(cx_cursor)); -} - -Usr ClangCursor::get_usr_hash() const { - CXString usr = clang_getCursorUSR(cx_cursor); - Usr ret = HashUsr(clang_getCString(usr)); - clang_disposeString(usr); - return ret; -} - -bool ClangCursor::is_definition() const { - return clang_isCursorDefinition(cx_cursor); -} - -ClangCursor ClangCursor::template_specialization_to_template_definition() - const { - CXCursor definition = clang_getSpecializedCursorTemplate(cx_cursor); - if (definition.kind == CXCursor_FirstInvalid) - return cx_cursor; - return definition; -} - -ClangCursor ClangCursor::get_referenced() const { - return ClangCursor(clang_getCursorReferenced(cx_cursor)); -} - -ClangCursor ClangCursor::get_canonical() const { - return ClangCursor(clang_getCanonicalCursor(cx_cursor)); -} - -ClangCursor ClangCursor::get_definition() const { - return ClangCursor(clang_getCursorDefinition(cx_cursor)); -} - -ClangCursor ClangCursor::get_lexical_parent() const { - return ClangCursor(clang_getCursorLexicalParent(cx_cursor)); -} - -ClangCursor ClangCursor::get_semantic_parent() const { - return ClangCursor(clang_getCursorSemanticParent(cx_cursor)); -} - -std::vector ClangCursor::get_arguments() const { - int size = clang_Cursor_getNumArguments(cx_cursor); - if (size < 0) - return std::vector(); - - std::vector cursors(size); - for (int i = 0; i < size; ++i) - cursors.emplace_back(clang_Cursor_getArgument(cx_cursor, i)); - return cursors; -} - -bool ClangCursor::is_valid_kind() const { - CXCursor referenced = clang_getCursorReferenced(cx_cursor); - if (clang_Cursor_isNull(referenced)) - return false; - - CXCursorKind kind = get_kind(); - return kind > CXCursor_UnexposedDecl && - (kind < CXCursor_FirstInvalid || kind > CXCursor_LastInvalid); -} - -std::string ClangCursor::get_type_description() const { - auto type = clang_getCursorType(cx_cursor); - return ::ToString(clang_getTypeSpelling(type)); -} - -NtString ClangCursor::get_comments() const { - CXSourceRange range = clang_Cursor_getCommentRange(cx_cursor); - if (clang_Range_isNull(range)) - return {}; - - unsigned start_column; - clang_getSpellingLocation(clang_getRangeStart(range), nullptr, nullptr, - &start_column, nullptr); - - // Get associated comment text. - CXString cx_raw = clang_Cursor_getRawCommentText(cx_cursor); - int pad = -1; - std::string ret; - for (const char* p = clang_getCString(cx_raw); *p;) { - // The first line starts with a comment marker, but the rest needs - // un-indenting. - unsigned skip = start_column - 1; - for (; skip > 0 && (*p == ' ' || *p == '\t'); p++) - skip--; - const char* q = p; - while (*q != '\n' && *q) - q++; - if (*q) - 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 == '/' || *p == '*') - p++; - if (*p == '<' || *p == '!') - p++; - if (*p == ' ') - p++; - pad = int(p - begin); - } else { - // Other lines, skip |pad| bytes - int prefix = pad; - while (prefix > 0 && - (*p == ' ' || *p == '/' || *p == '*' || *p == '<' || *p == '!')) - prefix--, p++; - } - ret.insert(ret.end(), p, q); - p = q; - } - clang_disposeString(cx_raw); - while (ret.size() && isspace(ret.back())) - ret.pop_back(); - if (EndsWith(ret, "*/")) { - ret.resize(ret.size() - 2); - } else if (EndsWith(ret, "\n/")) { - ret.resize(ret.size() - 2); - } - while (ret.size() && isspace(ret.back())) - ret.pop_back(); - if (ret.empty()) - return {}; - return static_cast(ret); -} - -std::string ClangCursor::ToString() const { - return ::ToString(get_kind()) + " " + get_spell_name(); -} diff --git a/src/clang_cursor.h b/src/clang_cursor.h deleted file mode 100644 index f7129b9de..000000000 --- a/src/clang_cursor.h +++ /dev/null @@ -1,116 +0,0 @@ -#pragma once - -#include "nt_string.h" -#include "position.h" - -#include -#include - -#include -#include -#include - -using Usr = uint64_t; - -Range ResolveCXSourceRange(const CXSourceRange& range, - CXFile* cx_file = nullptr); - -class ClangCursor; - -class ClangType { - public: - ClangType(); - ClangType(const CXType& other); - - bool operator==(const ClangType& rhs) const; - - // Returns true if this is a fundamental type like int. - bool is_builtin() const { - // NOTE: This will return false for pointed types. Should we call - // strip_qualifiers for the user? - return cx_type.kind >= CXType_FirstBuiltin && - cx_type.kind <= CXType_LastBuiltin; - } - - ClangCursor get_declaration() const; - std::string get_usr() const; - Usr get_usr_hash() const; - std::string get_spell_name() const; - ClangType get_canonical() const; - - // Try to resolve this type and remove qualifies, ie, Foo* will become Foo - ClangType strip_qualifiers() const; - - ClangType get_return_type() const; - std::vector get_arguments() const; - std::vector get_template_arguments() const; - - CXType cx_type; -}; - -class ClangCursor { - public: - ClangCursor(); - ClangCursor(const CXCursor& other); - - explicit operator bool() const; - bool operator==(const ClangCursor& rhs) const; - bool operator!=(const ClangCursor& rhs) const; - - CXCursorKind get_kind() const; - ClangType get_type() const; - std::string get_spell_name() const; - Range get_spell(CXFile* cx_file = nullptr) const; - Range get_extent() const; - std::string get_display_name() const; - std::string get_usr() const; - Usr get_usr_hash() const; - - bool is_definition() const; - - // If the given cursor points to a template specialization, this - // will return the cursor pointing to the template definition. - // If the given cursor is not a template specialization, this will - // just return the same cursor. - // - // This means it is always safe to call this method. - ClangCursor template_specialization_to_template_definition() const; - - ClangCursor get_referenced() const; - ClangCursor get_canonical() const; - ClangCursor get_definition() const; - ClangCursor get_lexical_parent() const; - ClangCursor get_semantic_parent() const; - std::vector get_arguments() const; - bool is_valid_kind() const; - - std::string get_type_description() const; - NtString get_comments() const; - - std::string ToString() const; - - enum class VisitResult { Break, Continue, Recurse }; - - template - using Visitor = VisitResult (*)(ClangCursor cursor, - ClangCursor parent, - TClientData* client_data); - - template - void VisitChildren(Visitor visitor, - TClientData* client_data) const { - clang_visitChildren(cx_cursor, reinterpret_cast(visitor), - client_data); - } - - CXCursor cx_cursor; -}; - -namespace std { -template <> -struct hash { - size_t operator()(const ClangCursor& x) const { - return clang_hashCursor(x.cx_cursor); - } -}; -} // namespace std diff --git a/src/clang_format.cc b/src/clang_format.cc deleted file mode 100644 index 5bb238a83..000000000 --- a/src/clang_format.cc +++ /dev/null @@ -1,117 +0,0 @@ -#if USE_CLANG_CXX - -#include "clang_format.h" -#include "working_files.h" - -#include -#include - -using namespace clang; -using clang::format::FormatStyle; - -namespace { - -// TODO Objective-C 'header/interface' files may use .h, we should get this from -// project information. -FormatStyle::LanguageKind getLanguageKindFromFilename( - llvm::StringRef filename) { - if (filename.endswith(".m") || filename.endswith(".mm")) { - return FormatStyle::LK_ObjC; - } - return FormatStyle::LK_Cpp; -} - -} // namespace - -std::vector ClangFormatDocument( - WorkingFile* working_file, - int start, - int end, - lsFormattingOptions options) { - const auto language_kind = - getLanguageKindFromFilename(working_file->filename); - FormatStyle predefined_style; - getPredefinedStyle("chromium", language_kind, &predefined_style); - llvm::Expected style = - format::getStyle("file", working_file->filename, "chromium"); - if (!style) { - // If, for some reason, we cannot get a format style, use Chromium's with - // tab configuration provided by the client editor. - LOG_S(ERROR) << llvm::toString(style.takeError()); - predefined_style.UseTab = options.insertSpaces - ? FormatStyle::UseTabStyle::UT_Never - : FormatStyle::UseTabStyle::UT_Always; - predefined_style.IndentWidth = options.tabSize; - } - - auto format_result = reformat( - style ? *style : predefined_style, working_file->buffer_content, - llvm::ArrayRef(tooling::Range(start, end - start)), - working_file->filename); - return std::vector(format_result.begin(), - format_result.end()); -} - -TEST_SUITE("ClangFormat") { - TEST_CASE("entireDocument") { - const std::string sample_document = "int main() { int *i = 0; return 0; }"; - WorkingFile* file = new WorkingFile("foo.cc", sample_document); - lsFormattingOptions formatting_options; - formatting_options.insertSpaces = true; - const auto replacements = ClangFormatDocument( - file, 0, sample_document.size(), formatting_options); - - // echo "int main() { int *i = 0; return 0; }" | clang-format - // -style=Chromium -output-replacements-xml - // - // - // - // - // - // - // - // - // - - REQUIRE(replacements.size() == 5); - REQUIRE(replacements[0].getOffset() == 12); - REQUIRE(replacements[0].getLength() == 1); - REQUIRE(replacements[0].getReplacementText() == "\n "); - - REQUIRE(replacements[1].getOffset() == 16); - REQUIRE(replacements[1].getLength() == 1); - REQUIRE(replacements[1].getReplacementText() == ""); - - REQUIRE(replacements[2].getOffset() == 18); - REQUIRE(replacements[2].getLength() == 0); - REQUIRE(replacements[2].getReplacementText() == " "); - - REQUIRE(replacements[3].getOffset() == 24); - REQUIRE(replacements[3].getLength() == 1); - REQUIRE(replacements[3].getReplacementText() == "\n "); - - REQUIRE(replacements[4].getOffset() == 34); - REQUIRE(replacements[4].getLength() == 1); - REQUIRE(replacements[4].getReplacementText() == "\n"); - } - - TEST_CASE("range") { - const std::string sampleDocument = "int main() { int *i = 0; return 0; }"; - WorkingFile* file = new WorkingFile("foo.cc", sampleDocument); - lsFormattingOptions formattingOptions; - formattingOptions.insertSpaces = true; - const auto replacements = - ClangFormatDocument(file, 30, sampleDocument.size(), formattingOptions); - - REQUIRE(replacements.size() == 2); - REQUIRE(replacements[0].getOffset() == 24); - REQUIRE(replacements[0].getLength() == 1); - REQUIRE(replacements[0].getReplacementText() == "\n "); - - REQUIRE(replacements[1].getOffset() == 34); - REQUIRE(replacements[1].getLength() == 1); - REQUIRE(replacements[1].getReplacementText() == "\n"); - } -} - -#endif diff --git a/src/clang_format.h b/src/clang_format.h deleted file mode 100644 index 862f3e067..000000000 --- a/src/clang_format.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#if USE_CLANG_CXX - -#include "lsp.h" -#include "working_files.h" - -#include - -#include - -std::vector ClangFormatDocument( - WorkingFile* working_file, - int start, - int end, - lsFormattingOptions options); - -#endif diff --git a/src/clang_index.cc b/src/clang_index.cc deleted file mode 100644 index 9c74fdfac..000000000 --- a/src/clang_index.cc +++ /dev/null @@ -1,22 +0,0 @@ -#include "clang_index.h" - -#include - -ClangIndex::ClangIndex() : ClangIndex(1, 0) {} - -ClangIndex::ClangIndex(int exclude_declarations_from_pch, - int display_diagnostics) { - // llvm::InitializeAllTargets (and possibly others) called by - // clang_createIndex transtively modifies/reads lib/Support/TargetRegistry.cpp - // FirstTarget. There will be a race condition if two threads call - // clang_createIndex concurrently. - static std::mutex mutex_; - std::lock_guard lock(mutex_); - - cx_index = - clang_createIndex(exclude_declarations_from_pch, display_diagnostics); -} - -ClangIndex::~ClangIndex() { - clang_disposeIndex(cx_index); -} diff --git a/src/clang_index.h b/src/clang_index.h deleted file mode 100644 index 1350d9000..000000000 --- a/src/clang_index.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include - -// Simple RAII wrapper about CXIndex. -// Note: building a ClangIndex instance acquires a global lock, since libclang -// API does not appear to be thread-safe here. -class ClangIndex { - public: - ClangIndex(); - ClangIndex(int exclude_declarations_from_pch, int display_diagnostics); - ~ClangIndex(); - CXIndex cx_index; -}; diff --git a/src/clang_indexer.cc b/src/clang_indexer.cc deleted file mode 100644 index df82f2426..000000000 --- a/src/clang_indexer.cc +++ /dev/null @@ -1,2413 +0,0 @@ -#include "indexer.h" - -#include "clang_cursor.h" -#include "clang_utils.h" -#include "platform.h" -#include "serializer.h" -#include "timer.h" -#include "type_printer.h" - -#include - -#include -#include -#include -#include -#include - -// TODO: See if we can use clang_indexLoc_getFileLocation to get a type ref on -// |Foobar| in DISALLOW_COPY(Foobar) - -#if CINDEX_VERSION >= 47 -#define CINDEX_HAVE_PRETTY 1 -#endif -#if CINDEX_VERSION >= 48 -#define CINDEX_HAVE_ROLE 1 -#endif - -namespace { - -// For typedef/using spanning less than or equal to (this number) of lines, -// display their declarations on hover. -constexpr int kMaxLinesDisplayTypeAliasDeclarations = 3; - -// TODO How to check if a reference to type is a declaration? -// This currently also includes constructors/destructors. -// It seems declarations in functions are not indexed. -bool IsDeclContext(CXIdxEntityKind kind) { - switch (kind) { - case CXIdxEntity_CXXClass: - case CXIdxEntity_CXXNamespace: - case CXIdxEntity_ObjCCategory: - case CXIdxEntity_ObjCClass: - case CXIdxEntity_ObjCProtocol: - case CXIdxEntity_Struct: - return true; - default: - return false; - } -} - -Role GetRole(const CXIdxEntityRefInfo* ref_info, Role role) { -#if CINDEX_HAVE_ROLE - return static_cast(static_cast(ref_info->role)); -#else - return role; -#endif -} - -SymbolKind GetSymbolKind(CXCursorKind kind) { - switch (kind) { - case CXCursor_TranslationUnit: - return SymbolKind::File; - - case CXCursor_FunctionDecl: - case CXCursor_CXXMethod: - case CXCursor_Constructor: - case CXCursor_Destructor: - case CXCursor_ConversionFunction: - case CXCursor_FunctionTemplate: - case CXCursor_OverloadedDeclRef: - case CXCursor_LambdaExpr: - case CXCursor_ObjCInstanceMethodDecl: - case CXCursor_ObjCClassMethodDecl: - return SymbolKind::Func; - - case CXCursor_StructDecl: - case CXCursor_UnionDecl: - case CXCursor_ClassDecl: - case CXCursor_EnumDecl: - case CXCursor_ObjCInterfaceDecl: - case CXCursor_ObjCCategoryDecl: - case CXCursor_ObjCImplementationDecl: - case CXCursor_Namespace: - return SymbolKind::Type; - - default: - return SymbolKind::Invalid; - } -} - -// Inverse of libclang/CXIndexDataConsumer.cpp getEntityKindFromSymbolKind -lsSymbolKind GetSymbolKind(CXIdxEntityKind kind) { - switch (kind) { - case CXIdxEntity_Unexposed: - return lsSymbolKind::Unknown; - case CXIdxEntity_Typedef: - return lsSymbolKind::TypeAlias; - case CXIdxEntity_Function: - return lsSymbolKind::Function; - case CXIdxEntity_Variable: - // Can also be Parameter - return lsSymbolKind::Variable; - case CXIdxEntity_Field: - return lsSymbolKind::Field; - case CXIdxEntity_EnumConstant: - return lsSymbolKind::EnumMember; - - case CXIdxEntity_ObjCClass: - return lsSymbolKind::Class; - case CXIdxEntity_ObjCProtocol: - return lsSymbolKind::Interface; - case CXIdxEntity_ObjCCategory: - return lsSymbolKind::Interface; - - case CXIdxEntity_ObjCInstanceMethod: - return lsSymbolKind::Method; - case CXIdxEntity_ObjCClassMethod: - return lsSymbolKind::StaticMethod; - case CXIdxEntity_ObjCProperty: - return lsSymbolKind::Property; - case CXIdxEntity_ObjCIvar: - return lsSymbolKind::Field; - - case CXIdxEntity_Enum: - return lsSymbolKind::Enum; - case CXIdxEntity_Struct: - case CXIdxEntity_Union: - return lsSymbolKind::Struct; - - case CXIdxEntity_CXXClass: - return lsSymbolKind::Class; - case CXIdxEntity_CXXNamespace: - return lsSymbolKind::Namespace; - case CXIdxEntity_CXXNamespaceAlias: - return lsSymbolKind::Namespace; - case CXIdxEntity_CXXStaticVariable: - return lsSymbolKind::Field; - case CXIdxEntity_CXXStaticMethod: - return lsSymbolKind::StaticMethod; - case CXIdxEntity_CXXInstanceMethod: - return lsSymbolKind::Method; - case CXIdxEntity_CXXConstructor: - return lsSymbolKind::Constructor; - case CXIdxEntity_CXXDestructor: - return lsSymbolKind::Method; - case CXIdxEntity_CXXConversionFunction: - return lsSymbolKind::Constructor; - case CXIdxEntity_CXXTypeAlias: - return lsSymbolKind::TypeAlias; - case CXIdxEntity_CXXInterface: - return lsSymbolKind::Struct; - } -} - -StorageClass GetStorageClass(CX_StorageClass storage) { - switch (storage) { - case CX_SC_Invalid: - case CX_SC_OpenCLWorkGroupLocal: - return StorageClass::Invalid; - case CX_SC_None: - return StorageClass::None; - case CX_SC_Extern: - return StorageClass::Extern; - case CX_SC_Static: - return StorageClass::Static; - case CX_SC_PrivateExtern: - return StorageClass::PrivateExtern; - case CX_SC_Auto: - return StorageClass::Auto; - case CX_SC_Register: - return StorageClass::Register; - } -} - -// Caches all instances of constructors, regardless if they are indexed or not. -// The constructor may have a make_unique call associated with it that we need -// to export. If we do not capture the parameter type description for the -// constructor we will not be able to attribute the constructor call correctly. -struct ConstructorCache { - struct Constructor { - Usr usr; - std::vector param_type_desc; - }; - std::unordered_map> constructors_; - - // This should be called whenever there is a constructor declaration. - void NotifyConstructor(ClangCursor ctor_cursor) { - auto build_type_desc = [](ClangCursor cursor) { - std::vector type_desc; - for (ClangCursor arg : cursor.get_arguments()) { - if (arg.get_kind() == CXCursor_ParmDecl) - type_desc.push_back(arg.get_type_description()); - } - return type_desc; - }; - - Constructor ctor{ctor_cursor.get_usr_hash(), build_type_desc(ctor_cursor)}; - - // Insert into |constructors_|. - auto type_usr_hash = ctor_cursor.get_semantic_parent().get_usr_hash(); - auto existing_ctors = constructors_.find(type_usr_hash); - if (existing_ctors != constructors_.end()) { - existing_ctors->second.push_back(ctor); - } else { - constructors_[type_usr_hash] = {ctor}; - } - } - - // Tries to lookup a constructor in |type_usr| that takes arguments most - // closely aligned to |param_type_desc|. - optional TryFindConstructorUsr( - Usr type_usr, - const std::vector& param_type_desc) { - auto count_matching_prefix_length = [](const char* a, const char* b) { - int matched = 0; - while (*a && *b) { - if (*a != *b) - break; - ++a; - ++b; - ++matched; - } - // Additional score if the strings were the same length, which makes - // "a"/"a" match higher than "a"/"a&" - if (*a == *b) - matched += 1; - return matched; - }; - - // Try to find constructors for the type. If there are no constructors - // available, return an empty result. - auto ctors_it = constructors_.find(type_usr); - if (ctors_it == constructors_.end()) - return nullopt; - const std::vector& ctors = ctors_it->second; - if (ctors.empty()) - return nullopt; - - Usr best_usr = ctors[0].usr; - int best_score = INT_MIN; - - // Scan constructors for the best possible match. - for (const Constructor& ctor : ctors) { - // If |param_type_desc| is empty and the constructor is as well, we don't - // need to bother searching, as this is the match. - if (param_type_desc.empty() && ctor.param_type_desc.empty()) { - best_usr = ctor.usr; - break; - } - - // Weight matching parameter length heavily, as it is more accurate than - // the fuzzy type matching approach. - int score = 0; - if (param_type_desc.size() == ctor.param_type_desc.size()) - score += param_type_desc.size() * 1000; - - // Do prefix-based match on parameter type description. This works well in - // practice because clang appends qualifiers to the end of the type, ie, - // |foo *&&| - for (size_t i = 0; - i < std::min(param_type_desc.size(), ctor.param_type_desc.size()); - ++i) { - score += count_matching_prefix_length(param_type_desc[i].c_str(), - ctor.param_type_desc[i].c_str()); - } - - if (score > best_score) { - best_usr = ctor.usr; - best_score = score; - } - } - - return best_usr; - } -}; - -struct IndexParam { - Config* config = nullptr; - - std::unordered_set seen_cx_files; - std::vector seen_files; - FileContentsMap file_contents; - std::unordered_map file_modification_times; - - // Only use this when strictly needed (ie, primary translation unit is - // needed). Most logic should get the IndexFile instance via - // |file_consumer|. - // - // This can be null if we're not generating an index for the primary - // translation unit. - IndexFile* primary_file = nullptr; - - ClangTranslationUnit* tu = nullptr; - - FileConsumer* file_consumer = nullptr; - NamespaceHelper ns; - ConstructorCache ctors; - - IndexParam(Config* config, - ClangTranslationUnit* tu, - FileConsumer* file_consumer) - : config(config), tu(tu), file_consumer(file_consumer) {} - -#if CINDEX_HAVE_PRETTY - CXPrintingPolicy print_policy = nullptr; - CXPrintingPolicy print_policy_more = nullptr; - ~IndexParam() { - clang_PrintingPolicy_dispose(print_policy); - clang_PrintingPolicy_dispose(print_policy_more); - } - - std::string PrettyPrintCursor(CXCursor cursor, bool initializer = true) { - if (!print_policy) { - print_policy = clang_getCursorPrintingPolicy(cursor); - clang_PrintingPolicy_setProperty(print_policy, - CXPrintingPolicy_TerseOutput, 1); - clang_PrintingPolicy_setProperty(print_policy, - CXPrintingPolicy_FullyQualifiedName, 1); - clang_PrintingPolicy_setProperty( - print_policy, CXPrintingPolicy_SuppressInitializers, 1); - print_policy_more = clang_getCursorPrintingPolicy(cursor); - clang_PrintingPolicy_setProperty(print_policy_more, - CXPrintingPolicy_FullyQualifiedName, 1); - clang_PrintingPolicy_setProperty(print_policy_more, - CXPrintingPolicy_TerseOutput, 1); - } - return ToString(clang_getCursorPrettyPrinted( - cursor, initializer ? print_policy_more : print_policy)); - } -#endif -}; - -IndexFile* ConsumeFile(IndexParam* param, CXFile file) { - bool is_first_ownership = false; - IndexFile* db = param->file_consumer->TryConsumeFile( - file, &is_first_ownership, ¶m->file_contents); - - // If this is the first time we have seen the file (ignoring if we are - // generating an index for it): - if (param->seen_cx_files.insert(file).second) { - std::string file_name = FileName(file); - // file_name may be empty when it contains .. and is outside of WorkingDir. - // https://reviews.llvm.org/D42893 - // https://github.com/cquery-project/cquery/issues/413 - if (!file_name.empty()) { - // Add to all files we have seen so we can generate proper dependency - // graph. - param->seen_files.push_back(file_name); - - // Set modification time. - optional modification_time = GetLastModificationTime(file_name); - LOG_IF_S(ERROR, !modification_time) - << "Failed fetching modification time for " << file_name; - if (modification_time) - param->file_modification_times[file_name] = *modification_time; - } - } - - if (is_first_ownership) { - // Report skipped source range list. - CXSourceRangeList* skipped = clang_getSkippedRanges(param->tu->cx_tu, file); - for (unsigned i = 0; i < skipped->count; ++i) { - Range range = ResolveCXSourceRange(skipped->ranges[i]); - // clang_getSkippedRanges reports start one token after the '#', move it - // back so it starts at the '#' - range.start.column -= 1; - db->skipped_by_preprocessor.push_back(range); - } - clang_disposeSourceRangeList(skipped); - } - - return db; -} - -// Returns true if the given entity kind can be called implicitly, ie, without -// actually being written in the source code. -bool CanBeCalledImplicitly(CXIdxEntityKind kind) { - switch (kind) { - case CXIdxEntity_CXXConstructor: - case CXIdxEntity_CXXConversionFunction: - case CXIdxEntity_CXXDestructor: - return true; - default: - return false; - } -} - -// Returns true if the cursor spelling contains the given string. This is -// useful to check for implicit function calls. -bool CursorSpellingContainsString(CXCursor cursor, - CXTranslationUnit cx_tu, - std::string_view needle) { - CXSourceRange range = clang_Cursor_getSpellingNameRange(cursor, 0, 0); - CXToken* tokens; - unsigned num_tokens; - clang_tokenize(cx_tu, range, &tokens, &num_tokens); - - bool result = false; - - for (unsigned i = 0; i < num_tokens; ++i) { - CXString name = clang_getTokenSpelling(cx_tu, tokens[i]); - if (needle == clang_getCString(name)) { - result = true; - break; - } - clang_disposeString(name); - } - - clang_disposeTokens(cx_tu, tokens, num_tokens); - return result; -} - -// Returns the document content for the given range. May not work perfectly -// when there are tabs instead of spaces. -std::string GetDocumentContentInRange(CXTranslationUnit cx_tu, - CXSourceRange range) { - std::string result; - - CXToken* tokens; - unsigned num_tokens; - clang_tokenize(cx_tu, range, &tokens, &num_tokens); - - optional previous_token_range; - - for (unsigned i = 0; i < num_tokens; ++i) { - // Add whitespace between the previous token and this one. - Range token_range = - ResolveCXSourceRange(clang_getTokenExtent(cx_tu, tokens[i])); - if (previous_token_range) { - // Insert newlines. - int16_t line_delta = - token_range.start.line - previous_token_range->end.line; - assert(line_delta >= 0); - if (line_delta > 0) { - result.append((size_t)line_delta, '\n'); - // Reset column so we insert starting padding. - previous_token_range->end.column = 0; - } - // Insert spaces. - int16_t column_delta = - token_range.start.column - previous_token_range->end.column; - assert(column_delta >= 0); - result.append((size_t)column_delta, ' '); - } - previous_token_range = token_range; - - // Add token content. - CXString spelling = clang_getTokenSpelling(cx_tu, tokens[i]); - result += clang_getCString(spelling); - clang_disposeString(spelling); - } - - clang_disposeTokens(cx_tu, tokens, num_tokens); - - return result; -} - -void SetUsePreflight(IndexFile* db, ClangCursor parent) { - switch (GetSymbolKind(parent.get_kind())) { - case SymbolKind::Func: - (void)db->ToFuncId(parent.cx_cursor); - break; - case SymbolKind::Type: - (void)db->ToTypeId(parent.cx_cursor); - break; - case SymbolKind::Var: - (void)db->ToVarId(parent.cx_cursor); - break; - default: - break; - } -} - -// |parent| should be resolved before using |SetUsePreflight| so that |def| will -// not be invalidated by |To{Func,Type,Var}Id|. -Use SetUse(IndexFile* db, Range range, ClangCursor parent, Role role) { - switch (GetSymbolKind(parent.get_kind())) { - case SymbolKind::Func: - return Use(range, db->ToFuncId(parent.cx_cursor), SymbolKind::Func, role, - {}); - case SymbolKind::Type: - return Use(range, db->ToTypeId(parent.cx_cursor), SymbolKind::Type, role, - {}); - case SymbolKind::Var: - return Use(range, db->ToVarId(parent.cx_cursor), SymbolKind::Var, role, - {}); - default: - return Use(range, Id(), SymbolKind::File, role, {}); - } -} - -const char* GetAnonName(CXCursorKind kind) { - switch (kind) { - case CXCursor_ClassDecl: - return "(anon class)"; - case CXCursor_EnumDecl: - return "(anon enum)"; - case CXCursor_StructDecl: - return "(anon struct)"; - case CXCursor_UnionDecl: - return "(anon union)"; - default: - return "(anon)"; - } -} - -void SetTypeName(IndexType* type, - const ClangCursor& cursor, - const CXIdxContainerInfo* container, - const char* name, - IndexParam* param) { - CXIdxContainerInfo parent; - // |name| can be null in an anonymous struct (see - // tests/types/anonymous_struct.cc). - if (!name) - name = GetAnonName(cursor.get_kind()); - if (!container) - parent.cursor = cursor.get_semantic_parent().cx_cursor; - // Investigate why clang_getCursorPrettyPrinted gives `struct A {}` `namespace - // ns {}` which are not qualified. - // type->def.detailed_name = param->PrettyPrintCursor(cursor.cx_cursor); - type->def.detailed_name = - param->ns.QualifiedName(container ? container : &parent, name); - auto idx = type->def.detailed_name.rfind(name); - assert(idx != std::string::npos); - type->def.short_name_offset = idx; - type->def.short_name_size = strlen(name); -} - -// Finds the cursor associated with the declaration type of |cursor|. This -// strips -// qualifies from |cursor| (ie, Foo* => Foo) and removes template arguments -// (ie, Foo => Foo<*,*>). -optional ResolveToDeclarationType(IndexFile* db, - ClangCursor cursor, - IndexParam* param) { - ClangType type = cursor.get_type(); - - // auto x = new Foo() will not be deduced to |Foo| if we do not use the - // canonical type. However, a canonical type will look past typedefs so we - // will not accurately report variables on typedefs if we always do this. - if (type.cx_type.kind == CXType_Auto) - type = type.get_canonical(); - - type = type.strip_qualifiers(); - - if (type.is_builtin()) { - // For builtin types, use type kinds as USR hash. - return db->ToTypeId(type.cx_type.kind); - } - - ClangCursor declaration = - type.get_declaration().template_specialization_to_template_definition(); - CXString cx_usr = clang_getCursorUSR(declaration.cx_cursor); - const char* str_usr = clang_getCString(cx_usr); - if (!str_usr || str_usr[0] == '\0') { - clang_disposeString(cx_usr); - return nullopt; - } - Usr usr = HashUsr(str_usr); - clang_disposeString(cx_usr); - IndexTypeId type_id = db->ToTypeId(usr); - IndexType* typ = db->Resolve(type_id); - if (typ->def.detailed_name.empty()) { - std::string name = declaration.get_spell_name(); - SetTypeName(typ, declaration, nullptr, name.c_str(), param); - } - return type_id; -} - -void SetVarDetail(IndexVar* var, - std::string_view short_name, - const ClangCursor& cursor, - const CXIdxContainerInfo* semanticContainer, - bool is_first_seen, - IndexFile* db, - IndexParam* param) { - IndexVar::Def& def = var->def; - const CXType cx_type = clang_getCursorType(cursor.cx_cursor); - std::string type_name = ToString(clang_getTypeSpelling(cx_type)); - // clang may report "(lambda at foo.cc)" which end up being a very long - // string. Shorten it to just "lambda". - if (type_name.find("(lambda at") != std::string::npos) - type_name = "lambda"; - if (param->config->index.comments) - def.comments = cursor.get_comments(); - def.storage = GetStorageClass(clang_Cursor_getStorageClass(cursor.cx_cursor)); - - // TODO how to make PrettyPrint'ed variable name qualified? - std::string qualified_name = -#if 0 && CINDEX_HAVE_PRETTY - cursor.get_kind() != CXCursor_EnumConstantDecl - ? param->PrettyPrintCursor(cursor.cx_cursor) - : -#endif - param->ns.QualifiedName(semanticContainer, short_name); - - if (cursor.get_kind() == CXCursor_EnumConstantDecl && semanticContainer) { - CXType enum_type = clang_getCanonicalType( - clang_getEnumDeclIntegerType(semanticContainer->cursor)); - std::string hover = qualified_name + " = "; - if (enum_type.kind == CXType_UInt || enum_type.kind == CXType_ULong || - enum_type.kind == CXType_ULongLong) - hover += std::to_string( - clang_getEnumConstantDeclUnsignedValue(cursor.cx_cursor)); - else - hover += std::to_string(clang_getEnumConstantDeclValue(cursor.cx_cursor)); - def.detailed_name = std::move(qualified_name); - def.hover = hover; - } else { -#if 0 && CINDEX_HAVE_PRETTY - //def.detailed_name = param->PrettyPrintCursor(cursor.cx_cursor, false); -#else - ConcatTypeAndName(type_name, qualified_name); - def.detailed_name = type_name; - // Append the textual initializer, bit field, constructor to |hover|. - // Omit |hover| for these types: - // int (*a)(); int (&a)(); int (&&a)(); int a[1]; auto x = ... - // We can take these into consideration after we have better support for - // inside-out syntax. - CXType deref = cx_type; - while (deref.kind == CXType_Pointer || deref.kind == CXType_MemberPointer || - deref.kind == CXType_LValueReference || - deref.kind == CXType_RValueReference) - deref = clang_getPointeeType(deref); - if (deref.kind != CXType_Unexposed && deref.kind != CXType_Auto && - clang_getResultType(deref).kind == CXType_Invalid && - clang_getElementType(deref).kind == CXType_Invalid) { - const FileContents& fc = param->file_contents[db->path]; - optional spell_end = fc.ToOffset(cursor.get_spell().end); - optional extent_end = fc.ToOffset(cursor.get_extent().end); - if (extent_end && *spell_end < *extent_end) - def.hover = std::string(def.detailed_name.c_str()) + - fc.content.substr(*spell_end, *extent_end - *spell_end); - } -#endif - } - // FIXME QualifiedName should return index - auto idx = def.detailed_name.rfind(short_name.begin(), std::string::npos, - short_name.size()); - assert(idx != std::string::npos); - def.short_name_offset = idx; - def.short_name_size = short_name.size(); - - if (is_first_seen) { - optional var_type = - ResolveToDeclarationType(db, cursor, param); - if (var_type) { - // Don't treat enum definition variables as instantiations. - bool is_enum_member = semanticContainer && - semanticContainer->cursor.kind == CXCursor_EnumDecl; - if (!is_enum_member) - db->Resolve(var_type.value())->instances.push_back(var->id); - - def.type = *var_type; - } - } -} - -void OnIndexReference_Function(IndexFile* db, - Range loc, - ClangCursor parent_cursor, - IndexFuncId called_id, - Role role) { - switch (GetSymbolKind(parent_cursor.get_kind())) { - case SymbolKind::Func: { - IndexFunc* parent = db->Resolve(db->ToFuncId(parent_cursor.cx_cursor)); - IndexFunc* called = db->Resolve(called_id); - parent->def.callees.push_back( - SymbolRef(loc, called->id, SymbolKind::Func, role)); - called->uses.push_back(Use(loc, parent->id, SymbolKind::Func, role, {})); - break; - } - case SymbolKind::Type: { - IndexType* parent = db->Resolve(db->ToTypeId(parent_cursor.cx_cursor)); - IndexFunc* called = db->Resolve(called_id); - called = db->Resolve(called_id); - called->uses.push_back(Use(loc, parent->id, SymbolKind::Type, role, {})); - break; - } - default: { - IndexFunc* called = db->Resolve(called_id); - called->uses.push_back(Use(loc, Id(), SymbolKind::File, role, {})); - break; - } - } -} - -} // namespace - -// static -const int IndexFile::kMajorVersion = 15; -const int IndexFile::kMinorVersion = 0; - -IndexFile::IndexFile(const std::string& path, const std::string& contents) - : id_cache(path), path(path), file_contents(contents) {} - -IndexTypeId IndexFile::ToTypeId(Usr usr) { - auto it = id_cache.usr_to_type_id.find(usr); - if (it != id_cache.usr_to_type_id.end()) - return it->second; - - IndexTypeId id(types.size()); - types.push_back(IndexType(id, usr)); - id_cache.usr_to_type_id[usr] = id; - id_cache.type_id_to_usr[id] = usr; - return id; -} -IndexFuncId IndexFile::ToFuncId(Usr usr) { - auto it = id_cache.usr_to_func_id.find(usr); - if (it != id_cache.usr_to_func_id.end()) - return it->second; - - IndexFuncId id(funcs.size()); - funcs.push_back(IndexFunc(id, usr)); - id_cache.usr_to_func_id[usr] = id; - id_cache.func_id_to_usr[id] = usr; - return id; -} -IndexVarId IndexFile::ToVarId(Usr usr) { - auto it = id_cache.usr_to_var_id.find(usr); - if (it != id_cache.usr_to_var_id.end()) - return it->second; - - IndexVarId id(vars.size()); - vars.push_back(IndexVar(id, usr)); - id_cache.usr_to_var_id[usr] = id; - id_cache.var_id_to_usr[id] = usr; - return id; -} - -IndexTypeId IndexFile::ToTypeId(const CXCursor& cursor) { - return ToTypeId(ClangCursor(cursor).get_usr_hash()); -} - -IndexFuncId IndexFile::ToFuncId(const CXCursor& cursor) { - return ToFuncId(ClangCursor(cursor).get_usr_hash()); -} - -IndexVarId IndexFile::ToVarId(const CXCursor& cursor) { - return ToVarId(ClangCursor(cursor).get_usr_hash()); -} - -IndexType* IndexFile::Resolve(IndexTypeId id) { - return &types[id.id]; -} -IndexFunc* IndexFile::Resolve(IndexFuncId id) { - return &funcs[id.id]; -} -IndexVar* IndexFile::Resolve(IndexVarId id) { - return &vars[id.id]; -} - -std::string IndexFile::ToString() { - return Serialize(SerializeFormat::Json, *this); -} - -IndexType::IndexType(IndexTypeId id, Usr usr) : usr(usr), id(id) {} - -template -void Uniquify(std::vector>& ids) { - std::unordered_set> seen; - size_t n = 0; - for (size_t i = 0; i < ids.size(); i++) - if (seen.insert(ids[i]).second) - ids[n++] = ids[i]; - ids.resize(n); -} - -void Uniquify(std::vector& uses) { - std::unordered_set seen; - size_t n = 0; - for (size_t i = 0; i < uses.size(); i++) - if (seen.insert(uses[i].range).second) - uses[n++] = uses[i]; - uses.resize(n); -} - -// FIXME Reference: set id in call sites and remove this -//void AddUse(std::vector& values, Range value) { -// values.push_back( -// Use(value, Id(), SymbolKind::File, Role::Reference, {})); -//} - -void AddUse(IndexFile* db, - std::vector& uses, - Range range, - ClangCursor parent, - Role role = Role::Reference) { - switch (GetSymbolKind(parent.get_kind())) { - case SymbolKind::Func: - uses.push_back(Use(range, db->ToFuncId(parent.cx_cursor), - SymbolKind::Func, role, {})); - break; - case SymbolKind::Type: - uses.push_back(Use(range, db->ToTypeId(parent.cx_cursor), - SymbolKind::Type, role, {})); - break; - default: - uses.push_back(Use(range, Id(), SymbolKind::File, role, {})); - break; - } -} - -CXCursor fromContainer(const CXIdxContainerInfo* parent) { - return parent ? parent->cursor : clang_getNullCursor(); -} - -void AddUseSpell(IndexFile* db, std::vector& uses, ClangCursor cursor) { - AddUse(db, uses, cursor.get_spell(), cursor.get_lexical_parent().cx_cursor); -} - -IdCache::IdCache(const std::string& primary_file) - : primary_file(primary_file) {} - -void OnIndexDiagnostic(CXClientData client_data, - CXDiagnosticSet diagnostics, - void* reserved) { - IndexParam* param = static_cast(client_data); - - for (unsigned i = 0; i < clang_getNumDiagnosticsInSet(diagnostics); ++i) { - CXDiagnostic diagnostic = clang_getDiagnosticInSet(diagnostics, i); - - CXSourceLocation diag_loc = clang_getDiagnosticLocation(diagnostic); - // Skip diagnostics in system headers. - // if (clang_Location_isInSystemHeader(diag_loc)) - // continue; - - // Get db so we can attribute diagnostic to the right indexed file. - CXFile file; - unsigned int line, column; - clang_getSpellingLocation(diag_loc, &file, &line, &column, nullptr); - // Skip empty diagnostic. - if (!line && !column) - continue; - IndexFile* db = ConsumeFile(param, file); - if (!db) - continue; - - // Build diagnostic. - optional ls_diagnostic = - BuildAndDisposeDiagnostic(diagnostic, db->path); - if (ls_diagnostic) - db->diagnostics_.push_back(*ls_diagnostic); - } -} - -CXIdxClientFile OnIndexIncludedFile(CXClientData client_data, - const CXIdxIncludedFileInfo* file) { - IndexParam* param = static_cast(client_data); - - // file->hashLoc only has the position of the hash. We don't have the full - // range for the include. - CXSourceLocation hash_loc = clang_indexLoc_getCXSourceLocation(file->hashLoc); - CXFile cx_file; - unsigned int line; - clang_getSpellingLocation(hash_loc, &cx_file, &line, nullptr, nullptr); - line--; - - IndexFile* db = ConsumeFile(param, cx_file); - if (!db) - return nullptr; - - IndexInclude include; - include.line = line; - include.resolved_path = FileName(file->file); - if (!include.resolved_path.empty()) - db->includes.push_back(include); - - return nullptr; -} - -ClangCursor::VisitResult DumpVisitor(ClangCursor cursor, - ClangCursor parent, - int* level) { - for (int i = 0; i < *level; ++i) - std::cerr << " "; - std::cerr << ToString(cursor.get_kind()) << " " << cursor.get_spell_name() - << std::endl; - - *level += 1; - cursor.VisitChildren(&DumpVisitor, level); - *level -= 1; - - return ClangCursor::VisitResult::Continue; -} - -void Dump(ClangCursor cursor) { - int level = 0; - cursor.VisitChildren(&DumpVisitor, &level); -} - -struct FindChildOfKindParam { - CXCursorKind target_kind; - optional result; - - FindChildOfKindParam(CXCursorKind target_kind) : target_kind(target_kind) {} -}; - -ClangCursor::VisitResult FindTypeVisitor(ClangCursor cursor, - ClangCursor parent, - optional* result) { - switch (cursor.get_kind()) { - case CXCursor_TypeRef: - case CXCursor_TemplateRef: - *result = cursor; - return ClangCursor::VisitResult::Break; - default: - break; - } - - return ClangCursor::VisitResult::Recurse; -} - -optional FindType(ClangCursor cursor) { - optional result; - cursor.VisitChildren(&FindTypeVisitor, &result); - return result; -} - -bool IsTypeDefinition(const CXIdxContainerInfo* container) { - if (!container) - return false; - return GetSymbolKind(container->cursor.kind) == SymbolKind::Type; -} - -struct VisitDeclForTypeUsageParam { - IndexFile* db; - optional toplevel_type; - int has_processed_any = false; - optional previous_cursor; - optional initial_type; - - VisitDeclForTypeUsageParam(IndexFile* db, optional toplevel_type) - : db(db), toplevel_type(toplevel_type) {} -}; - -void VisitDeclForTypeUsageVisitorHandler(ClangCursor cursor, - VisitDeclForTypeUsageParam* param) { - param->has_processed_any = true; - IndexFile* db = param->db; - - // For |A a| where there is a specialization for |A|, - // the |referenced_usr| below resolves to the primary template and - // attributes the use to the primary template instead of the specialization. - // |toplevel_type| is retrieved |clang_getCursorType| which can be a - // specialization. If its name is the same as the primary template's, we - // assume the use should be attributed to the specialization. This heuristic - // fails when a member class bears the same name with its container. - // - // template - // struct C { struct C {}; }; - // C::C a; - // - // We will attribute |::C| to the parent class. - if (param->toplevel_type) { - IndexType* ref_type = db->Resolve(*param->toplevel_type); - std::string name = cursor.get_referenced().get_spell_name(); - if (name == ref_type->def.ShortName()) { - AddUseSpell(db, ref_type->uses, cursor); - param->toplevel_type = nullopt; - return; - } - } - - std::string referenced_usr = - cursor.get_referenced() - .template_specialization_to_template_definition() - .get_usr(); - // TODO: things in STL cause this to be empty. Figure out why and document it. - if (referenced_usr == "") - return; - - IndexTypeId ref_type_id = db->ToTypeId(HashUsr(referenced_usr)); - - if (!param->initial_type) - param->initial_type = ref_type_id; - - IndexType* ref_type_def = db->Resolve(ref_type_id); - // TODO: Should we even be visiting this if the file is not from the main - // def? Try adding assert on |loc| later. - AddUseSpell(db, ref_type_def->uses, cursor); -} - -ClangCursor::VisitResult VisitDeclForTypeUsageVisitor( - ClangCursor cursor, - ClangCursor parent, - VisitDeclForTypeUsageParam* param) { - switch (cursor.get_kind()) { - case CXCursor_TemplateRef: - case CXCursor_TypeRef: - if (param->previous_cursor) { - VisitDeclForTypeUsageVisitorHandler(param->previous_cursor.value(), - param); - } - - param->previous_cursor = cursor; - return ClangCursor::VisitResult::Continue; - - // We do not want to recurse for everything, since if we do that we will end - // up visiting method definition bodies/etc. Instead, we only recurse for - // things that can logically appear as part of an inline variable - // initializer, - // ie, - // - // class Foo { - // int x = (Foo)3; - // } - case CXCursor_CallExpr: - case CXCursor_CStyleCastExpr: - case CXCursor_CXXStaticCastExpr: - case CXCursor_CXXReinterpretCastExpr: - return ClangCursor::VisitResult::Recurse; - - default: - return ClangCursor::VisitResult::Continue; - } - - return ClangCursor::VisitResult::Continue; -} - -// Add usages to any seen TypeRef or TemplateRef under the given |decl_cursor|. -// This returns the first seen TypeRef or TemplateRef value, which can be -// useful if trying to figure out ie, what a using statement refers to. If -// trying to generally resolve a cursor to a type, use -// ResolveToDeclarationType, which works in more scenarios. -// If |decl_cursor| is a variable of a template type, clang_getCursorType -// may return a specialized template which is preciser than the primary -// template. -// We use |toplevel_type| to attribute the use to the specialized template -// instead of the primary template. -optional AddDeclTypeUsages( - IndexFile* db, - ClangCursor decl_cursor, - optional toplevel_type, - const CXIdxContainerInfo* semantic_container, - const CXIdxContainerInfo* lexical_container) { - // - // The general AST format for definitions follows this pattern: - // - // template - // struct Container; - // - // struct S1; - // struct S2; - // - // Container, S2> foo; - // - // => - // - // VarDecl - // TemplateRef Container - // TemplateRef Container - // TypeRef struct S1 - // TypeRef struct S2 - // TypeRef struct S2 - // - // - // Here is another example: - // - // enum A {}; - // enum B {}; - // - // template - // struct Foo { - // struct Inner {}; - // }; - // - // Foo::Inner a; - // Foo b; - // - // => - // - // EnumDecl A - // EnumDecl B - // ClassTemplate Foo - // TemplateTypeParameter T - // StructDecl Inner - // VarDecl a - // TemplateRef Foo - // TypeRef enum A - // TypeRef struct Foo::Inner - // CallExpr Inner - // VarDecl b - // TemplateRef Foo - // TypeRef enum B - // CallExpr Foo - // - // - // Determining the actual type of the variable/declaration from just the - // children is tricky. Doing so would require looking up the template - // definition associated with a TemplateRef, figuring out how many children - // it has, and then skipping that many TypeRef values. This also has to work - // with the example below (skipping the last TypeRef). As a result, we - // determine variable types using |ResolveToDeclarationType|. - // - // - // We skip the last type reference for methods/variables which are defined - // out-of-line w.r.t. the parent type. - // - // S1* Foo::foo() {} - // - // The above example looks like this in the AST: - // - // CXXMethod foo - // TypeRef struct S1 - // TypeRef class Foo - // CompoundStmt - // ... - // - // The second TypeRef is an uninteresting usage. - bool process_last_type_ref = true; - if (IsTypeDefinition(semantic_container) && - !IsTypeDefinition(lexical_container)) { - // - // In some code, such as the following example, we receive a cursor which is - // not - // a definition and is not associated with a definition due to an error - // condition. - // In this case, it is the Foo::Foo constructor. - // - // struct Foo {}; - // - // template - // Foo::Foo() {} - // - if (!decl_cursor.is_definition()) { - ClangCursor def = decl_cursor.get_definition(); - if (def.get_kind() != CXCursor_FirstInvalid) - decl_cursor = def; - } - process_last_type_ref = false; - } - - VisitDeclForTypeUsageParam param(db, toplevel_type); - decl_cursor.VisitChildren(&VisitDeclForTypeUsageVisitor, ¶m); - - // VisitDeclForTypeUsageVisitor guarantees that if there are multiple TypeRef - // children, the first one will always be visited. - if (param.previous_cursor && process_last_type_ref) { - VisitDeclForTypeUsageVisitorHandler(param.previous_cursor.value(), ¶m); - } else { - // If we are not processing the last type ref, it *must* be a TypeRef or - // TemplateRef. - // - // We will not visit every child if the is_interseting is false, so - // previous_cursor - // may not point to the last TemplateRef. - assert(param.previous_cursor.has_value() == false || - (param.previous_cursor.value().get_kind() == CXCursor_TypeRef || - param.previous_cursor.value().get_kind() == CXCursor_TemplateRef)); - } - - if (param.initial_type) - return param.initial_type; - CXType cx_under = clang_getTypedefDeclUnderlyingType(decl_cursor.cx_cursor); - if (cx_under.kind == CXType_Invalid) - return nullopt; - return db->ToTypeId(ClangType(cx_under).strip_qualifiers().get_usr_hash()); -} - -// Various versions of LLVM (ie, 4.0) will not visit inline variable references -// for template arguments. -ClangCursor::VisitResult AddDeclInitializerUsagesVisitor(ClangCursor cursor, - ClangCursor parent, - IndexFile* db) { - /* - We need to index the |DeclRefExpr| below (ie, |var| inside of - Foo::var). - - template - struct Foo { - static constexpr int var = 3; - }; - - int a = Foo::var; - - => - - VarDecl a - UnexposedExpr var - DeclRefExpr var - TemplateRef Foo - - */ - - switch (cursor.get_kind()) { - case CXCursor_DeclRefExpr: { - if (cursor.get_referenced().get_kind() != CXCursor_VarDecl) - break; - - // TODO: when we resolve the template type to the definition, we get a - // different Usr. - - // ClangCursor ref = - // cursor.get_referenced().template_specialization_to_template_definition().get_type().strip_qualifiers().get_usr_hash(); - // std::string ref_usr = - // cursor.get_referenced().template_specialization_to_template_definition().get_type().strip_qualifiers().get_usr_hash(); - auto ref_usr = cursor.get_referenced() - .template_specialization_to_template_definition() - .get_usr(); - // std::string ref_usr = ref.get_usr_hash(); - if (ref_usr == "") - break; - - IndexVar* ref_var = db->Resolve(db->ToVarId(HashUsr(ref_usr))); - AddUseSpell(db, ref_var->uses, cursor); - break; - } - - default: - break; - } - - return ClangCursor::VisitResult::Recurse; -} - -ClangCursor::VisitResult VisitMacroDefinitionAndExpansions(ClangCursor cursor, - ClangCursor parent, - IndexParam* param) { - switch (cursor.get_kind()) { - case CXCursor_MacroDefinition: - case CXCursor_MacroExpansion: { - // Resolve location, find IndexFile instance. - CXSourceRange cx_source_range = - clang_Cursor_getSpellingNameRange(cursor.cx_cursor, 0, 0); - CXFile file; - Range decl_loc_spelling = ResolveCXSourceRange(cx_source_range, &file); - IndexFile* db = ConsumeFile(param, file); - if (!db) - break; - - // TODO: Considering checking clang_Cursor_isMacroFunctionLike, but the - // only real difference will be that we show 'callers' instead of 'refs' - // (especially since macros cannot have overrides) - - Usr decl_usr; - if (cursor.get_kind() == CXCursor_MacroDefinition) - decl_usr = cursor.get_usr_hash(); - else - decl_usr = cursor.get_referenced().get_usr_hash(); - - SetUsePreflight(db, parent); - IndexVar* var_def = db->Resolve(db->ToVarId(decl_usr)); - if (cursor.get_kind() == CXCursor_MacroDefinition) { - CXSourceRange cx_extent = clang_getCursorExtent(cursor.cx_cursor); - var_def->def.detailed_name = cursor.get_display_name(); - var_def->def.short_name_offset = 0; - var_def->def.short_name_size = - int16_t(strlen(var_def->def.detailed_name.c_str())); - var_def->def.hover = - "#define " + GetDocumentContentInRange(param->tu->cx_tu, cx_extent); - var_def->def.kind = lsSymbolKind::Macro; - if (param->config->index.comments) - var_def->def.comments = cursor.get_comments(); - var_def->def.spell = - SetUse(db, decl_loc_spelling, parent, Role::Definition); - var_def->def.extent = SetUse( - db, ResolveCXSourceRange(cx_extent, nullptr), parent, Role::None); - } else - AddUse(db, var_def->uses, decl_loc_spelling, parent); - - break; - } - default: - break; - } - - return ClangCursor::VisitResult::Continue; -} - -namespace { - -// TODO Move to another file and use clang C++ API -struct TemplateVisitorData { - IndexFile* db; - IndexParam* param; - ClangCursor container; -}; - -ClangCursor::VisitResult TemplateVisitor(ClangCursor cursor, - ClangCursor parent, - TemplateVisitorData* data) { - IndexFile* db = data->db; - IndexParam* param = data->param; - switch (cursor.get_kind()) { - default: - break; - case CXCursor_DeclRefExpr: { - ClangCursor ref_cursor = clang_getCursorReferenced(cursor.cx_cursor); - if (ref_cursor.get_kind() == CXCursor_NonTypeTemplateParameter) { - IndexVarId ref_var_id = db->ToVarId(ref_cursor.get_usr_hash()); - IndexVar* ref_var = db->Resolve(ref_var_id); - if (ref_var->def.detailed_name.empty()) { - ClangCursor sem_parent = ref_cursor.get_semantic_parent(); - ClangCursor lex_parent = ref_cursor.get_lexical_parent(); - SetUsePreflight(db, sem_parent); - SetUsePreflight(db, lex_parent); - ref_var = db->Resolve(ref_var_id); - ref_var->def.spell = - SetUse(db, ref_cursor.get_spell(), sem_parent, Role::Definition); - ref_var->def.extent = - SetUse(db, ref_cursor.get_extent(), lex_parent, Role::None); - ref_var = db->Resolve(ref_var_id); - ref_var->def.kind = lsSymbolKind::TypeParameter; - SetVarDetail(ref_var, ref_cursor.get_spell_name(), ref_cursor, - nullptr, true, db, param); - - ClangType ref_type = clang_getCursorType(ref_cursor.cx_cursor); - // TODO optimize - if (ref_type.get_usr().size()) { - IndexType* ref_type_index = - db->Resolve(db->ToTypeId(ref_type.get_usr_hash())); - // The cursor extent includes `type name`, not just `name`. There - // seems no way to extract the spelling range of `type` and we do - // not want to do subtraction here. - // See https://github.com/jacobdufault/cquery/issues/252 - AddUse(db, ref_type_index->uses, ref_cursor.get_extent(), - ref_cursor.get_lexical_parent()); - } - } - AddUseSpell(db, ref_var->uses, cursor); - } - break; - } - case CXCursor_OverloadedDeclRef: { - unsigned num_overloaded = clang_getNumOverloadedDecls(cursor.cx_cursor); - for (unsigned i = 0; i != num_overloaded; i++) { - ClangCursor overloaded = clang_getOverloadedDecl(cursor.cx_cursor, i); - switch (overloaded.get_kind()) { - default: - break; - case CXCursor_FunctionDecl: - case CXCursor_FunctionTemplate: { - IndexFuncId called_id = db->ToFuncId(overloaded.get_usr_hash()); - OnIndexReference_Function(db, cursor.get_spell(), data->container, - called_id, Role::Call); - break; - } - } - } - break; - } - case CXCursor_TemplateRef: { - ClangCursor ref_cursor = clang_getCursorReferenced(cursor.cx_cursor); - if (ref_cursor.get_kind() == CXCursor_TemplateTemplateParameter) { - IndexTypeId ref_type_id = db->ToTypeId(ref_cursor.get_usr_hash()); - IndexType* ref_type = db->Resolve(ref_type_id); - // TODO It seems difficult to get references to template template - // parameters. - // CXCursor_TemplateTemplateParameter can be visited by visiting - // CXCursor_TranslationUnit, but not (confirm this) by visiting - // {Class,Function}Template. Thus we need to initialize it here. - if (ref_type->def.detailed_name.empty()) { - ClangCursor sem_parent = ref_cursor.get_semantic_parent(); - ClangCursor lex_parent = ref_cursor.get_lexical_parent(); - SetUsePreflight(db, sem_parent); - SetUsePreflight(db, lex_parent); - ref_type = db->Resolve(ref_type_id); - ref_type->def.spell = - SetUse(db, ref_cursor.get_spell(), sem_parent, Role::Definition); - ref_type->def.extent = - SetUse(db, ref_cursor.get_extent(), lex_parent, Role::None); -#if 0 && CINDEX_HAVE_PRETTY - ref_type->def.detailed_name = param->PrettyPrintCursor(ref_cursor.cx_cursor); -#else - ref_type->def.detailed_name = ref_cursor.get_spell_name(); -#endif - ref_type->def.short_name_offset = 0; - ref_type->def.short_name_size = - int16_t(strlen(ref_type->def.detailed_name.c_str())); - ref_type->def.kind = lsSymbolKind::TypeParameter; - } - AddUseSpell(db, ref_type->uses, cursor); - } - break; - } - case CXCursor_TypeRef: { - ClangCursor ref_cursor = clang_getCursorReferenced(cursor.cx_cursor); - if (ref_cursor.get_kind() == CXCursor_TemplateTypeParameter) { - IndexTypeId ref_type_id = db->ToTypeId(ref_cursor.get_usr_hash()); - IndexType* ref_type = db->Resolve(ref_type_id); - // TODO It seems difficult to get a FunctionTemplate's template - // parameters. - // CXCursor_TemplateTypeParameter can be visited by visiting - // CXCursor_TranslationUnit, but not (confirm this) by visiting - // {Class,Function}Template. Thus we need to initialize it here. - if (ref_type->def.detailed_name.empty()) { - ClangCursor sem_parent = ref_cursor.get_semantic_parent(); - ClangCursor lex_parent = ref_cursor.get_lexical_parent(); - SetUsePreflight(db, sem_parent); - SetUsePreflight(db, lex_parent); - ref_type = db->Resolve(ref_type_id); - ref_type->def.spell = - SetUse(db, ref_cursor.get_spell(), sem_parent, Role::Definition); - ref_type->def.extent = - SetUse(db, ref_cursor.get_extent(), lex_parent, Role::None); -#if 0 && CINDEX_HAVE_PRETTY - // template void f(T t){} // weird, the name is empty - ref_type->def.detailed_name = param->PrettyPrintCursor(ref_cursor.cx_cursor); -#else - ref_type->def.detailed_name = ref_cursor.get_spell_name(); -#endif - ref_type->def.short_name_offset = 0; - ref_type->def.short_name_size = - int16_t(strlen(ref_type->def.detailed_name.c_str())); - ref_type->def.kind = lsSymbolKind::TypeParameter; - } - AddUseSpell(db, ref_type->uses, cursor); - } - break; - } - } - return ClangCursor::VisitResult::Recurse; -} - -} // namespace - -std::string NamespaceHelper::QualifiedName(const CXIdxContainerInfo* container, - std::string_view unqualified_name) { - if (!container) - return std::string(unqualified_name); - // Anonymous namespaces are not processed by indexDeclaration. We trace - // nested namespaces bottom-up through clang_getCursorSemanticParent until - // one that we know its qualified name. Then do another trace top-down and - // put their names into a map of USR -> qualified_name. - ClangCursor cursor(container->cursor); - std::vector namespaces; - std::string qualifier; - while (cursor.get_kind() != CXCursor_TranslationUnit && - GetSymbolKind(cursor.get_kind()) == SymbolKind::Type) { - auto it = container_cursor_to_qualified_name.find(cursor); - if (it != container_cursor_to_qualified_name.end()) { - qualifier = it->second; - break; - } - namespaces.push_back(cursor); - cursor = clang_getCursorSemanticParent(cursor.cx_cursor); - } - for (size_t i = namespaces.size(); i > 0;) { - i--; - std::string name = namespaces[i].get_spell_name(); - // Empty name indicates unnamed namespace, anonymous struct, anonymous - // union, ... - if (name.size()) - qualifier += name; - else - qualifier += GetAnonName(namespaces[i].get_kind()); - qualifier += "::"; - container_cursor_to_qualified_name[namespaces[i]] = qualifier; - } - // C++17 string::append - return qualifier + std::string(unqualified_name); -} - -void OnIndexDeclaration(CXClientData client_data, const CXIdxDeclInfo* decl) { - IndexParam* param = static_cast(client_data); - - // Track all constructor declarations, as we may need to use it to manually - // associate std::make_unique and the like as constructor invocations. - if (decl->entityInfo->kind == CXIdxEntity_CXXConstructor) { - param->ctors.NotifyConstructor(decl->cursor); - } - - CXFile file; - clang_getSpellingLocation(clang_indexLoc_getCXSourceLocation(decl->loc), - &file, nullptr, nullptr, nullptr); - IndexFile* db = ConsumeFile(param, file); - if (!db) - return; - - // The language of this declaration - LanguageId decl_lang = [&decl]() { - switch (clang_getCursorLanguage(decl->cursor)) { - case CXLanguage_C: - return LanguageId::C; - case CXLanguage_CPlusPlus: - return LanguageId::Cpp; - case CXLanguage_ObjC: - return LanguageId::ObjC; - default: - return LanguageId::Unknown; - }; - }(); - - // Only update the file language if the new language is "greater" than the old - if (decl_lang > db->language) { - db->language = decl_lang; - } - - ClangCursor sem_parent(fromContainer(decl->semanticContainer)); - ClangCursor lex_parent(fromContainer(decl->lexicalContainer)); - SetUsePreflight(db, sem_parent); - SetUsePreflight(db, lex_parent); - ClangCursor cursor = decl->cursor; - - switch (decl->entityInfo->kind) { - case CXIdxEntity_Unexposed: - LOG_S(INFO) << "CXIdxEntity_Unexposed " << cursor.get_spell_name(); - break; - - case CXIdxEntity_CXXNamespace: { - Range spell = cursor.get_spell(); - IndexTypeId ns_id = db->ToTypeId(HashUsr(decl->entityInfo->USR)); - IndexType* ns = db->Resolve(ns_id); - ns->def.kind = GetSymbolKind(decl->entityInfo->kind); - if (ns->def.detailed_name.empty()) { - SetTypeName(ns, cursor, decl->semanticContainer, decl->entityInfo->name, - param); - ns->def.spell = SetUse(db, spell, sem_parent, Role::Definition); - ns->def.extent = - SetUse(db, cursor.get_extent(), lex_parent, Role::None); - if (decl->semanticContainer) { - IndexTypeId parent_id = db->ToTypeId( - ClangCursor(decl->semanticContainer->cursor).get_usr_hash()); - db->Resolve(parent_id)->derived.push_back(ns_id); - // |ns| may be invalidated. - ns = db->Resolve(ns_id); - ns->def.bases.push_back(parent_id); - } - } - AddUse(db, ns->uses, spell, lex_parent); - break; - } - - case CXIdxEntity_CXXNamespaceAlias: - assert(false && "CXXNamespaceAlias"); - break; - - case CXIdxEntity_ObjCProperty: - case CXIdxEntity_ObjCIvar: - case CXIdxEntity_EnumConstant: - case CXIdxEntity_Field: - case CXIdxEntity_Variable: - case CXIdxEntity_CXXStaticVariable: { - Range spell = cursor.get_spell(); - - // Do not index implicit template instantiations. - if (cursor != cursor.template_specialization_to_template_definition()) - break; - - IndexVarId var_id = db->ToVarId(HashUsr(decl->entityInfo->USR)); - IndexVar* var = db->Resolve(var_id); - - // TODO: Eventually run with this if. Right now I want to iron out bugs - // this may shadow. - // TODO: Verify this gets called multiple times - // if (!decl->isRedeclaration) { - SetVarDetail(var, std::string(decl->entityInfo->name), decl->cursor, - decl->semanticContainer, !decl->isRedeclaration, db, param); - - // FIXME https://github.com/jacobdufault/cquery/issues/239 - var->def.kind = GetSymbolKind(decl->entityInfo->kind); - if (var->def.kind == lsSymbolKind::Variable && - decl->cursor.kind == CXCursor_ParmDecl) - var->def.kind = lsSymbolKind::Parameter; - //} - - if (decl->isDefinition) { - var->def.spell = SetUse(db, spell, sem_parent, Role::Definition); - var->def.extent = - SetUse(db, cursor.get_extent(), lex_parent, Role::None); - } else { - var->declarations.push_back( - SetUse(db, spell, lex_parent, Role::Declaration)); - } - - cursor.VisitChildren(&AddDeclInitializerUsagesVisitor, db); - var = db->Resolve(var_id); - - // Declaring variable type information. Note that we do not insert an - // interesting reference for parameter declarations - that is handled when - // the function declaration is encountered since we won't receive ParmDecl - // declarations for unnamed parameters. - // TODO: See if we can remove this function call. - AddDeclTypeUsages(db, cursor, var->def.type, decl->semanticContainer, - decl->lexicalContainer); - - // We don't need to assign declaring type multiple times if this variable - // has already been seen. - - if (decl->isDefinition && decl->semanticContainer) { - switch (GetSymbolKind(decl->semanticContainer->cursor.kind)) { - case SymbolKind::Func: { - db->Resolve(db->ToFuncId(decl->semanticContainer->cursor)) - ->def.vars.push_back(var_id); - break; - } - case SymbolKind::Type: - if (decl->semanticContainer->cursor.kind != CXCursor_EnumDecl) { - db->Resolve(db->ToTypeId(decl->semanticContainer->cursor)) - ->def.vars.push_back(var_id); - } - break; - default: - break; - } - } - - break; - } - - case CXIdxEntity_ObjCInstanceMethod: - case CXIdxEntity_ObjCClassMethod: - case CXIdxEntity_Function: - case CXIdxEntity_CXXConstructor: - case CXIdxEntity_CXXDestructor: - case CXIdxEntity_CXXInstanceMethod: - case CXIdxEntity_CXXStaticMethod: - case CXIdxEntity_CXXConversionFunction: { - Range spell = cursor.get_spell(); - Range extent = cursor.get_extent(); - - ClangCursor decl_cursor_resolved = - cursor.template_specialization_to_template_definition(); - bool is_template_specialization = cursor != decl_cursor_resolved; - - IndexFuncId func_id = db->ToFuncId(decl_cursor_resolved.cx_cursor); - IndexFunc* func = db->Resolve(func_id); - if (param->config->index.comments) - func->def.comments = cursor.get_comments(); - func->def.kind = GetSymbolKind(decl->entityInfo->kind); - func->def.storage = - GetStorageClass(clang_Cursor_getStorageClass(decl->cursor)); - - // We don't actually need to know the return type, but we need to mark it - // as an interesting usage. - AddDeclTypeUsages(db, cursor, nullopt, decl->semanticContainer, - decl->lexicalContainer); - - // Add definition or declaration. This is a bit tricky because we treat - // template specializations as declarations, even though they are - // technically definitions. - // TODO: Support multiple function definitions, which is common for - // template specializations. - if (decl->isDefinition && !is_template_specialization) { - // assert(!func->def.spell); - // assert(!func->def.extent); - func->def.spell = SetUse(db, spell, sem_parent, Role::Definition); - func->def.extent = SetUse(db, extent, lex_parent, Role::None); - } else { - IndexFunc::Declaration declaration; - declaration.spell = SetUse(db, spell, lex_parent, Role::Declaration); - - // Add parameters. - for (ClangCursor arg : cursor.get_arguments()) { - switch (arg.get_kind()) { - case CXCursor_ParmDecl: { - Range param_spelling = arg.get_spell(); - - // If the name is empty (which is common for parameters), clang - // will report a range with length 1, which is not correct. - if (param_spelling.start.column == - (param_spelling.end.column - 1) && - arg.get_display_name().empty()) { - param_spelling.end.column -= 1; - } - - declaration.param_spellings.push_back(param_spelling); - break; - } - default: - break; - } - } - - func->declarations.push_back(declaration); - } - - // Emit definition data for the function. We do this even if it isn't a - // definition because there can be, for example, interfaces, or a class - // declaration that doesn't have a definition yet. If we never end up - // indexing the definition, then there will not be any (ie) outline - // information. - if (!is_template_specialization) { - // Build detailed name. The type desc looks like void (void *). We - // insert the qualified name before the first '('. - // FIXME GetFunctionSignature should set index -#if CINDEX_HAVE_PRETTY - func->def.detailed_name = param->PrettyPrintCursor(decl->cursor); -#else - func->def.detailed_name = GetFunctionSignature(db, ¶m->ns, decl); -#endif - auto idx = func->def.detailed_name.find(decl->entityInfo->name); - assert(idx != std::string::npos); - func->def.short_name_offset = idx; - func->def.short_name_size = strlen(decl->entityInfo->name); - - // CXCursor_OverloadedDeclRef in templates are not processed by - // OnIndexReference, thus we use TemplateVisitor to collect function - // references. - if (decl->entityInfo->templateKind == CXIdxEntity_Template) { - TemplateVisitorData data; - data.db = db; - data.param = param; - data.container = cursor; - cursor.VisitChildren(&TemplateVisitor, &data); - // TemplateVisitor calls ToFuncId which invalidates func - func = db->Resolve(func_id); - } - - // Add function usage information. We only want to do it once per - // definition/declaration. Do it on definition since there should only - // ever be one of those in the entire program. - if (IsTypeDefinition(decl->semanticContainer)) { - IndexTypeId declaring_type_id = - db->ToTypeId(decl->semanticContainer->cursor); - IndexType* declaring_type_def = db->Resolve(declaring_type_id); - func->def.declaring_type = declaring_type_id; - - // Mark a type reference at the ctor/dtor location. - if (decl->entityInfo->kind == CXIdxEntity_CXXConstructor) - AddUse(db, declaring_type_def->uses, spell, - fromContainer(decl->lexicalContainer)); - - // Add function to declaring type. - declaring_type_def->def.funcs.push_back(func_id); - } - - // Process inheritance. - if (clang_CXXMethod_isVirtual(decl->cursor)) { - CXCursor* overridden; - unsigned int num_overridden; - clang_getOverriddenCursors(decl->cursor, &overridden, - &num_overridden); - - for (unsigned i = 0; i < num_overridden; ++i) { - ClangCursor parent = - ClangCursor(overridden[i]) - .template_specialization_to_template_definition(); - IndexFuncId parent_id = db->ToFuncId(parent.get_usr_hash()); - IndexFunc* parent_def = db->Resolve(parent_id); - func = db->Resolve(func_id); // ToFuncId invalidated func_def - - func->def.bases.push_back(parent_id); - parent_def->derived.push_back(func_id); - } - - clang_disposeOverriddenCursors(overridden); - } - } - break; - } - - case CXIdxEntity_Typedef: - case CXIdxEntity_CXXTypeAlias: { - // Note we want to fetch the first TypeRef. Running - // ResolveCursorType(decl->cursor) would return - // the type of the typedef/using, not the type of the referenced type. - optional alias_of = AddDeclTypeUsages( - db, cursor, nullopt, decl->semanticContainer, decl->lexicalContainer); - - IndexTypeId type_id = db->ToTypeId(HashUsr(decl->entityInfo->USR)); - IndexType* type = db->Resolve(type_id); - - if (alias_of) - type->def.alias_of = alias_of.value(); - - ClangCursor decl_cursor = decl->cursor; - Range spell = decl_cursor.get_spell(); - Range extent = decl_cursor.get_extent(); - type->def.spell = SetUse(db, spell, sem_parent, Role::Definition); - type->def.extent = SetUse(db, extent, lex_parent, Role::None); - - SetTypeName(type, decl_cursor, decl->semanticContainer, - decl->entityInfo->name, param); - type->def.kind = GetSymbolKind(decl->entityInfo->kind); - if (param->config->index.comments) - type->def.comments = decl_cursor.get_comments(); - - // For Typedef/CXXTypeAlias spanning a few lines, display the declaration - // line, with spelling name replaced with qualified name. - // TODO Think how to display multi-line declaration like `typedef struct { - // ... } foo;` https://github.com/jacobdufault/cquery/issues/29 - if (extent.end.line - extent.start.line < - kMaxLinesDisplayTypeAliasDeclarations) { - FileContents& fc = param->file_contents[db->path]; - optional extent_start = fc.ToOffset(extent.start), - spell_start = fc.ToOffset(spell.start), - spell_end = fc.ToOffset(spell.end), - extent_end = fc.ToOffset(extent.end); - if (extent_start && spell_start && spell_end && extent_end) { - type->def.hover = - fc.content.substr(*extent_start, *spell_start - *extent_start) + - type->def.detailed_name.c_str() + - fc.content.substr(*spell_end, *extent_end - *spell_end); - } - } - - AddUse(db, type->uses, spell, fromContainer(decl->lexicalContainer)); - break; - } - - case CXIdxEntity_ObjCProtocol: - case CXIdxEntity_ObjCCategory: - case CXIdxEntity_ObjCClass: - case CXIdxEntity_Enum: - case CXIdxEntity_Union: - case CXIdxEntity_Struct: - case CXIdxEntity_CXXInterface: - case CXIdxEntity_CXXClass: { - Range spell = cursor.get_spell(); - - IndexTypeId type_id = db->ToTypeId(HashUsr(decl->entityInfo->USR)); - IndexType* type = db->Resolve(type_id); - - // TODO: Eventually run with this if. Right now I want to iron out bugs - // this may shadow. - // TODO: For type section, verify if this ever runs for non definitions? - // if (!decl->isRedeclaration) { - - SetTypeName(type, cursor, decl->semanticContainer, - decl->entityInfo->name, param); - type->def.kind = GetSymbolKind(decl->entityInfo->kind); - if (param->config->index.comments) - type->def.comments = cursor.get_comments(); - // } - - if (decl->isDefinition) { - type->def.spell = SetUse(db, spell, sem_parent, Role::Definition); - type->def.extent = - SetUse(db, cursor.get_extent(), lex_parent, Role::None); - - if (cursor.get_kind() == CXCursor_EnumDecl) { - ClangType enum_type = clang_getEnumDeclIntegerType(decl->cursor); - if (!enum_type.is_builtin()) { - IndexType* int_type = - db->Resolve(db->ToTypeId(enum_type.get_usr_hash())); - AddUse(db, int_type->uses, spell, - fromContainer(decl->lexicalContainer)); - // type is invalidated. - type = db->Resolve(type_id); - } - } - } else - AddUse(db, type->declarations, spell, - fromContainer(decl->lexicalContainer), Role::Declaration); - - switch (decl->entityInfo->templateKind) { - default: - break; - case CXIdxEntity_TemplateSpecialization: - case CXIdxEntity_TemplatePartialSpecialization: { - // TODO Use a different dimension - ClangCursor origin_cursor = - cursor.template_specialization_to_template_definition(); - IndexTypeId origin_id = db->ToTypeId(origin_cursor.get_usr_hash()); - IndexType* origin = db->Resolve(origin_id); - // |type| may be invalidated. - type = db->Resolve(type_id); - // template class function; // not visited by - // OnIndexDeclaration template<> class function {}; // current - // cursor - if (origin->def.detailed_name.empty()) { - SetTypeName(origin, origin_cursor, nullptr, - &type->def.ShortName()[0], param); - origin->def.kind = type->def.kind; - } - // TODO The name may be assigned in |ResolveToDeclarationType| but - // |spell| is nullopt. - CXFile origin_file; - Range origin_spell = origin_cursor.get_spell(&origin_file); - if (!origin->def.spell && file == origin_file) { - ClangCursor origin_sem = origin_cursor.get_semantic_parent(); - ClangCursor origin_lex = origin_cursor.get_lexical_parent(); - SetUsePreflight(db, origin_sem); - SetUsePreflight(db, origin_lex); - origin = db->Resolve(origin_id); - type = db->Resolve(type_id); - origin->def.spell = - SetUse(db, origin_spell, origin_sem, Role::Definition); - origin->def.extent = - SetUse(db, origin_cursor.get_extent(), origin_lex, Role::None); - } - origin->derived.push_back(type_id); - type->def.bases.push_back(origin_id); - } - // fallthrough - case CXIdxEntity_Template: { - TemplateVisitorData data; - data.db = db; - data.container = cursor; - data.param = param; - cursor.VisitChildren(&TemplateVisitor, &data); - break; - } - } - - // type_def->alias_of - // type_def->funcs - // type_def->types - // type_def->uses - // type_def->vars - - // Add type-level inheritance information. - CXIdxCXXClassDeclInfo const* class_info = - clang_index_getCXXClassDeclInfo(decl); - if (class_info) { - for (unsigned int i = 0; i < class_info->numBases; ++i) { - const CXIdxBaseClassInfo* base_class = class_info->bases[i]; - - AddDeclTypeUsages(db, base_class->cursor, nullopt, - decl->semanticContainer, decl->lexicalContainer); - optional parent_type_id = - ResolveToDeclarationType(db, base_class->cursor, param); - // type_def ptr could be invalidated by ResolveToDeclarationType and - // TemplateVisitor. - type = db->Resolve(type_id); - if (parent_type_id) { - IndexType* parent_type_def = db->Resolve(parent_type_id.value()); - parent_type_def->derived.push_back(type_id); - type->def.bases.push_back(*parent_type_id); - } - } - } - break; - } - } -} - -// https://github.com/jacobdufault/cquery/issues/174 -// Type-dependent member access expressions do not have accurate spelling -// ranges. -// -// Not type dependent -// C f; f.x // .x produces a MemberRefExpr which has a spelling range -// of `x`. -// -// Type dependent -// C e; e.x // .x produces a MemberRefExpr which has a spelling range -// of `e` (weird) and an empty spelling name. -// -// To attribute the use of `x` in `e.x`, we use cursor extent `e.x` -// minus cursor spelling `e` minus the period. -void CheckTypeDependentMemberRefExpr(Range* spell, - const ClangCursor& cursor, - IndexParam* param, - const IndexFile* db) { - if (cursor.get_kind() == CXCursor_MemberRefExpr && - cursor.get_spell_name().empty()) { - *spell = cursor.get_extent().RemovePrefix(spell->end); - const FileContents& fc = param->file_contents[db->path]; - optional maybe_period = fc.ToOffset(spell->start); - if (maybe_period) { - int i = *maybe_period; - if (fc.content[i] == '.') - spell->start.column++; - // -> is likely unexposed. - } - } -} - -void OnIndexReference(CXClientData client_data, const CXIdxEntityRefInfo* ref) { - // TODO: Use clang_getFileUniqueID - CXFile file; - clang_getSpellingLocation(clang_indexLoc_getCXSourceLocation(ref->loc), &file, - nullptr, nullptr, nullptr); - IndexParam* param = static_cast(client_data); - IndexFile* db = ConsumeFile(param, file); - if (!db) - return; - - ClangCursor cursor(ref->cursor); - ClangCursor lex_parent(fromContainer(ref->container)); - ClangCursor referenced; - if (ref->referencedEntity) - referenced = ref->referencedEntity->cursor; - SetUsePreflight(db, lex_parent); - - switch (ref->referencedEntity->kind) { - case CXIdxEntity_Unexposed: - LOG_S(INFO) << "CXIdxEntity_Unexposed " << cursor.get_spell_name(); - break; - - case CXIdxEntity_CXXNamespace: { - IndexType* ns = db->Resolve(db->ToTypeId(referenced.get_usr_hash())); - AddUse(db, ns->uses, cursor.get_spell(), fromContainer(ref->container)); - break; - } - - case CXIdxEntity_CXXNamespaceAlias: { - IndexType* ns = db->Resolve(db->ToTypeId(referenced.get_usr_hash())); - AddUse(db, ns->uses, cursor.get_spell(), fromContainer(ref->container)); - if (!ns->def.spell) { - ClangCursor sem_parent = referenced.get_semantic_parent(); - ClangCursor lex_parent = referenced.get_lexical_parent(); - SetUsePreflight(db, sem_parent); - SetUsePreflight(db, lex_parent); - ns->def.spell = - SetUse(db, referenced.get_spell(), sem_parent, Role::Definition); - ns->def.extent = - SetUse(db, referenced.get_extent(), lex_parent, Role::None); - std::string name = referenced.get_spell_name(); - SetTypeName(ns, referenced, nullptr, name.c_str(), param); - } - break; - } - - case CXIdxEntity_ObjCProperty: - case CXIdxEntity_ObjCIvar: - case CXIdxEntity_EnumConstant: - case CXIdxEntity_CXXStaticVariable: - case CXIdxEntity_Variable: - case CXIdxEntity_Field: { - Range loc = cursor.get_spell(); - CheckTypeDependentMemberRefExpr(&loc, cursor, param, db); - - referenced = referenced.template_specialization_to_template_definition(); - - IndexVarId var_id = db->ToVarId(referenced.get_usr_hash()); - IndexVar* var = db->Resolve(var_id); - // Lambda paramaters are not processed by OnIndexDeclaration and - // may not have a short_name yet. Note that we only process the lambda - // parameter as a definition if it is in the same file as the reference, - // as lambdas cannot be split across files. - if (var->def.detailed_name.empty()) { - CXFile referenced_file; - Range spell = referenced.get_spell(&referenced_file); - if (file == referenced_file) { - var->def.spell = SetUse(db, spell, lex_parent, Role::Definition); - var->def.extent = - SetUse(db, referenced.get_extent(), lex_parent, Role::None); - - // TODO Some of the logic here duplicates CXIdxEntity_Variable branch - // of OnIndexDeclaration. But there `decl` is of type CXIdxDeclInfo - // and has more information, thus not easy to reuse the code. - SetVarDetail(var, referenced.get_spell_name(), referenced, nullptr, - true, db, param); - var->def.kind = lsSymbolKind::Parameter; - } - } - AddUse(db, var->uses, loc, fromContainer(ref->container), - GetRole(ref, Role::Reference)); - break; - } - - case CXIdxEntity_CXXConversionFunction: - case CXIdxEntity_CXXStaticMethod: - case CXIdxEntity_CXXInstanceMethod: - case CXIdxEntity_ObjCInstanceMethod: - case CXIdxEntity_ObjCClassMethod: - case CXIdxEntity_Function: - case CXIdxEntity_CXXConstructor: - case CXIdxEntity_CXXDestructor: { - // TODO: Redirect container to constructor for the following example, ie, - // we should be inserting an outgoing function call from the Foo - // ctor. - // - // int Gen() { return 5; } - // class Foo { - // int x = Gen(); - // } - - // TODO: search full history? - Range loc = cursor.get_spell(); - - IndexFuncId called_id = db->ToFuncId(HashUsr(ref->referencedEntity->USR)); - IndexFunc* called = db->Resolve(called_id); - - std::string_view short_name = called->def.ShortName(); - // libclang doesn't provide a nice api to check if the given function - // call is implicit. ref->kind should probably work (it's either direct - // or implicit), but libclang only supports implicit for objective-c. - bool is_implicit = - CanBeCalledImplicitly(ref->referencedEntity->kind) && - // Treats empty short_name as an implicit call like implicit move - // constructor in `vector a = f();` - (short_name.empty() || - // For explicit destructor call, ref->cursor may be "~" while - // called->def.short_name is "~A" - // "~A" is not a substring of ref->cursor, but we should take this - // case as not `is_implicit`. - (short_name[0] != '~' && - !CursorSpellingContainsString(ref->cursor, param->tu->cx_tu, - short_name))); - - // Extents have larger ranges and thus less specific, and will be - // overriden by other functions if exist. - // - // Type-dependent member access expressions do not have useful spelling - // ranges. See the comment above for the CXIdxEntity_Field case. - if (is_implicit) - loc = cursor.get_extent(); - else - CheckTypeDependentMemberRefExpr(&loc, cursor, param, db); - - OnIndexReference_Function( - db, loc, ref->container->cursor, called_id, - GetRole(ref, Role::Call) | - (is_implicit ? Role::Implicit : Role::None)); - - // Checks if |str| starts with |start|. Ignores case. - auto str_begin = [](const char* start, const char* str) { - while (*start && *str) { - char a = tolower(*start); - char b = tolower(*str); - if (a != b) - return false; - ++start; - ++str; - } - return !*start; - }; - - bool is_template = ref->referencedEntity->templateKind != - CXIdxEntityCXXTemplateKind::CXIdxEntity_NonTemplate; - if (param->config->index.attributeMakeCallsToCtor && is_template && - str_begin("make", ref->referencedEntity->name)) { - // Try to find the return type of called function. That type will have - // the constructor function we add a usage to. - optional opt_found_type = FindType(ref->cursor); - if (opt_found_type) { - Usr ctor_type_usr = opt_found_type->get_referenced().get_usr_hash(); - ClangCursor call_cursor = ref->cursor; - - // Build a type description from the parameters of the call, so we - // can try to find a constructor with the same type description. - std::vector call_type_desc; - for (ClangType type : call_cursor.get_type().get_arguments()) { - std::string type_desc = type.get_spell_name(); - if (!type_desc.empty()) - call_type_desc.push_back(type_desc); - } - - // Try to find the constructor and add a reference. - optional ctor_usr = - param->ctors.TryFindConstructorUsr(ctor_type_usr, call_type_desc); - if (ctor_usr) { - IndexFunc* ctor = db->Resolve(db->ToFuncId(*ctor_usr)); - ctor->uses.push_back(Use(loc, Id(), SymbolKind::File, - Role::Call | Role::Implicit, {})); - } - } - } - - break; - } - - case CXIdxEntity_ObjCCategory: - case CXIdxEntity_ObjCProtocol: - case CXIdxEntity_ObjCClass: - case CXIdxEntity_Typedef: - case CXIdxEntity_CXXInterface: // MSVC __interface - case CXIdxEntity_CXXTypeAlias: - case CXIdxEntity_Enum: - case CXIdxEntity_Union: - case CXIdxEntity_Struct: - case CXIdxEntity_CXXClass: { - referenced = referenced.template_specialization_to_template_definition(); - IndexType* ref_type = - db->Resolve(db->ToTypeId(referenced.get_usr_hash())); - if (!ref->parentEntity || IsDeclContext(ref->parentEntity->kind)) - AddUseSpell(db, ref_type->declarations, ref->cursor); - else - AddUseSpell(db, ref_type->uses, ref->cursor); - break; - } - } -} - -optional>> Parse( - Config* config, - FileConsumerSharedState* file_consumer_shared, - std::string file, - const std::vector& args, - const std::vector& file_contents, - PerformanceImportFile* perf, - ClangIndex* index, - bool dump_ast) { - if (!config->index.enabled) - return nullopt; - - file = NormalizePath(file); - - Timer timer; - - std::vector unsaved_files; - for (const FileContents& contents : file_contents) { - CXUnsavedFile unsaved; - unsaved.Filename = contents.path.c_str(); - unsaved.Contents = contents.content.c_str(); - unsaved.Length = (unsigned long)contents.content.size(); - unsaved_files.push_back(unsaved); - } - - std::unique_ptr tu = ClangTranslationUnit::Create( - index, file, args, unsaved_files, - CXTranslationUnit_KeepGoing | - CXTranslationUnit_DetailedPreprocessingRecord); - if (!tu) - return nullopt; - - perf->index_parse = timer.ElapsedMicrosecondsAndReset(); - - if (dump_ast) - Dump(clang_getTranslationUnitCursor(tu->cx_tu)); - - return ParseWithTu(config, file_consumer_shared, perf, tu.get(), index, file, - args, unsaved_files); -} - -optional>> ParseWithTu( - Config* config, - FileConsumerSharedState* file_consumer_shared, - PerformanceImportFile* perf, - ClangTranslationUnit* tu, - ClangIndex* index, - const std::string& file, - const std::vector& args, - const std::vector& file_contents) { - Timer timer; - - IndexerCallbacks callback = {0}; - // Available callbacks: - // - abortQuery - // - enteredMainFile - // - ppIncludedFile - // - importedASTFile - // - startedTranslationUnit - callback.diagnostic = &OnIndexDiagnostic; - callback.ppIncludedFile = &OnIndexIncludedFile; - callback.indexDeclaration = &OnIndexDeclaration; - callback.indexEntityReference = &OnIndexReference; - - FileConsumer file_consumer(file_consumer_shared, file); - IndexParam param(config, tu, &file_consumer); - for (const CXUnsavedFile& contents : file_contents) { - param.file_contents[contents.Filename] = FileContents( - contents.Filename, std::string(contents.Contents, contents.Length)); - } - - CXFile cx_file = clang_getFile(tu->cx_tu, file.c_str()); - param.primary_file = ConsumeFile(¶m, cx_file); - - CXIndexAction index_action = clang_IndexAction_create(index->cx_index); - - // |index_result| is a CXErrorCode instance. - int index_result = clang_indexTranslationUnit( - index_action, ¶m, &callback, sizeof(IndexerCallbacks), - CXIndexOpt_IndexFunctionLocalSymbols | - CXIndexOpt_SkipParsedBodiesInSession | - CXIndexOpt_IndexImplicitTemplateInstantiations, - tu->cx_tu); - if (index_result != CXError_Success) { - LOG_S(ERROR) << "Indexing " << file - << " failed with errno=" << index_result; - return nullopt; - } - - clang_IndexAction_dispose(index_action); - - ClangCursor(clang_getTranslationUnitCursor(tu->cx_tu)) - .VisitChildren(&VisitMacroDefinitionAndExpansions, ¶m); - - perf->index_build = timer.ElapsedMicrosecondsAndReset(); - - std::unordered_map inc_to_line; - // TODO - if (param.primary_file) - for (auto& inc : param.primary_file->includes) - inc_to_line[inc.resolved_path] = inc.line; - - auto result = param.file_consumer->TakeLocalState(); - for (std::unique_ptr& entry : result) { - entry->import_file = file; - entry->args = args; - for (IndexFunc& func : entry->funcs) { - // e.g. declaration + out-of-line definition - Uniquify(func.derived); - Uniquify(func.uses); - } - for (IndexType& type : entry->types) { - Uniquify(type.derived); - Uniquify(type.uses); - // e.g. declaration + out-of-line definition - Uniquify(type.def.funcs); - } - for (IndexVar& var : entry->vars) - Uniquify(var.uses); - - if (param.primary_file) { - // If there are errors, show at least one at the include position. - auto it = inc_to_line.find(entry->path); - if (it != inc_to_line.end()) { - int line = it->second; - for (auto ls_diagnostic : entry->diagnostics_) { - if (ls_diagnostic.severity != lsDiagnosticSeverity::Error) - continue; - ls_diagnostic.range = - lsRange(lsPosition(line, 10), lsPosition(line, 10)); - param.primary_file->diagnostics_.push_back(ls_diagnostic); - break; - } - } - } - - // Update file contents and modification time. - entry->last_modification_time = param.file_modification_times[entry->path]; - - // Update dependencies for the file. Do not include the file in its own - // dependency set. - entry->dependencies = param.seen_files; - entry->dependencies.erase( - std::remove(entry->dependencies.begin(), entry->dependencies.end(), - entry->path), - entry->dependencies.end()); - } - - return std::move(result); -} - -void ConcatTypeAndName(std::string& type, const std::string& name) { - if (type.size() && - (type.back() != ' ' && type.back() != '*' && type.back() != '&')) - type.push_back(' '); - type.append(name); -} - -void IndexInit() { - clang_enableStackTraces(); - if (!getenv("LIBCLANG_DISABLE_CRASH_RECOVERY")) - clang_toggleCrashRecovery(1); -} - -void ClangSanityCheck() { - std::vector args = {"clang", "index_tests/vars/class_member.cc"}; - unsigned opts = 0; - CXIndex index = clang_createIndex(0, 1); - CXTranslationUnit tu; - clang_parseTranslationUnit2FullArgv(index, nullptr, args.data(), args.size(), - nullptr, 0, opts, &tu); - assert(tu); - - IndexerCallbacks callback = {0}; - callback.abortQuery = [](CXClientData client_data, void* reserved) { - return 0; - }; - callback.diagnostic = [](CXClientData client_data, - CXDiagnosticSet diagnostics, void* reserved) {}; - callback.enteredMainFile = [](CXClientData client_data, CXFile mainFile, - void* reserved) -> CXIdxClientFile { - return nullptr; - }; - callback.ppIncludedFile = - [](CXClientData client_data, - const CXIdxIncludedFileInfo* file) -> CXIdxClientFile { - return nullptr; - }; - callback.importedASTFile = - [](CXClientData client_data, - const CXIdxImportedASTFileInfo*) -> CXIdxClientASTFile { - return nullptr; - }; - callback.startedTranslationUnit = [](CXClientData client_data, - void* reserved) -> CXIdxClientContainer { - return nullptr; - }; - callback.indexDeclaration = [](CXClientData client_data, - const CXIdxDeclInfo* decl) {}; - callback.indexEntityReference = [](CXClientData client_data, - const CXIdxEntityRefInfo* ref) {}; - - const unsigned kIndexOpts = 0; - CXIndexAction index_action = clang_IndexAction_create(index); - int index_param = 0; - clang_toggleCrashRecovery(0); - clang_indexTranslationUnit(index_action, &index_param, &callback, - sizeof(IndexerCallbacks), kIndexOpts, tu); - clang_IndexAction_dispose(index_action); - - clang_disposeTranslationUnit(tu); - clang_disposeIndex(index); -} - -std::string GetClangVersion() { - return ToString(clang_getClangVersion()); -} - -// |SymbolRef| is serialized this way. -// |Use| also uses this though it has an extra field |file|, -// which is not used by Index* so it does not need to be serialized. -void Reflect(Reader& visitor, Reference& value) { - if (visitor.Format() == SerializeFormat::Json) { - std::string t = visitor.GetString(); - char* s = const_cast(t.c_str()); - value.range = Range(s); - s = strchr(s, '|'); - value.id.id = RawId(strtol(s + 1, &s, 10)); - value.kind = static_cast(strtol(s + 1, &s, 10)); - value.role = static_cast(strtol(s + 1, &s, 10)); - } else { - Reflect(visitor, value.range); - Reflect(visitor, value.id); - Reflect(visitor, value.kind); - Reflect(visitor, value.role); - } -} -void Reflect(Writer& visitor, Reference& value) { - if (visitor.Format() == SerializeFormat::Json) { - std::string s = value.range.ToString(); - // RawId(-1) -> "-1" - s += '|' + std::to_string( - static_cast::type>(value.id.id)); - s += '|' + std::to_string(int(value.kind)); - s += '|' + std::to_string(int(value.role)); - Reflect(visitor, s); - } else { - Reflect(visitor, value.range); - Reflect(visitor, value.id); - Reflect(visitor, value.kind); - Reflect(visitor, value.role); - } -} diff --git a/src/clang_translation_unit.cc b/src/clang_translation_unit.cc deleted file mode 100644 index 88365f23a..000000000 --- a/src/clang_translation_unit.cc +++ /dev/null @@ -1,156 +0,0 @@ -#include "clang_translation_unit.h" - -#include "clang_utils.h" -#include "platform.h" -#include "utils.h" - -#include - -namespace { - -void EmitDiagnostics(std::string path, - std::vector args, - CXTranslationUnit tu) { - std::string output = "Fatal errors while trying to parse " + path + "\n"; - output += - "Args: " + - StringJoinMap(args, [](const char* arg) { return std::string(arg); }) + - "\n"; - - size_t num_diagnostics = clang_getNumDiagnostics(tu); - for (unsigned i = 0; i < num_diagnostics; ++i) { - output += " - "; - - CXDiagnostic diagnostic = clang_getDiagnostic(tu, i); - - // Location. - CXFile file; - unsigned int line, column; - clang_getSpellingLocation(clang_getDiagnosticLocation(diagnostic), &file, - &line, &column, nullptr); - std::string path = FileName(file); - output += path + ":" + std::to_string(line - 1) + ":" + - std::to_string(column) + " "; - - // Severity - switch (clang_getDiagnosticSeverity(diagnostic)) { - case CXDiagnostic_Ignored: - case CXDiagnostic_Note: - output += "[info]"; - break; - case CXDiagnostic_Warning: - output += "[warning]"; - break; - case CXDiagnostic_Error: - output += "[error]"; - break; - case CXDiagnostic_Fatal: - output += "[fatal]"; - break; - } - - // Content. - output += " " + ToString(clang_getDiagnosticSpelling(diagnostic)); - - clang_disposeDiagnostic(diagnostic); - - output += "\n"; - } - - LOG_S(WARNING) << output; -} -} // namespace - -// static -std::unique_ptr ClangTranslationUnit::Create( - ClangIndex* index, - const std::string& filepath, - const std::vector& arguments, - std::vector& unsaved_files, - unsigned flags) { - std::vector args; - for (auto& arg : arguments) - args.push_back(arg.c_str()); - - CXTranslationUnit cx_tu; - CXErrorCode error_code; - { - error_code = clang_parseTranslationUnit2FullArgv( - index->cx_index, nullptr, args.data(), (int)args.size(), - unsaved_files.data(), (unsigned)unsaved_files.size(), flags, &cx_tu); - } - - if (error_code != CXError_Success && cx_tu) - EmitDiagnostics(filepath, args, cx_tu); - - // We sometimes dump the command to logs and ask the user to run it. Include - // -fsyntax-only so they don't do a full compile. - auto make_msg = [&]() { - return "Please try running the following, identify which flag causes the " - "issue, and report a bug. cquery will then filter the flag for you " - " automatically:\n$ " + - StringJoin(args, " ") + " -fsyntax-only"; - }; - - switch (error_code) { - case CXError_Success: - return std::make_unique(cx_tu); - case CXError_Failure: - LOG_S(ERROR) << "libclang generic failure for " << filepath << ". " - << make_msg(); - return nullptr; - case CXError_Crashed: - LOG_S(ERROR) << "libclang crashed for " << filepath << ". " << make_msg(); - return nullptr; - case CXError_InvalidArguments: - LOG_S(ERROR) << "libclang had invalid arguments for " << filepath << ". " - << make_msg(); - return nullptr; - case CXError_ASTReadError: - LOG_S(ERROR) << "libclang had ast read error for " << filepath << ". " - << make_msg(); - return nullptr; - } - - return nullptr; -} - -// static -std::unique_ptr ClangTranslationUnit::Reparse( - std::unique_ptr tu, - std::vector& unsaved) { - int error_code; - { - error_code = clang_reparseTranslationUnit( - tu->cx_tu, (unsigned)unsaved.size(), unsaved.data(), - clang_defaultReparseOptions(tu->cx_tu)); - } - - if (error_code != CXError_Success && tu->cx_tu) - EmitDiagnostics("", {}, tu->cx_tu); - - switch (error_code) { - case CXError_Success: - return tu; - case CXError_Failure: - LOG_S(ERROR) << "libclang reparse generic failure"; - return nullptr; - case CXError_Crashed: - LOG_S(ERROR) << "libclang reparse crashed"; - return nullptr; - case CXError_InvalidArguments: - LOG_S(ERROR) << "libclang reparse had invalid arguments"; - return nullptr; - case CXError_ASTReadError: - LOG_S(ERROR) << "libclang reparse had ast read error"; - return nullptr; - } - - return nullptr; -} - -ClangTranslationUnit::ClangTranslationUnit(CXTranslationUnit tu) : cx_tu(tu) {} - -ClangTranslationUnit::~ClangTranslationUnit() { - clang_disposeTranslationUnit(cx_tu); -} diff --git a/src/clang_translation_unit.h b/src/clang_translation_unit.h deleted file mode 100644 index efeedc6c6..000000000 --- a/src/clang_translation_unit.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include "clang_cursor.h" -#include "clang_index.h" - -#include - -#include -#include -#include - -// RAII wrapper around CXTranslationUnit which also makes it much more -// challenging to use a CXTranslationUnit instance that is not correctly -// initialized. -struct ClangTranslationUnit { - static std::unique_ptr Create( - ClangIndex* index, - const std::string& filepath, - const std::vector& arguments, - std::vector& unsaved_files, - unsigned flags); - - static std::unique_ptr Reparse( - std::unique_ptr tu, - std::vector& unsaved); - - explicit ClangTranslationUnit(CXTranslationUnit tu); - ~ClangTranslationUnit(); - - CXTranslationUnit cx_tu; -}; \ No newline at end of file diff --git a/src/clang_tu.cc b/src/clang_tu.cc new file mode 100644 index 000000000..1132d10dd --- /dev/null +++ b/src/clang_tu.cc @@ -0,0 +1,251 @@ +/* Copyright 2017-2018 ccls Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#include "clang_tu.hh" + +#include "config.hh" +#include "platform.hh" + +#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 symlinks outside of workspace folders, e.g. /usr/include/c++/7.3.0 + return normalizeFolder(ret) ? ret : realPath(ret); +} + +static Pos decomposed2LineAndCol(const SourceManager &sm, + std::pair i) { + int l = sm.getLineNumber(i.first, i.second) - 1, + c = 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 {(uint16_t)std::min(l, UINT16_MAX), + (int16_t)std::min(c, INT16_MAX)}; +} + +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 sr, FileID *fid) { + return fromCharSourceRange(sm, lang, CharSourceRange::getTokenRange(sr), fid); +} + +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) { + std::string save = "-resource-dir=" + g_config->clang.resourceDir; + args.push_back(save.c_str()); + 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; + // 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) { + switch (BuiltinType::Kind(kind)) { + case BuiltinType::Void: + return "void"; + case BuiltinType::Bool: + return "bool"; + case BuiltinType::Char_S: + return "char"; + case BuiltinType::Char_U: + return "char"; + case BuiltinType::SChar: + return "signed char"; + case BuiltinType::Short: + return "short"; + case BuiltinType::Int: + return "int"; + case BuiltinType::Long: + return "long"; + case BuiltinType::LongLong: + return "long long"; + case BuiltinType::Int128: + return "__int128"; + case BuiltinType::UChar: + return "unsigned char"; + case BuiltinType::UShort: + return "unsigned short"; + case BuiltinType::UInt: + return "unsigned int"; + case BuiltinType::ULong: + return "unsigned long"; + case BuiltinType::ULongLong: + return "unsigned long long"; + case BuiltinType::UInt128: + return "unsigned __int128"; + case BuiltinType::Half: + return "__fp16"; + case BuiltinType::Float: + return "float"; + case BuiltinType::Double: + return "double"; + case BuiltinType::LongDouble: + return "long double"; + case BuiltinType::ShortAccum: + return "short _Accum"; + case BuiltinType::Accum: + return "_Accum"; + case BuiltinType::LongAccum: + return "long _Accum"; + case BuiltinType::UShortAccum: + return "unsigned short _Accum"; + case BuiltinType::UAccum: + return "unsigned _Accum"; + case BuiltinType::ULongAccum: + return "unsigned long _Accum"; + case BuiltinType::BuiltinType::ShortFract: + return "short _Fract"; + case BuiltinType::BuiltinType::Fract: + return "_Fract"; + case BuiltinType::BuiltinType::LongFract: + return "long _Fract"; + case BuiltinType::BuiltinType::UShortFract: + return "unsigned short _Fract"; + case BuiltinType::BuiltinType::UFract: + return "unsigned _Fract"; + case BuiltinType::BuiltinType::ULongFract: + return "unsigned long _Fract"; + case BuiltinType::BuiltinType::SatShortAccum: + return "_Sat short _Accum"; + case BuiltinType::BuiltinType::SatAccum: + return "_Sat _Accum"; + case BuiltinType::BuiltinType::SatLongAccum: + return "_Sat long _Accum"; + case BuiltinType::BuiltinType::SatUShortAccum: + return "_Sat unsigned short _Accum"; + case BuiltinType::BuiltinType::SatUAccum: + return "_Sat unsigned _Accum"; + case BuiltinType::BuiltinType::SatULongAccum: + return "_Sat unsigned long _Accum"; + case BuiltinType::BuiltinType::SatShortFract: + return "_Sat short _Fract"; + case BuiltinType::BuiltinType::SatFract: + return "_Sat _Fract"; + case BuiltinType::BuiltinType::SatLongFract: + return "_Sat long _Fract"; + case BuiltinType::BuiltinType::SatUShortFract: + return "_Sat unsigned short _Fract"; + case BuiltinType::BuiltinType::SatUFract: + return "_Sat unsigned _Fract"; + case BuiltinType::BuiltinType::SatULongFract: + return "_Sat unsigned long _Fract"; + case BuiltinType::Float16: + return "_Float16"; + case BuiltinType::Float128: + return "__float128"; + case BuiltinType::WChar_S: + case BuiltinType::WChar_U: + return "wchar_t"; + case BuiltinType::Char8: + return "char8_t"; + case BuiltinType::Char16: + return "char16_t"; + case BuiltinType::Char32: + return "char32_t"; + case BuiltinType::NullPtr: + return "nullptr_t"; + case BuiltinType::Overload: + return ""; + case BuiltinType::BoundMember: + return ""; + case BuiltinType::PseudoObject: + return ""; + case BuiltinType::Dependent: + return ""; + case BuiltinType::UnknownAny: + return ""; + case BuiltinType::ARCUnbridgedCast: + return ""; + case BuiltinType::BuiltinFn: + return ""; + case BuiltinType::ObjCId: + return "id"; + case BuiltinType::ObjCClass: + return "Class"; + case BuiltinType::ObjCSel: + return "SEL"; + case BuiltinType::OCLSampler: + return "sampler_t"; + case BuiltinType::OCLEvent: + return "event_t"; + case BuiltinType::OCLClkEvent: + return "clk_event_t"; + case BuiltinType::OCLQueue: + return "queue_t"; + case BuiltinType::OCLReserveID: + return "reserve_id_t"; + case BuiltinType::OMPArraySection: + return ""; + default: + return ""; + } +} +} // namespace ccls diff --git a/src/clang_tu.hh b/src/clang_tu.hh new file mode 100644 index 000000000..ede1a6ac4 --- /dev/null +++ b/src/clang_tu.hh @@ -0,0 +1,54 @@ +/* Copyright 2017-2018 ccls Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#pragma once + +#include "position.hh" + +#include +#include +#include +#include + +#if LLVM_VERSION_MAJOR < 8 +// D52783 Lift VFS from clang to llvm +namespace llvm { +namespace vfs = clang::vfs; +} +#endif + +namespace ccls { +std::string pathFromFileEntry(const clang::FileEntry &file); + +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 &lang, clang::SourceRange sr, + clang::FileID *fid = nullptr); + +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, + llvm::IntrusiveRefCntPtr VFS); + +const char *clangBuiltinTypeName(int); +} // namespace ccls diff --git a/src/clang_utils.cc b/src/clang_utils.cc deleted file mode 100644 index 8be4a15ca..000000000 --- a/src/clang_utils.cc +++ /dev/null @@ -1,231 +0,0 @@ -#include "clang_utils.h" - -#include "platform.h" - -#include - -namespace { - -lsRange GetLsRangeForFixIt(const CXSourceRange& range) { - CXSourceLocation start = clang_getRangeStart(range); - CXSourceLocation end = clang_getRangeEnd(range); - - unsigned int start_line, start_column; - clang_getSpellingLocation(start, nullptr, &start_line, &start_column, - nullptr); - unsigned int end_line, end_column; - clang_getSpellingLocation(end, nullptr, &end_line, &end_column, nullptr); - - return lsRange(lsPosition(start_line - 1, start_column - 1) /*start*/, - lsPosition(end_line - 1, end_column) /*end*/); -} - -} // namespace - -// See clang_formatDiagnostic -optional BuildAndDisposeDiagnostic(CXDiagnostic diagnostic, - const std::string& path) { - // Get diagnostic location. - CXFile file; - unsigned start_line, start_column; - clang_getSpellingLocation(clang_getDiagnosticLocation(diagnostic), &file, - &start_line, &start_column, nullptr); - - if (file && path != FileName(file)) { - clang_disposeDiagnostic(diagnostic); - return nullopt; - } - - unsigned end_line = start_line, end_column = start_column, - num_ranges = clang_getDiagnosticNumRanges(diagnostic); - for (unsigned i = 0; i < num_ranges; i++) { - CXFile file0, file1; - unsigned line0, column0, line1, column1; - CXSourceRange range = clang_getDiagnosticRange(diagnostic, i); - clang_getSpellingLocation(clang_getRangeStart(range), &file0, &line0, - &column0, nullptr); - clang_getSpellingLocation(clang_getRangeEnd(range), &file1, &line1, - &column1, nullptr); - if (file0 != file1 || file0 != file) - continue; - if (line0 < start_line || (line0 == start_line && column0 < start_column)) { - start_line = line0; - start_column = column0; - } - if (line1 > end_line || (line1 == end_line && column1 > end_column)) { - end_line = line1; - end_column = column1; - } - } - - // Build diagnostic. - lsDiagnostic ls_diagnostic; - ls_diagnostic.range = lsRange(lsPosition(start_line - 1, start_column - 1), - lsPosition(end_line - 1, end_column - 1)); - - ls_diagnostic.message = ToString(clang_getDiagnosticSpelling(diagnostic)); - - // Append the flag that enables this diagnostic, ie, [-Wswitch] - std::string enabling_flag = - ToString(clang_getDiagnosticOption(diagnostic, nullptr)); - if (!enabling_flag.empty()) - ls_diagnostic.message += " [" + enabling_flag + "]"; - - ls_diagnostic.code = clang_getDiagnosticCategory(diagnostic); - - switch (clang_getDiagnosticSeverity(diagnostic)) { - case CXDiagnostic_Ignored: - // llvm_unreachable - break; - case CXDiagnostic_Note: - ls_diagnostic.severity = lsDiagnosticSeverity::Information; - break; - case CXDiagnostic_Warning: - ls_diagnostic.severity = lsDiagnosticSeverity::Warning; - break; - case CXDiagnostic_Error: - case CXDiagnostic_Fatal: - ls_diagnostic.severity = lsDiagnosticSeverity::Error; - break; - } - - // Report fixits - unsigned num_fixits = clang_getDiagnosticNumFixIts(diagnostic); - for (unsigned i = 0; i < num_fixits; ++i) { - CXSourceRange replacement_range; - CXString text = clang_getDiagnosticFixIt(diagnostic, i, &replacement_range); - - lsTextEdit edit; - edit.newText = ToString(text); - edit.range = GetLsRangeForFixIt(replacement_range); - ls_diagnostic.fixits_.push_back(edit); - } - - clang_disposeDiagnostic(diagnostic); - - return ls_diagnostic; -} - -#if USE_CLANG_CXX -static lsPosition OffsetToRange(llvm::StringRef document, size_t offset) { - // TODO: Support Windows line endings, etc. - llvm::StringRef text_before = document.substr(0, offset); - int num_line = text_before.count('\n'); - int num_column = text_before.size() - text_before.rfind('\n') - 1; - return {num_line, num_column}; -} - -std::vector ConvertClangReplacementsIntoTextEdits( - llvm::StringRef document, - const std::vector& clang_replacements) { - std::vector text_edits_result; - for (const auto& replacement : clang_replacements) { - const auto startPosition = OffsetToRange(document, replacement.getOffset()); - const auto endPosition = OffsetToRange( - document, replacement.getOffset() + replacement.getLength()); - text_edits_result.push_back( - {{startPosition, endPosition}, replacement.getReplacementText()}); - } - return text_edits_result; -} - -#endif - -std::string FileName(CXFile file) { - CXString cx_name = clang_getFileName(file); - std::string name = ToString(cx_name); - return NormalizePath(name); -} - -std::string ToString(CXString cx_string) { - std::string string; - if (cx_string.data != nullptr) { - string = clang_getCString(cx_string); - clang_disposeString(cx_string); - } - return string; -} - -std::string ToString(CXCursorKind kind) { - return ToString(clang_getCursorKindSpelling(kind)); -} - -const char* ClangBuiltinTypeName(CXTypeKind kind) { - switch (kind) { - // clang-format off - case CXType_Bool: return "bool"; - case CXType_Char_U: return "char"; - case CXType_UChar: return "unsigned char"; - case CXType_UShort: return "unsigned short"; - case CXType_UInt: return "unsigned int"; - case CXType_ULong: return "unsigned long"; - case CXType_ULongLong: return "unsigned long long"; - case CXType_UInt128: return "unsigned __int128"; - case CXType_Char_S: return "char"; - case CXType_SChar: return "signed char"; - case CXType_WChar: return "wchar_t"; - case CXType_Int: return "int"; - case CXType_Long: return "long"; - case CXType_LongLong: return "long long"; - case CXType_Int128: return "__int128"; - case CXType_Float: return "float"; - case CXType_Double: return "double"; - case CXType_LongDouble: return "long double"; - case CXType_Float128: return "__float128"; -#if CINDEX_VERSION_MINOR >= 43 - case CXType_Half: return "_Float16"; -#endif - case CXType_NullPtr: return "nullptr"; - default: return ""; - // clang-format on - } -} - -#if USE_CLANG_CXX -TEST_SUITE("ClangUtils") { - TEST_CASE("replacements") { - const std::string sample_document = - "int \n" - "main() { int *i = 0; return 0; \n" - "}"; - const std::vector clang_replacements = { - {"foo.cc", 3, 2, " "}, {"foo.cc", 13, 1, "\n "}, - {"foo.cc", 17, 1, ""}, {"foo.cc", 19, 0, " "}, - {"foo.cc", 25, 1, "\n "}, {"foo.cc", 35, 2, "\n"}}; - - // Expected format: - // - // int main() { - // int *i = 0; - // return 0; - // } - - const auto text_edits = ConvertClangReplacementsIntoTextEdits( - sample_document, clang_replacements); - REQUIRE(text_edits.size() == 6); - REQUIRE(text_edits[0].range.start.line == 0); - REQUIRE(text_edits[0].range.start.character == 3); - REQUIRE(text_edits[0].newText == " "); - - REQUIRE(text_edits[1].range.start.line == 1); - REQUIRE(text_edits[1].range.start.character == 8); - REQUIRE(text_edits[1].newText == "\n "); - - REQUIRE(text_edits[2].range.start.line == 1); - REQUIRE(text_edits[2].range.start.character == 12); - REQUIRE(text_edits[2].newText == ""); - - REQUIRE(text_edits[3].range.start.line == 1); - REQUIRE(text_edits[3].range.start.character == 14); - REQUIRE(text_edits[3].newText == " "); - - REQUIRE(text_edits[4].range.start.line == 1); - REQUIRE(text_edits[4].range.start.character == 20); - REQUIRE(text_edits[4].newText == "\n "); - - REQUIRE(text_edits[5].range.start.line == 1); - REQUIRE(text_edits[5].range.start.character == 30); - REQUIRE(text_edits[5].newText == "\n"); - } -} -#endif diff --git a/src/clang_utils.h b/src/clang_utils.h deleted file mode 100644 index 24175efd8..000000000 --- a/src/clang_utils.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include "lsp_diagnostic.h" - -#include -#if USE_CLANG_CXX -#include -#endif -#include - -#include - -optional BuildAndDisposeDiagnostic(CXDiagnostic diagnostic, - const std::string& path); - -// Returns the absolute path to |file|. -std::string FileName(CXFile file); - -std::string ToString(CXString cx_string); - -std::string ToString(CXCursorKind cursor_kind); - -const char* ClangBuiltinTypeName(CXTypeKind); - -// Converts Clang formatting replacement operations into LSP text edits. -#if USE_CLANG_CXX -std::vector ConvertClangReplacementsIntoTextEdits( - llvm::StringRef document, - const std::vector& clang_replacements); -#endif diff --git a/src/code_complete_cache.cc b/src/code_complete_cache.cc deleted file mode 100644 index df9244619..000000000 --- a/src/code_complete_cache.cc +++ /dev/null @@ -1,12 +0,0 @@ -#include "code_complete_cache.h" - -void CodeCompleteCache::WithLock(std::function action) { - std::lock_guard lock(mutex_); - action(); -} - -bool CodeCompleteCache::IsCacheValid(lsTextDocumentPositionParams position) { - std::lock_guard lock(mutex_); - return cached_path_ == position.textDocument.uri.GetPath() && - cached_completion_position_ == position.position; -} \ No newline at end of file diff --git a/src/code_complete_cache.h b/src/code_complete_cache.h deleted file mode 100644 index 76ed61049..000000000 --- a/src/code_complete_cache.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include "lsp_completion.h" - -#include - -#include - -// Cached completion information, so we can give fast completion results when -// the user erases a character. vscode will resend the completion request if -// that happens. -struct CodeCompleteCache { - // NOTE: Make sure to access these variables under |WithLock|. - optional cached_path_; - optional cached_completion_position_; - std::vector cached_results_; - - std::mutex mutex_; - - void WithLock(std::function action); - bool IsCacheValid(lsTextDocumentPositionParams position); -}; diff --git a/src/command_line.cc b/src/command_line.cc deleted file mode 100644 index 717ba3af8..000000000 --- a/src/command_line.cc +++ /dev/null @@ -1,502 +0,0 @@ -// TODO: cleanup includes -#include "cache_manager.h" -#include "clang_complete.h" -#include "code_complete_cache.h" -#include "diagnostics_engine.h" -#include "file_consumer.h" -#include "import_manager.h" -#include "import_pipeline.h" -#include "include_complete.h" -#include "indexer.h" -#include "lsp_diagnostic.h" -#include "lex_utils.h" -#include "lru_cache.h" -#include "match.h" -#include "message_handler.h" -#include "options.h" -#include "platform.h" -#include "project.h" -#include "query.h" -#include "query_utils.h" -#include "queue_manager.h" -#include "recorder.h" -#include "semantic_highlight_symbol_cache.h" -#include "serializer.h" -#include "serializers/json.h" -#include "test.h" -#include "timer.h" -#include "timestamp_manager.h" -#include "work_thread.h" -#include "working_files.h" - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// TODO: provide a feature like 'https://github.com/goldsborough/clang-expand', -// ie, a fully linear view of a function with inline function calls expanded. -// We can probably use vscode decorators to achieve it. - -// TODO: implement ThreadPool type which monitors CPU usage / number of work -// items per second completed and scales up/down number of running threads. - -std::string g_init_options; - -namespace { - -std::vector kEmptyArgs; - -// This function returns true if e2e timing should be displayed for the given -// IpcId. -bool ShouldDisplayIpcTiming(IpcId id) { - switch (id) { - case IpcId::TextDocumentPublishDiagnostics: - case IpcId::CqueryPublishInactiveRegions: - case IpcId::Unknown: - return false; - default: - return true; - } -} - -REGISTER_IPC_MESSAGE(Ipc_CancelRequest); - -void PrintHelp() { - std::cout - << R"help(cquery is a low-latency C/C++/Objective-C language server. - -Mode: - --clang-sanity-check - Run a simple index test. Verifies basic clang functionality. - Needs to be executed from the cquery root checkout directory. - --test-unit Run unit tests. - --test-index - Run index tests. opt_filter_path can be used to specify which - test to run (ie, "foo" will run all tests which contain "foo" - in the path). If not provided all tests are run. - (default if no other mode is specified) - Run as a language server over stdin and stdout - -Other command line options: - --init - Override client provided initialization options - https://github.com/cquery-project/cquery/wiki/Initialization-options - --record - Writes stdin to .in and stdout to .out - --log-file Logging file for diagnostics - --log-file-append Like --log-file, but appending - --log-all-to-stderr Write all log messages to STDERR. - --wait-for-input Wait for an '[Enter]' before exiting - --help Print this help information. - --ci Prevents tests from prompting the user for input. Used for - continuous integration so it can fail faster instead of timing - out. - -See more on https://github.com/cquery-project/cquery/wiki -)help"; -} - -} // namespace - -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -// QUERYDB MAIN //////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// - -bool QueryDbMainLoop(Config* config, - QueryDatabase* db, - MultiQueueWaiter* waiter, - Project* project, - FileConsumerSharedState* file_consumer_shared, - ImportManager* import_manager, - ImportPipelineStatus* status, - TimestampManager* timestamp_manager, - SemanticHighlightSymbolCache* semantic_cache, - WorkingFiles* working_files, - ClangCompleteManager* clang_complete, - IncludeComplete* include_complete, - CodeCompleteCache* global_code_complete_cache, - CodeCompleteCache* non_global_code_complete_cache, - CodeCompleteCache* signature_cache) { - auto* queue = QueueManager::instance(); - std::vector> messages = - queue->for_querydb.DequeueAll(); - bool did_work = messages.size(); - for (auto& message : messages) { - for (MessageHandler* handler : *MessageHandler::message_handlers) { - if (handler->GetId() == message->method_id) { - handler->Run(std::move(message)); - break; - } - } - - if (message) { - LOG_S(FATAL) << "Exiting; unhandled IPC message " - << IpcIdToString(message->method_id); - exit(1); - } - } - - // TODO: consider rate-limiting and checking for IPC messages so we don't - // block requests / we can serve partial requests. - - if (QueryDb_ImportMain(config, db, import_manager, status, semantic_cache, - working_files)) { - did_work = true; - } - - return did_work; -} - -void RunQueryDbThread(const std::string& bin_name, - Config* config, - MultiQueueWaiter* querydb_waiter, - MultiQueueWaiter* indexer_waiter) { - Project project; - SemanticHighlightSymbolCache semantic_cache; - WorkingFiles working_files; - FileConsumerSharedState file_consumer_shared; - DiagnosticsEngine diag_engine; - - ClangCompleteManager clang_complete( - config, &project, &working_files, - [&](std::string path, std::vector diagnostics) { - diag_engine.Publish(&working_files, path, diagnostics); - }, - [&](ClangTranslationUnit* tu, const std::vector& unsaved, - const std::string& path, const std::vector& args) { - IndexWithTuFromCodeCompletion(config, &file_consumer_shared, tu, - unsaved, path, args); - }, - [](lsRequestId id) { - if (!std::holds_alternative(id)) { - Out_Error out; - out.id = id; - out.error.code = lsErrorCodes::InternalError; - out.error.message = "Dropping completion request; a newer request " - "has come in that will be serviced instead."; - QueueManager::WriteStdout(IpcId::Unknown, out); - } - }); - - IncludeComplete include_complete(config, &project); - auto global_code_complete_cache = std::make_unique(); - auto non_global_code_complete_cache = std::make_unique(); - auto signature_cache = std::make_unique(); - ImportManager import_manager; - ImportPipelineStatus import_pipeline_status; - TimestampManager timestamp_manager; - QueryDatabase db; - - // Setup shared references. - for (MessageHandler* handler : *MessageHandler::message_handlers) { - handler->config = config; - handler->db = &db; - handler->waiter = indexer_waiter; - handler->project = &project; - handler->diag_engine = &diag_engine; - handler->file_consumer_shared = &file_consumer_shared; - handler->import_manager = &import_manager; - handler->import_pipeline_status = &import_pipeline_status; - handler->timestamp_manager = ×tamp_manager; - handler->semantic_cache = &semantic_cache; - handler->working_files = &working_files; - handler->clang_complete = &clang_complete; - handler->include_complete = &include_complete; - handler->global_code_complete_cache = global_code_complete_cache.get(); - handler->non_global_code_complete_cache = - non_global_code_complete_cache.get(); - handler->signature_cache = signature_cache.get(); - } - - // Run query db main loop. - SetCurrentThreadName("querydb"); - while (true) { - bool did_work = QueryDbMainLoop( - config, &db, querydb_waiter, &project, &file_consumer_shared, - &import_manager, &import_pipeline_status, ×tamp_manager, - &semantic_cache, &working_files, &clang_complete, &include_complete, - global_code_complete_cache.get(), non_global_code_complete_cache.get(), - signature_cache.get()); - - // Cleanup and free any unused memory. - FreeUnusedMemory(); - - if (!did_work) { - auto* queue = QueueManager::instance(); - querydb_waiter->Wait(&queue->on_indexed, &queue->for_querydb, - &queue->do_id_map); - } - } -} - -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -// STDIN MAIN ////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -// Separate thread whose only job is to read from stdin and -// dispatch read commands to the actual indexer program. This -// cannot be done on the main thread because reading from std::cin -// blocks. -// -// |ipc| is connected to a server. -void LaunchStdinLoop(Config* config, - std::unordered_map* request_times) { - // If flushing cin requires flushing cout there could be deadlocks in some - // clients. - std::cin.tie(nullptr); - - WorkThread::StartThread("stdin", [request_times]() { - auto* queue = QueueManager::instance(); - while (true) { - std::unique_ptr message; - optional err = - MessageRegistry::instance()->ReadMessageFromStdin(&message); - - // Message parsing can fail if we don't recognize the method. - if (err) { - // The message may be partially deserialized. - // Emit an error ResponseMessage if |id| is available. - if (message) { - lsRequestId id = message->GetRequestId(); - if (!std::holds_alternative(id)) { - Out_Error out; - out.id = id; - out.error.code = lsErrorCodes::InvalidParams; - out.error.message = std::move(*err); - queue->WriteStdout(IpcId::Unknown, out); - } - } - continue; - } - - // Cache |method_id| so we can access it after moving |message|. - IpcId method_id = message->method_id; - (*request_times)[method_id] = Timer(); - - switch (method_id) { - case IpcId::Initialized: { - // TODO: don't send output until we get this notification - break; - } - - case IpcId::CancelRequest: { - // TODO: support cancellation - break; - } - - case IpcId::Exit: -#define CASE(name, method) case IpcId::name: -#include "methods.inc" -#undef CASE - queue->for_querydb.PushBack(std::move(message)); - break; - - case IpcId::Unknown: - break; - } - - // If the message was to exit then querydb will take care of the actual - // exit. Stop reading from stdin since it might be detached. - if (method_id == IpcId::Exit) - break; - } - }); -} - -void LaunchStdoutThread(std::unordered_map* request_times, - MultiQueueWaiter* waiter) { - WorkThread::StartThread("stdout", [=]() { - auto* queue = QueueManager::instance(); - - while (true) { - std::vector messages = queue->for_stdout.DequeueAll(); - if (messages.empty()) { - waiter->Wait(&queue->for_stdout); - continue; - } - - for (auto& message : messages) { - if (ShouldDisplayIpcTiming(message.id)) { - Timer time = (*request_times)[message.id]; - time.ResetAndPrint("[e2e] Running " + - std::string(IpcIdToString(message.id))); - } - - RecordOutput(message.content); - - fwrite(message.content.c_str(), message.content.size(), 1, stdout); - fflush(stdout); - } - } - }); -} - -void LanguageServerMain(const std::string& bin_name, - Config* config, - MultiQueueWaiter* querydb_waiter, - MultiQueueWaiter* indexer_waiter, - MultiQueueWaiter* stdout_waiter) { - std::unordered_map request_times; - - LaunchStdinLoop(config, &request_times); - - // We run a dedicated thread for writing to stdout because there can be an - // unknown number of delays when output information. - LaunchStdoutThread(&request_times, stdout_waiter); - - // Start querydb which takes over this thread. The querydb will launch - // indexer threads as needed. - RunQueryDbThread(bin_name, config, querydb_waiter, indexer_waiter); -} - -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -// MAIN //////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -int main(int argc, char** argv) { - TraceMe(); - - std::unordered_map options = - ParseOptions(argc, argv); - - if (HasOption(options, "-h") || HasOption(options, "--help")) { - PrintHelp(); - // Also emit doctest help if --test-unit is passed. - if (!HasOption(options, "--test-unit")) - return 0; - } - - if (!HasOption(options, "--log-all-to-stderr")) - loguru::g_stderr_verbosity = loguru::Verbosity_WARNING; - - loguru::g_flush_interval_ms = 0; - loguru::init(argc, argv); - - MultiQueueWaiter querydb_waiter, indexer_waiter, stdout_waiter; - QueueManager::Init(&querydb_waiter, &indexer_waiter, &stdout_waiter); - - PlatformInit(); - IndexInit(); - - bool language_server = true; - - if (HasOption(options, "--log-file")) { - loguru::add_file(options["--log-file"].c_str(), loguru::Truncate, - loguru::Verbosity_MAX); - } - if (HasOption(options, "--log-file-append")) { - loguru::add_file(options["--log-file-append"].c_str(), loguru::Append, - loguru::Verbosity_MAX); - } - - if (HasOption(options, "--record")) - EnableRecording(options["--record"]); - - if (HasOption(options, "--clang-sanity-check")) { - language_server = false; - ClangSanityCheck(); - } - - if (HasOption(options, "--test-unit")) { - language_server = false; - doctest::Context context; - context.applyCommandLine(argc, argv); - int res = context.run(); - if (res != 0 || context.shouldExit()) - return res; - } - - if (HasOption(options, "--test-index")) { - language_server = false; - if (!RunIndexTests(options["--test-index"], !HasOption(options, "--ci"))) - return 1; - } - - if (language_server) { - if (HasOption(options, "--init")) { - // We check syntax error here but override client-side - // initializationOptions in messages/initialize.cc - g_init_options = options["--init"]; - rapidjson::Document reader; - rapidjson::ParseResult ok = reader.Parse(g_init_options.c_str()); - if (!ok) { - std::cerr << "Failed to parse --init as JSON: " - << rapidjson::GetParseError_En(ok.Code()) << " (" - << ok.Offset() << ")\n"; - return 1; - } - JsonReader json_reader{&reader}; - try { - Config config; - Reflect(json_reader, config); - } catch (std::invalid_argument& e) { - std::cerr << "Fail to parse --init " - << static_cast(json_reader).GetPath() - << ", expected " << e.what() << "\n"; - return 1; - } - } - - // std::cerr << "Running language server" << std::endl; - auto config = std::make_unique(); - LanguageServerMain(argv[0], config.get(), &querydb_waiter, &indexer_waiter, - &stdout_waiter); - } - - if (HasOption(options, "--wait-for-input")) { - std::cerr << std::endl << "[Enter] to exit" << std::endl; - getchar(); - } - - return 0; -} diff --git a/src/config.cc b/src/config.cc new file mode 100644 index 000000000..9e87fb292 --- /dev/null +++ b/src/config.cc @@ -0,0 +1,31 @@ +/* Copyright 2017-2018 ccls Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#include "config.hh" + +namespace ccls { +Config *g_config; + +void doPathMapping(std::string &arg) { + for (const std::string &mapping : g_config->clang.pathMappings) { + auto colon = mapping.find('>'); + if (colon != std::string::npos) { + auto p = arg.find(mapping.substr(0, colon)); + if (p != std::string::npos) + arg.replace(p, colon, mapping.substr(colon + 1)); + } + } +} +} // namespace ccls diff --git a/src/config.h b/src/config.h deleted file mode 100644 index f06397a5c..000000000 --- a/src/config.h +++ /dev/null @@ -1,281 +0,0 @@ -#pragma once - -#include "serializer.h" - -#include - -/* -The language client plugin needs to send initialization options in the -`initialize` request to the cquery language server. The only required option is -`cacheDirectory`, which is where index files will be stored. - - { - "initializationOptions": { - "cacheDirectory": "/tmp/cquery" - } - } - -If necessary, the command line option --init can be used to override -initialization options specified by the client. For example, in shell syntax: - - '--init={"index": {"comments": 2, "whitelist": ["."]}}' -*/ -struct Config { - // Root directory of the project. **Not available for configuration** - std::string projectRoot; - // 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. - // The stdout of the command should be the JSON compilation database. - std::string compilationDatabaseCommand; - // Directory containing compile_commands.json. - std::string compilationDatabaseDirectory; - // Cache directory for indexed files. - std::string cacheDirectory; - // Cache serialization format. - // - // "json" generates `cacheDirectory/.../xxx.json` files which can be pretty - // printed with jq. - // - // "msgpack" uses a compact binary serialization format (the underlying wire - // format is [MessagePack](https://msgpack.org/index.html)) which typically - // takes only 60% of the corresponding JSON size, but is difficult to inspect. - // msgpack does not store map keys and you need to re-index whenever a struct - // member has changed. - SerializeFormat cacheFormat = SerializeFormat::Json; - // Value to use for clang -resource-dir if not present in - // compile_commands.json. - // - // cquery includes a resource directory, this should not need to be configured - // unless you're using an esoteric configuration. Consider reporting a bug and - // fixing upstream instead of configuring this. - // - // Example value: "/path/to/lib/clang/5.0.1/" - std::string resourceDirectory; - - // Additional arguments to pass to clang. - std::vector extraClangArguments; - - // If true, cquery will send progress reports while indexing - // How often should cquery send progress report messages? - // -1: never - // 0: as often as possible - // xxx: at most every xxx milliseconds - // - // Empty progress reports (ie, idle) are delivered as often as they are - // available and may exceed this value. - // - // This does not guarantee a progress report will be delivered every - // interval; it could take significantly longer if cquery is completely idle. - int progressReportFrequencyMs = 500; - - // If true, document links are reported for #include directives. - bool showDocumentLinksOnIncludes = true; - - // Version of the client. If undefined the version check is skipped. Used to - // inform users their vscode client is too old and needs to be updated. - optional clientVersion; - - struct ClientCapability { - // TextDocumentClientCapabilities.completion.completionItem.snippetSupport - bool snippetSupport = false; - }; - ClientCapability client; - - struct CodeLens { - // Enables code lens on parameter and function variables. - bool localVariables = true; - } codeLens; - - struct Completion { - // Some completion UI, such as Emacs' completion-at-point and company-lsp, - // display completion item label and detail side by side. - // This does not look right, when you see things like: - // "foo" "int foo()" - // "bar" "void bar(int i = 0)" - // When this option is enabled, the completion item label is very detailed, - // it shows the full signature of the candidate. - // The detail just contains the completion item parent context. - // Also, in this mode, functions with default arguments, - // generates one more item per default argument - // so that the right function call can be selected. - // That is, you get something like: - // "int foo()" "Foo" - // "void bar()" "Foo" - // "void bar(int i = 0)" "Foo" - // Be wary, this is quickly quite verbose, - // items can end up truncated by the UIs. - bool detailedLabel = false; - - // On large projects, completion can take a long time. By default if cquery - // receives multiple completion requests while completion is still running - // it will only service the newest request. If this is set to false then all - // completion requests will be serviced. - bool dropOldRequests = true; - - // If true, filter and sort completion response. cquery filters and sorts - // completions to try to be nicer to clients that can't handle big numbers - // of completion candidates. This behaviour can be disabled by specifying - // false for the option. This option is the most useful for LSP clients - // that implement their own filtering and sorting logic. - bool filterAndSort = true; - - // Regex patterns to match include completion candidates against. They - // receive the absolute file path. - // - // For example, to hide all files in a /CACHE/ folder, use ".*/CACHE/.*" - std::vector includeBlacklist; - - // Maximum path length to show in completion results. Paths longer than this - // will be elided with ".." put at the front. Set to 0 or a negative number - // to disable eliding. - int includeMaxPathSize = 30; - - // Whitelist that file paths will be tested against. If a file path does not - // end in one of these values, it will not be considered for - // auto-completion. An example value is { ".h", ".hpp" } - // - // This is significantly faster than using a regex. - std::vector includeSuffixWhitelist = {".h", ".hpp", ".hh"}; - - std::vector includeWhitelist; - } completion; - - struct Diagnostics { - // Like index.{whitelist,blacklist}, don't publish diagnostics to - // blacklisted files. - std::vector blacklist; - - // How often should cquery publish diagnostics in completion? - // -1: never - // 0: as often as possible - // xxx: at most every xxx milliseconds - int frequencyMs = 0; - - // If true, diagnostics from a full document parse will be reported. - bool onParse = true; - - std::vector whitelist; - } diagnostics; - - // Semantic highlighting - struct Highlight { - // Like index.{whitelist,blacklist}, don't publish semantic highlighting to - // blacklisted files. - std::vector blacklist; - - std::vector whitelist; - } highlight; - - struct Index { - // Attempt to convert calls of make* functions to constructors based on - // hueristics. - // - // For example, this will show constructor calls for std::make_unique - // invocations. Specifically, cquery will try to attribute a ctor call - // whenever the function name starts with make (ignoring case). - bool attributeMakeCallsToCtor = true; - - // If a translation unit's absolute path matches any EMCAScript regex in the - // whitelist, or does not match any regex in the blacklist, it will be - // indexed. To only index files in the whitelist, add ".*" to the blacklist. - // `std::regex_search(path, regex, std::regex_constants::match_any)` - // - // Example: `ash/.*\.cc` - std::vector blacklist; - - // 0: none, 1: Doxygen, 2: all comments - // Plugin support for clients: - // - https://github.com/emacs-lsp/lsp-ui - // - https://github.com/autozimu/LanguageClient-neovim/issues/224 - int comments = 2; - - // If false, the indexer will be disabled. - bool enabled = true; - - // If true, project paths that were skipped by the whitelist/blacklist will - // be logged. - bool logSkippedPaths = false; - - // Number of indexer threads. If 0, 80% of cores are used. - int threads = 0; - - std::vector whitelist; - } index; - - struct WorkspaceSymbol { - // Maximum workspace search results. - int maxNum = 1000; - // If true, workspace search results will be dynamically rescored/reordered - // as the search progresses. Some clients do their own ordering and assume - // that the results stay sorted in the same order as the search progresses. - bool sort = true; - } workspaceSymbol; - - struct Xref { - // If true, |Location[]| response will include lexical container. - bool container = false; - // Maximum number of definition/reference/... results. - int maxNum = 2000; - } xref; - - //// For debugging - - // Dump AST after parsing if some pattern matches the source filename. - std::vector dumpAST; -}; -MAKE_REFLECT_STRUCT(Config::ClientCapability, snippetSupport); -MAKE_REFLECT_STRUCT(Config::CodeLens, localVariables); -MAKE_REFLECT_STRUCT(Config::Completion, - detailedLabel, - filterAndSort, - includeBlacklist, - includeMaxPathSize, - includeSuffixWhitelist, - includeWhitelist); -MAKE_REFLECT_STRUCT(Config::Diagnostics, - blacklist, - frequencyMs, - onParse, - whitelist) -MAKE_REFLECT_STRUCT(Config::Highlight, - blacklist, - whitelist) -MAKE_REFLECT_STRUCT(Config::Index, - attributeMakeCallsToCtor, - blacklist, - comments, - enabled, - logSkippedPaths, - threads, - whitelist); -MAKE_REFLECT_STRUCT(Config::WorkspaceSymbol, maxNum, sort); -MAKE_REFLECT_STRUCT(Config::Xref, container, maxNum); -MAKE_REFLECT_STRUCT(Config, - compilationDatabaseCommand, - compilationDatabaseDirectory, - cacheDirectory, - cacheFormat, - resourceDirectory, - - extraClangArguments, - - progressReportFrequencyMs, - - showDocumentLinksOnIncludes, - - clientVersion, - - client, - codeLens, - completion, - diagnostics, - highlight, - index, - workspaceSymbol, - xref, - - dumpAST); - -// Expected client version. We show an error if this doesn't match. -constexpr const int kExpectedClientVersion = 3; diff --git a/src/config.hh b/src/config.hh new file mode 100644 index 000000000..2bb303672 --- /dev/null +++ b/src/config.hh @@ -0,0 +1,368 @@ +/* Copyright 2017-2018 ccls Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#pragma once + +#include "serializer.hh" + +#include + +namespace ccls { +/* +The language client plugin needs to send initialization options in the +`initialize` request to the ccls language server. + +If necessary, the command line option --init can be used to override +initialization options specified by the client. For example, in shell syntax: + + '--init={"index": {"comments": 2, "whitelist": ["."]}}' +*/ +struct Config { + // **Not available for configuration** + std::string fallbackFolder; + 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. + // The stdout of the command should be the JSON compilation database. + std::string compilationDatabaseCommand; + // Directory containing compile_commands.json. + std::string compilationDatabaseDirectory; + + 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 + // + // 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. + std::vector extraArgs; + + // Translate absolute paths in compile_commands.json entries, .ccls options + // and cache files. This allows to reuse cache files built otherwhere if the + // source paths are different. + // + // This is a list of colon-separated strings, e.g. ["/container:/host"] + // + // An entry of "clang -I /container/include /container/a.cc" will be + // translated to "clang -I /host/include /host/a.cc". This is simple string + // replacement, so "clang /prefix/container/a.cc" will become "clang + // /prefix/host/a.cc". + std::vector pathMappings; + + // Value to use for clang -resource-dir if not specified. + // + // This option defaults to clang -print-resource-dir and should not be + // specified unless you are using an esoteric configuration. + std::string resourceDir; + } clang; + + struct ClientCapability { + // TextDocumentClientCapabilities.publishDiagnostics.relatedInformation + bool diagnosticsRelatedInformation = true; + // TextDocumentClientCapabilities.documentSymbol.hierarchicalDocumentSymbolSupport + bool hierarchicalDocumentSymbolSupport = true; + // TextDocumentClientCapabilities.definition.linkSupport + bool linkSupport = true; + // TextDocumentClientCapabilities.completion.completionItem.snippetSupport + bool snippetSupport = true; + } client; + + struct CodeLens { + // Enables code lens on parameter and function variables. + bool localVariables = true; + } codeLens; + + struct Completion { + // 0: case-insensitive + // 1: case-folded, i.e. insensitive if no input character is uppercase. + // 2: case-sensitive + int caseSensitivity = 2; + + // Some completion UI, such as Emacs' completion-at-point and company-lsp, + // display completion item label and detail side by side. + // This does not look right, when you see things like: + // "foo" "int foo()" + // "bar" "void bar(int i = 0)" + // When this option is enabled, the completion item label is very detailed, + // it shows the full signature of the candidate. + // The detail just contains the completion item parent context. + bool detailedLabel = true; + + // On large projects, completion can take a long time. By default if ccls + // receives multiple completion requests while completion is still running + // it will only service the newest request. If this is set to false then all + // completion requests will be serviced. + bool dropOldRequests = true; + + // Functions with default arguments, generate one more item per default + // argument. That is, you get something like: + // "int foo()" "Foo" + // "void bar()" "Foo" + // "void bar(int i = 0)" "Foo" + // Be wary, this is quickly quite verbose, + // items can end up truncated by the UIs. + bool duplicateOptional = true; + + // If true, filter and sort completion response. ccls filters and sorts + // completions to try to be nicer to clients that can't handle big numbers + // of completion candidates. This behaviour can be disabled by specifying + // false for the option. This option is the most useful for LSP clients + // 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. + // + // For example, to hide all files in a /CACHE/ folder, use ".*/CACHE/.*" + std::vector blacklist; + + // Maximum path length to show in completion results. Paths longer than + // this will be elided with ".." put at the front. Set to 0 or a negative + // number to disable eliding. + int maxPathSize = 30; + + // Whitelist that file paths will be tested against. If a file path does + // not end in one of these values, it will not be considered for + // auto-completion. An example value is { ".h", ".hpp" } + // + // This is significantly faster than using a regex. + std::vector suffixWhitelist = {".h", ".hpp", ".hh", ".inc"}; + + std::vector whitelist; + } include; + } completion; + + struct Diagnostics { + // Like index.{whitelist,blacklist}, don't publish diagnostics to + // blacklisted files. + std::vector blacklist; + + // Time to wait before computing diagnostics for textDocument/didChange. + // -1: disable diagnostics on change + // 0: immediately + // positive (e.g. 500): wait for 500 milliseconds. didChange requests in + // this period of time will only cause one computation. + int onChange = 1000; + + // Time to wait before computing diagnostics for textDocument/didOpen. + int onOpen = 0; + + // Time to wait before computing diagnostics for textDocument/didSave. + int onSave = 0; + + bool spellChecking = true; + + std::vector whitelist; + } diagnostics; + + // Semantic highlighting + struct Highlight { + // Disable semantic highlighting for files larger than the size. + int64_t largeFileSize = 2 * 1024 * 1024; + + // true: LSP line/character; false: position + bool lsRanges = false; + + // Like index.{whitelist,blacklist}, don't publish semantic highlighting to + // blacklisted files. + std::vector blacklist; + + std::vector whitelist; + } highlight; + + struct Index { + // If a translation unit's absolute path matches any EMCAScript regex in the + // whitelist, or does not match any regex in the blacklist, it will be + // indexed. To only index files in the whitelist, add ".*" to the blacklist. + // `std::regex_search(path, regex, std::regex_constants::match_any)` + // + // Example: `ash/.*\.cc` + std::vector blacklist; + + // 0: none, 1: Doxygen, 2: all comments + // Plugin support for clients: + // - https://github.com/emacs-lsp/lsp-ui + // - https://github.com/autozimu/LanguageClient-neovim/issues/224 + int comments = 2; + + // 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 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 + // still only be indexed for one version. + 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; + + // If true, index parameters in declarations. + bool parametersInDeclarations = true; + + // Number of indexer threads. If 0, 80% of cores are used. + int threads = 0; + + // Whether to reparse a file if write times of its dependencies have + // changed. The file will always be reparsed if its own write time changes. + // 0: no, 1: only during initial load of project, 2: yes + int trackDependency = 2; + + std::vector whitelist; + } index; + + struct Request { + // If the document of a request has not been indexed, wait up to this many + // milleseconds before reporting error. + int64_t timeout = 5000; + } request; + + struct Session { + int maxNum = 10; + } session; + + struct WorkspaceSymbol { + int caseSensitivity = 1; + // Maximum workspace search results. + int maxNum = 1000; + // If true, workspace search results will be dynamically rescored/reordered + // as the search progresses. Some clients do their own ordering and assume + // that the results stay sorted in the same order as the search progresses. + bool sort = true; + } workspaceSymbol; + + struct Xref { + // Maximum number of definition/reference/... results. + 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, 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); +REFLECT_STRUCT(Config::Diagnostics, blacklist, onChange, onOpen, onSave, + spellChecking, whitelist) +REFLECT_STRUCT(Config::Highlight, largeFileSize, lsRanges, blacklist, whitelist) +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, + cache, capabilities, clang, client, codeLens, completion, + diagnostics, highlight, index, request, session, workspaceSymbol, + xref); + +extern Config *g_config; + +void doPathMapping(std::string &arg); +} // namespace ccls diff --git a/src/diagnostics_engine.cc b/src/diagnostics_engine.cc deleted file mode 100644 index 8d66325f0..000000000 --- a/src/diagnostics_engine.cc +++ /dev/null @@ -1,33 +0,0 @@ -#include "diagnostics_engine.h" - -#include "queue_manager.h" - -#include - -void DiagnosticsEngine::Init(Config* config) { - frequencyMs_ = config->diagnostics.frequencyMs; - match_ = std::make_unique(config->diagnostics.whitelist, - config->diagnostics.blacklist); -} - -void DiagnosticsEngine::Publish(WorkingFiles* working_files, - std::string path, - std::vector diagnostics) { - // Cache diagnostics so we can show fixits. - working_files->DoActionOnFile(path, [&](WorkingFile* working_file) { - if (working_file) - working_file->diagnostics_ = diagnostics; - }); - - int64_t now = std::chrono::duration_cast( - std::chrono::high_resolution_clock::now().time_since_epoch()).count(); - if (frequencyMs_ >= 0 && (nextPublish_ <= now || diagnostics.empty()) && - match_->IsMatch(path)) { - nextPublish_ = now + frequencyMs_; - - Out_TextDocumentPublishDiagnostics out; - out.params.uri = lsDocumentUri::FromPath(path); - out.params.diagnostics = diagnostics; - QueueManager::WriteStdout(IpcId::TextDocumentPublishDiagnostics, out); - } -} diff --git a/src/diagnostics_engine.h b/src/diagnostics_engine.h deleted file mode 100644 index ec1066fbd..000000000 --- a/src/diagnostics_engine.h +++ /dev/null @@ -1,16 +0,0 @@ -#include "lsp_diagnostic.h" - -#include "match.h" -#include "working_files.h" - -class DiagnosticsEngine { - std::unique_ptr match_; - int64_t nextPublish_ = 0; - int frequencyMs_; - - public: - void Init(Config*); - void Publish(WorkingFiles* working_files, - std::string path, - std::vector diagnostics); -}; diff --git a/src/file_consumer.cc b/src/file_consumer.cc deleted file mode 100644 index 13042d795..000000000 --- a/src/file_consumer.cc +++ /dev/null @@ -1,107 +0,0 @@ -#include "file_consumer.h" - -#include "clang_utils.h" -#include "indexer.h" -#include "platform.h" -#include "utils.h" - -#include - -namespace { - -optional GetFileContents(const std::string& path, - FileContentsMap* file_contents) { - auto it = file_contents->find(path); - if (it == file_contents->end()) { - optional content = ReadContent(path); - if (content) - (*file_contents)[path] = FileContents(path, *content); - return content; - } - return it->second.content; -} - -} // namespace - -bool operator==(const CXFileUniqueID& a, const CXFileUniqueID& b) { - return a.data[0] == b.data[0] && a.data[1] == b.data[1] && - a.data[2] == b.data[2]; -} - -bool FileConsumerSharedState::Mark(const std::string& file) { - std::lock_guard lock(mutex); - return used_files.insert(file).second; -} - -void FileConsumerSharedState::Reset(const std::string& file) { - std::lock_guard lock(mutex); - auto it = used_files.find(file); - if (it != used_files.end()) - used_files.erase(it); -} - -FileConsumer::FileConsumer(FileConsumerSharedState* shared_state, - const std::string& parse_file) - : shared_(shared_state), parse_file_(parse_file) {} - -IndexFile* FileConsumer::TryConsumeFile(CXFile file, - bool* is_first_ownership, - FileContentsMap* file_contents_map) { - assert(is_first_ownership); - - CXFileUniqueID file_id; - if (clang_getFileUniqueID(file, &file_id) != 0) { - EmitError(file); - return nullptr; - } - - // Try to find cached local result. - auto it = local_.find(file_id); - if (it != local_.end()) { - *is_first_ownership = false; - return it->second.get(); - } - - std::string file_name = FileName(file); - - // No result in local; we need to query global. - bool did_insert = shared_->Mark(file_name); - - // We did not take the file from global. Cache that we failed so we don't try - // again and return nullptr. - if (!did_insert) { - local_[file_id] = nullptr; - return nullptr; - } - - // Read the file contents, if we fail then we cannot index the file. - optional contents = - GetFileContents(file_name, file_contents_map); - if (!contents) { - *is_first_ownership = false; - return nullptr; - } - - // Build IndexFile instance. - *is_first_ownership = true; - local_[file_id] = std::make_unique(file_name, *contents); - return local_[file_id].get(); -} - -std::vector> FileConsumer::TakeLocalState() { - std::vector> result; - for (auto& entry : local_) { - if (entry.second) - result.push_back(std::move(entry.second)); - } - return result; -} - -void FileConsumer::EmitError(CXFile file) const { - std::string file_name = ToString(clang_getFileName(file)); - // TODO: Investigate this more, why can we get an empty file name? - if (!file_name.empty()) { - LOG_S(ERROR) << "Could not get unique file id for " << file_name - << " when parsing " << parse_file_; - } -} \ No newline at end of file diff --git a/src/file_consumer.h b/src/file_consumer.h deleted file mode 100644 index 6ac94eddb..000000000 --- a/src/file_consumer.h +++ /dev/null @@ -1,62 +0,0 @@ -#pragma once - -#include "file_contents.h" -#include "utils.h" - -#include - -#include -#include -#include -#include - -struct IndexFile; - -// Needed for unordered_map usage below. -MAKE_HASHABLE(CXFileUniqueID, t.data[0], t.data[1], t.data[2]); -bool operator==(const CXFileUniqueID& a, const CXFileUniqueID& b); - -struct FileConsumerSharedState { - mutable std::unordered_set used_files; - mutable std::mutex mutex; - - // Mark the file as used. Returns true if the file was not previously used. - bool Mark(const std::string& file); - // Reset the used state (ie, mark the file as unused). - void Reset(const std::string& file); -}; - -// FileConsumer is used by the indexer. When it encouters a file, it tries to -// take ownership over it. If the indexer has ownership over a file, it will -// produce an index, otherwise, it will emit nothing for that declarations -// and references coming from that file. -// -// The indexer does this because header files do not have their own translation -// units but we still want to index them. -struct FileConsumer { - FileConsumer(FileConsumerSharedState* shared_state, - const std::string& parse_file); - - // Returns true if this instance owns given |file|. This will also attempt to - // take ownership over |file|. - // - // Returns IndexFile for the file or nullptr. |is_first_ownership| is set - // to true iff the function just took ownership over the file. Otherwise it - // is set to false. - // - // note: file_contents is passed as a parameter instead of as a member - // variable since it is large and we do not want to copy it. - IndexFile* TryConsumeFile(CXFile file, - bool* is_first_ownership, - FileContentsMap* file_contents); - - // Returns and passes ownership of all local state. - std::vector> TakeLocalState(); - - private: - void EmitError(CXFile file) const; - - std::unordered_map> local_; - FileConsumerSharedState* shared_; - std::string parse_file_; -}; \ No newline at end of file diff --git a/src/file_contents.cc b/src/file_contents.cc deleted file mode 100644 index 4e716e65a..000000000 --- a/src/file_contents.cc +++ /dev/null @@ -1,29 +0,0 @@ -#include "file_contents.h" - -FileContents::FileContents() : line_offsets_{0} {} - -FileContents::FileContents(const std::string& path, const std::string& content) - : path(path), content(content) { - line_offsets_.push_back(0); - for (size_t i = 0; i < content.size(); i++) { - if (content[i] == '\n') - line_offsets_.push_back(i + 1); - } -} - -optional FileContents::ToOffset(Position p) const { - if (0 <= p.line && size_t(p.line) < line_offsets_.size()) { - int ret = line_offsets_[p.line] + p.column; - if (size_t(ret) < content.size()) - return ret; - } - return nullopt; -} - -optional FileContents::ContentsInRange(Range range) const { - optional start_offset = ToOffset(range.start), - end_offset = ToOffset(range.end); - if (start_offset && end_offset && *start_offset < *end_offset) - return content.substr(*start_offset, *end_offset - *start_offset); - return nullopt; -} diff --git a/src/file_contents.h b/src/file_contents.h deleted file mode 100644 index 0a6cffb51..000000000 --- a/src/file_contents.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include "position.h" - -#include "optional.h" - -#include -#include -#include - -struct FileContents { - FileContents(); - FileContents(const std::string& path, const std::string& content); - - optional ToOffset(Position p) const; - optional ContentsInRange(Range range) const; - - std::string path; - std::string content; - // {0, 1 + position of first newline, 1 + position of second newline, ...} - std::vector line_offsets_; -}; - -using FileContentsMap = std::unordered_map; diff --git a/src/filesystem.cc b/src/filesystem.cc new file mode 100644 index 000000000..8a9e0fffc --- /dev/null +++ b/src/filesystem.cc @@ -0,0 +1,71 @@ +/* Copyright 2017-2018 ccls Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#include "filesystem.hh" +using namespace llvm; + +#include "utils.hh" + +#include +#include + +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)) + return; + sys::fs::UniqueID id; + std::vector curr{folder}; + std::vector> succ; + std::set seen{status.getUniqueID()}; + while (curr.size() || succ.size()) { + if (curr.empty()) { + for (auto &it : succ) + if (!seen.count(it.second.getUniqueID())) + curr.push_back(std::move(it.first)); + succ.clear(); + } else { + 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); + if ((filename[0] == '.' && filename != ".ccls") || + sys::fs::status(path, status, false)) + continue; + if (sys::fs::is_symlink_file(status)) { + if (sys::fs::status(path, status, true)) + continue; + if (sys::fs::is_directory(status)) { + if (recursive) + succ.emplace_back(path, status); + continue; + } + } + 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())) { + curr.push_back(path); + seen.insert(id); + } + } + } + } +} diff --git a/src/filesystem.hh b/src/filesystem.hh new file mode 100644 index 000000000..3ec639720 --- /dev/null +++ b/src/filesystem.hh @@ -0,0 +1,11 @@ +#pragma once + +#include +#include + +#include +#include + +void getFilesInFolder(std::string folder, bool recursive, + bool add_folder_to_path, + const std::function &handler); diff --git a/src/fuzzy_match.cc b/src/fuzzy_match.cc index bda1a6883..0f57652a8 100644 --- a/src/fuzzy_match.cc +++ b/src/fuzzy_match.cc @@ -1,120 +1,203 @@ -#include "fuzzy_match.h" +/* Copyright 2017-2018 ccls Authors -#include -#include -#include +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 -// Penalty of dropping a leading character in str -constexpr int kLeadingGapScore = -4; -// Penalty of dropping a non-leading character in str -constexpr int kGapScore = -5; -// Bonus of aligning with an initial character of a word in pattern. Must be -// greater than 1 -constexpr int kPatternStartMultiplier = 2; +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ -constexpr int kWordStartScore = 50; -constexpr int kNonWordScore = 40; -constexpr int kCaseMatchScore = 2; +#include "fuzzy_match.hh" -// Less than kWordStartScore -constexpr int kConsecutiveScore = kWordStartScore + kGapScore; -// Slightly less than kConsecutiveScore -constexpr int kCamelScore = kWordStartScore + kGapScore - 1; +#include +#include +#include +#include -enum class CharClass { Lower, Upper, Digit, NonWord }; +namespace ccls { +namespace { +enum CharClass { Other, Lower, Upper }; +enum CharRole { None, Tail, Head }; -static CharClass GetCharClass(int c) { +CharClass getCharClass(int c) { if (islower(c)) - return CharClass::Lower; + return Lower; if (isupper(c)) - return CharClass::Upper; - if (isdigit(c)) - return CharClass::Digit; - return CharClass::NonWord; + return Upper; + return Other; } -static int GetScoreFor(CharClass prev, CharClass curr) { - if (prev == CharClass::NonWord && curr != CharClass::NonWord) - return kWordStartScore; - if ((prev == CharClass::Lower && curr == CharClass::Upper) || - (prev != CharClass::Digit && curr == CharClass::Digit)) - return kCamelScore; - if (curr == CharClass::NonWord) - return kNonWordScore; - return 0; +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; + *class_set = 1 << cur; + auto fn = [&]() { + if (cur == Other) + return None; + // U(U)L is Head while U(U)U is Tail + return pre == Other || (cur == Upper && (pre == Lower || suc == Lower)) + ? Head + : Tail; + }; + for (size_t i = 0; i < s.size() - 1; i++) { + suc = getCharClass(s[i + 1]); + *class_set |= 1 << suc; + roles[i] = fn(); + pre = cur; + cur = suc; + } + roles[s.size() - 1] = fn(); +} +} // namespace + +int FuzzyMatcher::missScore(int j, bool last) { + int s = -3; + if (last) + s -= 10; + if (text_role[j] == Head) + s -= 10; + return s; } -/* -fuzzyEvaluate implements a global sequence alignment algorithm to find the -maximum accumulated score by aligning `pattern` to `str`. It applies when -`pattern` is a subsequence of `str`. - -Scoring criteria -- Prefer matches at the start of a word, or the start of subwords in -CamelCase/camelCase/camel123 words. See kWordStartScore/kCamelScore -- Non-word characters matter. See kNonWordScore -- The first characters of words of `pattern` receive bonus because they usually -have more significance than the rest. See kPatternStartMultiplier -- Superfluous characters in `str` will reduce the score (gap penalty). See -kGapScore -- Prefer early occurrence of the first character. See kLeadingGapScore/kGapScore - -The recurrence of the dynamic programming: -dp[i][j]: maximum accumulated score by aligning pattern[0..i] to str[0..j] -dp[0][j] = leading_gap_penalty(0, j) + score[j] -dp[i][j] = max(dp[i-1][j-1] + CONSECUTIVE_SCORE, max(dp[i-1][k] + -gap_penalty(k+1, j) + score[j] : k < j)) -The first dimension can be suppressed since we do not need a matching scheme, -which reduces the space complexity from O(N*M) to O(M) -*/ -int FuzzyEvaluate(std::string_view pattern, - std::string_view str, - std::vector& score, - std::vector& dp) { - bool pfirst = true, // aligning the first character of pattern - pstart = true; // whether we are aligning the start of a word in pattern - int uleft = 0, // value of the upper left cell - ulefts = 0, // maximum value of uleft and cells on the left - left, lefts; // similar to uleft/ulefts, but for the next row - - // Calculate position score for each character in str. - CharClass prev = CharClass::NonWord; - for (int i = 0; i < int(str.size()); i++) { - CharClass cur = GetCharClass(str[i]); - score[i] = GetScoreFor(prev, cur); - prev = cur; +int FuzzyMatcher::matchScore(int i, int j, bool last) { + int s = 0; + // Case matching. + if (pat[i] == text[j]) { + s++; + // pat contains uppercase letters or prefix matching. + if ((pat_set & 1 << Upper) || i == j) + s++; } - std::fill_n(dp.begin(), str.size(), kMinScore); + if (pat_role[i] == Head) { + if (text_role[j] == Head) + s += 30; + else if (text_role[j] == Tail) + s -= 10; + } + // Matching a tail while previous char wasn't matched. + if (text_role[j] == Tail && i && !last) + s -= 30; + // First char of pat matches a tail. + if (i == 0 && text_role[j] == Tail) + s -= 40; + return s; +} - // Align each character of pattern. - for (unsigned char pc : pattern) { - if (isspace(pc)) { - pstart = true; - continue; +FuzzyMatcher::FuzzyMatcher(std::string_view pattern, int sensitivity) { + calculateRoles(pattern, pat_role, &pat_set); + if (sensitivity == 1) + sensitivity = pat_set & 1 << Upper ? 2 : 0; + case_sensitivity = sensitivity; + size_t n = 0; + for (size_t i = 0; i < pattern.size(); i++) + if (pattern[i] != ' ') { + pat += pattern[i]; + low_pat[n] = ::tolower(pattern[i]); + pat_role[n] = pat_role[i]; + n++; } - lefts = kMinScore; - // Enumerate the character in str to be aligned with pc. - for (int i = 0; i < int(str.size()); i++) { - left = dp[i]; - lefts = std::max(lefts + kGapScore, left); - // Use lower() if case-insensitive - if (tolower(pc) == tolower(str[i])) { - int t = score[i] * (pstart ? kPatternStartMultiplier : 1); - dp[i] = (pfirst ? kLeadingGapScore * i + t - : std::max(uleft + kConsecutiveScore, ulefts + t)) + - (pc == str[i] ? kCaseMatchScore : 0); - } else - dp[i] = kMinScore; - uleft = left; - ulefts = lefts; +} + +int FuzzyMatcher::match(std::string_view text, bool strict) { + if (pat.empty() != text.empty()) + return kMinScore; + int n = int(text.size()); + if (n > kMaxText) + return kMinScore + 1; + this->text = text; + for (int i = 0; i < n; i++) + low_text[i] = ::tolower(text[i]); + 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][1] = kMinScore * 2; + } + for (int i = 0; i < int(pat.size()); i++) { + int(*pre)[2] = dp[i & 1]; + 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)); + // 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)) + : kMinScore * 2; } - pfirst = pstart = false; } // Enumerate the end position of the match in str. Each removed trailing - // character has a penulty of kGapScore. - lefts = kMinScore; - for (int i = 0; i < int(str.size()); i++) - lefts = std::max(lefts + kGapScore, dp[i]); - return lefts; + // character has a penulty. + int ret = kMinScore; + for (int j = pat.size(); j <= n; j++) + ret = std::max(ret, dp[pat.size() & 1][j][1] - 2 * (n - j)); + return ret; +} +} // namespace ccls + +#if 0 +TEST_SUITE("fuzzy_match") { + bool Ranks(std::string_view pat, std::vector texts) { + FuzzyMatcher fuzzy(pat, 0); + std::vector scores; + for (auto text : texts) + scores.push_back(fuzzy.Match(text)); + bool ret = true; + for (size_t i = 0; i < texts.size() - 1; i++) + if (scores[i] < scores[i + 1]) { + ret = false; + break; + } + if (!ret) { + for (size_t i = 0; i < texts.size(); i++) + printf("%s %d ", texts[i], scores[i]); + puts(""); + } + return ret; + } + + TEST_CASE("test") { + FuzzyMatcher fuzzy("", 0); + CHECK(fuzzy.Match("") == 0); + CHECK(fuzzy.Match("aaa") < 0); + + // case + CHECK(Ranks("monad", {"monad", "Monad", "mONAD"})); + // initials + CHECK(Ranks("ab", {"ab", "aoo_boo", "acb"})); + CHECK(Ranks("CC", {"CamelCase", "camelCase", "camelcase"})); + CHECK(Ranks("cC", {"camelCase", "CamelCase", "camelcase"})); + CHECK(Ranks("c c", {"camelCase", "camel case", "CamelCase", "camelcase", + "camel ace"})); + CHECK(Ranks("Da.Te", + {"Data.Text", "Data.Text.Lazy", "Data.Aeson.Encoding.text"})); + CHECK(Ranks("foo bar.h", {"foo/bar.h", "foobar.h"})); + // prefix + CHECK(Ranks("is", {"isIEEE", "inSuf"})); + // shorter + CHECK(Ranks("ma", {"map", "many", "maximum"})); + CHECK(Ranks("print", {"printf", "sprintf"})); + // score(PRINT) = kMinScore + CHECK(Ranks("ast", {"ast", "AST", "INT_FAST16_MAX"})); + // score(PRINT) > kMinScore + CHECK(Ranks("Int", {"int", "INT", "PRINT"})); + } } +#endif diff --git a/src/fuzzy_match.h b/src/fuzzy_match.h deleted file mode 100644 index 3e38a5a76..000000000 --- a/src/fuzzy_match.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include - -#include -#include - -// Negative but far from INT_MIN so that intermediate results are hard to -// overflow -constexpr int kMinScore = INT_MIN / 2; - -// Evaluate the score matching |pattern| against |str|, the larger the better. -// |score| and |dp| must be at least as long as |str|. -int FuzzyEvaluate(std::string_view pattern, - std::string_view str, - std::vector& score, - std::vector& dp); diff --git a/src/fuzzy_match.hh b/src/fuzzy_match.hh new file mode 100644 index 000000000..723f62203 --- /dev/null +++ b/src/fuzzy_match.hh @@ -0,0 +1,46 @@ +/* Copyright 2017-2018 ccls Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#pragma once + +#include +#include +#include + +namespace ccls { +class FuzzyMatcher { +public: + constexpr static int kMaxPat = 100; + constexpr static int kMaxText = 200; + // Negative but far from INT_MIN so that intermediate results are hard to + // overflow. + constexpr static int kMinScore = INT_MIN / 4; + + FuzzyMatcher(std::string_view pattern, int case_sensitivity); + int match(std::string_view text, bool strict); + +private: + int case_sensitivity; + std::string pat; + std::string_view text; + int pat_set, text_set; + char low_pat[kMaxPat], low_text[kMaxText]; + 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); +}; +} // namespace ccls diff --git a/src/hierarchy.hh b/src/hierarchy.hh new file mode 100644 index 000000000..aa498bd82 --- /dev/null +++ b/src/hierarchy.hh @@ -0,0 +1,44 @@ +/* Copyright 2017-2018 ccls Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#pragma once + +#include "lsp.hh" + +#include +#include + +namespace ccls { +template +std::vector flattenHierarchy(const std::optional &root) { + if (!root) + return {}; + std::vector ret; + std::queue q; + for (auto &entry : root->children) + q.push(&entry); + while (q.size()) { + auto *entry = q.front(); + q.pop(); + if (entry->location.uri.raw_uri.size()) + ret.push_back({entry->location}); + for (auto &entry1 : entry->children) + q.push(&entry1); + } + std::sort(ret.begin(), ret.end()); + ret.erase(std::unique(ret.begin(), ret.end()), ret.end()); + return ret; +} +} // namespace ccls diff --git a/src/iindexer.cc b/src/iindexer.cc deleted file mode 100644 index e07584100..000000000 --- a/src/iindexer.cc +++ /dev/null @@ -1,92 +0,0 @@ -#include "iindexer.h" - -#include "indexer.h" - -namespace { -struct ClangIndexer : IIndexer { - ~ClangIndexer() override = default; - - optional>> Index( - Config* config, - FileConsumerSharedState* file_consumer_shared, - std::string file, - const std::vector& args, - const std::vector& file_contents, - PerformanceImportFile* perf) override { - bool dump_ast = false; - for (const std::string& pattern : config->dumpAST) - if (file.find(pattern) != std::string::npos) { - dump_ast = true; - break; - } - return Parse(config, file_consumer_shared, file, args, file_contents, perf, - &index, dump_ast); - } - - // Note: constructing this acquires a global lock - ClangIndex index; -}; - -struct TestIndexer : IIndexer { - static std::unique_ptr FromEntries( - const std::vector& entries) { - auto result = std::make_unique(); - - for (const TestEntry& entry : entries) { - std::vector> indexes; - - if (entry.num_indexes > 0) - indexes.push_back(std::make_unique(entry.path, "")); - for (int i = 1; i < entry.num_indexes; ++i) { - indexes.push_back(std::make_unique( - entry.path + "_extra_" + std::to_string(i) + ".h", "")); - } - - result->indexes.insert(std::make_pair(entry.path, std::move(indexes))); - } - - return result; - } - - ~TestIndexer() override = default; - - optional>> Index( - Config* config, - FileConsumerSharedState* file_consumer_shared, - std::string file, - const std::vector& args, - const std::vector& file_contents, - PerformanceImportFile* perf) override { - auto it = indexes.find(file); - if (it == indexes.end()) { - // Don't return any indexes for unexpected data. - assert(false && "no indexes"); - return nullopt; - } - - // FIXME: allow user to control how many times we return the index for a - // specific file (atm it is always 1) - auto result = std::move(it->second); - indexes.erase(it); - return std::move(result); - } - - std::unordered_map>> - indexes; -}; - -} // namespace - -IIndexer::TestEntry::TestEntry(const std::string& path, int num_indexes) - : path(path), num_indexes(num_indexes) {} - -// static -std::unique_ptr IIndexer::MakeClangIndexer() { - return std::make_unique(); -} - -// static -std::unique_ptr IIndexer::MakeTestIndexer( - std::initializer_list entries) { - return TestIndexer::FromEntries(entries); -} diff --git a/src/iindexer.h b/src/iindexer.h deleted file mode 100644 index f8a453472..000000000 --- a/src/iindexer.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include - -#include -#include -#include -#include - -// TODO: -// - rename indexer.h to clang_indexer.h and pull out non-clang specific code -// like IndexFile -// - rename this file to indexer.h - -struct Config; -struct IndexFile; -struct FileContents; -struct FileConsumerSharedState; -struct PerformanceImportFile; - -// Abstracts away the actual indexing process. Each IIndexer instance is -// per-thread and constructing an instance may be extremely expensive (ie, -// acquire a lock) and should be done as rarely as possible. -struct IIndexer { - struct TestEntry { - std::string path; - int num_indexes = 0; - - TestEntry(const std::string& path, int num_indexes); - }; - - static std::unique_ptr MakeClangIndexer(); - static std::unique_ptr MakeTestIndexer( - std::initializer_list entries); - - virtual ~IIndexer() = default; - virtual optional>> Index( - Config* config, - FileConsumerSharedState* file_consumer_shared, - std::string file, - const std::vector& args, - const std::vector& file_contents, - PerformanceImportFile* perf) = 0; -}; diff --git a/src/import_manager.cc b/src/import_manager.cc deleted file mode 100644 index 11b13f296..000000000 --- a/src/import_manager.cc +++ /dev/null @@ -1,14 +0,0 @@ -#include "import_manager.h" - -bool ImportManager::TryMarkDependencyImported(const std::string& path) { - std::lock_guard lock(dependency_mutex_); - return dependency_imported_.insert(path).second; -} - -bool ImportManager::StartQueryDbImport(const std::string& path) { - return querydb_processing_.insert(path).second; -} - -void ImportManager::DoneQueryDbImport(const std::string& path) { - querydb_processing_.erase(path); -} diff --git a/src/import_manager.h b/src/import_manager.h deleted file mode 100644 index 57dc7ffe5..000000000 --- a/src/import_manager.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include -#include -#include - -// Manages files inside of the indexing pipeline so we don't have the same file -// being imported multiple times. -// -// NOTE: This is not thread safe and should only be used on the querydb thread. -struct ImportManager { - // Try to mark the given dependency as imported. A dependency can only ever be - // imported once. - bool TryMarkDependencyImported(const std::string& path); - - // Try to import the given file into querydb. We should only ever be - // importing a file into querydb once per file. Returns true if the file - // can be imported. - bool StartQueryDbImport(const std::string& path); - - // The file has been fully imported and can be imported again later on. - void DoneQueryDbImport(const std::string& path); - - std::unordered_set querydb_processing_; - - // TODO: use std::shared_mutex so we can have multiple readers. - std::mutex dependency_mutex_; - std::unordered_set dependency_imported_; -}; \ No newline at end of file diff --git a/src/import_pipeline.cc b/src/import_pipeline.cc deleted file mode 100644 index dc84f642a..000000000 --- a/src/import_pipeline.cc +++ /dev/null @@ -1,913 +0,0 @@ -#include "import_pipeline.h" - -#include "cache_manager.h" -#include "config.h" -#include "diagnostics_engine.h" -#include "iindexer.h" -#include "import_manager.h" -#include "lsp.h" -#include "message_handler.h" -#include "platform.h" -#include "project.h" -#include "query_utils.h" -#include "queue_manager.h" -#include "timer.h" -#include "timestamp_manager.h" - -#include -#include - -#include -#include -#include -#include - -namespace { - -struct Out_Progress : public lsOutMessage { - struct Params { - int indexRequestCount = 0; - int doIdMapCount = 0; - int loadPreviousIndexCount = 0; - int onIdMappedCount = 0; - int onIndexedCount = 0; - int activeThreads = 0; - }; - std::string method = "$cquery/progress"; - Params params; -}; -MAKE_REFLECT_STRUCT(Out_Progress::Params, - indexRequestCount, - doIdMapCount, - loadPreviousIndexCount, - onIdMappedCount, - onIndexedCount, - activeThreads); -MAKE_REFLECT_STRUCT(Out_Progress, jsonrpc, method, params); - -struct IModificationTimestampFetcher { - virtual ~IModificationTimestampFetcher() = default; - virtual optional GetModificationTime(const std::string& path) = 0; -}; -struct RealModificationTimestampFetcher : IModificationTimestampFetcher { - ~RealModificationTimestampFetcher() override = default; - - // IModificationTimestamp: - optional GetModificationTime(const std::string& path) override { - return GetLastModificationTime(path); - } -}; -struct FakeModificationTimestampFetcher : IModificationTimestampFetcher { - std::unordered_map> entries; - - ~FakeModificationTimestampFetcher() override = default; - - // IModificationTimestamp: - optional GetModificationTime(const std::string& path) override { - auto it = entries.find(path); - assert(it != entries.end()); - return it->second; - } -}; - -long long GetCurrentTimeInMilliseconds() { - auto time_since_epoch = Timer::Clock::now().time_since_epoch(); - long long elapsed_milliseconds = - std::chrono::duration_cast(time_since_epoch) - .count(); - return elapsed_milliseconds; -} - -struct ActiveThread { - ActiveThread(Config* config, ImportPipelineStatus* status) - : config_(config), status_(status) { - if (config_->progressReportFrequencyMs < 0) - return; - - ++status_->num_active_threads; - } - ~ActiveThread() { - if (config_->progressReportFrequencyMs < 0) - return; - - --status_->num_active_threads; - EmitProgress(); - } - - // Send indexing progress to client if reporting is enabled. - void EmitProgress() { - auto* queue = QueueManager::instance(); - Out_Progress out; - out.params.indexRequestCount = queue->index_request.Size(); - out.params.doIdMapCount = queue->do_id_map.Size(); - out.params.loadPreviousIndexCount = queue->load_previous_index.Size(); - out.params.onIdMappedCount = queue->on_id_mapped.Size(); - out.params.onIndexedCount = queue->on_indexed.Size(); - out.params.activeThreads = status_->num_active_threads; - - // Ignore this progress update if the last update was too recent. - if (config_->progressReportFrequencyMs != 0) { - // Make sure we output a status update if queue lengths are zero. - bool all_zero = - out.params.indexRequestCount == 0 && out.params.doIdMapCount == 0 && - out.params.loadPreviousIndexCount == 0 && - out.params.onIdMappedCount == 0 && out.params.onIndexedCount == 0 && - out.params.activeThreads == 0; - if (!all_zero && - GetCurrentTimeInMilliseconds() < status_->next_progress_output) - return; - status_->next_progress_output = - GetCurrentTimeInMilliseconds() + config_->progressReportFrequencyMs; - } - - QueueManager::WriteStdout(IpcId::Unknown, out); - } - - Config* config_; - ImportPipelineStatus* status_; -}; - -enum class ShouldParse { Yes, No, NoSuchFile }; - -// Checks if |path| needs to be reparsed. This will modify cached state -// such that calling this function twice with the same path may return true -// the first time but will return false the second. -// -// |from|: The file which generated the parse request for this file. -ShouldParse FileNeedsParse( - bool is_interactive, - TimestampManager* timestamp_manager, - IModificationTimestampFetcher* modification_timestamp_fetcher, - ImportManager* import_manager, - const std::shared_ptr& cache_manager, - IndexFile* opt_previous_index, - const std::string& path, - const std::vector& args, - const optional& from) { - auto unwrap_opt = [](const optional& opt) -> std::string { - if (opt) - return " (via " + *opt + ")"; - return ""; - }; - - // If the file is a dependency but another file as already imported it, - // don't bother. - if (!is_interactive && from && - !import_manager->TryMarkDependencyImported(path)) { - return ShouldParse::No; - } - - optional modification_timestamp = - modification_timestamp_fetcher->GetModificationTime(path); - - // Cannot find file. - if (!modification_timestamp) - return ShouldParse::NoSuchFile; - - optional last_cached_modification = - timestamp_manager->GetLastCachedModificationTime(cache_manager.get(), - path); - - // File has been changed. - if (!last_cached_modification || - modification_timestamp != *last_cached_modification) { - LOG_S(INFO) << "Timestamp has changed for " << path << unwrap_opt(from); - return ShouldParse::Yes; - } - - // Command-line arguments changed. - auto is_file = [](const std::string& arg) { - return EndsWithAny(arg, {".h", ".c", ".cc", ".cpp", ".hpp", ".m", ".mm"}); - }; - if (opt_previous_index) { - auto& prev_args = opt_previous_index->args; - bool same = prev_args.size() == args.size(); - for (size_t i = 0; i < args.size() && same; ++i) { - same = prev_args[i] == args[i] || - (is_file(prev_args[i]) && is_file(args[i])); - } - if (!same) { - LOG_S(INFO) << "Arguments have changed for " << path << unwrap_opt(from); - return ShouldParse::Yes; - } - } - - // File has not changed, do not parse it. - return ShouldParse::No; -}; - -enum CacheLoadResult { Parse, DoNotParse }; -CacheLoadResult TryLoadFromCache( - FileConsumerSharedState* file_consumer_shared, - TimestampManager* timestamp_manager, - IModificationTimestampFetcher* modification_timestamp_fetcher, - ImportManager* import_manager, - const std::shared_ptr& cache_manager, - bool is_interactive, - const Project::Entry& entry, - const std::string& path_to_index) { - // Always run this block, even if we are interactive, so we can check - // dependencies and reset files in |file_consumer_shared|. - IndexFile* previous_index = cache_manager->TryLoad(path_to_index); - if (!previous_index) - return CacheLoadResult::Parse; - - // If none of the dependencies have changed and the index is not - // interactive (ie, requested by a file save), skip parsing and just load - // from cache. - - // Check timestamps and update |file_consumer_shared|. - ShouldParse path_state = FileNeedsParse( - is_interactive, timestamp_manager, modification_timestamp_fetcher, - import_manager, cache_manager, previous_index, path_to_index, entry.args, - nullopt); - if (path_state == ShouldParse::Yes) - file_consumer_shared->Reset(path_to_index); - - // Target file does not exist on disk, do not emit any indexes. - // TODO: Dependencies should be reassigned to other files. We can do this by - // updating the "primary_file" if it doesn't exist. Might not actually be a - // problem in practice. - if (path_state == ShouldParse::NoSuchFile) - return CacheLoadResult::DoNotParse; - - bool needs_reparse = is_interactive || path_state == ShouldParse::Yes; - - for (const std::string& dependency : previous_index->dependencies) { - assert(!dependency.empty()); - - if (FileNeedsParse(is_interactive, timestamp_manager, - modification_timestamp_fetcher, import_manager, - cache_manager, previous_index, dependency, entry.args, - previous_index->path) == ShouldParse::Yes) { - needs_reparse = true; - - // Do not break here, as we need to update |file_consumer_shared| for - // every dependency that needs to be reparsed. - file_consumer_shared->Reset(dependency); - } - } - - // FIXME: should we still load from cache? - if (needs_reparse) - return CacheLoadResult::Parse; - - // No timestamps changed - load directly from cache. - LOG_S(INFO) << "Skipping parse; no timestamp change for " << path_to_index; - - // TODO/FIXME: real perf - PerformanceImportFile perf; - - std::vector result; - result.push_back(Index_DoIdMap(cache_manager->TakeOrLoad(path_to_index), - cache_manager, perf, is_interactive, - false /*write_to_disk*/)); - for (const std::string& dependency : previous_index->dependencies) { - // Only load a dependency if it is not already loaded. - // - // This is important for perf in large projects where there are lots of - // dependencies shared between many files. - if (!file_consumer_shared->Mark(dependency)) - continue; - - LOG_S(INFO) << "Emitting index result for " << dependency << " (via " - << previous_index->path << ")"; - - std::unique_ptr dependency_index = - cache_manager->TryTakeOrLoad(dependency); - - // |dependency_index| may be null if there is no cache for it but - // another file has already started importing it. - if (!dependency_index) - continue; - - result.push_back(Index_DoIdMap(std::move(dependency_index), cache_manager, - perf, is_interactive, - false /*write_to_disk*/)); - } - - QueueManager::instance()->do_id_map.EnqueueAll(std::move(result)); - return CacheLoadResult::DoNotParse; -} - -std::vector PreloadFileContents( - const std::shared_ptr& cache_manager, - const Project::Entry& entry, - const std::string& entry_contents, - const std::string& path_to_index) { - // Load file contents for all dependencies into memory. If the dependencies - // for the file changed we may not end up using all of the files we - // preloaded. If a new dependency was added the indexer will grab the file - // contents as soon as possible. - // - // We do this to minimize the race between indexing a file and capturing the - // file contents. - // - // TODO: We might be able to optimize perf by only copying for files in - // working_files. We can pass that same set of files to the indexer as - // well. We then default to a fast file-copy if not in working set. - - // index->file_contents comes from cache, so we need to check if that cache is - // still valid. if so, we can use it, otherwise we need to load from disk. - auto get_latest_content = [](const std::string& path, int64_t cached_time, - const std::string& cached) -> std::string { - optional mod_time = GetLastModificationTime(path); - if (!mod_time) - return ""; - - if (*mod_time == cached_time) - return cached; - - optional fresh_content = ReadContent(path); - if (!fresh_content) { - LOG_S(ERROR) << "Failed to load content for " << path; - return ""; - } - return *fresh_content; - }; - - std::vector file_contents; - file_contents.push_back(FileContents(entry.filename, entry_contents)); - cache_manager->IterateLoadedCaches([&](IndexFile* index) { - if (index->path == entry.filename) - return; - file_contents.push_back(FileContents( - index->path, - get_latest_content(index->path, index->last_modification_time, - index->file_contents))); - }); - - return file_contents; -} - -void ParseFile(Config* config, - DiagnosticsEngine* diag_engine, - WorkingFiles* working_files, - FileConsumerSharedState* file_consumer_shared, - TimestampManager* timestamp_manager, - IModificationTimestampFetcher* modification_timestamp_fetcher, - ImportManager* import_manager, - IIndexer* indexer, - const Index_Request& request, - const Project::Entry& entry) { - // If the file is inferred, we may not actually be able to parse that file - // directly (ie, a header file, which are not listed in the project). If this - // file is inferred, then try to use the file which originally imported it. - std::string path_to_index = entry.filename; - if (entry.is_inferred) { - IndexFile* entry_cache = request.cache_manager->TryLoad(entry.filename); - if (entry_cache) - path_to_index = entry_cache->import_file; - } - - // Try to load the file from cache. - if (TryLoadFromCache(file_consumer_shared, timestamp_manager, - modification_timestamp_fetcher, import_manager, - request.cache_manager, request.is_interactive, entry, - path_to_index) == CacheLoadResult::DoNotParse) { - return; - } - - LOG_S(INFO) << "Parsing " << path_to_index; - std::vector file_contents = PreloadFileContents( - request.cache_manager, entry, request.contents, path_to_index); - - std::vector result; - PerformanceImportFile perf; - auto indexes = indexer->Index(config, file_consumer_shared, path_to_index, - entry.args, file_contents, &perf); - - if (!indexes) { - if (config->index.enabled && - !std::holds_alternative(request.id)) { - Out_Error out; - out.id = request.id; - out.error.code = lsErrorCodes::InternalError; - out.error.message = "Failed to index " + path_to_index; - QueueManager::WriteStdout(IpcId::Unknown, out); - } - return; - } - - for (std::unique_ptr& new_index : *indexes) { - Timer time; - - // Only emit diagnostics for non-interactive sessions, which makes it easier - // to identify indexing problems. For interactive sessions, diagnostics are - // handled by code completion. - if (!request.is_interactive) - diag_engine->Publish(working_files, new_index->path, - new_index->diagnostics_); - - // When main thread does IdMap request it will request the previous index if - // needed. - LOG_S(INFO) << "Emitting index result for " << new_index->path; - result.push_back(Index_DoIdMap(std::move(new_index), request.cache_manager, - perf, request.is_interactive, - true /*write_to_disk*/)); - } - - QueueManager::instance()->do_id_map.EnqueueAll(std::move(result), - request.is_interactive); -} - -bool IndexMain_DoParse( - Config* config, - DiagnosticsEngine* diag_engine, - WorkingFiles* working_files, - FileConsumerSharedState* file_consumer_shared, - TimestampManager* timestamp_manager, - IModificationTimestampFetcher* modification_timestamp_fetcher, - ImportManager* import_manager, - IIndexer* indexer) { - auto* queue = QueueManager::instance(); - optional request = queue->index_request.TryPopFront(); - if (!request) - return false; - - Project::Entry entry; - entry.filename = request->path; - entry.args = request->args; - ParseFile(config, diag_engine, working_files, file_consumer_shared, - timestamp_manager, modification_timestamp_fetcher, import_manager, - indexer, request.value(), entry); - return true; -} - -bool IndexMain_DoCreateIndexUpdate(TimestampManager* timestamp_manager) { - auto* queue = QueueManager::instance(); - optional response = queue->on_id_mapped.TryPopFront(); - if (!response) - return false; - - Timer time; - - IdMap* previous_id_map = nullptr; - IndexFile* previous_index = nullptr; - if (response->previous) { - previous_id_map = response->previous->ids.get(); - previous_index = response->previous->file.get(); - } - - // Build delta update. - IndexUpdate update = - IndexUpdate::CreateDelta(previous_id_map, response->current->ids.get(), - previous_index, response->current->file.get()); - response->perf.index_make_delta = time.ElapsedMicrosecondsAndReset(); - LOG_S(INFO) << "Built index update for " << response->current->file->path - << " (is_delta=" << !!response->previous << ")"; - - // Write current index to disk if requested. - if (response->write_to_disk) { - LOG_S(INFO) << "Writing cached index to disk for " - << response->current->file->path; - time.Reset(); - response->cache_manager->WriteToCache(*response->current->file); - response->perf.index_save_to_disk = time.ElapsedMicrosecondsAndReset(); - timestamp_manager->UpdateCachedModificationTime( - response->current->file->path, - response->current->file->last_modification_time); - } - -#if false -#define PRINT_SECTION(name) \ - if (response->perf.name) { \ - total += response->perf.name; \ - output << " " << #name << ": " << FormatMicroseconds(response->perf.name); \ - } - std::stringstream output; - long long total = 0; - output << "[perf]"; - PRINT_SECTION(index_parse); - PRINT_SECTION(index_build); - PRINT_SECTION(index_save_to_disk); - PRINT_SECTION(index_load_cached); - PRINT_SECTION(querydb_id_map); - PRINT_SECTION(index_make_delta); - output << "\n total: " << FormatMicroseconds(total); - output << " path: " << response->current_index->path; - LOG_S(INFO) << output.rdbuf(); -#undef PRINT_SECTION - - if (response->is_interactive) - LOG_S(INFO) << "Applying IndexUpdate" << std::endl << update.ToString(); -#endif - - Index_OnIndexed reply(std::move(update), response->perf); - queue->on_indexed.PushBack(std::move(reply), response->is_interactive); - - return true; -} - -bool IndexMain_LoadPreviousIndex() { - auto* queue = QueueManager::instance(); - optional response = queue->load_previous_index.TryPopFront(); - if (!response) - return false; - - response->previous = - response->cache_manager->TryTakeOrLoad(response->current->path); - LOG_IF_S(ERROR, !response->previous) - << "Unable to load previous index for already imported index " - << response->current->path; - - queue->do_id_map.PushBack(std::move(*response)); - return true; -} - -bool IndexMergeIndexUpdates() { - auto* queue = QueueManager::instance(); - optional root = queue->on_indexed.TryPopBack(); - if (!root) - return false; - - bool did_merge = false; - while (true) { - optional to_join = queue->on_indexed.TryPopBack(); - if (!to_join) { - queue->on_indexed.PushFront(std::move(*root)); - return did_merge; - } - - did_merge = true; - Timer time; - root->update.Merge(std::move(to_join->update)); - // time.ResetAndPrint("Joined querydb updates for files: " + - // StringJoinMap(root->update.files_def_update, - //[](const QueryFile::DefUpdate& update) { - // return update.path; - //})); - } -} - -} // namespace - -ImportPipelineStatus::ImportPipelineStatus() - : num_active_threads(0), next_progress_output(0) {} - -// Index a file using an already-parsed translation unit from code completion. -// Since most of the time for indexing a file comes from parsing, we can do -// real-time indexing. -// TODO: add option to disable this. -void IndexWithTuFromCodeCompletion( - Config* config, - FileConsumerSharedState* file_consumer_shared, - ClangTranslationUnit* tu, - const std::vector& file_contents, - const std::string& path, - const std::vector& args) { - file_consumer_shared->Reset(path); - - PerformanceImportFile perf; - ClangIndex index; - auto indexes = ParseWithTu(config, file_consumer_shared, &perf, tu, &index, - path, args, file_contents); - if (!indexes) - return; - - std::vector result; - for (std::unique_ptr& new_index : *indexes) { - Timer time; - - std::shared_ptr cache_manager; - assert(false && "FIXME cache_manager"); - // When main thread does IdMap request it will request the previous index if - // needed. - LOG_S(INFO) << "Emitting index result for " << new_index->path; - result.push_back(Index_DoIdMap(std::move(new_index), cache_manager, perf, - true /*is_interactive*/, - true /*write_to_disk*/)); - } - - LOG_IF_S(WARNING, result.size() > 1) - << "Code completion index update generated more than one index"; - - QueueManager::instance()->do_id_map.EnqueueAll(std::move(result)); -} - -void Indexer_Main(Config* config, - DiagnosticsEngine* diag_engine, - FileConsumerSharedState* file_consumer_shared, - TimestampManager* timestamp_manager, - ImportManager* import_manager, - ImportPipelineStatus* status, - Project* project, - WorkingFiles* working_files, - MultiQueueWaiter* waiter) { - RealModificationTimestampFetcher modification_timestamp_fetcher; - auto* queue = QueueManager::instance(); - // Build one index per-indexer, as building the index acquires a global lock. - auto indexer = IIndexer::MakeClangIndexer(); - - while (true) { - bool did_work = false; - - { - ActiveThread active_thread(config, status); - - // TODO: process all off IndexMain_DoIndex before calling - // IndexMain_DoCreateIndexUpdate for better icache behavior. We need to - // have some threads spinning on both though otherwise memory usage will - // get bad. - - // We need to make sure to run both IndexMain_DoParse and - // IndexMain_DoCreateIndexUpdate so we don't starve querydb from doing any - // work. Running both also lets the user query the partially constructed - // index. - did_work = IndexMain_DoParse(config, diag_engine, working_files, - file_consumer_shared, timestamp_manager, - &modification_timestamp_fetcher, - import_manager, indexer.get()) || - did_work; - - did_work = IndexMain_DoCreateIndexUpdate(timestamp_manager) || did_work; - - did_work = IndexMain_LoadPreviousIndex() || did_work; - - // Nothing to index and no index updates to create, so join some already - // created index updates to reduce work on querydb thread. - if (!did_work) - did_work = IndexMergeIndexUpdates() || did_work; - } - - // We didn't do any work, so wait for a notification. - if (!did_work) { - waiter->Wait(&queue->on_indexed, &queue->index_request, - &queue->on_id_mapped, &queue->load_previous_index); - } - } -} - -namespace { -void QueryDb_DoIdMap(QueueManager* queue, - QueryDatabase* db, - ImportManager* import_manager, - Index_DoIdMap* request) { - assert(request->current); - - // If the request does not have previous state and we have already imported - // it, load the previous state from disk and rerun IdMap logic later. Do not - // do this if we have already attempted in the past. - if (!request->load_previous && !request->previous && - db->usr_to_file.find(NormalizedPath(request->current->path)) != - db->usr_to_file.end()) { - assert(!request->load_previous); - request->load_previous = true; - queue->load_previous_index.PushBack(std::move(*request)); - return; - } - - // Check if the file is already being imported into querydb. If it is, drop - // the request. - // - // Note, we must do this *after* we have checked for the previous index, - // otherwise we will never actually generate the IdMap. - if (!import_manager->StartQueryDbImport(request->current->path)) { - LOG_S(INFO) << "Dropping index as it is already being imported for " - << request->current->path; - return; - } - - Index_OnIdMapped response(request->cache_manager, request->perf, - request->is_interactive, request->write_to_disk); - Timer time; - - auto make_map = [db](std::unique_ptr file) - -> std::unique_ptr { - if (!file) - return nullptr; - - auto id_map = std::make_unique(db, file->id_cache); - return std::make_unique(std::move(file), - std::move(id_map)); - }; - response.current = make_map(std::move(request->current)); - response.previous = make_map(std::move(request->previous)); - response.perf.querydb_id_map = time.ElapsedMicrosecondsAndReset(); - - queue->on_id_mapped.PushBack(std::move(response)); -} - -void QueryDb_OnIndexed(QueueManager* queue, - QueryDatabase* db, - ImportManager* import_manager, - ImportPipelineStatus* status, - SemanticHighlightSymbolCache* semantic_cache, - WorkingFiles* working_files, - Index_OnIndexed* response) { - Timer time; - db->ApplyIndexUpdate(&response->update); - time.ResetAndPrint("Applying index update for " + - StringJoinMap(response->update.files_def_update, - [](const QueryFile::DefUpdate& value) { - return value.value.path; - })); - - // Update indexed content, inactive lines, and semantic highlighting. - for (auto& updated_file : response->update.files_def_update) { - WorkingFile* working_file = - working_files->GetFileByFilename(updated_file.value.path); - if (working_file) { - // Update indexed content. - working_file->SetIndexContent(updated_file.file_content); - - // Inactive lines. - EmitInactiveLines(working_file, updated_file.value.inactive_regions); - - // Semantic highlighting. - QueryFileId file_id = - db->usr_to_file[NormalizedPath(working_file->filename)]; - QueryFile* file = &db->files[file_id.id]; - EmitSemanticHighlighting(db, semantic_cache, working_file, file); - } - - // Mark the files as being done in querydb stage after we apply the index - // update. - import_manager->DoneQueryDbImport(updated_file.value.path); - } -} -} // namespace - -bool QueryDb_ImportMain(Config* config, - QueryDatabase* db, - ImportManager* import_manager, - ImportPipelineStatus* status, - SemanticHighlightSymbolCache* semantic_cache, - WorkingFiles* working_files) { - auto* queue = QueueManager::instance(); - - ActiveThread active_thread(config, status); - - bool did_work = false; - - while (true) { - optional request = queue->do_id_map.TryPopFront(); - if (!request) - break; - did_work = true; - QueryDb_DoIdMap(queue, db, import_manager, &*request); - } - - while (true) { - optional response = queue->on_indexed.TryPopFront(); - if (!response) - break; - did_work = true; - QueryDb_OnIndexed(queue, db, import_manager, status, semantic_cache, - working_files, &*response); - } - - return did_work; -} - -TEST_SUITE("ImportPipeline") { - struct Fixture { - Fixture() { - QueueManager::Init(&querydb_waiter, &indexer_waiter, &stdout_waiter); - - queue = QueueManager::instance(); - cache_manager = ICacheManager::MakeFake({}); - indexer = IIndexer::MakeTestIndexer({}); - diag_engine.Init(&config); - } - - bool PumpOnce() { - return IndexMain_DoParse(&config, &diag_engine, &working_files, - &file_consumer_shared, ×tamp_manager, - &modification_timestamp_fetcher, &import_manager, - indexer.get()); - } - - void MakeRequest(const std::string& path, - const std::vector& args = {}, - bool is_interactive = false, - const std::string& contents = "void foo();") { - queue->index_request.PushBack( - Index_Request(path, args, is_interactive, contents, cache_manager)); - } - - MultiQueueWaiter querydb_waiter; - MultiQueueWaiter indexer_waiter; - MultiQueueWaiter stdout_waiter; - - QueueManager* queue = nullptr; - Config config; - DiagnosticsEngine diag_engine; - WorkingFiles working_files; - FileConsumerSharedState file_consumer_shared; - TimestampManager timestamp_manager; - FakeModificationTimestampFetcher modification_timestamp_fetcher; - ImportManager import_manager; - std::shared_ptr cache_manager; - std::unique_ptr indexer; - }; - - TEST_CASE_FIXTURE(Fixture, "FileNeedsParse") { - auto check = [&](const std::string& file, bool is_dependency = false, - bool is_interactive = false, - const std::vector& old_args = {}, - const std::vector& new_args = {}) { - std::unique_ptr opt_previous_index; - if (!old_args.empty()) { - opt_previous_index = std::make_unique("---.cc", ""); - opt_previous_index->args = old_args; - } - optional from; - if (is_dependency) - from = std::string("---.cc"); - return FileNeedsParse(is_interactive /*is_interactive*/, - ×tamp_manager, &modification_timestamp_fetcher, - &import_manager, cache_manager, - opt_previous_index.get(), file, new_args, from); - }; - - // A file with no timestamp is not imported, since this implies the file no - // longer exists on disk. - modification_timestamp_fetcher.entries["bar.h"] = nullopt; - REQUIRE(check("bar.h", false /*is_dependency*/) == ShouldParse::NoSuchFile); - - // A dependency is only imported once. - modification_timestamp_fetcher.entries["foo.h"] = 5; - REQUIRE(check("foo.h", true /*is_dependency*/) == ShouldParse::Yes); - REQUIRE(check("foo.h", true /*is_dependency*/) == ShouldParse::No); - - // An interactive dependency is imported. - REQUIRE(check("foo.h", true /*is_dependency*/) == ShouldParse::No); - REQUIRE(check("foo.h", true /*is_dependency*/, true /*is_interactive*/) == - ShouldParse::Yes); - - // A file whose timestamp has not changed is not imported. When the - // timestamp changes (either forward or backward) it is reimported. - auto check_timestamp_change = [&](int64_t timestamp) { - modification_timestamp_fetcher.entries["aa.cc"] = timestamp; - REQUIRE(check("aa.cc") == ShouldParse::Yes); - REQUIRE(check("aa.cc") == ShouldParse::Yes); - REQUIRE(check("aa.cc") == ShouldParse::Yes); - timestamp_manager.UpdateCachedModificationTime("aa.cc", timestamp); - REQUIRE(check("aa.cc") == ShouldParse::No); - }; - check_timestamp_change(5); - check_timestamp_change(6); - check_timestamp_change(5); - check_timestamp_change(4); - - // Argument change implies reimport, even if timestamp has not changed. - timestamp_manager.UpdateCachedModificationTime("aa.cc", 5); - modification_timestamp_fetcher.entries["aa.cc"] = 5; - REQUIRE(check("aa.cc", false /*is_dependency*/, false /*is_interactive*/, - {"b"} /*old_args*/, - {"b", "a"} /*new_args*/) == ShouldParse::Yes); - } - - // FIXME: validate other state like timestamp_manager, etc. - // FIXME: add more interesting tests that are not the happy path - // FIXME: test - // - IndexMain_DoCreateIndexUpdate - // - IndexMain_LoadPreviousIndex - // - QueryDb_ImportMain - - TEST_CASE_FIXTURE(Fixture, "index request with zero results") { - indexer = IIndexer::MakeTestIndexer({IIndexer::TestEntry{"foo.cc", 0}}); - - MakeRequest("foo.cc"); - - REQUIRE(queue->index_request.Size() == 1); - REQUIRE(queue->do_id_map.Size() == 0); - PumpOnce(); - REQUIRE(queue->index_request.Size() == 0); - REQUIRE(queue->do_id_map.Size() == 0); - - REQUIRE(file_consumer_shared.used_files.empty()); - } - - TEST_CASE_FIXTURE(Fixture, "one index request") { - indexer = IIndexer::MakeTestIndexer({IIndexer::TestEntry{"foo.cc", 100}}); - - MakeRequest("foo.cc"); - - REQUIRE(queue->index_request.Size() == 1); - REQUIRE(queue->do_id_map.Size() == 0); - PumpOnce(); - REQUIRE(queue->index_request.Size() == 0); - REQUIRE(queue->do_id_map.Size() == 100); - - REQUIRE(file_consumer_shared.used_files.empty()); - } - - TEST_CASE_FIXTURE(Fixture, "multiple index requests") { - indexer = IIndexer::MakeTestIndexer( - {IIndexer::TestEntry{"foo.cc", 100}, IIndexer::TestEntry{"bar.cc", 5}}); - - MakeRequest("foo.cc"); - MakeRequest("bar.cc"); - - REQUIRE(queue->index_request.Size() == 2); - REQUIRE(queue->do_id_map.Size() == 0); - while (PumpOnce()) { - } - REQUIRE(queue->index_request.Size() == 0); - REQUIRE(queue->do_id_map.Size() == 105); - - REQUIRE(file_consumer_shared.used_files.empty()); - } -} diff --git a/src/import_pipeline.h b/src/import_pipeline.h deleted file mode 100644 index 7ede6ae63..000000000 --- a/src/import_pipeline.h +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once - -// FIXME: do not include clang-c outside of clang_ files. -#include - -#include -#include -#include -#include - -struct ClangTranslationUnit; -struct Config; -class DiagnosticsEngine; -struct FileConsumerSharedState; -struct ImportManager; -struct MultiQueueWaiter; -struct Project; -struct QueryDatabase; -struct SemanticHighlightSymbolCache; -struct TimestampManager; -struct WorkingFiles; - -struct ImportPipelineStatus { - std::atomic num_active_threads; - std::atomic next_progress_output; - - ImportPipelineStatus(); -}; - -void IndexWithTuFromCodeCompletion( - Config* config, - FileConsumerSharedState* file_consumer_shared, - ClangTranslationUnit* tu, - const std::vector& file_contents, - const std::string& path, - const std::vector& args); - -void Indexer_Main(Config* config, - DiagnosticsEngine* diag_engine, - FileConsumerSharedState* file_consumer_shared, - TimestampManager* timestamp_manager, - ImportManager* import_manager, - ImportPipelineStatus* status, - Project* project, - WorkingFiles* working_files, - MultiQueueWaiter* waiter); - -bool QueryDb_ImportMain(Config* config, - QueryDatabase* db, - ImportManager* import_manager, - ImportPipelineStatus* status, - SemanticHighlightSymbolCache* semantic_cache, - WorkingFiles* working_files); diff --git a/src/include_complete.cc b/src/include_complete.cc index 9f4476c02..ba33c4aaa 100644 --- a/src/include_complete.cc +++ b/src/include_complete.cc @@ -1,220 +1,197 @@ -#include "include_complete.h" +/* Copyright 2017-2018 ccls Authors -#include "match.h" -#include "platform.h" -#include "project.h" -#include "standard_includes.h" -#include "timer.h" -#include "work_thread.h" +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#include "include_complete.hh" + +#include "filesystem.hh" +#include "platform.hh" +#include "project.hh" + +#include +#include +#include + +#include +using namespace llvm; #include +namespace ccls { namespace { struct CompletionCandidate { std::string absolute_path; - lsCompletionItem completion_item; + CompletionItem completion_item; }; -std::string ElideLongPath(Config* config, const std::string& path) { - if (config->completion.includeMaxPathSize <= 0) - return path; - - if ((int)path.size() <= config->completion.includeMaxPathSize) +std::string elideLongPath(const std::string &path) { + if (g_config->completion.include.maxPathSize <= 0 || + (int)path.size() <= g_config->completion.include.maxPathSize) return path; - size_t start = path.size() - config->completion.includeMaxPathSize; + size_t start = path.size() - g_config->completion.include.maxPathSize; return ".." + path.substr(start + 2); } -size_t TrimCommonPathPrefix(const std::string& result, - const std::string& trimmer) { - size_t i = 0; - while (i < result.size() && i < trimmer.size()) { - char a = result[i]; - char b = trimmer[i]; -#if defined(_WIN32) - a = (char)tolower(a); - b = (char)tolower(b); +size_t trimCommonPathPrefix(const std::string &result, + const std::string &trimmer) { +#ifdef _WIN32 + std::string s = result, t = trimmer; + std::transform(s.begin(), s.end(), s.begin(), ::tolower); + std::transform(t.begin(), t.end(), t.begin(), ::tolower); + if (s.compare(0, t.size(), t) == 0) + return t.size(); +#else + if (result.compare(0, trimmer.size(), trimmer) == 0) + return trimmer.size(); #endif - if (a != b) - break; - ++i; - } - - if (i == trimmer.size()) - return i; return 0; } -// Returns true iff angle brackets should be used. -bool TrimPath(Project* project, - const std::string& project_root, - std::string* insert_path) { - size_t start = 0; - bool angle = false; - - size_t len = TrimCommonPathPrefix(*insert_path, project_root); - if (len > start) - start = len; - - for (auto& include_dir : project->quote_include_directories) { - len = TrimCommonPathPrefix(*insert_path, include_dir); - if (len > start) - start = len; - } - - for (auto& include_dir : project->angle_include_directories) { - len = TrimCommonPathPrefix(*insert_path, include_dir); - if (len > start) { - start = len; - angle = true; - } - } - - *insert_path = insert_path->substr(start); - return angle; +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) + pos = t, kind = search_dir_kind; + path = path.substr(pos); + return kind; } -lsCompletionItem BuildCompletionItem(Config* config, - const std::string& path, - bool use_angle_brackets, - bool is_stl) { - lsCompletionItem item; - item.label = ElideLongPath(config, path); - item.detail = path; // the include path, used in de-duplicating - item.textEdit = lsTextEdit(); - item.textEdit->newText = path; - item.insertTextFormat = lsInsertTextFormat::PlainText; - item.use_angle_brackets_ = use_angle_brackets; - if (is_stl) { - item.kind = lsCompletionItemKind::Module; - item.priority_ = 2; - } else { - item.kind = lsCompletionItemKind::File; - item.priority_ = 1; - } +CompletionItem buildCompletionItem(const std::string &path, int kind) { + CompletionItem item; + item.label = elideLongPath(path); + item.detail = path; // the include path, used in de-duplicating + item.textEdit.newText = path; + item.insertTextFormat = InsertTextFormat::PlainText; + item.kind = CompletionItemKind::File; + item.quote_kind_ = kind; + item.priority_ = 0; return item; } +} // namespace -} // namespace +IncludeComplete::IncludeComplete(Project *project) + : is_scanning(false), project_(project) {} -IncludeComplete::IncludeComplete(Config* config, Project* project) - : is_scanning(false), config_(config), project_(project) {} +IncludeComplete::~IncludeComplete() { + // Spin until the scanning has completed. + while (is_scanning.load()) + std::this_thread::sleep_for(std::chrono::milliseconds(100)); +} -void IncludeComplete::Rescan() { - if (is_scanning) +void IncludeComplete::rescan() { + if (is_scanning || LLVM_VERSION_MAJOR >= 8) return; completion_items.clear(); absolute_path_to_completion_item.clear(); inserted_paths.clear(); - if (!match_ && (!config_->completion.includeWhitelist.empty() || - !config_->completion.includeBlacklist.empty())) - match_ = std::make_unique(config_->completion.includeWhitelist, - config_->completion.includeBlacklist); + if (!match_ && (g_config->completion.include.whitelist.size() || + g_config->completion.include.blacklist.size())) + match_ = + std::make_unique(g_config->completion.include.whitelist, + g_config->completion.include.blacklist); is_scanning = true; - WorkThread::StartThread("scan_includes", [this]() { - Timer timer; - - InsertStlIncludes(); - InsertIncludesFromDirectory(config_->projectRoot, - false /*use_angle_brackets*/); - for (const std::string& dir : project_->quote_include_directories) - InsertIncludesFromDirectory(dir, false /*use_angle_brackets*/); - for (const std::string& dir : project_->angle_include_directories) - InsertIncludesFromDirectory(dir, true /*use_angle_brackets*/); - - timer.ResetAndPrint("[perf] Scanning for includes"); + std::thread([this]() { + set_thread_name("include"); + for (auto &[root, folder] : project_->root2folder) { + for (auto &search_kind : folder.search_dir2kind) { + const std::string &search = search_kind.first; + int kind = search_kind.second; + assert(search.back() == '/'); + 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); + }); + + std::lock_guard lock(completion_items_mutex); + for (CompletionCandidate &result : results) + insertCompletionItem(result.absolute_path, + std::move(result.completion_item)); + } + } + is_scanning = false; - }); + }).detach(); } -void IncludeComplete::InsertCompletionItem(const std::string& absolute_path, - lsCompletionItem&& item) { - if (inserted_paths.insert({item.detail, inserted_paths.size()}).second) { +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); // insert if not found or with shorter include path auto it = absolute_path_to_completion_item.find(absolute_path); if (it == absolute_path_to_completion_item.end() || - completion_items[it->second].detail.length() > item.detail.length()) - absolute_path_to_completion_item[absolute_path] = completion_items.size(); - } else { - lsCompletionItem& inserted_item = - completion_items[inserted_paths[item.detail]]; - // Update |use_angle_brackets_|, prefer quotes. - if (!item.use_angle_brackets_) - inserted_item.use_angle_brackets_ = false; + completion_items[it->second].detail.length() > item.detail.length()) { + absolute_path_to_completion_item[absolute_path] = + completion_items.size() - 1; + } } } -void IncludeComplete::AddFile(const std::string& absolute_path) { - if (!EndsWithAny(absolute_path, config_->completion.includeSuffixWhitelist)) +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_->IsMatch(absolute_path)) + if (match_ && !match_->matches(path)) return; - std::string trimmed_path = absolute_path; - bool use_angle_brackets = - TrimPath(project_, config_->projectRoot, &trimmed_path); - lsCompletionItem item = BuildCompletionItem( - config_, trimmed_path, use_angle_brackets, false /*is_stl*/); + std::string trimmed_path = path; + 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(absolute_path, std::move(item)); - if (lock) - lock.unlock(); -} - -void IncludeComplete::InsertIncludesFromDirectory(std::string directory, - bool use_angle_brackets) { - directory = NormalizePath(directory); - EnsureEndsInSlash(directory); - if (match_ && !match_->IsMatch(directory)) { - // Don't even enter the directory if it fails the patterns. - return; - } - - std::vector results; - GetFilesInFolder( - directory, true /*recursive*/, false /*add_folder_to_path*/, - [&](const std::string& path) { - if (!EndsWithAny(path, config_->completion.includeSuffixWhitelist)) - return; - if (match_ && !match_->IsMatch(directory + path)) - return; - - CompletionCandidate candidate; - candidate.absolute_path = directory + path; - candidate.completion_item = BuildCompletionItem( - config_, path, use_angle_brackets, false /*is_stl*/); - results.push_back(candidate); - }); - - std::lock_guard lock(completion_items_mutex); - for (CompletionCandidate& result : results) - InsertCompletionItem(result.absolute_path, - std::move(result.completion_item)); -} - -void IncludeComplete::InsertStlIncludes() { - std::lock_guard lock(completion_items_mutex); - for (const char* stl_header : kStandardLibraryIncludes) { - completion_items.push_back(BuildCompletionItem( - config_, stl_header, true /*use_angle_brackets*/, true /*is_stl*/)); - } + insertCompletionItem(path, std::move(item)); } -optional IncludeComplete::FindCompletionItemForAbsolutePath( - const std::string& absolute_path) { +std::optional +IncludeComplete::findCompletionItemForAbsolutePath( + const std::string &absolute_path) { std::lock_guard lock(completion_items_mutex); auto it = absolute_path_to_completion_item.find(absolute_path); if (it == absolute_path_to_completion_item.end()) - return nullopt; + return std::nullopt; return completion_items[it->second]; } +} // namespace ccls diff --git a/src/include_complete.h b/src/include_complete.h deleted file mode 100644 index 9806cffe7..000000000 --- a/src/include_complete.h +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -#include "lsp_completion.h" - -#include -#include -#include - -struct GroupMatch; -struct Project; - -struct IncludeComplete { - IncludeComplete(Config* config, Project* project); - - // Starts scanning directories. Clears existing cache. - void Rescan(); - - // Ensures the one-off file is inside |completion_items|. - void AddFile(const std::string& absolute_path); - - // Scans the given directory and inserts all includes from this. This is a - // blocking function and should be run off the querydb thread. - void InsertIncludesFromDirectory(std::string directory, - bool use_angle_brackets); - void InsertStlIncludes(); - - optional 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, - lsCompletionItem&& item); - - // Guards |completion_items| when |is_scanning| is true. - std::mutex completion_items_mutex; - std::atomic is_scanning; - std::vector completion_items; - - // Absolute file path to the completion item in |completion_items|. - // Keep the one with shortest include path. - std::unordered_map absolute_path_to_completion_item; - - // Only one completion item per include path. - std::unordered_map inserted_paths; - - // Cached references - Config* config_; - Project* project_; - std::unique_ptr match_; -}; diff --git a/src/include_complete.hh b/src/include_complete.hh new file mode 100644 index 000000000..9cf9a2a38 --- /dev/null +++ b/src/include_complete.hh @@ -0,0 +1,61 @@ +/* Copyright 2017-2018 ccls Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#pragma once + +#include "message_handler.hh" + +#include +#include + +namespace ccls { +struct GroupMatch; +struct Project; + +struct IncludeComplete { + IncludeComplete(Project *project); + ~IncludeComplete(); + + // Starts scanning directories. Clears existing cache. + void rescan(); + + // Ensures the one-off file is inside |completion_items|. + void addFile(const std::string &absolute_path); + + std::optional + 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, + ccls::CompletionItem &&item); + + // Guards |completion_items| when |is_scanning| is true. + std::mutex completion_items_mutex; + std::atomic is_scanning; + std::vector completion_items; + + // Absolute file path to the completion item in |completion_items|. + // Keep the one with shortest include path. + std::unordered_map absolute_path_to_completion_item; + + // Only one completion item per include path. + std::unordered_map inserted_paths; + + // Cached references + Project *project_; + std::unique_ptr match_; +}; +} // namespace ccls diff --git a/src/indexer.cc b/src/indexer.cc new file mode 100644 index 000000000..1c1e4e96e --- /dev/null +++ b/src/indexer.cc @@ -0,0 +1,1501 @@ +/* Copyright 2017-2018 ccls Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#include "indexer.hh" + +#include "clang_tu.hh" +#include "log.hh" +#include "pipeline.hh" +#include "platform.hh" +#include "sema_manager.hh" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace clang; + +namespace ccls { +namespace { + +GroupMatch *multiVersionMatcher; + +struct File { + std::string path; + int64_t mtime; + std::string content; + std::unique_ptr db; +}; + +struct IndexParam { + std::unordered_map uid2file; + std::unordered_map uid2multi; + struct DeclInfo { + Usr usr; + std::string short_name; + std::string qualified; + }; + std::unordered_map decl2Info; + + VFS &vfs; + ASTContext *ctx; + bool no_linkage; + IndexParam(VFS &vfs, bool no_linkage) : vfs(vfs), no_linkage(no_linkage) {} + + 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(fid); + if (inserted) { + const FileEntry *fe = ctx->getSourceManager().getFileEntryForID(fid); + if (!fe) + return; + std::string path = pathFromFileEntry(*fe); + it->second.path = path; + it->second.mtime = fe->getModificationTime(); + if (!it->second.mtime) + if (auto tim = lastWriteTime(path)) + it->second.mtime = *tim; + if (std::optional content = readContent(path)) + it->second.content = *content; + + if (!vfs.stamp(path, it->second.mtime, no_linkage ? 3 : 1)) + return; + it->second.db = + std::make_unique(path, it->second.content, no_linkage); + } + } + + IndexFile *consumeFile(FileID fid) { + seenFile(fid); + return uid2file[fid].db.get(); + } + + bool useMultiVersion(FileID fid) { + auto it = uid2multi.try_emplace(fid); + if (it.second) + 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 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); + if (invalid) + return ""; + return buf.substr(bInfo.second, + eInfo.second + + Lexer::MeasureTokenLength(eLoc, sm, langOpts) - + bInfo.second); +} + +Kind getKind(const Decl *d, SymbolKind &kind) { + switch (d->getKind()) { + case Decl::LinkageSpec: + return Kind::Invalid; + case Decl::Namespace: + case Decl::NamespaceAlias: + kind = SymbolKind::Namespace; + return Kind::Type; + case Decl::ObjCCategory: + case Decl::ObjCCategoryImpl: + case Decl::ObjCImplementation: + case Decl::ObjCInterface: + case Decl::ObjCProtocol: + kind = SymbolKind::Interface; + return Kind::Type; + case Decl::ObjCMethod: + kind = SymbolKind::Method; + return Kind::Func; + case Decl::ObjCProperty: + kind = SymbolKind::Property; + return Kind::Type; + case Decl::ClassTemplate: + kind = SymbolKind::Class; + return Kind::Type; + case Decl::FunctionTemplate: + kind = SymbolKind::Function; + return Kind::Func; + case Decl::TypeAliasTemplate: + kind = SymbolKind::TypeAlias; + return Kind::Type; + case Decl::VarTemplate: + kind = SymbolKind::Variable; + return Kind::Var; + case Decl::TemplateTemplateParm: + kind = SymbolKind::TypeParameter; + return Kind::Type; + case Decl::Enum: + kind = SymbolKind::Enum; + return Kind::Type; + case Decl::CXXRecord: + case Decl::Record: + kind = SymbolKind::Class; + // spec has no Union, use Class + 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; + case Decl::Field: + case Decl::ObjCIvar: + kind = SymbolKind::Field; + return Kind::Var; + case Decl::Function: + kind = SymbolKind::Function; + return Kind::Func; + case Decl::CXXMethod: { + const auto *md = cast(d); + kind = md->isStatic() ? SymbolKind::StaticMethod : SymbolKind::Method; + return Kind::Func; + } + case Decl::CXXConstructor: + kind = SymbolKind::Constructor; + return Kind::Func; + case Decl::CXXConversion: + 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; + return Kind::Var; + case Decl::ImplicitParam: + case Decl::ParmVar: + // ccls extension + kind = SymbolKind::Parameter; + return Kind::Var; + case Decl::VarTemplateSpecialization: + case Decl::VarTemplatePartialSpecialization: + kind = SymbolKind::Variable; + return Kind::Var; + case Decl::EnumConstant: + kind = SymbolKind::EnumMember; + return Kind::Var; + case Decl::UnresolvedUsingValue: + kind = SymbolKind::Variable; + return Kind::Var; + case Decl::TranslationUnit: + return Kind::Invalid; + + default: + return Kind::Invalid; + } +} + +LanguageId getDeclLanguage(const Decl *d) { + switch (d->getKind()) { + default: + return LanguageId::C; + case Decl::ImplicitParam: + case Decl::ObjCAtDefsField: + case Decl::ObjCCategory: + case Decl::ObjCCategoryImpl: + case Decl::ObjCCompatibleAlias: + case Decl::ObjCImplementation: + case Decl::ObjCInterface: + case Decl::ObjCIvar: + case Decl::ObjCMethod: + case Decl::ObjCProperty: + case Decl::ObjCPropertyImpl: + case Decl::ObjCProtocol: + case Decl::ObjCTypeParam: + return LanguageId::ObjC; + case Decl::CXXConstructor: + case Decl::CXXConversion: + case Decl::CXXDestructor: + case Decl::CXXMethod: + case Decl::CXXRecord: + case Decl::ClassTemplate: + case Decl::ClassTemplatePartialSpecialization: + case Decl::ClassTemplateSpecialization: + case Decl::Friend: + case Decl::FriendTemplate: + case Decl::FunctionTemplate: + case Decl::LinkageSpec: + case Decl::Namespace: + case Decl::NamespaceAlias: + case Decl::NonTypeTemplateParm: + case Decl::StaticAssert: + case Decl::TemplateTemplateParm: + case Decl::TemplateTypeParm: + case Decl::UnresolvedUsingTypename: + case Decl::UnresolvedUsingValue: + case Decl::Using: + case Decl::UsingDirective: + case Decl::UsingShadow: + return LanguageId::Cpp; + } +} + +// 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(); + else if (deduce_auto) { + if (const AutoType *aTy = baseType->getAs()) + baseType = aTy->getDeducedType(); + else + break; + } else + break; + } + 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) + return nullptr; + +try_again: + switch (tp->getTypeClass()) { + case Type::Typedef: + d = cast(tp)->getDecl(); + break; + case Type::ObjCObject: + d = cast(tp)->getInterface(); + break; + case Type::ObjCInterface: + d = cast(tp)->getDecl(); + break; + case Type::Record: + case Type::Enum: + d = cast(tp)->getDecl(); + break; + case Type::TemplateTypeParm: + d = cast(tp)->getDecl(); + break; + case Type::TemplateSpecialization: + if (specialization) + *specialization = true; + if (const RecordType *record = tp->getAs()) + d = record->getDecl(); + else + d = cast(tp) + ->getTemplateName() + .getAsTemplateDecl(); + break; + + case Type::Auto: + case Type::DeducedTemplateSpecialization: + tp = cast(tp)->getDeducedType().getTypePtrOrNull(); + if (tp) + goto try_again; + break; + + case Type::InjectedClassName: + d = cast(tp)->getDecl(); + break; + + // FIXME: Template type parameters! + + case Type::Elaborated: + tp = cast(tp)->getNamedType().getTypePtrOrNull(); + goto try_again; + + default: + break; + } + 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()) { + llvm::PointerUnion + result = s->getSpecializedTemplateOrPartial(); + if (result.is()) + d = result.get(); + else + d = result.get(); + continue; + } + } else if (auto *d1 = r->getInstantiatedFromMemberClass()) { + d = d1; + continue; + } + } else if (auto *ed = dyn_cast(d)) { + if (auto *d1 = ed->getInstantiatedFromMemberEnum()) { + d = d1; + continue; + } + } + break; + } + return d; +} + +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)) + return false; + } + return true; +} + +class IndexDataConsumer : public index::IndexDataConsumer { +public: + ASTContext *ctx; + IndexParam ¶m; + + 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 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 uint8_t *p = raw.bytes_begin(), *e = raw.bytes_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++) + skip--; + bool high = false; + const uint8_t *q = p; + while (q < e && *q != '\n') + high |= *q++ >= 0x80; + 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 uint8_t *begin = p; + while (p < e && (*p == '/' || *p == '*' || *p == '-' || *p == '=')) + p++; + if (p < e && (*p == '<' || *p == '!')) + p++; + if (p < e && *p == ' ') + p++; + if (p + 1 == q) + p++; + else + pad = int(p - begin); + } else { + // Other lines, skip |pad| bytes + int prefix = pad; + while (prefix > 0 && p < e && + (*p == ' ' || *p == '/' || *p == '*' || *p == '<' || *p == '!')) + prefix--, p++; + } + if (high) { + while (p < q) { + int i = 0, c = *p < 0x80 ? 0 + : *p < 0xc0 || *p >= 0xf8 + ? -1 + : *p >= 0xf0 ? 3 : *p >= 0xe0 ? 2 : 1; + const uint8_t *r = p + 1; + for (; i < c && r < q && *r >= 0x80; i++, r++) + ; + if (i == c) + ret.insert(ret.end(), (const char *)p, (const char *)r); + else + ret += '?'; + p = r; + } + } else { + ret.insert(ret.end(), (const char *)p, (const char *)q); + } + p = q; + } + while (ret.size() && isspace(ret.back())) + ret.pop_back(); + if (StringRef(ret).endswith("*/")) + ret.resize(ret.size() - 2); + else if (StringRef(ret).endswith("\n/")) + ret.resize(ret.size() - 2); + while (ret.size() && isspace(ret.back())) + ret.pop_back(); + return ret; + } + + 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); + auto &info = it->second; + 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) + *info = &it->second; + 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.SuppressUnwrittenScope = g_config->index.name.suppressUnwrittenScope; + pp.SuppressInitializers = true; + pp.FullyQualifiedName = false; + return pp; + } + + static void simplifyAnonymous(std::string &name) { + for (std::string::size_type i = 0;;) { + if ((i = name.find("(anonymous ", i)) == std::string::npos) + break; + i++; + if (name.size() - i > 19 && name.compare(i + 10, 9, "namespace") == 0) + name.replace(i, 19, "anon ns"); + else + name.replace(i, 9, "anon"); + } + } + + template + 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()); + + std::string name = os.str(); + 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) + break; + name.replace(i, 3, "{}"); + } + 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()]))) + i = name.find(short_name, i + short_name.size()); + if (i == std::string::npos) { + // e.g. operator type-parameter-1 + i = 0; + def.short_name_offset = 0; + } else if (short_name.empty() || (i >= 2 && name[i - 2] == ':')) { + // Don't replace name with qualified name in ns::name Cls::*name + def.short_name_offset = i; + } else { + name.replace(i, short_name.size(), qualified); + def.short_name_offset = i + qualified.size() - short_name.size(); + } + def.short_name_size = short_name.size(); + for (int paren = 0; i; i--) { + // Skip parentheses in "(anon struct)::name" + if (name[i - 1] == ')') + paren++; + else if (name[i - 1] == '(') + paren--; + else if (!(paren > 0 || isIdentifierBody(name[i - 1]) || + name[i - 1] == ':')) + break; + } + def.qual_name_offset = i; + def.detailed_name = intern(name); + } + + void setVarName(const Decl *d, std::string_view short_name, + std::string_view qualified, IndexVar::Def &def) { + 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(); + deduced = true; + } + if (!t.isNull()) { + if (t->getContainedDeducedType()) { + deduced = true; + } else if (auto *dt = dyn_cast(t)) { + // decltype(y) x; + 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(); + def.short_name_size = short_name.size(); + str += StringRef(qualified.data(), qualified.size()); + def.detailed_name = intern(str); + } else { + setName(d, short_name, qualified, def); + } + if (init) { + 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, 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; + def.hover = + def.storage == SC_Static && strncmp(def.detailed_name, "static ", 7) + ? intern(("static " + t).str()) + : intern(t.str()); + } + } + + static int getFileLID(IndexFile *db, SourceManager &sm, FileID fid) { + auto [it, inserted] = db->uid2lid_and_path.try_emplace(fid); + if (inserted) { + const FileEntry *fe = sm.getFileEntryForID(fid); + if (!fe) { + it->second.first = -1; + return -1; + } + 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 sl) const { + FileID fid = sm.getFileID(sl); + int lid = getFileLID(db, sm, fid); + if (lid < 0) + return; + 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); + break; + case Kind::Type: + db->toType(usr).uses.push_back(use); + break; + case Kind::Var: + 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()) { + int offset; + 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}); + } + } + } + } + +public: + IndexDataConsumer(IndexParam ¶m) : param(param) {} + void initialize(ASTContext &ctx) override { this->ctx = param.ctx = &ctx; } + bool handleDeclOccurence(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; + } + SourceManager &sm = ctx->getSourceManager(); + const LangOptions &lang = ctx->getLangOpts(); + FileID fid; + SourceLocation spell = sm.getSpellingLoc(src_loc); + Range loc; + 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(fid)) { + db = param.consumeFile(sm.getMainFileID()); + if (!db) + return true; + param.seenFile(fid); + if (!sm.isWrittenInMainFile(r.getBegin())) + lid = getFileLID(db, sm, fid); + } else { + db = param.consumeFile(fid); + if (!db) + return true; + } + + // spell, extent, comments use OrigD while most others use adjusted |D|. + 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(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)); + + 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 = SymbolKind::Unknown; + Kind kind = getKind(d, ls_kind); + + if (is_def) + 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 (src_loc.isFileID()) { + SourceRange sr = + cast(origD)->getNameInfo().getSourceRange(); + if (sr.getEnd().isFileID()) + loc = fromTokenRange(sm, lang, sr); + } + break; + default: + break; + } + else { + // e.g. typedef Foo gg; => Foo has an unadjusted `D` + const Decl *d1 = getAdjustedDecl(d); + if (d1 && d1 != d) + d = d1; + } + + IndexParam::DeclInfo *info; + Usr usr = getUsr(d, &info); + + auto do_def_decl = [&](auto *entity) { + Use use{{loc, role}, lid}; + if (is_def) { + SourceRange sr = origD->getSourceRange(); + entity->def.spell = {use, + fromTokenRangeDefaulted(sm, lang, sr, fid, loc)}; + getKind(cast(sem_dc), entity->def.parent_kind); + } else if (is_decl) { + SourceRange sr = origD->getSourceRange(); + entity->declarations.push_back( + {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)); + }; + switch (kind) { + case Kind::Invalid: + 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->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)) + role = Role(role | Role::Implicit); + do_def_decl(func); + 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); + if (is_def || is_decl) { + 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(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->def.kind = ls_kind; + do_def_decl(type); + 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(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->def.kind = ls_kind; + do_def_decl(var); + 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(); + if (is_def || is_decl) { + 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(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 LLVM_VERSION_MAJOR < 9 + if (isa(d1)) { + // e.g. TemplateTypeParmDecl is not handled by + // handleDeclOccurence. + SourceRange sr1 = d1->getSourceRange(); + if (sm.getFileID(sr1.getBegin()) == fid) { + IndexParam::DeclInfo *info1; + Usr usr1 = getUsr(d1, &info1); + IndexType &type1 = db->toType(usr1); + SourceLocation sl1 = d1->getLocation(); + type1.def.spell = { + Use{{fromTokenRange(sm, lang, {sl1, sl1}), Role::Definition}, + lid}, + 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; + var->def.type = usr1; + type1.instances.push_back(usr); + break; + } + } +#endif + + IndexParam::DeclInfo *info1; + Usr usr1 = getUsr(d1, &info1); + var->def.type = usr1; + 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) == fid) { + var->def.spell = { + Use{{fromTokenRange(sm, lang, {l, l}), Role::Definition}, lid}, + fromTokenRange(sm, lang, d->getSourceRange())}; + var->def.parent_kind = SymbolKind::Method; + } + } + break; + } + + 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); + type->def.bases.push_back(usr1); + 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); + type->def.alias_of = 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); + type->def.bases.push_back(usr1); + db->toType(usr1).derived.push_back(usr); + } + } + [[fallthrough]]; + case Decl::Record: + if (auto *rd = 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; + } + if (TypedefNameDecl *td = rd->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); + type->def.short_name_size = name.size(); + } + } + if (is_def) + 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)) { + llvm::PointerUnion + result = sd->getSpecializedTemplateOrPartial(); + if (result.is()) + d1 = result.get(); + else + d1 = result.get(); + + } else + d1 = rd->getInstantiatedFromMemberClass(); + if (d1) { + Usr usr1 = getUsr(d1); + type->def.bases.push_back(usr1); + db->toType(usr1).derived.push_back(usr); + } + } + } + break; + case Decl::TypeAlias: + case Decl::Typedef: + case Decl::UnresolvedUsingTypename: + 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); + 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) == fid) + type1.uses.push_back( + {{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); + func->def.bases.push_back(usr1); + 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(); + std::string init = + " = " + (val.isSigned() ? std::to_string(val.getSExtValue()) + : std::to_string(val.getZExtValue())); + var->def.hover = intern(var->def.detailed_name + init); + } + break; + default: + break; + } + return true; + } +}; + +class IndexPPCallbacks : public PPCallbacks { + 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)}; + } + +public: + 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; + 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)}); + } + } + 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 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, sr); + var.def.hover = + 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 &, 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(), {sl, sl}, nullptr), + Role::Dynamic}}); + } + } + 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 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(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 = 21; +const int IndexFile::kMinorVersion = 0; + +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) { + auto [it, inserted] = usr2func.try_emplace(usr); + if (inserted) + it->second.usr = usr; + return it->second; +} + +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) { + 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); +} + +template void uniquify(std::vector &a) { + std::unordered_set seen; + size_t n = 0; + for (size_t i = 0; i < a.size(); i++) + if (seen.insert(a[i]).second) + a[n++] = a[i]; + a.resize(n); +} + +namespace idx { +void init() { + multiVersionMatcher = new GroupMatch(g_config->index.multiVersionWhitelist, + g_config->index.multiVersionBlacklist); +} + +std::vector> +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 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); + // e.g. .s + if (!ci) + return {}; + ok = false; + // -fparse-all-comments enables documentation in the indexer and in + // code completion. + ci->getLangOpts()->CommentOpts.ParseAllComments = + g_config->index.comments > 1; + 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()); + } + + 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, no_linkage); + + index::IndexingOptions indexOpts; + indexOpts.SystemSymbolFilter = + index::IndexingOptions::SystemSymbolFilterKind::All; + if (no_linkage) { + indexOpts.IndexFunctionLocals = true; + indexOpts.IndexImplicitInstantiation = true; +#if LLVM_VERSION_MAJOR >= 9 + + indexOpts.IndexParametersInDeclarations = + g_config->index.parametersInDeclarations; + indexOpts.IndexTemplateParameters = true; +#endif + } + +#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; + auto parse = [&]() { + 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; + } +#else + if (!action->Execute()) + return; +#endif + action->EndSourceFile(); + ok = true; + }; + if (!crc.RunSafely(parse)) { + LOG_S(ERROR) << "clang crashed for " << main; + return {}; + } + } + if (!ok) { + LOG_S(ERROR) << "failed to index " << main + << (reason.empty() ? "" : ": " + reason); + return {}; + } + + std::vector> result; + 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) + 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); + } + for (auto &it : entry->usr2type) { + 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); + } + for (auto &it : entry->usr2var) + uniquify(it.second.uses); + + // Update dependencies for the file. + 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))] = + file.mtime; + } + result.push_back(std::move(entry)); + } + + return result; +} +} // namespace idx + +void reflect(JsonReader &vis, SymbolRef &v) { + std::string t = vis.getString(); + char *s = const_cast(t.c_str()); + 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(); + char *s = const_cast(t.c_str()); + 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(); + char *s = const_cast(t.c_str()); + v.range = Range::fromString(s); + s = strchr(s, '|') + 1; + 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) { + char buf[99]; + 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); +} +void reflect(JsonWriter &vis, Use &v) { + char buf[99]; + 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); +} +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); + std::string s(buf); + 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, 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(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, DeclRef &v) { + reflect(vis, static_cast(v)); + reflect(vis, v.extent); +} +} // namespace ccls diff --git a/src/indexer.h b/src/indexer.h deleted file mode 100644 index c671fca37..000000000 --- a/src/indexer.h +++ /dev/null @@ -1,550 +0,0 @@ -#pragma once - -#include "clang_cursor.h" -#include "clang_index.h" -#include "clang_translation_unit.h" -#include "clang_utils.h" -#include "file_consumer.h" -#include "file_contents.h" -#include "language.h" -#include "lsp.h" -#include "maybe.h" -#include "nt_string.h" -#include "performance.h" -#include "position.h" -#include "serializer.h" -#include "symbol.h" -#include "utils.h" - -#include -#include - -#include -#include -#include -#include -#include -#include - -struct IndexFile; -struct IndexType; -struct IndexFunc; -struct IndexVar; -struct QueryFile; - -using RawId = uint32_t; - -template -struct Id { - RawId id; - - // Invalid id. - Id() : id(-1) {} - explicit Id(RawId id) : id(id) {} - // Id -> Id or Id -> Id is allowed implicitly. - template ::value || - std::is_same::value, - bool>::type = false> - Id(Id o) : id(o.id) {} - template ::value || - std::is_same::value), - bool>::type = false> - explicit Id(Id o) : id(o.id) {} - - // Needed for google::dense_hash_map. - explicit operator RawId() const { return id; } - - bool HasValueForMaybe_() const { return id != RawId(-1); } - - bool operator==(const Id& o) const { return id == o.id; } - bool operator!=(const Id& o) const { return id != o.id; } - bool operator<(const Id& o) const { return id < o.id; } -}; - -namespace std { -template -struct hash> { - size_t operator()(const Id& k) const { return hash()(k.id); } -}; -} // namespace std - -template -void Reflect(TVisitor& visitor, Id& id) { - Reflect(visitor, id.id); -} - -using IndexFileId = Id; -using IndexTypeId = Id; -using IndexFuncId = Id; -using IndexVarId = Id; - -struct SymbolIdx { - Id id; - SymbolKind kind; - - bool operator==(const SymbolIdx& o) const { - return id == o.id && kind == o.kind; - } - bool operator!=(const SymbolIdx& o) const { return !(*this == o); } - bool operator<(const SymbolIdx& o) const { - if (id != o.id) - return id < o.id; - return kind < o.kind; - } -}; -MAKE_REFLECT_STRUCT(SymbolIdx, kind, id); -MAKE_HASHABLE(SymbolIdx, t.kind, t.id); - -struct Reference { - Range range; - Id id; - SymbolKind kind; - Role role; - - bool HasValueForMaybe_() const { return range.HasValueForMaybe_(); } - operator SymbolIdx() const { return {id, kind}; } - std::tuple, SymbolKind, Role> ToTuple() const { - return std::make_tuple(range, id, kind, role); - } - bool operator==(const Reference& o) const { return ToTuple() == o.ToTuple(); } - bool operator<(const Reference& o) const { return ToTuple() < o.ToTuple(); } -}; - -// |id,kind| refer to the referenced entity. -struct SymbolRef : Reference { - SymbolRef() = default; - SymbolRef(Range range, Id id, SymbolKind kind, Role role) - : Reference{range, id, kind, role} {} -}; - -// Represents an occurrence of a variable/type, |id,kind| refer to the lexical -// parent. -struct Use : Reference { - // |file| is used in Query* but not in Index* - Id file; - Use() = default; - Use(Range range, Id id, SymbolKind kind, Role role, Id file) - : Reference{range, id, kind, role}, file(file) {} -}; -// Used by |HANDLE_MERGEABLE| so only |range| is needed. -MAKE_HASHABLE(Use, t.range); - -void Reflect(Reader& visitor, Reference& value); -void Reflect(Writer& visitor, Reference& value); - -struct IndexFamily { - using FileId = Id; - using FuncId = Id; - using TypeId = Id; - using VarId = Id; - using Range = ::Range; -}; - -template -struct TypeDefDefinitionData { - // General metadata. - std::string detailed_name; - NtString hover; - NtString comments; - - // While a class/type can technically have a separate declaration/definition, - // it doesn't really happen in practice. The declaration never contains - // comments or insightful information. The user always wants to jump from - // the declaration to the definition - never the other way around like in - // functions and (less often) variables. - // - // It's also difficult to identify a `class Foo;` statement with the clang - // indexer API (it's doable using cursor AST traversal), so we don't bother - // supporting the feature. - Maybe spell; - Maybe extent; - - // Immediate parent types. - std::vector bases; - - // Types, functions, and variables defined in this type. - std::vector types; - std::vector funcs; - std::vector vars; - - typename F::FileId file; - // If set, then this is the same underlying type as the given value (ie, this - // type comes from a using or typedef statement). - Maybe alias_of; - - int16_t short_name_offset = 0; - int16_t short_name_size = 0; - lsSymbolKind kind = lsSymbolKind::Unknown; - - bool operator==(const TypeDefDefinitionData& o) const { - return detailed_name == o.detailed_name && spell == o.spell && - extent == o.extent && alias_of == o.alias_of && - bases == o.bases && types == o.types && funcs == o.funcs && - vars == o.vars && kind == o.kind && hover == o.hover && - comments == o.comments; - } - bool operator!=(const TypeDefDefinitionData& o) const { - return !(*this == o); - } - - std::string_view ShortName() const { - return std::string_view(detailed_name.c_str() + short_name_offset, - short_name_size); - } - // Used by cquery_inheritance_hierarchy.cc:Expand generic lambda - std::string_view DetailedName(bool) const { - return detailed_name; - } -}; -template -void Reflect(TVisitor& visitor, TypeDefDefinitionData& value) { - REFLECT_MEMBER_START(); - REFLECT_MEMBER(detailed_name); - REFLECT_MEMBER(short_name_offset); - REFLECT_MEMBER(short_name_size); - REFLECT_MEMBER(kind); - REFLECT_MEMBER(hover); - REFLECT_MEMBER(comments); - REFLECT_MEMBER(spell); - REFLECT_MEMBER(extent); - REFLECT_MEMBER(file); - REFLECT_MEMBER(alias_of); - REFLECT_MEMBER(bases); - REFLECT_MEMBER(types); - REFLECT_MEMBER(funcs); - REFLECT_MEMBER(vars); - REFLECT_MEMBER_END(); -} - -struct IndexType { - using Def = TypeDefDefinitionData; - - Usr usr; - IndexTypeId id; - - Def def; - std::vector declarations; - - // Immediate derived types. - std::vector derived; - - // Declared variables of this type. - std::vector instances; - - // Every usage, useful for things like renames. - // NOTE: Do not insert directly! Use AddUsage instead. - std::vector uses; - - IndexType() {} // For serialization. - IndexType(IndexTypeId id, Usr usr); - - bool operator<(const IndexType& other) const { return id < other.id; } -}; -MAKE_HASHABLE(IndexType, t.id); - -template -struct FuncDefDefinitionData { - // General metadata. - std::string detailed_name; - NtString hover; - NtString comments; - Maybe spell; - Maybe extent; - - // Method this method overrides. - std::vector bases; - - // Local variables or parameters. - std::vector vars; - - // Functions that this function calls. - std::vector callees; - - typename F::FileId file; - // Type which declares this one (ie, it is a method) - Maybe declaring_type; - int16_t short_name_offset = 0; - int16_t short_name_size = 0; - lsSymbolKind kind = lsSymbolKind::Unknown; - StorageClass storage = StorageClass::Invalid; - - bool operator==(const FuncDefDefinitionData& o) const { - return detailed_name == o.detailed_name && spell == o.spell && - extent == o.extent && declaring_type == o.declaring_type && - bases == o.bases && vars == o.vars && callees == o.callees && - kind == o.kind && storage == o.storage && hover == o.hover && - comments == o.comments; - } - bool operator!=(const FuncDefDefinitionData& o) const { - return !(*this == o); - } - - std::string_view ShortName() const { - return std::string_view(detailed_name.c_str() + short_name_offset, - short_name_size); - } - std::string_view DetailedName(bool params) const { - if (params) - return detailed_name; - return std::string_view(detailed_name) - .substr(0, short_name_offset + short_name_size); - } -}; - -template -void Reflect(TVisitor& visitor, FuncDefDefinitionData& value) { - REFLECT_MEMBER_START(); - REFLECT_MEMBER(detailed_name); - REFLECT_MEMBER(short_name_offset); - REFLECT_MEMBER(short_name_size); - REFLECT_MEMBER(kind); - REFLECT_MEMBER(storage); - REFLECT_MEMBER(hover); - REFLECT_MEMBER(comments); - REFLECT_MEMBER(spell); - REFLECT_MEMBER(extent); - REFLECT_MEMBER(file); - REFLECT_MEMBER(declaring_type); - REFLECT_MEMBER(bases); - REFLECT_MEMBER(vars); - REFLECT_MEMBER(callees); - REFLECT_MEMBER_END(); -} - -struct IndexFunc { - using Def = FuncDefDefinitionData; - - Usr usr; - IndexFuncId id; - - Def def; - - struct Declaration { - // Range of only the function name. - Use spell; - // Location of the parameter names. - std::vector param_spellings; - }; - - // Places the function is forward-declared. - std::vector declarations; - - // Methods which directly override this one. - std::vector derived; - - // Calls/usages of this function. If the call is coming from outside a - // function context then the FuncRef will not have an associated id. - // - // To get all usages, also include the ranges inside of declarations and - // def.spell. - std::vector uses; - - IndexFunc() {} // For serialization. - IndexFunc(IndexFuncId id, Usr usr) : usr(usr), id(id) {} - - bool operator<(const IndexFunc& other) const { return id < other.id; } -}; -MAKE_HASHABLE(IndexFunc, t.id); -MAKE_REFLECT_STRUCT(IndexFunc::Declaration, spell, param_spellings); - -template -struct VarDefDefinitionData { - // General metadata. - std::string detailed_name; - NtString hover; - NtString comments; - // TODO: definitions should be a list of ranges, since there can be more - // than one - when?? - Maybe spell; - Maybe extent; - - typename F::FileId file; - // Type of the variable. - Maybe type; - - // Function/type which declares this one. - int16_t short_name_offset = 0; - int16_t short_name_size = 0; - - lsSymbolKind kind = lsSymbolKind::Unknown; - // Note a variable may have instances of both |None| and |Extern| - // (declaration). - StorageClass storage = StorageClass::Invalid; - - bool is_local() const { return kind == lsSymbolKind::Variable; } - - bool operator==(const VarDefDefinitionData& o) const { - return detailed_name == o.detailed_name && spell == o.spell && - extent == o.extent && type == o.type && kind == o.kind && - storage == o.storage && hover == o.hover && comments == o.comments; - } - bool operator!=(const VarDefDefinitionData& o) const { return !(*this == o); } - - std::string_view ShortName() const { - return std::string_view(detailed_name.c_str() + short_name_offset, - short_name_size); - } - std::string DetailedName(bool qualified) const { - if (qualified) - return detailed_name; - int i = short_name_offset; - for (int paren = 0; i; i--) { - // Skip parentheses in "(anon struct)::name" - if (detailed_name[i - 1] == ')') - paren++; - else if (detailed_name[i - 1] == '(') - paren--; - else if (!(paren > 0 || isalnum(detailed_name[i - 1]) || - detailed_name[i - 1] == '_' || detailed_name[i - 1] == ':')) - break; - } - return detailed_name.substr(0, i) + detailed_name.substr(short_name_offset); - } -}; - -template -void Reflect(TVisitor& visitor, VarDefDefinitionData& value) { - REFLECT_MEMBER_START(); - REFLECT_MEMBER(detailed_name); - REFLECT_MEMBER(short_name_size); - REFLECT_MEMBER(short_name_offset); - REFLECT_MEMBER(hover); - REFLECT_MEMBER(comments); - REFLECT_MEMBER(spell); - REFLECT_MEMBER(extent); - REFLECT_MEMBER(file); - REFLECT_MEMBER(type); - REFLECT_MEMBER(kind); - REFLECT_MEMBER(storage); - REFLECT_MEMBER_END(); -} - -struct IndexVar { - using Def = VarDefDefinitionData; - - Usr usr; - IndexVarId id; - - Def def; - - std::vector declarations; - std::vector uses; - - IndexVar() {} // For serialization. - IndexVar(IndexVarId id, Usr usr) : usr(usr), id(id) {} - - bool operator<(const IndexVar& other) const { return id < other.id; } -}; -MAKE_HASHABLE(IndexVar, t.id); - -struct IdCache { - std::string primary_file; - std::unordered_map usr_to_type_id; - std::unordered_map usr_to_func_id; - std::unordered_map usr_to_var_id; - std::unordered_map type_id_to_usr; - std::unordered_map func_id_to_usr; - std::unordered_map var_id_to_usr; - - IdCache(const std::string& primary_file); -}; - -struct IndexInclude { - // Line that has the include directive. We don't have complete range - // information - a line is good enough for clicking. - int line = 0; - // Absolute path to the index. - std::string resolved_path; -}; - -struct IndexFile { - IdCache id_cache; - - // For both JSON and MessagePack cache files. - static const int kMajorVersion; - // For MessagePack cache files. - // JSON has good forward compatibility because field addition/deletion do not - // harm but currently no efforts have been made to make old MessagePack cache - // files accepted by newer cquery. - static const int kMinorVersion; - - std::string path; - std::vector args; - int64_t last_modification_time = 0; - LanguageId language = LanguageId::Unknown; - - // The path to the translation unit cc file which caused the creation of this - // IndexFile. When parsing a translation unit we generate many IndexFile - // instances (ie, each header has a separate one). When the user edits a - // header we need to lookup the original translation unit and reindex that. - std::string import_file; - - // Source ranges that were not processed. - std::vector skipped_by_preprocessor; - - std::vector includes; - std::vector dependencies; - std::vector types; - std::vector funcs; - std::vector vars; - - // Diagnostics found when indexing this file. Not serialized. - std::vector diagnostics_; - // File contents at the time of index. Not serialized. - std::string file_contents; - - IndexFile(const std::string& path, const std::string& contents); - - IndexTypeId ToTypeId(Usr usr); - IndexFuncId ToFuncId(Usr usr); - IndexVarId ToVarId(Usr usr); - IndexTypeId ToTypeId(const CXCursor& usr); - IndexFuncId ToFuncId(const CXCursor& usr); - IndexVarId ToVarId(const CXCursor& usr); - IndexType* Resolve(IndexTypeId id); - IndexFunc* Resolve(IndexFuncId id); - IndexVar* Resolve(IndexVarId id); - - std::string ToString(); -}; - -struct NamespaceHelper { - std::unordered_map - container_cursor_to_qualified_name; - - std::string QualifiedName(const CXIdxContainerInfo* container, - std::string_view unqualified_name); -}; - -// |import_file| is the cc file which is what gets passed to clang. -// |desired_index_file| is the (h or cc) file which has actually changed. -// |dependencies| are the existing dependencies of |import_file| if this is a -// reparse. -optional>> Parse( - Config* config, - FileConsumerSharedState* file_consumer_shared, - std::string file, - const std::vector& args, - const std::vector& file_contents, - PerformanceImportFile* perf, - ClangIndex* index, - bool dump_ast = false); -optional>> ParseWithTu( - Config* config, - FileConsumerSharedState* file_consumer_shared, - PerformanceImportFile* perf, - ClangTranslationUnit* tu, - ClangIndex* index, - const std::string& file, - const std::vector& args, - const std::vector& file_contents); - -void ConcatTypeAndName(std::string& type, const std::string& name); - -void IndexInit(); - -void ClangSanityCheck(); - -std::string GetClangVersion(); diff --git a/src/indexer.hh b/src/indexer.hh new file mode 100644 index 000000000..c4c9809ad --- /dev/null +++ b/src/indexer.hh @@ -0,0 +1,355 @@ +/* Copyright 2017-2018 ccls Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#pragma once + +#include "lsp.hh" +#include "position.hh" +#include "serializer.hh" +#include "utils.hh" + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace std { +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 +// front of others. +enum class Kind : uint8_t { Invalid, File, Type, Func, Var }; +REFLECT_UNDERLYING_B(Kind); + +enum class Role : uint16_t { + None = 0, + Declaration = 1 << 0, + Definition = 1 << 1, + Reference = 1 << 2, + Read = 1 << 3, + Write = 1 << 4, + Call = 1 << 5, + Dynamic = 1 << 6, + Address = 1 << 7, + Implicit = 1 << 8, + All = (1 << 9) - 1, +}; +REFLECT_UNDERLYING_B(Role); +inline uint16_t operator&(Role lhs, Role rhs) { + return uint16_t(lhs) & uint16_t(rhs); +} +inline Role operator|(Role lhs, Role rhs) { + return Role(uint16_t(lhs) | uint16_t(rhs)); +} + +struct SymbolIdx { + Usr usr; + Kind kind; + + bool operator==(const SymbolIdx &o) const { + return usr == o.usr && kind == o.kind; + } + bool operator<(const SymbolIdx &o) const { + return usr != o.usr ? usr < o.usr : kind < o.kind; + } +}; + +// |id,kind| refer to the referenced entity. +struct SymbolRef { + Range range; + Usr usr; + Kind kind; + Role role; + operator SymbolIdx() const { return {usr, kind}; } + 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(); } +}; + +struct ExtentRef : SymbolRef { + Range extent; + std::tuple toTuple() const { + return std::make_tuple(range, usr, kind, role, extent); + } + 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 { + 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(); } +}; + +// Represents an occurrence of a variable/type, |usr,kind| refer to the lexical +// parent. +struct Use : Ref { + // |file| is used in Query* but not in Index* + int file_id = -1; + bool operator==(const Use &o) const { + // lexical container info is ignored. + return range == o.range && file_id == o.file_id; + } +}; + +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); + +template using VectorAdapter = std::vector>; + +template struct NameMixin { + std::string_view name(bool qualified) const { + auto self = static_cast(this); + return qualified + ? std::string_view(self->detailed_name + self->qual_name_offset, + self->short_name_offset - + self->qual_name_offset + + self->short_name_size) + : std::string_view(self->detailed_name + self->short_name_offset, + self->short_name_size); + } +}; + +template