├── .gitignore ├── tests ├── case │ ├── main.cpp │ └── main_error.cpp ├── token_test.cc ├── source_location_test.cc ├── translation_unit_test.cc ├── cursor_test.cc ├── diagnostics_test.cc ├── CMakeLists.txt ├── code_complete_results_test.cc └── completion_string_test.cc ├── src ├── index.cc ├── index.h ├── compile_command.cc ├── utility.h ├── compile_command.h ├── clangmm.h ├── compilation_database.cc ├── compilation_database.h ├── compile_commands.h ├── source_range.cc ├── source_range.h ├── compile_commands.cc ├── tokens.h ├── code_complete_results.h ├── diagnostic.h ├── CMakeLists.txt ├── diagnostic.cc ├── token.h ├── source_location.cc ├── completion_string.h ├── token.cc ├── source_location.h ├── code_complete_results.cc ├── translation_unit.h ├── utility.cc ├── tokens.cc ├── completion_string.cc ├── translation_unit.cc ├── cursor.h └── cursor.cc ├── ci ├── static_analysis.sh ├── compile.sh ├── execute.sh └── update_ci.sh ├── .travis.yml ├── CMakeLists.txt ├── README.md ├── appveyor.yml ├── LICENSE ├── docs └── install.md └── cmake_modules └── FindLibClang.cmake /.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | ./* 3 | !.gitignore 4 | !*.h 5 | !*.cc 6 | !CMakeLists.txt 7 | 8 | -------------------------------------------------------------------------------- /tests/case/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char *argv[]) 4 | { 5 | std::cout << "Hello, World!" << std::endl; 6 | return 0; 7 | } 8 | -------------------------------------------------------------------------------- /tests/case/main_error.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char *argv[]) { 4 | std::cout << "Hello, World!" << std::endl; 5 | std::cout << undeclared_variable << std::endl; 6 | return 0; 7 | } 8 | -------------------------------------------------------------------------------- /src/index.cc: -------------------------------------------------------------------------------- 1 | #include "index.h" 2 | 3 | clangmm::Index::Index(int excludeDeclarationsFromPCH, int displayDiagnostics) { 4 | cx_index = clang_createIndex(excludeDeclarationsFromPCH, displayDiagnostics); 5 | } 6 | 7 | clangmm::Index::~Index() { 8 | clang_disposeIndex(cx_index); 9 | } -------------------------------------------------------------------------------- /src/index.h: -------------------------------------------------------------------------------- 1 | #ifndef INDEX_H_ 2 | #define INDEX_H_ 3 | #include 4 | 5 | namespace clangmm { 6 | class Index { 7 | public: 8 | Index(int excludeDeclarationsFromPCH, int displayDiagnostics); 9 | ~Index(); 10 | CXIndex cx_index; 11 | }; 12 | } // namespace clangmm 13 | #endif // INDEX_H_ 14 | -------------------------------------------------------------------------------- /src/compile_command.cc: -------------------------------------------------------------------------------- 1 | #include "compile_command.h" 2 | #include "compile_commands.h" 3 | #include "utility.h" 4 | 5 | std::vector clangmm::CompileCommand::get_arguments() { 6 | unsigned size = clang_CompileCommand_getNumArgs(cx_command); 7 | std::vector arguments; 8 | for (unsigned i = 0; i < size; i++) 9 | arguments.emplace_back(to_string(clang_CompileCommand_getArg(cx_command, i))); 10 | return arguments; 11 | } 12 | -------------------------------------------------------------------------------- /src/utility.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILITY_H_ 2 | #define UTILITY_H_ 3 | #include 4 | #include 5 | 6 | namespace clangmm { 7 | std::string to_string(CXString cx_string); 8 | 9 | class String { 10 | public: 11 | String(const CXString &cx_string); 12 | ~String(); 13 | CXString cx_string; 14 | const char *c_str; 15 | }; 16 | 17 | void remove_include_guard(std::string &buffer); 18 | } 19 | 20 | #endif // UTILITY_H_ 21 | -------------------------------------------------------------------------------- /ci/static_analysis.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "${cmake_command}" == "" ]; then 4 | cmake_command="scan-build cmake .." 5 | fi 6 | 7 | if [ "${make_command}" == "" ]; then 8 | make_command="scan-build --status-bugs make -j 2" 9 | fi 10 | 11 | cd libclangmm || echo "Can't cd into libclangmm" 12 | mkdir -p build && cd build || echo "Error making build directory" 13 | sh -c "${cmake_command}" || echo "Cmake configuration failed" 14 | exec sh -c "${make_command}" 15 | -------------------------------------------------------------------------------- /src/compile_command.h: -------------------------------------------------------------------------------- 1 | #ifndef COMPILECOMMAND_H_ 2 | #define COMPILECOMMAND_H_ 3 | #include 4 | #include 5 | #include 6 | 7 | namespace clangmm { 8 | class CompileCommand { 9 | public: 10 | CompileCommand(const CXCompileCommand& cx_command) : cx_command(cx_command) {}; 11 | std::vector get_arguments(); 12 | 13 | CXCompileCommand cx_command; 14 | }; 15 | } 16 | #endif // COMPILECOMMAND_H_ 17 | -------------------------------------------------------------------------------- /src/clangmm.h: -------------------------------------------------------------------------------- 1 | #ifndef CLANGMM_H_ 2 | #define CLANGMM_H_ 3 | #include "translation_unit.h" 4 | #include "source_location.h" 5 | #include "source_range.h" 6 | #include "token.h" 7 | #include "tokens.h" 8 | #include "compilation_database.h" 9 | #include "compile_commands.h" 10 | #include "compile_command.h" 11 | #include "code_complete_results.h" 12 | #include "completion_string.h" 13 | #include "index.h" 14 | #include "cursor.h" 15 | #include "diagnostic.h" 16 | #include "utility.h" 17 | #endif // CLANGMM_H_ 18 | -------------------------------------------------------------------------------- /src/compilation_database.cc: -------------------------------------------------------------------------------- 1 | #include "compilation_database.h" 2 | #include 3 | 4 | clangmm::CompilationDatabase::CompilationDatabase(const std::string &project_path) { 5 | cx_db = clang_CompilationDatabase_fromDirectory(project_path.c_str(), &cx_db_error); 6 | } 7 | 8 | clangmm::CompilationDatabase::~CompilationDatabase() { 9 | clang_CompilationDatabase_dispose(cx_db); 10 | } 11 | 12 | clangmm::CompilationDatabase::operator bool() const { 13 | return !static_cast(cx_db_error); 14 | } 15 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | 3 | env: 4 | - distribution: ubuntu 5 | - distribution: fedora 6 | - distribution: arch 7 | - distribution: debian-testing 8 | - distribution: debian 9 | 10 | services: 11 | - docker 12 | 13 | before_install: 14 | - ./ci/update_ci.sh 15 | 16 | script: 17 | - script=static_analysis ./ci/execute.sh 18 | - script=clean ./ci/execute.sh 19 | - script=compile CXX=g++ CC=gcc ./ci/execute.sh 20 | - script=compile make_command="CTEST_OUTPUT_ON_FAILURE=1 make test" ./ci/execute.sh 21 | -------------------------------------------------------------------------------- /src/compilation_database.h: -------------------------------------------------------------------------------- 1 | #ifndef COMPILATIONDATABASE_H_ 2 | #define COMPILATIONDATABASE_H_ 3 | #include 4 | #include 5 | #include 6 | 7 | namespace clangmm { 8 | class CompilationDatabase { 9 | CXCompilationDatabase_Error cx_db_error; 10 | public: 11 | explicit CompilationDatabase(const std::string &project_path); 12 | ~CompilationDatabase(); 13 | 14 | operator bool() const; 15 | 16 | CXCompilationDatabase cx_db; 17 | }; 18 | } 19 | 20 | #endif // COMPILATIONDATABASE_H_ 21 | -------------------------------------------------------------------------------- /src/compile_commands.h: -------------------------------------------------------------------------------- 1 | #ifndef COMPILECOMMANDS_H_ 2 | #define COMPILECOMMANDS_H_ 3 | #include "compilation_database.h" 4 | #include "compile_command.h" 5 | #include 6 | #include 7 | #include 8 | 9 | namespace clangmm { 10 | class CompileCommands { 11 | public: 12 | CompileCommands(const std::string &filename, CompilationDatabase &db); 13 | std::vector get_commands(); 14 | ~CompileCommands(); 15 | 16 | CXCompileCommands cx_commands; 17 | }; 18 | } 19 | #endif // COMPILECOMMANDS_H_ 20 | -------------------------------------------------------------------------------- /tests/token_test.cc: -------------------------------------------------------------------------------- 1 | #include "clangmm.h" 2 | #include 3 | #include 4 | 5 | int main() { 6 | std::string tests_path=LIBCLANGMM_TESTS_PATH; 7 | std::string path(tests_path+"/case/main.cpp"); 8 | 9 | clangmm::Index index(0, 0); 10 | 11 | clangmm::TranslationUnit tu(index, path, {}); 12 | 13 | auto tokens=tu.get_tokens(0, 113); 14 | 15 | assert(tokens->size() == 32); 16 | assert((*tokens)[1].get_kind() == clangmm::Token::Kind::Identifier); 17 | 18 | std::string str = (*tokens)[28].get_spelling(); 19 | assert(str == "return"); 20 | } 21 | -------------------------------------------------------------------------------- /tests/source_location_test.cc: -------------------------------------------------------------------------------- 1 | #include "clangmm.h" 2 | #include 3 | #include 4 | 5 | int main() { 6 | std::string tests_path=LIBCLANGMM_TESTS_PATH; 7 | std::string path(tests_path+"/case/main.cpp"); 8 | 9 | clangmm::Index index(0, 0); 10 | 11 | clangmm::TranslationUnit tu(index, path, {}); 12 | auto tokens=tu.get_tokens(0, 113); 13 | 14 | auto offsets=(*tokens)[28].get_source_range().get_offsets(); 15 | 16 | assert(offsets.first.line == 6 && offsets.first.index == 3); 17 | assert(offsets.second.line == 6 && offsets.second.index == 9); 18 | } 19 | -------------------------------------------------------------------------------- /tests/translation_unit_test.cc: -------------------------------------------------------------------------------- 1 | #include "clangmm.h" 2 | #include 3 | #include 4 | #include 5 | 6 | int main() { 7 | std::string tests_path=LIBCLANGMM_TESTS_PATH; 8 | std::string path(tests_path+"/case/main.cpp"); 9 | 10 | clangmm::Index index(0, 0); 11 | 12 | clangmm::TranslationUnit tu(index, path, {}); 13 | 14 | std::string buffer = "int main(int argc, char *argv[]) {\n" 15 | "std::cout << \"Hello World!\" << std::endl;\n" 16 | "return 0\n" 17 | "}\n"; 18 | 19 | assert(tu.reparse(buffer) == 0); 20 | } 21 | -------------------------------------------------------------------------------- /src/source_range.cc: -------------------------------------------------------------------------------- 1 | #include "source_range.h" 2 | 3 | clangmm::SourceRange::SourceRange(const clangmm::SourceLocation &start, const clangmm::SourceLocation &end) { 4 | cx_range = clang_getRange(start.cx_location, end.cx_location); 5 | } 6 | 7 | clangmm::SourceLocation clangmm::SourceRange::get_start() const { 8 | return SourceLocation(clang_getRangeStart(cx_range)); 9 | } 10 | 11 | clangmm::SourceLocation clangmm::SourceRange::get_end() const { 12 | return SourceLocation(clang_getRangeEnd(cx_range)); 13 | } 14 | 15 | 16 | std::pair clangmm::SourceRange::get_offsets() const { 17 | return {get_start().get_offset(), get_end().get_offset()}; 18 | } -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | project(clangmm) 4 | 5 | add_compile_options(-std=c++11 -Wall -Wextra -Wno-unused-parameter) 6 | 7 | if(APPLE) 8 | set(CMAKE_MACOSX_RPATH 1) 9 | endif() 10 | 11 | if("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}") 12 | set(NOT_SUB_PROJECT 1) 13 | endif() 14 | 15 | list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake_modules") 16 | find_package(LibClang REQUIRED) 17 | 18 | add_subdirectory(src) 19 | 20 | option(BUILD_TESTING OFF) 21 | 22 | if(${NOT_SUB_PROJECT}) 23 | set(BUILD_TESTING ON) 24 | endif() 25 | 26 | if(BUILD_TESTING) 27 | enable_testing() 28 | add_subdirectory(tests) 29 | endif() 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **_This project has moved to https://gitlab.com/cppit/libclangmm._** 2 | 3 | # libclangmm 4 | 5 | ###### an easy-to-use C++ wrapper for libclang 6 | 7 | ## About ## 8 | Provides C++ bindings and class structure to the [libclang](http://www.llvm.org) C library. 9 | 10 | Developed for [juCi++](https://gitlab.com/cppit/jucipp), a lightweight, platform-independent C++ IDE. 11 | 12 | ## Dependencies ## 13 | * libclang 14 | 15 | ## Installation ## 16 | See [installation guide](https://gitlab.com/cppit/libclangmm/blob/master/docs/install.md) 17 | 18 | # Tests # 19 | To run the unit tests: 20 | ```sh 21 | mkdir build && cd build 22 | cmake -DBUILD_TESTING=1 .. 23 | make 24 | make test 25 | ``` 26 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | platform: 2 | - x64 3 | 4 | environment: 5 | MSYSTEM: MSYS 6 | 7 | before_build: 8 | - C:\msys64\usr\bin\pacman --noconfirm --sync --refresh --refresh pacman 9 | - C:\msys64\usr\bin\pacman --noconfirm --sync --refresh --refresh git 10 | - C:\msys64\usr\bin\pacman --noconfirm --sync --refresh --refresh --sysupgrade --sysupgrade 11 | - C:\msys64\usr\bin\bash -lc "$(cygpath ${APPVEYOR_BUILD_FOLDER})/ci/update_ci.sh" 12 | 13 | build_script: 14 | - C:\msys64\usr\bin\bash -lc "script=compile $(cygpath ${APPVEYOR_BUILD_FOLDER})/ci/execute.sh" 15 | - C:\msys64\usr\bin\bash -lc "script=compile make_command='CTEST_OUTPUT_ON_FAILURE=1 make test' $(cygpath ${APPVEYOR_BUILD_FOLDER})/ci/execute.sh" 16 | -------------------------------------------------------------------------------- /ci/compile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "${cmake_command}" == "" ]; then 4 | if [ "$APPVEYOR" != "" ]; then 5 | if [ "$PLATFORM" == "x64" ]; then 6 | mingw="mingw64" 7 | else 8 | mingw="mingw32" 9 | fi 10 | cmake_command="cmake -G\"MSYS Makefiles\" -DCMAKE_INSTALL_PREFIX=/${mingw} -DBUILD_TESTING=1 .." 11 | make_command="make" 12 | else 13 | cmake_command="cmake -DBUILD_TESTING=1 .." 14 | fi 15 | fi 16 | 17 | if [ "${make_command}" == "" ]; then 18 | make_command="make -j 2" 19 | fi 20 | 21 | cd libclangmm || echo "Can't cd into libclangmm" 22 | mkdir -p build && cd build || echo "Error making build directory" 23 | sh -c "${cmake_command}" || echo "Cmake configuration failed" 24 | exec sh -c "${make_command}" 25 | -------------------------------------------------------------------------------- /src/source_range.h: -------------------------------------------------------------------------------- 1 | #ifndef SOURCERANGE_H_ 2 | #define SOURCERANGE_H_ 3 | #include 4 | #include "source_location.h" 5 | #include 6 | #include 7 | 8 | namespace clangmm { 9 | class SourceRange { 10 | public: 11 | SourceRange(const CXSourceRange& cx_range) : cx_range(cx_range) {} 12 | SourceRange(const SourceLocation &start, const SourceLocation &end); 13 | SourceLocation get_start() const; 14 | SourceLocation get_end() const; 15 | std::pair get_offsets() const; 16 | 17 | friend std::ostream &operator<<(std::ostream &os, const SourceRange &range) { 18 | os << range.get_start() << '-' << range.get_end(); 19 | return os; 20 | } 21 | 22 | CXSourceRange cx_range; 23 | }; 24 | } // namespace clangmm 25 | #endif // SOURCERANGE_H_ 26 | -------------------------------------------------------------------------------- /src/compile_commands.cc: -------------------------------------------------------------------------------- 1 | #include "compile_commands.h" 2 | 3 | clangmm::CompileCommands::CompileCommands(const std::string &filename, CompilationDatabase &db) { 4 | if(!filename.empty()) 5 | cx_commands = clang_CompilationDatabase_getCompileCommands(db.cx_db, filename.c_str()); 6 | if(filename.empty() || clang_CompileCommands_getSize(cx_commands)==0) 7 | cx_commands = clang_CompilationDatabase_getAllCompileCommands(db.cx_db); 8 | } 9 | 10 | clangmm::CompileCommands::~CompileCommands() { 11 | clang_CompileCommands_dispose(cx_commands); 12 | } 13 | 14 | std::vector clangmm::CompileCommands::get_commands() { 15 | unsigned size = clang_CompileCommands_getSize(cx_commands); 16 | std::vector commands; 17 | commands.reserve(size); 18 | for (unsigned i = 0; i < size; i++) 19 | commands.emplace_back(clang_CompileCommands_getCommand(cx_commands, i)); 20 | return commands; 21 | } 22 | -------------------------------------------------------------------------------- /src/tokens.h: -------------------------------------------------------------------------------- 1 | #ifndef TOKENS_H_ 2 | #define TOKENS_H_ 3 | #include 4 | #include "source_range.h" 5 | #include "token.h" 6 | #include 7 | #include 8 | #include 9 | 10 | namespace clangmm { 11 | class Tokens : public std::vector { 12 | friend class TranslationUnit; 13 | friend class Diagnostic; 14 | Tokens(CXTranslationUnit &cx_tu, const SourceRange &range, bool annotate_tokens=true); 15 | public: 16 | ~Tokens(); 17 | std::vector > get_similar_token_offsets(Cursor::Kind kind, const std::string &spelling, 18 | const std::unordered_set &usrs); 19 | private: 20 | CXToken *cx_tokens; 21 | std::unique_ptr cx_cursors; 22 | CXTranslationUnit& cx_tu; 23 | }; 24 | } // namespace clangmm 25 | #endif // TOKENS_H_ 26 | -------------------------------------------------------------------------------- /tests/cursor_test.cc: -------------------------------------------------------------------------------- 1 | #include "clangmm.h" 2 | #include 3 | #include 4 | #include 5 | 6 | int main() { 7 | std::string tests_path=LIBCLANGMM_TESTS_PATH; 8 | std::string path(tests_path+"/case/main.cpp"); 9 | 10 | clangmm::Index index(0, 0); 11 | 12 | std::vector arguments; 13 | auto clang_version_string=clangmm::to_string(clang_getClangVersion()); 14 | const static std::regex clang_version_regex("^[A-Za-z ]+([0-9.]+).*$"); 15 | std::smatch sm; 16 | if(std::regex_match(clang_version_string, sm, clang_version_regex)) { 17 | auto clang_version=sm[1].str(); 18 | arguments.emplace_back("-I/usr/lib/clang/"+clang_version+"/include"); 19 | arguments.emplace_back("-I/usr/lib64/clang/"+clang_version+"/include"); // For Fedora 20 | } 21 | 22 | clangmm::TranslationUnit tu(index, path, arguments); 23 | 24 | auto cursor=tu.get_cursor(path, 103); 25 | 26 | assert(cursor.get_kind() == clangmm::Cursor::Kind::ReturnStmt); 27 | } 28 | -------------------------------------------------------------------------------- /src/code_complete_results.h: -------------------------------------------------------------------------------- 1 | #ifndef CODECOMPLETERESULTS_H_ 2 | #define CODECOMPLETERESULTS_H_ 3 | #include 4 | #include 5 | #include 6 | #include "completion_string.h" 7 | 8 | namespace clangmm { 9 | class CodeCompleteResults { 10 | friend class TranslationUnit; 11 | 12 | CodeCompleteResults(CXTranslationUnit &cx_tu, const std::string &buffer, 13 | unsigned line_num, unsigned column); 14 | public: 15 | CodeCompleteResults(CodeCompleteResults &) = delete; 16 | CodeCompleteResults(CodeCompleteResults &&rhs); 17 | CodeCompleteResults &operator=(const CodeCompleteResults &rhs) = delete; 18 | CodeCompleteResults &operator=(CodeCompleteResults &&rhs); 19 | ~CodeCompleteResults(); 20 | CompletionString get(unsigned index) const; 21 | unsigned size() const; 22 | std::string get_usr() const; 23 | 24 | CXCodeCompleteResults *cx_results; 25 | }; 26 | } // namespace clangmm 27 | #endif // CODECOMPLETERESULTS_H_ 28 | -------------------------------------------------------------------------------- /src/diagnostic.h: -------------------------------------------------------------------------------- 1 | #ifndef DIAGNOSTIC_H_ 2 | #define DIAGNOSTIC_H_ 3 | #include 4 | #include 5 | #include 6 | #include "source_range.h" 7 | 8 | namespace clangmm { 9 | class Diagnostic { 10 | friend class TranslationUnit; 11 | Diagnostic(CXTranslationUnit& cx_tu, CXDiagnostic& cx_diagnostic); 12 | public: 13 | enum class Severity { 14 | Ignored = 0, 15 | Note, 16 | Warning, 17 | Error, 18 | Fatal 19 | }; 20 | 21 | class FixIt { 22 | public: 23 | FixIt(const std::string &source, const std::pair &offsets): 24 | source(source), offsets(offsets) {} 25 | std::string source; 26 | std::pair offsets; 27 | }; 28 | 29 | Severity severity; 30 | std::string spelling; 31 | std::string path; 32 | std::pair offsets; 33 | std::vector fix_its; 34 | }; 35 | } 36 | 37 | #endif // DIAGNOSTIC_H_ 38 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(clangmm 2 | code_complete_results.cc 3 | compilation_database.cc 4 | compile_command.cc 5 | compile_commands.cc 6 | completion_string.cc 7 | cursor.cc 8 | index.cc 9 | source_location.cc 10 | source_range.cc 11 | token.cc 12 | tokens.cc 13 | translation_unit.cc 14 | diagnostic.cc 15 | utility.cc 16 | ) 17 | 18 | include_directories(${LIBCLANG_INCLUDE_DIRS}) 19 | target_link_libraries(clangmm ${LIBCLANG_LIBRARIES}) 20 | target_include_directories(clangmm PUBLIC ${LIBCLANG_INCLUDE_DIRS} .) 21 | 22 | if(${NOT_SUB_PROJECT}) 23 | install(TARGETS clangmm ARCHIVE DESTINATION lib) 24 | install(FILES 25 | clangmm.h 26 | code_complete_results.h 27 | compilation_database.h 28 | compile_command.h 29 | compile_commands.h 30 | completion_string.h 31 | cursor.h 32 | index.h 33 | source_location.h 34 | source_range.h 35 | token.h 36 | tokens.h 37 | translation_unit.h 38 | diagnostic.h 39 | utility.h 40 | DESTINATION include/libclangmm 41 | ) 42 | endif() 43 | -------------------------------------------------------------------------------- /ci/execute.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function linux () { 4 | cd ci || exit 5 | if [ "${script}" == "clean" ]; then 6 | sudo rm ../build -rf 7 | return 0 8 | fi 9 | sudo docker run -it \ 10 | -e "CXX=$CXX" \ 11 | -e "CC=$CC" \ 12 | -e "make_command=$make_command" \ 13 | -e "cmake_command=$cmake_command" \ 14 | -e "distribution=$distribution" \ 15 | -v "$PWD/../:/libclangmm" \ 16 | --entrypoint="/libclangmm/ci/${script}.sh" \ 17 | "cppit/jucipp:$distribution" 18 | } 19 | 20 | function windows () { 21 | export PATH="/mingw64/bin:/usr/local/bin:/usr/bin:/bin:/c/WINDOWS/system32:/c/WINDOWS:/c/WINDOWS/System32/Wbem:/c/WINDOWS/System32/WindowsPowerShell/v1.0:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl" 22 | bf=$(cygpath ${APPVEYOR_BUILD_FOLDER}) 23 | cd "$bf" || (echo "Error changing directory"; return 1) 24 | if [ "${script}" == "clean" ]; then 25 | sudo rm "./build" -rf 26 | return 0 27 | fi 28 | sh -c "${bf}/ci/${script}.sh" 29 | } 30 | 31 | 32 | if [ "$TRAVIS_OS_NAME" == "" ]; then 33 | TRAVIS_OS_NAME=windows 34 | fi 35 | 36 | $TRAVIS_OS_NAME 37 | -------------------------------------------------------------------------------- /tests/diagnostics_test.cc: -------------------------------------------------------------------------------- 1 | #include "clangmm.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace std; 8 | 9 | int main() { 10 | std::string tests_path=LIBCLANGMM_TESTS_PATH; 11 | std::string path(tests_path+"/case/main_error.cpp"); 12 | 13 | clangmm::Index index(0, 0); 14 | 15 | std::vector arguments; 16 | auto clang_version_string=clangmm::to_string(clang_getClangVersion()); 17 | const static std::regex clang_version_regex("^[A-Za-z ]+([0-9.]+).*$"); 18 | std::smatch sm; 19 | if(std::regex_match(clang_version_string, sm, clang_version_regex)) { 20 | auto clang_version=sm[1].str(); 21 | arguments.emplace_back("-I/usr/lib/clang/"+clang_version+"/include"); 22 | arguments.emplace_back("-I/usr/lib64/clang/"+clang_version+"/include"); // For Fedora 23 | } 24 | 25 | clangmm::TranslationUnit tu(index, path, arguments); 26 | 27 | auto diagnostics=tu.get_diagnostics(); 28 | assert(diagnostics.size()>0); 29 | assert(!diagnostics[0].spelling.empty()); 30 | assert(diagnostics[0].severity==clangmm::Diagnostic::Severity::Error); 31 | } 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2017 cppit (zippit) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /src/diagnostic.cc: -------------------------------------------------------------------------------- 1 | #include "diagnostic.h" 2 | #include "source_location.h" 3 | #include "tokens.h" 4 | #include "utility.h" 5 | 6 | clangmm::Diagnostic::Diagnostic(CXTranslationUnit& cx_tu, CXDiagnostic& cx_diagnostic) { 7 | severity=static_cast(clang_getDiagnosticSeverity(cx_diagnostic)); 8 | spelling=to_string(clang_getDiagnosticSpelling(cx_diagnostic)); 9 | 10 | SourceLocation location(clang_getDiagnosticLocation(cx_diagnostic)); 11 | path=location.get_path(); 12 | auto offset=location.get_offset(); 13 | auto corrected_location=SourceLocation(cx_tu, path.c_str(), offset.line, offset.index); // to avoid getting macro tokens 14 | Tokens tokens(cx_tu, SourceRange(corrected_location, corrected_location), false); 15 | if(tokens.size()==1) 16 | offsets={offset, tokens.begin()->get_source_range().get_offsets().second}; 17 | 18 | unsigned num_fix_its=clang_getDiagnosticNumFixIts(cx_diagnostic); 19 | for(unsigned c=0;c 4 | #include "source_location.h" 5 | #include "source_range.h" 6 | #include "cursor.h" 7 | #include 8 | 9 | namespace clangmm { 10 | class Token { 11 | friend class Tokens; 12 | public: 13 | enum Kind { 14 | Punctuation, 15 | Keyword, 16 | Identifier, 17 | Literal, 18 | Comment 19 | }; 20 | private: 21 | Token(CXTranslationUnit &cx_tu, CXToken &cx_token, CXCursor &cx_cursor): 22 | cx_tu(cx_tu), cx_token(cx_token), cx_cursor(cx_cursor) {} 23 | public: 24 | Kind get_kind() const; 25 | std::string get_spelling() const; 26 | SourceLocation get_source_location() const; 27 | SourceRange get_source_range() const; 28 | clangmm::Cursor get_cursor() const {return clangmm::Cursor(cx_cursor);} 29 | 30 | bool is_identifier() const; 31 | 32 | friend std::ostream &operator<<(std::ostream &os, const Token &token) { 33 | os << token.get_source_range() << ' ' << token.get_spelling(); 34 | return os; 35 | } 36 | 37 | CXTranslationUnit &cx_tu; 38 | CXToken& cx_token; 39 | CXCursor& cx_cursor; 40 | }; 41 | } // namespace clangmm 42 | #endif // TOKEN_H_ 43 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_definitions(-DLIBCLANGMM_TESTS_PATH="${CMAKE_CURRENT_SOURCE_DIR}") 2 | 3 | add_executable(code_complete_results_test code_complete_results_test.cc) 4 | target_link_libraries(code_complete_results_test clangmm) 5 | add_test(code_complete_results_test code_complete_results_test) 6 | 7 | add_executable(completion_string_test completion_string_test.cc) 8 | target_link_libraries(completion_string_test clangmm) 9 | add_test(completion_string_test completion_string_test) 10 | 11 | add_executable(cursor_test cursor_test.cc) 12 | target_link_libraries(cursor_test clangmm) 13 | add_test(cursor_test cursor_test) 14 | 15 | add_executable(diagnostics_test diagnostics_test.cc) 16 | target_link_libraries(diagnostics_test clangmm) 17 | add_test(diagnostics_test diagnostics_test) 18 | 19 | add_executable(source_location_test source_location_test.cc) 20 | target_link_libraries(source_location_test clangmm) 21 | add_test(source_location_test source_location_test) 22 | 23 | add_executable(token_test token_test.cc) 24 | target_link_libraries(token_test clangmm) 25 | add_test(token_test token_test) 26 | 27 | add_executable(translation_unit_test translation_unit_test.cc) 28 | target_link_libraries(translation_unit_test clangmm) 29 | add_test(translation_unit_test translation_unit_test) 30 | -------------------------------------------------------------------------------- /src/source_location.cc: -------------------------------------------------------------------------------- 1 | #include "source_location.h" 2 | #include "utility.h" 3 | 4 | // // // // // // // // 5 | // SourceLocation // 6 | // // // // // // // // 7 | clangmm::SourceLocation::SourceLocation(CXTranslationUnit &tu, const std::string &filepath, unsigned offset) { 8 | CXFile file = clang_getFile(tu, filepath.c_str()); 9 | cx_location = clang_getLocationForOffset(tu, file, offset); 10 | } 11 | 12 | clangmm::SourceLocation::SourceLocation(CXTranslationUnit &tu, const std::string &filepath, unsigned line, unsigned column) { 13 | CXFile file = clang_getFile(tu, filepath.c_str()); 14 | cx_location = clang_getLocation(tu, file, line, column); 15 | } 16 | 17 | std::string clangmm::SourceLocation::get_path() const { 18 | std::string path; 19 | get_data(&path, nullptr, nullptr, nullptr); 20 | return path; 21 | } 22 | clangmm::Offset clangmm::SourceLocation::get_offset() const { 23 | unsigned line, index; 24 | get_data(nullptr, &line, &index, nullptr); 25 | return {line, index}; 26 | } 27 | 28 | void clangmm::SourceLocation::get_data(std::string* path, unsigned *line, unsigned *column, unsigned *offset) const { 29 | if(path==nullptr) 30 | clang_getExpansionLocation(cx_location, NULL, line, column, offset); 31 | else { 32 | CXFile file; 33 | clang_getExpansionLocation(cx_location, &file, line, column, offset); 34 | if (file!=NULL) { 35 | *path=to_string(clang_getFileName(file)); 36 | } 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /tests/code_complete_results_test.cc: -------------------------------------------------------------------------------- 1 | #include "clangmm.h" 2 | #include 3 | #include 4 | #include 5 | 6 | int main() { 7 | std::string tests_path=LIBCLANGMM_TESTS_PATH; 8 | std::string path(tests_path+"/case/main.cpp"); 9 | 10 | std::vector arguments; 11 | auto clang_version_string=clangmm::to_string(clang_getClangVersion()); 12 | const static std::regex clang_version_regex("^[A-Za-z ]+([0-9.]+).*$"); 13 | std::smatch sm; 14 | if(std::regex_match(clang_version_string, sm, clang_version_regex)) { 15 | auto clang_version=sm[1].str(); 16 | arguments.emplace_back("-I/usr/lib/clang/"+clang_version+"/include"); 17 | arguments.emplace_back("-I/usr/lib64/clang/"+clang_version+"/include"); // For Fedora 18 | } 19 | 20 | clangmm::Index index(0, 0); 21 | clangmm::TranslationUnit tu(index, path, arguments); 22 | 23 | std::string buffer="#include \n" 24 | "int main(int argc, char *argv[]) {\n" 25 | "std::string str;\n" 26 | "str.\n" 27 | "return 0\n" 28 | "}"; 29 | 30 | tu.reparse(buffer); 31 | auto results=tu.get_code_completions(buffer, 4, 5); 32 | 33 | bool substr_found=false; 34 | for(unsigned c=0;c 61 | -------------------------------------------------------------------------------- /tests/completion_string_test.cc: -------------------------------------------------------------------------------- 1 | #include "clangmm.h" 2 | #include 3 | #include 4 | #include 5 | 6 | int main() { 7 | { 8 | clangmm::CompletionChunk str("(", clangmm::CompletionChunk_LeftBrace); 9 | 10 | assert(str.text == "("); 11 | assert(str.kind == clangmm::CompletionChunk_LeftBrace); 12 | } 13 | 14 | { 15 | std::string tests_path=LIBCLANGMM_TESTS_PATH; 16 | std::string path(tests_path+"/case/main.cpp"); 17 | 18 | std::vector arguments; 19 | auto clang_version_string=clangmm::to_string(clang_getClangVersion()); 20 | const static std::regex clang_version_regex("^[A-Za-z ]+([0-9.]+).*$"); 21 | std::smatch sm; 22 | if(std::regex_match(clang_version_string, sm, clang_version_regex)) { 23 | auto clang_version=sm[1].str(); 24 | arguments.emplace_back("-I/usr/lib/clang/"+clang_version+"/include"); 25 | arguments.emplace_back("-I/usr/lib64/clang/"+clang_version+"/include"); // For Fedora 26 | } 27 | 28 | clangmm::Index index(0, 0); 29 | clangmm::TranslationUnit tu(index, path, arguments); 30 | 31 | std::string buffer="#include \n" 32 | "int main(int argc, char *argv[]) {\n" 33 | "std::string str;\n" 34 | "str.\n" 35 | "return 0\n" 36 | "}"; 37 | 38 | tu.reparse(buffer); 39 | auto results=tu.get_code_completions(buffer, 4, 5); 40 | 41 | auto str = results.get(0); 42 | 43 | assert(str.get_num_chunks()>0); 44 | assert(str.get_chunks().size()>0); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/completion_string.h: -------------------------------------------------------------------------------- 1 | #ifndef COMPLETIONSTRING_H_ 2 | #define COMPLETIONSTRING_H_ 3 | #include 4 | #include 5 | #include 6 | #include "cursor.h" 7 | 8 | namespace clangmm { 9 | enum CompletionChunkKind { 10 | CompletionChunk_Optional, CompletionChunk_TypedText, 11 | CompletionChunk_Text, CompletionChunk_Placeholder, 12 | CompletionChunk_Informative, CompletionChunk_CurrentParameter, 13 | CompletionChunk_LeftParen, CompletionChunk_RightParen, 14 | CompletionChunk_LeftBracket, CompletionChunk_RightBracket, 15 | CompletionChunk_LeftBrace, CompletionChunk_RightBrace, 16 | CompletionChunk_LeftAngle, CompletionChunk_RightAngle, 17 | CompletionChunk_Comma, CompletionChunk_ResultType, 18 | CompletionChunk_Colon, CompletionChunk_SemiColon, 19 | CompletionChunk_Equal, CompletionChunk_HorizontalSpace, 20 | CompletionChunk_VerticalSpace 21 | }; 22 | 23 | class CompletionChunk { 24 | public: 25 | CompletionChunk(std::string text, CompletionChunkKind kind); 26 | std::string text; 27 | CompletionChunkKind kind; 28 | }; 29 | 30 | class CompletionString { 31 | public: 32 | explicit CompletionString(const CXCompletionString &cx_completion_string); 33 | bool available() const; 34 | std::vector get_chunks() const; 35 | unsigned get_num_chunks() const; 36 | std::string get_brief_comment() const; 37 | 38 | /// Search for the corresponding cursor 39 | Cursor get_cursor(CXTranslationUnit &tu) const; 40 | 41 | CXCompletionString cx_completion_string; 42 | }; 43 | } // namespace clangmm 44 | #endif // COMPLETIONSTRING_H_ 45 | -------------------------------------------------------------------------------- /src/token.cc: -------------------------------------------------------------------------------- 1 | #include "token.h" 2 | #include "utility.h" 3 | 4 | // // // // // 5 | // Token // 6 | // // // // // 7 | 8 | // returns gets an source location for this token objekt 9 | // based on the translationunit given 10 | clangmm::SourceLocation clangmm::Token::get_source_location() const { 11 | return SourceLocation(clang_getTokenLocation(cx_tu, cx_token)); 12 | } 13 | 14 | // returns a sourcerange that covers this token 15 | clangmm::SourceRange clangmm::Token::get_source_range() const { 16 | return SourceRange(clang_getTokenExtent(cx_tu, cx_token)); 17 | } 18 | // returns a string description of this tokens kind 19 | std::string clangmm::Token::get_spelling() const { 20 | return to_string(clang_getTokenSpelling(cx_tu, cx_token)); 21 | } 22 | 23 | clangmm::Token::Kind clangmm::Token::get_kind() const { 24 | return static_cast(clang_getTokenKind(cx_token)); 25 | } 26 | 27 | bool clangmm::Token::is_identifier() const { 28 | auto token_kind=get_kind(); 29 | auto cursor=get_cursor(); 30 | if(token_kind==clangmm::Token::Kind::Identifier && cursor.is_valid_kind()) 31 | return true; 32 | else if(token_kind==clangmm::Token::Kind::Keyword && cursor.is_valid_kind()) { 33 | auto spelling=get_spelling(); 34 | if(spelling=="operator" || (spelling=="bool" && get_cursor().get_spelling()=="operator bool")) 35 | return true; 36 | } 37 | else if(token_kind==clangmm::Token::Kind::Punctuation && cursor.is_valid_kind()) { 38 | auto referenced=cursor.get_referenced(); 39 | if(referenced) { 40 | auto referenced_kind=referenced.get_kind(); 41 | if(referenced_kind==Cursor::Kind::FunctionDecl || referenced_kind==Cursor::Kind::CXXMethod || referenced_kind==Cursor::Kind::Constructor) 42 | return true; 43 | } 44 | } 45 | return false; 46 | } 47 | -------------------------------------------------------------------------------- /src/source_location.h: -------------------------------------------------------------------------------- 1 | #ifndef SOURCELOCATION_H_ 2 | #define SOURCELOCATION_H_ 3 | #include 4 | #include 5 | #include 6 | 7 | namespace clangmm { 8 | class Offset { 9 | public: 10 | bool operator==(const Offset &o) const {return line==o.line && index==o.index;} 11 | bool operator!=(const Offset &o) const {return !(*this==o);} 12 | bool operator<(const Offset &o) const {return line(const Offset &o) const {return o < *this;} 14 | bool operator<=(const Offset &o) const {return (*this == o) || (*this < o);} 15 | bool operator>=(const Offset &o) const {return (*this == o) || (*this > o);} 16 | unsigned line; 17 | unsigned index; //byte index in line (not char number) 18 | }; 19 | 20 | class SourceLocation { 21 | friend class TranslationUnit; 22 | friend class Diagnostic; 23 | SourceLocation(CXTranslationUnit &tu, const std::string &filepath, unsigned offset); 24 | SourceLocation(CXTranslationUnit &tu, const std::string &filepath, unsigned line, unsigned column); 25 | public: 26 | SourceLocation(const CXSourceLocation& cx_location) : cx_location(cx_location) {} 27 | 28 | public: 29 | std::string get_path() const; 30 | clangmm::Offset get_offset() const; 31 | 32 | friend std::ostream &operator<<(std::ostream &os, const SourceLocation &location) { 33 | auto offset=location.get_offset(); 34 | os << location.get_path() << ':' << offset.line << ':' << offset.index; 35 | return os; 36 | } 37 | 38 | CXSourceLocation cx_location; 39 | 40 | private: 41 | void get_data(std::string *path, unsigned *line, unsigned *column, unsigned *offset) const; 42 | }; 43 | 44 | } // namespace clangmm 45 | #endif // SOURCELOCATION_H_ 46 | -------------------------------------------------------------------------------- /src/code_complete_results.cc: -------------------------------------------------------------------------------- 1 | #include "code_complete_results.h" 2 | #include "completion_string.h" 3 | #include "utility.h" 4 | 5 | clangmm::CodeCompleteResults::CodeCompleteResults(CXTranslationUnit &cx_tu, 6 | const std::string &buffer, 7 | unsigned line_num, unsigned column) { 8 | CXUnsavedFile files[1]; 9 | auto file_path=to_string(clang_getTranslationUnitSpelling(cx_tu)); 10 | files[0].Filename = file_path.c_str(); 11 | files[0].Contents = buffer.c_str(); 12 | files[0].Length = buffer.size(); 13 | 14 | cx_results = clang_codeCompleteAt(cx_tu, 15 | file_path.c_str(), 16 | line_num, 17 | column, 18 | files, 19 | 1, 20 | clang_defaultCodeCompleteOptions()|CXCodeComplete_IncludeBriefComments); 21 | if(cx_results) 22 | clang_sortCodeCompletionResults(cx_results->Results, cx_results->NumResults); 23 | } 24 | 25 | clangmm::CodeCompleteResults::CodeCompleteResults(CodeCompleteResults &&rhs) : cx_results(rhs.cx_results) { 26 | rhs.cx_results = nullptr; 27 | } 28 | 29 | clangmm::CodeCompleteResults &clangmm::CodeCompleteResults::operator=(CodeCompleteResults &&rhs) { 30 | if(this!=&rhs) { 31 | if(cx_results) 32 | clang_disposeCodeCompleteResults(cx_results); 33 | cx_results=rhs.cx_results; 34 | rhs.cx_results=nullptr; 35 | } 36 | return *this; 37 | } 38 | 39 | clangmm::CodeCompleteResults::~CodeCompleteResults() { 40 | if(cx_results) 41 | clang_disposeCodeCompleteResults(cx_results); 42 | } 43 | 44 | unsigned clangmm::CodeCompleteResults::size() const { 45 | if(!cx_results) 46 | return 0; 47 | return cx_results->NumResults; 48 | } 49 | 50 | clangmm::CompletionString clangmm::CodeCompleteResults::get(unsigned i) const { 51 | return CompletionString(cx_results->Results[i].CompletionString); 52 | } 53 | 54 | std::string clangmm::CodeCompleteResults::get_usr() const { 55 | if(!cx_results) 56 | return std::string(); 57 | return to_string(clang_codeCompleteGetContainerUSR(cx_results)); 58 | } 59 | -------------------------------------------------------------------------------- /src/translation_unit.h: -------------------------------------------------------------------------------- 1 | #ifndef TRANSLATIONUNIT_H_ 2 | #define TRANSLATIONUNIT_H_ 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "index.h" 9 | #include "diagnostic.h" 10 | #include "tokens.h" 11 | #include "code_complete_results.h" 12 | #include "cursor.h" 13 | 14 | namespace clangmm { 15 | class TranslationUnit { 16 | public: 17 | TranslationUnit(Index &index, const std::string &file_path, 18 | const std::vector &command_line_args, 19 | const std::string &buffer, 20 | int flags=DefaultFlags()); 21 | TranslationUnit(Index &index, const std::string &file_path, 22 | const std::vector &command_line_args, 23 | int flags=DefaultFlags()); 24 | ~TranslationUnit(); 25 | 26 | int reparse(const std::string &buffer, int flags=DefaultFlags()); 27 | 28 | static int DefaultFlags(); 29 | 30 | void parse(Index &index, 31 | const std::string &file_path, 32 | const std::vector &command_line_args, 33 | const std::map &buffers, 34 | int flags=DefaultFlags()); 35 | 36 | CodeCompleteResults get_code_completions(const std::string &buffer, 37 | unsigned line_number, unsigned column); 38 | 39 | std::vector get_diagnostics(); 40 | 41 | std::unique_ptr get_tokens(); 42 | std::unique_ptr get_tokens(const std::string &path, unsigned start_offset, unsigned end_offset); 43 | std::unique_ptr get_tokens(unsigned start_offset, unsigned end_offset); 44 | std::unique_ptr get_tokens(unsigned start_line, unsigned start_column, 45 | unsigned end_line, unsigned end_column); 46 | 47 | Cursor get_cursor(const std::string &path, unsigned offset); 48 | Cursor get_cursor(const std::string &path, unsigned line, unsigned column); 49 | Cursor get_cursor(const SourceLocation &location); 50 | 51 | CXTranslationUnit cx_tu; 52 | }; 53 | } // namespace clangmm 54 | #endif // TRANSLATIONUNIT_H_ 55 | 56 | -------------------------------------------------------------------------------- /cmake_modules/FindLibClang.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Try to find libclang 3 | # 4 | # Once done this will define: 5 | # - LIBCLANG_FOUND 6 | # System has libclang. 7 | # - LIBCLANG_INCLUDE_DIRS 8 | # The libclang include directories. 9 | # - LIBCLANG_LIBRARIES 10 | # The libraries needed to use libclang. 11 | # - LIBCLANG_LIBRARY_DIR 12 | # The path to the directory containing libclang. 13 | # - LIBCLANG_KNOWN_LLVM_VERSIONS 14 | # Known LLVM release numbers. 15 | 16 | # most recent versions come first 17 | set(LIBCLANG_KNOWN_LLVM_VERSIONS 6.0.0 6.0 6 18 | 5.0.1 5.0.0 5.0 5 19 | 4.0.1 20 | 4.0.0_1 4.0.0 4.0 4 21 | 3.9.1 22 | 3.9.0 3.9 23 | 3.8.1 24 | 3.8.0 3.8 25 | 3.7.1 26 | 3.7 27 | 3.6.2 28 | 3.6.1 29 | 3.6 30 | 3.5.1 31 | 3.5.0 3.5 32 | 3.4.2 3.4.1 3.4 3.3 3.2 3.1) 33 | 34 | set(libclang_llvm_header_search_paths) 35 | set(libclang_llvm_lib_search_paths 36 | # LLVM Fedora 37 | /usr/lib/llvm 38 | ) 39 | 40 | foreach (version ${LIBCLANG_KNOWN_LLVM_VERSIONS}) 41 | list(APPEND libclang_llvm_header_search_paths 42 | # LLVM Debian/Ubuntu nightly packages: http://llvm.org/apt/ 43 | "/usr/lib/llvm-${version}/include" 44 | # LLVM Gentoo 45 | "/usr/lib/llvm/${version}/include" 46 | # LLVM MacPorts 47 | "/opt/local/libexec/llvm-${version}/include" 48 | # LLVM Homebrew 49 | "/usr/local/Cellar/llvm/${version}/include" 50 | # LLVM Homebrew/versions 51 | "/usr/local/lib/llvm-${version}/include" 52 | ) 53 | 54 | list(APPEND libclang_llvm_lib_search_paths 55 | # LLVM Debian/Ubuntu nightly packages: http://llvm.org/apt/ 56 | "/usr/lib/llvm-${version}/lib/" 57 | # LLVM Gentoo 58 | "/usr/lib/llvm/${version}/lib64/" 59 | "/usr/lib/llvm/${version}/lib32/" 60 | # LLVM MacPorts 61 | "/opt/local/libexec/llvm-${version}/lib" 62 | # LLVM Homebrew 63 | "/usr/local/Cellar/llvm/${version}/lib" 64 | # LLVM Homebrew/versions 65 | "/usr/local/lib/llvm-${version}/lib" 66 | ) 67 | endforeach() 68 | 69 | find_path(LIBCLANG_INCLUDE_DIR clang-c/Index.h 70 | PATHS ${libclang_llvm_header_search_paths} 71 | PATH_SUFFIXES LLVM/include #Windows package from http://llvm.org/releases/ 72 | llvm60/include llvm50/include llvm41/include llvm40/include llvm39/include llvm38/include llvm37/include llvm36/include # FreeBSD 73 | DOC "The path to the directory that contains clang-c/Index.h") 74 | 75 | # On Windows with MSVC, the import library uses the ".imp" file extension 76 | # instead of the comon ".lib" 77 | if (MSVC) 78 | find_file(LIBCLANG_LIBRARY libclang.imp 79 | PATH_SUFFIXES LLVM/lib 80 | DOC "The file that corresponds to the libclang library.") 81 | endif() 82 | 83 | find_library(LIBCLANG_LIBRARY NAMES libclang.imp libclang clang 84 | PATHS ${libclang_llvm_lib_search_paths} 85 | PATH_SUFFIXES LLVM/lib #Windows package from http://llvm.org/releases/ 86 | llvm60/lib llvm50/lib llvm41/lib llvm40/lib llvm39/lib llvm38/lib llvm37/lib llvm36/lib # FreeBSD 87 | DOC "The file that corresponds to the libclang library.") 88 | 89 | get_filename_component(LIBCLANG_LIBRARY_DIR ${LIBCLANG_LIBRARY} PATH) 90 | 91 | set(LIBCLANG_LIBRARIES ${LIBCLANG_LIBRARY}) 92 | set(LIBCLANG_INCLUDE_DIRS ${LIBCLANG_INCLUDE_DIR}) 93 | 94 | include(FindPackageHandleStandardArgs) 95 | # handle the QUIETLY and REQUIRED arguments and set LIBCLANG_FOUND to TRUE if 96 | # all listed variables are TRUE 97 | find_package_handle_standard_args(LibClang DEFAULT_MSG 98 | LIBCLANG_LIBRARY LIBCLANG_INCLUDE_DIR) 99 | 100 | mark_as_advanced(LIBCLANG_INCLUDE_DIR LIBCLANG_LIBRARY) 101 | -------------------------------------------------------------------------------- /src/utility.cc: -------------------------------------------------------------------------------- 1 | #include "utility.h" 2 | #include 3 | 4 | std::string clangmm::to_string(CXString cx_string) { 5 | std::string string; 6 | if(cx_string.data!=NULL) { 7 | string=clang_getCString(cx_string); 8 | clang_disposeString(cx_string); 9 | } 10 | return string; 11 | } 12 | 13 | clangmm::String::String(const CXString &cx_string) : cx_string(cx_string) { 14 | if(cx_string.data!=NULL) 15 | c_str=clang_getCString(cx_string); 16 | else 17 | c_str=""; 18 | } 19 | 20 | clangmm::String::~String() { 21 | if(cx_string.data!=NULL) 22 | clang_disposeString(cx_string); 23 | } 24 | 25 | void clangmm::remove_include_guard(std::string &buffer) { 26 | static std::regex ifndef_regex1("^[ \t]*#[ \t]*ifndef[ \t]+([A-Za-z0-9_]+).*$"); 27 | static std::regex ifndef_regex2("^[ \t]*#[ \t]*if[ \t]+![ \t]*defined[ \t]*\\([ \t]*([A-Za-z0-9_]+).*$"); 28 | static std::regex define_regex("^[ \t]*#[ \t]*define[ \t]+([A-Za-z0-9_]+).*$"); 29 | static std::regex endif_regex("^[ \t]*#[ \t]*endif.*$"); 30 | std::vector> ranges; 31 | bool found_ifndef=false, found_define=false; 32 | bool line_comment=false, multiline_comment=false; 33 | size_t start_of_line=0; 34 | std::string line; 35 | std::string preprocessor_identifier; 36 | for(size_t c=0;c 4 | #include 5 | #include 6 | 7 | clangmm::Tokens::Tokens(CXTranslationUnit &cx_tu, const SourceRange &range, bool annotate_tokens): cx_tu(cx_tu) { 8 | unsigned num_tokens; 9 | clang_tokenize(cx_tu, range.cx_range, &cx_tokens, &num_tokens); 10 | 11 | if(!annotate_tokens) { 12 | cx_cursors=std::unique_ptr(new CXCursor[num_tokens]); 13 | for (unsigned i = 0; i < num_tokens; i++) { 14 | cx_cursors[i]=clang_getNullCursor(); 15 | emplace_back(Token(cx_tu, cx_tokens[i], cx_cursors[i])); 16 | } 17 | return; 18 | } 19 | 20 | cx_cursors=std::unique_ptr(new CXCursor[num_tokens]); // To avoid allocation with initialization 21 | clang_annotateTokens(cx_tu, cx_tokens, num_tokens, cx_cursors.get()); 22 | 23 | bool tu_cursors=SourceRange(clang_getCursorExtent(clang_getTranslationUnitCursor(cx_tu))).get_start().get_path()==range.get_start().get_path(); 24 | std::map invalid_tokens; 25 | 26 | for (unsigned i = 0; i < num_tokens; i++) { 27 | if(cx_cursors[i].kind==CXCursor_DeclRefExpr) { // Temporary fix to a libclang bug 28 | auto real_cursor=clang_getCursor(cx_tu, clang_getTokenLocation(cx_tu, cx_tokens[i])); 29 | cx_cursors[i]=real_cursor; 30 | } 31 | // Corrects: when getting tokens from a header, FieldDecl tokens are getting ClassDecl or StructDecl cursors 32 | else if(!tu_cursors && (cx_cursors[i].kind==CXCursor_ClassDecl || cx_cursors[i].kind==CXCursor_StructDecl)) { 33 | Token token(cx_tu, cx_tokens[i], cx_cursors[i]); 34 | auto cursor=token.get_cursor(); 35 | auto token_offsets=token.get_source_range().get_offsets(); 36 | if(token_offsets.second!=cursor.get_source_range().get_offsets().second && token_offsets.first!=cursor.get_source_location().get_offset() && token.is_identifier()) 37 | invalid_tokens.emplace(i, token_offsets.first); 38 | } 39 | 40 | emplace_back(Token(cx_tu, cx_tokens[i], cx_cursors[i])); 41 | } 42 | if(!tu_cursors && !invalid_tokens.empty()) { 43 | class VisitorData { 44 | public: 45 | Tokens *tokens; 46 | const std::string &path; 47 | std::map &invalid_tokens; 48 | std::vector cursors; 49 | }; 50 | VisitorData data{this, range.get_start().get_path(), invalid_tokens, {}}; 51 | auto translation_unit_cursor = clang_getTranslationUnitCursor(cx_tu); 52 | clang_visitChildren(translation_unit_cursor, [](CXCursor cx_cursor, CXCursor cx_parent, CXClientData data_) { 53 | auto data=static_cast(data_); 54 | Cursor cursor(cx_cursor); 55 | if(cursor.get_source_location().get_path()==data->path) 56 | data->cursors.emplace_back(cursor); 57 | return CXChildVisit_Continue; 58 | }, &data); 59 | 60 | for(auto &cursor: data.cursors) { 61 | clang_visitChildren(cursor.cx_cursor, [](CXCursor cx_cursor, CXCursor cx_parent, CXClientData data_) { 62 | auto data=static_cast(data_); 63 | if(clang_getCursorKind(cx_cursor)==CXCursor_FieldDecl) { 64 | Cursor cursor(cx_cursor); 65 | auto clang_offset=cursor.get_source_location().get_offset(); 66 | for(auto it=data->invalid_tokens.begin();it!=data->invalid_tokens.end();) { 67 | if(it->second==clang_offset) { 68 | (*data->tokens)[it->first].cx_cursor=cursor.cx_cursor; 69 | it=data->invalid_tokens.erase(it); 70 | if(data->invalid_tokens.empty()) 71 | return CXChildVisit_Break; 72 | break; 73 | } 74 | else 75 | ++it; 76 | } 77 | } 78 | return CXChildVisit_Recurse; 79 | }, &data); 80 | if(invalid_tokens.empty()) 81 | break; 82 | } 83 | } 84 | } 85 | 86 | clangmm::Tokens::~Tokens() { 87 | clang_disposeTokens(cx_tu, cx_tokens, size()); 88 | } 89 | 90 | //This works across TranslationUnits. Similar tokens defined as tokens with equal canonical cursors. 91 | std::vector > clangmm::Tokens::get_similar_token_offsets(Cursor::Kind kind, const std::string &spelling, 92 | const std::unordered_set &usrs) { 93 | std::vector > offsets; 94 | for(auto &token: *this) { 95 | if(token.is_identifier()) { 96 | auto referenced=token.get_cursor().get_referenced(); 97 | if(referenced && Cursor::is_similar_kind(referenced.get_kind(), kind)) { 98 | bool equal_spelling=false; 99 | auto cx_string=clang_getTokenSpelling(cx_tu, token.cx_token); 100 | if(cx_string.data) 101 | equal_spelling=std::strcmp(static_cast(cx_string.data), spelling.c_str())==0; 102 | clang_disposeString(cx_string); 103 | if(equal_spelling) { 104 | auto referenced_usrs=referenced.get_all_usr_extended(); 105 | for(auto &usr: referenced_usrs) { 106 | if(usrs.count(usr)) { 107 | offsets.emplace_back(token.get_source_range().get_offsets()); 108 | break; 109 | } 110 | } 111 | } 112 | } 113 | } 114 | } 115 | return offsets; 116 | } 117 | -------------------------------------------------------------------------------- /src/completion_string.cc: -------------------------------------------------------------------------------- 1 | #include "completion_string.h" 2 | #include "utility.h" 3 | 4 | clangmm::CompletionChunk::CompletionChunk(std::string text, CompletionChunkKind kind) 5 | : text(std::move(text)), kind(kind) {} 6 | 7 | std::vector clangmm::CompletionString::get_chunks() const { 8 | std::vector chunks; 9 | for(unsigned i = 0; i < get_num_chunks(); ++i) 10 | chunks.emplace_back(to_string(clang_getCompletionChunkText(cx_completion_string, i)), 11 | static_cast(clang_getCompletionChunkKind(cx_completion_string, i))); 12 | return chunks; 13 | } 14 | 15 | clangmm::CompletionString::CompletionString(const CXCompletionString &cx_completion_string) 16 | : cx_completion_string(cx_completion_string) {} 17 | 18 | bool clangmm::CompletionString::available() const { 19 | return clang_getCompletionAvailability(cx_completion_string) == CXAvailability_Available; 20 | } 21 | 22 | unsigned clangmm::CompletionString::get_num_chunks() const { 23 | return clang_getNumCompletionChunks(cx_completion_string); 24 | } 25 | 26 | std::string clangmm::CompletionString::get_brief_comment() const { 27 | return to_string(clang_getCompletionBriefComment(cx_completion_string)); 28 | } 29 | 30 | // Several workarounds has been added due to clang_getCursorCompletionString result being different from normal completion strings 31 | clangmm::Cursor clangmm::CompletionString::get_cursor(CXTranslationUnit &tu) const { 32 | class VisitorData { 33 | public: 34 | std::vector &completion_chunks; 35 | std::vector parent_parts; 36 | clangmm::Cursor found_cursor; 37 | }; 38 | class ChunkString { 39 | public: 40 | static void remove_template_argument_and_namespace(std::string &chunk) { 41 | size_t pos1, pos2; 42 | if((pos1=chunk.find('<'))!=std::string::npos && (pos2=chunk.rfind('>'))!=std::string::npos) 43 | chunk=chunk.substr(0, pos1)+chunk.substr(pos2+1); 44 | if((pos2=chunk.rfind("::"))!=std::string::npos) { 45 | pos1=pos2-1; 46 | while(pos1!=std::string::npos && ((chunk[pos1]>='a' && chunk[pos1]<='z') || (chunk[pos1]>='A' && chunk[pos1]<='Z') || 47 | (chunk[pos1]>='0' && chunk[pos1]<='9') || chunk[pos1]==':' || chunk[pos1]=='_')) 48 | --pos1; 49 | chunk=chunk.substr(0, pos1+1)+chunk.substr(pos2+2); 50 | } 51 | } 52 | }; 53 | std::vector chunks; 54 | for(unsigned i=0;i parent_parts; 64 | if(!parent.empty()) { 65 | size_t pos=0; 66 | size_t last_pos=0; 67 | while((pos=parent.find("::", pos))!=std::string::npos) { 68 | parent_parts.emplace_back(parent.substr(last_pos, pos-last_pos)); 69 | pos+=2; 70 | last_pos=pos; 71 | } 72 | parent_parts.emplace_back(parent.substr(last_pos)); 73 | } 74 | 75 | VisitorData visitor_data{chunks, parent_parts, clangmm::Cursor()}; 76 | 77 | clang_visitChildren(clang_getTranslationUnitCursor(tu), [](CXCursor cx_cursor, CXCursor cx_parent, CXClientData cx_data) { 78 | auto data = static_cast(cx_data); 79 | 80 | bool equal=true; 81 | auto cx_tmp_cursor=cx_parent; 82 | if(clang_getCursorKind(cx_tmp_cursor)!=CXCursorKind::CXCursor_TranslationUnit) { 83 | int c=0; 84 | auto it=data->parent_parts.rbegin(); 85 | for(;it!=data->parent_parts.rend();++it) { 86 | auto name=clangmm::to_string(clang_getCursorDisplayName(cx_tmp_cursor)); 87 | size_t pos; 88 | if((pos=name.find('<'))!=std::string::npos) 89 | name=name.substr(0, pos); 90 | if(name!=*it) { 91 | equal=false; 92 | break; 93 | } 94 | cx_tmp_cursor=clang_getCursorSemanticParent(cx_tmp_cursor); 95 | if(clang_getCursorKind(cx_tmp_cursor)==CXCursorKind::CXCursor_TranslationUnit) { 96 | ++it; 97 | break; 98 | } 99 | ++c; 100 | } 101 | if(it!=data->parent_parts.rend()) 102 | equal=false; 103 | } 104 | else if(!data->parent_parts.empty()) 105 | return CXChildVisit_Recurse; 106 | 107 | if(equal) { 108 | auto completion_string = clang_getCursorCompletionString(cx_cursor); 109 | auto num_completion_chunks=clang_getNumCompletionChunks(completion_string); 110 | if(num_completion_chunks>=data->completion_chunks.size()) { 111 | bool equal=true; 112 | for(unsigned i=0;icompletion_chunks.size() && icompletion_chunks[i]!=chunk) { 118 | equal=false; 119 | break; 120 | } 121 | } 122 | } 123 | if(equal) { 124 | data->found_cursor=cx_cursor; 125 | return CXChildVisit_Break; 126 | } 127 | } 128 | } 129 | 130 | return CXChildVisit_Recurse; 131 | }, &visitor_data); 132 | 133 | return Cursor(visitor_data.found_cursor); 134 | } 135 | -------------------------------------------------------------------------------- /src/translation_unit.cc: -------------------------------------------------------------------------------- 1 | #include "translation_unit.h" 2 | #include "source_location.h" 3 | #include "tokens.h" 4 | #include "utility.h" 5 | #include 6 | #include 7 | 8 | #include //TODO: remove 9 | using namespace std; //TODO: remove 10 | 11 | clangmm::TranslationUnit::TranslationUnit(Index &index, const std::string &file_path, 12 | const std::vector &command_line_args, 13 | const std::string &buffer, int flags) { 14 | std::vector args; 15 | for(auto &a: command_line_args) { 16 | args.push_back(a.c_str()); 17 | } 18 | 19 | CXUnsavedFile files[1]; 20 | files[0].Filename=file_path.c_str(); 21 | files[0].Contents=buffer.c_str(); 22 | files[0].Length=buffer.size(); 23 | 24 | cx_tu = clang_parseTranslationUnit(index.cx_index, file_path.c_str(), args.data(), 25 | args.size(), files, 1, flags); 26 | } 27 | 28 | clangmm::TranslationUnit::TranslationUnit(Index &index, const std::string &file_path, 29 | const std::vector &command_line_args, 30 | int flags) { 31 | std::vector args; 32 | for(auto &a: command_line_args) { 33 | args.push_back(a.c_str()); 34 | } 35 | 36 | cx_tu = clang_parseTranslationUnit(index.cx_index, file_path.c_str(), args.data(), 37 | args.size(), NULL, 0, flags); 38 | } 39 | 40 | clangmm::TranslationUnit::~TranslationUnit() { 41 | clang_disposeTranslationUnit(cx_tu); 42 | } 43 | 44 | void clangmm::TranslationUnit::parse(Index &index, const std::string &file_path, 45 | const std::vector &command_line_args, 46 | const std::map &buffers, int flags) { 47 | std::vector files; 48 | for (auto &buffer : buffers) { 49 | CXUnsavedFile file; 50 | file.Filename = buffer.first.c_str(); 51 | file.Contents = buffer.second.c_str(); 52 | file.Length = buffer.second.size(); 53 | files.push_back(file); 54 | } 55 | std::vector args; 56 | for(auto &a: command_line_args) { 57 | args.push_back(a.c_str()); 58 | } 59 | cx_tu = clang_parseTranslationUnit(index.cx_index, file_path.c_str(), args.data(), 60 | args.size(), files.data(), files.size(), flags); 61 | } 62 | 63 | int clangmm::TranslationUnit::reparse(const std::string &buffer, int flags) { 64 | CXUnsavedFile files[1]; 65 | 66 | auto file_path=to_string(clang_getTranslationUnitSpelling(cx_tu)); 67 | 68 | files[0].Filename=file_path.c_str(); 69 | files[0].Contents=buffer.c_str(); 70 | files[0].Length=buffer.size(); 71 | 72 | return clang_reparseTranslationUnit(cx_tu, 1, files, flags); 73 | } 74 | 75 | int clangmm::TranslationUnit::DefaultFlags() { 76 | int flags=CXTranslationUnit_CacheCompletionResults | CXTranslationUnit_PrecompiledPreamble | CXTranslationUnit_Incomplete | CXTranslationUnit_IncludeBriefCommentsInCodeCompletion; 77 | #if CINDEX_VERSION_MAJOR>0 || (CINDEX_VERSION_MAJOR==0 && CINDEX_VERSION_MINOR>=35) 78 | flags|=CXTranslationUnit_KeepGoing; 79 | #endif 80 | return flags; 81 | } 82 | 83 | clangmm::CodeCompleteResults clangmm::TranslationUnit::get_code_completions(const std::string &buffer, 84 | unsigned line_number, unsigned column) { 85 | CodeCompleteResults results(cx_tu, buffer, line_number, column); 86 | return results; 87 | } 88 | 89 | std::vector clangmm::TranslationUnit::get_diagnostics() { 90 | std::vector diagnostics; 91 | for(unsigned c=0;c clangmm::TranslationUnit::get_tokens() { 100 | SourceRange range(clang_getCursorExtent(clang_getTranslationUnitCursor(cx_tu))); 101 | return std::unique_ptr(new Tokens(cx_tu, range)); 102 | } 103 | 104 | std::unique_ptr clangmm::TranslationUnit::get_tokens(const std::string &path, unsigned start_offset, unsigned end_offset) { 105 | SourceLocation start_location(cx_tu, path, start_offset); 106 | SourceLocation end_location(cx_tu, path, end_offset); 107 | SourceRange range(start_location, end_location); 108 | return std::unique_ptr(new Tokens(cx_tu, range)); 109 | } 110 | 111 | std::unique_ptr clangmm::TranslationUnit::get_tokens(unsigned start_offset, unsigned end_offset) { 112 | auto path=clangmm::to_string(clang_getTranslationUnitSpelling(cx_tu)); 113 | SourceLocation start_location(cx_tu, path, start_offset); 114 | SourceLocation end_location(cx_tu, path, end_offset); 115 | SourceRange range(start_location, end_location); 116 | return std::unique_ptr(new Tokens(cx_tu, range)); 117 | } 118 | 119 | std::unique_ptr clangmm::TranslationUnit::get_tokens(unsigned start_line, unsigned start_column, 120 | unsigned end_line, unsigned end_column) { 121 | auto path=to_string(clang_getTranslationUnitSpelling(cx_tu)); 122 | SourceLocation start_location(cx_tu, path, start_line, start_column); 123 | SourceLocation end_location(cx_tu, path, end_line, end_column); 124 | SourceRange range(start_location, end_location); 125 | return std::unique_ptr(new Tokens(cx_tu, range)); 126 | } 127 | 128 | clangmm::Cursor clangmm::TranslationUnit::get_cursor(const std::string &path, unsigned offset) { 129 | SourceLocation location(cx_tu, path, offset); 130 | return Cursor(clang_getCursor(cx_tu, location.cx_location)); 131 | } 132 | 133 | clangmm::Cursor clangmm::TranslationUnit::get_cursor(const std::string &path, unsigned line, unsigned column) { 134 | SourceLocation location(cx_tu, path, line, column); 135 | return Cursor(clang_getCursor(cx_tu, location.cx_location)); 136 | } 137 | 138 | clangmm::Cursor clangmm::TranslationUnit::get_cursor(const SourceLocation &location) { 139 | return Cursor(clang_getCursor(cx_tu, location.cx_location)); 140 | } 141 | -------------------------------------------------------------------------------- /src/cursor.h: -------------------------------------------------------------------------------- 1 | #ifndef CURSOR_H_ 2 | #define CURSOR_H_ 3 | #include 4 | #include "source_location.h" 5 | #include "source_range.h" 6 | #include 7 | #include 8 | #include 9 | 10 | namespace clangmm { 11 | class Cursor { 12 | public: 13 | enum class Kind { 14 | UnexposedDecl = 1, 15 | StructDecl = 2, 16 | UnionDecl = 3, 17 | ClassDecl = 4, 18 | EnumDecl = 5, 19 | FieldDecl = 6, 20 | EnumConstantDecl = 7, 21 | FunctionDecl = 8, 22 | VarDecl = 9, 23 | ParmDecl = 10, 24 | ObjCInterfaceDecl = 11, 25 | ObjCCategoryDecl = 12, 26 | ObjCProtocolDecl = 13, 27 | ObjCPropertyDecl = 14, 28 | ObjCIvarDecl = 15, 29 | ObjCInstanceMethodDecl = 16, 30 | ObjCClassMethodDecl = 17, 31 | ObjCImplementationDecl = 18, 32 | ObjCCategoryImplDecl = 19, 33 | TypedefDecl = 20, 34 | CXXMethod = 21, 35 | Namespace = 22, 36 | LinkageSpec = 23, 37 | Constructor = 24, 38 | Destructor = 25, 39 | ConversionFunction = 26, 40 | TemplateTypeParameter = 27, 41 | NonTypeTemplateParameter = 28, 42 | TemplateTemplateParameter = 29, 43 | FunctionTemplate = 30, 44 | ClassTemplate = 31, 45 | ClassTemplatePartialSpecialization = 32, 46 | NamespaceAlias = 33, 47 | UsingDirective = 34, 48 | UsingDeclaration = 35, 49 | TypeAliasDecl = 36, 50 | ObjCSynthesizeDecl = 37, 51 | ObjCDynamicDecl = 38, 52 | CXXAccessSpecifier = 39, 53 | FirstDecl = UnexposedDecl, 54 | LastDecl = CXXAccessSpecifier, 55 | FirstRef = 40, 56 | ObjCSuperClassRef = 40, 57 | ObjCProtocolRef = 41, 58 | ObjCClassRef = 42, 59 | TypeRef = 43, 60 | CXXBaseSpecifier = 44, 61 | TemplateRef = 45, 62 | NamespaceRef = 46, 63 | MemberRef = 47, 64 | LabelRef = 48, 65 | OverloadedDeclRef = 49, 66 | VariableRef = 50, 67 | LastRef = VariableRef, 68 | FirstInvalid = 70, 69 | InvalidFile = 70, 70 | NoDeclFound = 71, 71 | NotImplemented = 72, 72 | InvalidCode = 73, 73 | LastInvalid = InvalidCode, 74 | FirstExpr = 100, 75 | UnexposedExpr = 100, 76 | DeclRefExpr = 101, 77 | MemberRefExpr = 102, 78 | CallExpr = 103, 79 | ObjCMessageExpr = 104, 80 | BlockExpr = 105, 81 | IntegerLiteral = 106, 82 | FloatingLiteral = 107, 83 | ImaginaryLiteral = 108, 84 | StringLiteral = 109, 85 | CharacterLiteral = 110, 86 | ParenExpr = 111, 87 | UnaryOperator = 112, 88 | ArraySubscriptExpr = 113, 89 | BinaryOperator = 114, 90 | CompoundAssignOperator = 115, 91 | ConditionalOperator = 116, 92 | CStyleCastExpr = 117, 93 | CompoundLiteralExpr = 118, 94 | InitListExpr = 119, 95 | AddrLabelExpr = 120, 96 | StmtExpr = 121, 97 | GenericSelectionExpr = 122, 98 | GNUNullExpr = 123, 99 | CXXStaticCastExpr = 124, 100 | CXXDynamicCastExpr = 125, 101 | CXXReinterpretCastExpr = 126, 102 | CXXConstCastExpr = 127, 103 | CXXFunctionalCastExpr = 128, 104 | CXXTypeidExpr = 129, 105 | CXXBoolLiteralExpr = 130, 106 | CXXNullPtrLiteralExpr = 131, 107 | CXXThisExpr = 132, 108 | CXXThrowExpr = 133, 109 | CXXNewExpr = 134, 110 | CXXDeleteExpr = 135, 111 | UnaryExpr = 136, 112 | ObjCStringLiteral = 137, 113 | ObjCEncodeExpr = 138, 114 | ObjCSelectorExpr = 139, 115 | ObjCProtocolExpr = 140, 116 | ObjCBridgedCastExpr = 141, 117 | PackExpansionExpr = 142, 118 | SizeOfPackExpr = 143, 119 | LambdaExpr = 144, 120 | ObjCBoolLiteralExpr = 145, 121 | ObjCSelfExpr = 146, 122 | LastExpr = ObjCSelfExpr, 123 | FirstStmt = 200, 124 | UnexposedStmt = 200, 125 | LabelStmt = 201, 126 | CompoundStmt = 202, 127 | CaseStmt = 203, 128 | DefaultStmt = 204, 129 | IfStmt = 205, 130 | SwitchStmt = 206, 131 | WhileStmt = 207, 132 | DoStmt = 208, 133 | ForStmt = 209, 134 | GotoStmt = 210, 135 | IndirectGotoStmt = 211, 136 | ContinueStmt = 212, 137 | BreakStmt = 213, 138 | ReturnStmt = 214, 139 | GCCAsmStmt = 215, 140 | AsmStmt = GCCAsmStmt, 141 | ObjCAtTryStmt = 216, 142 | ObjCAtCatchStmt = 217, 143 | ObjCAtFinallyStmt = 218, 144 | ObjCAtThrowStmt = 219, 145 | ObjCAtSynchronizedStmt = 220, 146 | ObjCAutoreleasePoolStmt = 221, 147 | ObjCForCollectionStmt = 222, 148 | CXXCatchStmt = 223, 149 | CXXTryStmt = 224, 150 | CXXForRangeStmt = 225, 151 | SEHTryStmt = 226, 152 | SEHExceptStmt = 227, 153 | SEHFinallyStmt = 228, 154 | MSAsmStmt = 229, 155 | NullStmt = 230, 156 | DeclStmt = 231, 157 | LastStmt = DeclStmt, 158 | TranslationUnit = 300, 159 | FirstAttr = 400, 160 | UnexposedAttr = 400, 161 | IBActionAttr = 401, 162 | IBOutletAttr = 402, 163 | IBOutletCollectionAttr = 403, 164 | CXXFinalAttr = 404, 165 | CXXOverrideAttr = 405, 166 | AnnotateAttr = 406, 167 | AsmLabelAttr = 407, 168 | LastAttr = AsmLabelAttr, 169 | PreprocessingDirective = 500, 170 | MacroDefinition = 501, 171 | MacroExpansion = 502, 172 | MacroInstantiation = MacroExpansion, 173 | InclusionDirective = 503, 174 | FirstPreprocessing = PreprocessingDirective, 175 | LastPreprocessing = InclusionDirective, 176 | ModuleImportDecl = 600, 177 | FirstExtraDecl = ModuleImportDecl, 178 | LastExtraDecl = ModuleImportDecl, 179 | }; 180 | class Type { 181 | public: 182 | Type(const CXType &cx_type) : cx_type(cx_type) {} 183 | std::string get_spelling() const; 184 | Type get_result() const; 185 | Cursor get_cursor() const; 186 | bool operator==(const Cursor::Type& rhs) const; 187 | 188 | CXType cx_type; 189 | }; 190 | 191 | Cursor() { cx_cursor=clang_getNullCursor(); } 192 | Cursor(const CXCursor &cx_cursor) : cx_cursor(cx_cursor) {} 193 | Kind get_kind() const; 194 | std::string get_kind_spelling() const; 195 | static bool is_similar_kind(Kind kind, Kind other_kind); 196 | Type get_type() const; 197 | SourceLocation get_source_location() const; 198 | SourceRange get_source_range() const; 199 | std::string get_spelling() const; 200 | std::string get_display_name() const; 201 | std::string get_token_spelling() const; 202 | std::string get_usr() const; 203 | /// Improved usr that is also template and argument invariant 204 | std::string get_usr_extended() const; 205 | /// Also get overridden cursors 206 | std::unordered_set get_all_usr_extended() const; 207 | Cursor get_referenced() const; 208 | Cursor get_canonical() const; 209 | Cursor get_definition() const; 210 | Cursor get_semantic_parent() const; 211 | std::vector get_children() const; 212 | std::vector get_arguments() const; 213 | std::vector get_all_overridden_cursors() const; 214 | operator bool() const; 215 | bool operator==(const Cursor& rhs) const; 216 | unsigned hash() const; 217 | 218 | bool is_valid_kind() const; 219 | std::string get_type_description() const; 220 | std::string get_brief_comments() const; 221 | 222 | friend std::ostream &operator<<(std::ostream &os, const Cursor &cursor) { 223 | os << cursor.get_source_range() << ' ' << cursor.get_spelling(); 224 | return os; 225 | } 226 | 227 | CXCursor cx_cursor; 228 | }; 229 | } // namespace clangmm 230 | #endif // CURSOR_H_ 231 | -------------------------------------------------------------------------------- /src/cursor.cc: -------------------------------------------------------------------------------- 1 | #include "cursor.h" 2 | #include "utility.h" 3 | #include 4 | 5 | std::string clangmm::Cursor::Type::get_spelling() const { 6 | return to_string(clang_getTypeSpelling(cx_type)); 7 | } 8 | 9 | clangmm::Cursor::Type clangmm::Cursor::Type::get_result() const { 10 | return Type(clang_getResultType(cx_type)); 11 | } 12 | 13 | clangmm::Cursor clangmm::Cursor::Type::get_cursor() const { 14 | return Cursor(clang_getTypeDeclaration(cx_type)); 15 | } 16 | 17 | bool clangmm::Cursor::Type::operator==(const Cursor::Type& rhs) const { 18 | return clang_equalTypes(cx_type, rhs.cx_type); 19 | } 20 | 21 | clangmm::Cursor::Kind clangmm::Cursor::get_kind() const { 22 | return static_cast(clang_getCursorKind(cx_cursor)); 23 | } 24 | 25 | std::string clangmm::Cursor::get_kind_spelling() const { 26 | return to_string(clang_getCursorKindSpelling(clang_getCursorKind(cx_cursor))); 27 | } 28 | 29 | bool clangmm::Cursor::is_similar_kind(Kind kind, Kind other_kind) { 30 | auto is_function_or_method=[](Kind kind) { 31 | if(kind==Kind::FunctionDecl || kind==Kind::CXXMethod || kind==Kind::FunctionTemplate) 32 | return true; 33 | return false; 34 | }; 35 | auto is_class_or_struct=[](Kind kind) { 36 | if(kind==Kind::ClassDecl || kind==Kind::StructDecl || kind==Kind::ClassTemplate || 37 | kind==Cursor::Kind::Constructor || kind==Cursor::Kind::Destructor || kind==Cursor::Kind::FunctionTemplate) 38 | return true; 39 | return false; 40 | }; 41 | if(kind==Kind::FunctionTemplate) 42 | return is_function_or_method(other_kind) || is_class_or_struct(other_kind); 43 | if(is_function_or_method(kind)) 44 | return is_function_or_method(other_kind); 45 | if(is_class_or_struct(kind)) 46 | return is_class_or_struct(other_kind); 47 | return kind==other_kind; 48 | } 49 | 50 | clangmm::Cursor::Type clangmm::Cursor::get_type() const { 51 | return Type(clang_getCursorType(cx_cursor)); 52 | } 53 | 54 | clangmm::SourceLocation clangmm::Cursor::get_source_location() const { 55 | return SourceLocation(clang_getCursorLocation(cx_cursor)); 56 | } 57 | 58 | clangmm::SourceRange clangmm::Cursor::get_source_range() const { 59 | return SourceRange(clang_getCursorExtent(cx_cursor)); 60 | } 61 | 62 | std::string clangmm::Cursor::get_spelling() const { 63 | return to_string(clang_getCursorSpelling(cx_cursor)); 64 | } 65 | 66 | std::string clangmm::Cursor::get_display_name() const { 67 | return to_string(clang_getCursorDisplayName(cx_cursor)); 68 | } 69 | 70 | std::string clangmm::Cursor::get_token_spelling() const { 71 | auto spelling=get_spelling(); 72 | for(size_t i=0;i0 && spelling[0]=='~') 75 | return spelling.substr(1, i-1); 76 | return spelling.substr(0, i); 77 | } 78 | } 79 | if(!spelling.empty() && spelling[0]=='~') 80 | return spelling.substr(1); 81 | return spelling; 82 | } 83 | 84 | std::string clangmm::Cursor::get_usr() const { 85 | return to_string(clang_getCursorUSR(cx_cursor)); 86 | } 87 | 88 | std::string clangmm::Cursor::get_usr_extended() const { 89 | if(!is_valid_kind()) 90 | return std::string(); 91 | 92 | auto cursor=*this; 93 | auto kind=cursor.get_kind(); 94 | // If constructor, destructor or function template, and the token spelling is equal, set cursor to parent 95 | if(kind==Cursor::Kind::Constructor || kind==Cursor::Kind::Destructor || 96 | kind==Cursor::Kind::FunctionTemplate) { 97 | auto parent=cursor.get_semantic_parent(); 98 | auto parent_kind=parent.get_kind(); 99 | if((parent_kind==Cursor::Kind::ClassDecl || parent_kind==Cursor::Kind::StructDecl || parent_kind==Cursor::Kind::ClassTemplate) && 100 | cursor.get_token_spelling()==parent.get_token_spelling()) 101 | cursor=parent; 102 | } 103 | 104 | std::string usr=cursor.get_token_spelling(); 105 | auto parent=cursor.get_semantic_parent(); 106 | while((kind=parent.get_kind())!=Kind::TranslationUnit && parent.is_valid_kind()) { 107 | if(kind==Kind::CXXMethod || kind==Kind::FunctionDecl || kind==Kind::FunctionTemplate || 108 | kind==Kind::Constructor || kind==Kind::Destructor) { 109 | auto canonical=cursor.get_canonical(); 110 | auto location=canonical.get_source_location(); 111 | auto offset=location.get_offset(); 112 | return std::to_string(offset.line)+':'+std::to_string(offset.index)+':'+location.get_path(); 113 | } 114 | usr+=':'+parent.get_token_spelling(); 115 | parent=parent.get_semantic_parent(); 116 | } 117 | return usr; 118 | } 119 | 120 | std::unordered_set clangmm::Cursor::get_all_usr_extended() const { 121 | std::unordered_set usrs; 122 | if(get_kind()==Kind::CXXMethod) { 123 | class Recursive { 124 | public: 125 | static void overridden(std::unordered_set &usrs, const Cursor &cursor) { 126 | usrs.emplace(cursor.get_usr_extended()); 127 | CXCursor *cursors; 128 | unsigned size; 129 | clang_getOverriddenCursors(cursor.cx_cursor, &cursors, &size); 130 | for(unsigned c=0;c clangmm::Cursor::get_children() const { 161 | std::vector result; 162 | clang_visitChildren(cx_cursor, 163 | [](CXCursor cur, CXCursor /*parent*/, CXClientData data) { 164 | static_cast*>(data)->emplace_back(cur); 165 | return CXChildVisit_Continue; 166 | }, 167 | &result 168 | ); 169 | return result; 170 | } 171 | 172 | std::vector clangmm::Cursor::get_arguments() const { 173 | std::vector cursors; 174 | auto size=clang_Cursor_getNumArguments(cx_cursor); 175 | for(int c=0;c clangmm::Cursor::get_all_overridden_cursors() const { 181 | std::vector result; 182 | if(get_kind()!=Kind::CXXMethod) 183 | return result; 184 | 185 | class Recursive { 186 | public: 187 | static void overridden(std::vector &result, const Cursor &cursor, int depth) { 188 | if(depth>0) 189 | result.emplace_back(cursor); 190 | CXCursor *cursors; 191 | unsigned size; 192 | clang_getOverriddenCursors(cursor.cx_cursor, &cursors, &size); 193 | for(unsigned c=0;cKind::UnexposedDecl && (kindKind::LastInvalid); 220 | } 221 | 222 | std::string clangmm::Cursor::get_type_description() const { 223 | std::string spelling; 224 | auto referenced=clang_getCursorReferenced(cx_cursor); 225 | if(!clang_Cursor_isNull(referenced)) { 226 | auto type=clang_getCursorType(referenced); 227 | spelling=to_string(clang_getTypeSpelling(type)); 228 | 229 | #if CINDEX_VERSION_MAJOR==0 && CINDEX_VERSION_MINOR<32 230 | const std::string auto_str="auto"; 231 | if(spelling.size()>=4 && std::equal(auto_str.begin(), auto_str.end(), spelling.begin())) { 232 | auto canonical_type=clang_getCanonicalType(clang_getCursorType(cx_cursor)); 233 | auto canonical_spelling=to_string(clang_getTypeSpelling(canonical_type)); 234 | if(spelling.size()>5 && spelling[4]==' ' && spelling[5]=='&' && spelling!=canonical_spelling) 235 | return canonical_spelling+" &"; 236 | else 237 | return canonical_spelling; 238 | } 239 | 240 | const std::string const_auto_str="const auto"; 241 | if(spelling.size()>=10 && std::equal(const_auto_str.begin(), const_auto_str.end(), spelling.begin())) { 242 | auto canonical_type=clang_getCanonicalType(clang_getCursorType(cx_cursor)); 243 | auto canonical_spelling=to_string(clang_getTypeSpelling(canonical_type)); 244 | if(spelling.size()>11 && spelling[10]==' ' && spelling[11]=='&' && spelling!=canonical_spelling) 245 | return canonical_spelling+" &"; 246 | else 247 | return canonical_spelling; 248 | } 249 | #endif 250 | } 251 | 252 | if(spelling.empty()) 253 | return get_spelling(); 254 | 255 | return spelling; 256 | } 257 | 258 | std::string clangmm::Cursor::get_brief_comments() const { 259 | std::string comment_string; 260 | auto referenced=get_referenced(); 261 | if(referenced) { 262 | comment_string=to_string(clang_Cursor_getBriefCommentText(referenced.cx_cursor)); 263 | } 264 | return comment_string; 265 | } 266 | --------------------------------------------------------------------------------