diff --git a/README.md b/README.md index 0caaa36..32c001e 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,9 @@ If you don't have a proper clang version installed, it can also be downloaded. For that you need to set `LLVM_DOWNLOAD_OS_NAME`. This is the name of the operating system used on the [LLVM pre-built binary archive](http://releases.llvm.org/download.html#4.0.0), e.g. `x86_64-linux-gnu-ubuntu-16.10` for Ubuntu 16.10. +If you don't have `llvm-config`, you need to pass the locations explictly. +For that set the option `LLVM_VERSION_EXPLICIT` to the version you're using, `LIBCLANG_LIBRARY` to the location of the libclang library file, `LIBCLANG_INCLUDE_DIR` to the directory where the header files are located (so they can be included with `clang-c/Index.h`), `LIBCLANG_SYSTEM_INCLUDE_DIR` where the system header files are located (i.e. `stddef.h` etc, usually under `prefix/lib/clang//include`) and `CLANG_BINARY` to the full path of the `clang++` exectuable. + The other dependencies like [type_safe](http://type_safe.foonathan.net) are installed automatically with git submodules, if they're not installed already. If you run into any issues with the installation, please report them. diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..99c981f --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,11 @@ +version: '{build}' +build_script: + - cmd: mkdir build\ && cd build\ + - cmd: call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x64 + - cmd: clang++ --version + - cmd: cmake -G"Visual Studio 14 2015 Win64" -DLLVM_VERSION_EXPLICIT=4.0.0 -DLIBCLANG_LIBRARY="C:/Program Files/LLVM/lib/libclang.lib" -DLIBCLANG_INCLUDE_DIR="C:/Program Files/LLVM/include" -DLIBCLANG_SYSTEM_INCLUDE_DIR="C:/"Program Files"/LLVM/lib/clang/4.0.0/include" -DCLANG_BINARY="C:/Program Files/LLVM/bin/clang++.exe" ../ + + - cmd: cmake --build . -- /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" + +test_script: + - cmd: test\Debug\cppast_test.exe diff --git a/external/external.cmake b/external/external.cmake index 1dd5e9a..1e6e07f 100644 --- a/external/external.cmake +++ b/external/external.cmake @@ -150,19 +150,16 @@ function(_cppast_find_libclang config_tool min_version force) endif() endfunction() -# create libclang target -# target: _cppast_libclang -function(_cppast_create_libclang_target library_path include_path) - add_library(_cppast_libclang SHARED IMPORTED) - set_target_properties(_cppast_libclang PROPERTIES - IMPORTED_LOCATION ${library_path} - INTERFACE_INCLUDE_DIRECTORIES ${include_path}) -endfunction() - set(llvm_min_version 3.9.1) set(LLVM_PREFERRED_VERSION 4.0.0 CACHE STRING "the preferred LLVM version") -if(NOT LLVM_CONFIG_BINARY) +if(DEFINED LLVM_VERSION_EXPLICIT) + if(LLVM_VERSION_EXPLICIT VERSION_LESS llvm_min_version) + message(FATAL_ERROR "Outdated LLVM version ${LLVM_VERSION_EXPLICIT}, minimal supported is ${llvm_min_version}") + endif() + set(LLVM_VERSION ${LLVM_VERSION_EXPLICIT} CACHE INTERNAL "") + message(STATUS "Using manually specified LLVM version ${LLVM_VERSION}") +elseif(NOT LLVM_CONFIG_BINARY) if(DEFINED LLVM_DOWNLOAD_OS_NAME) _cppast_download_llvm(${LLVM_PREFERRED_VERSION} ${LLVM_DOWNLOAD_OS_NAME}) endif() @@ -172,4 +169,10 @@ else() _cppast_find_libclang(${LLVM_CONFIG_BINARY} ${llvm_min_version} 0) endif() -_cppast_create_libclang_target(${LIBCLANG_LIBRARY} ${LIBCLANG_INCLUDE_DIR}) +add_library(_cppast_libclang INTERFACE) +target_link_libraries(_cppast_libclang INTERFACE ${LIBCLANG_LIBRARY}) +target_include_directories(_cppast_libclang INTERFACE ${LIBCLANG_INCLUDE_DIR}) +target_compile_definitions(_cppast_libclang INTERFACE + CPPAST_LIBCLANG_SYSTEM_INCLUDE_DIR="${LIBCLANG_SYSTEM_INCLUDE_DIR}" + CPPAST_CLANG_BINARY="${CLANG_BINARY}" + CPPAST_CLANG_VERSION_STRING="${LLVM_VERSION}") diff --git a/include/cppast/code_generator.hpp b/include/cppast/code_generator.hpp index c97f594..2d1d769 100644 --- a/include/cppast/code_generator.hpp +++ b/include/cppast/code_generator.hpp @@ -109,20 +109,14 @@ namespace cppast using token_seq = detail::semantic_string_view; /// Tag type to represent an end-of-line character. - constexpr struct newl_t + const struct newl_t { - constexpr newl_t() noexcept - { - } - } newl; + } newl{}; /// Tag type to represent a single space character. - constexpr struct whitespace_t + const struct whitespace_t { - constexpr whitespace_t() noexcept - { - } - } whitespace; + } whitespace{}; /// Base class to control the code generation. /// diff --git a/include/cppast/cpp_forward_declarable.hpp b/include/cppast/cpp_forward_declarable.hpp index 66bd055..b476f5b 100644 --- a/include/cppast/cpp_forward_declarable.hpp +++ b/include/cppast/cpp_forward_declarable.hpp @@ -54,7 +54,7 @@ namespace cppast /// \notes This may include template parameters. std::string semantic_scope() const noexcept { - return semantic_parent_.map(&cpp_entity_ref::name).value_or(""); + return type_safe::copy(semantic_parent_.map(&cpp_entity_ref::name)).value_or(""); } protected: diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 07c68a0..dabd9d4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -101,9 +101,6 @@ add_library(cppast ${detail_header} ${header} ${source} ${libclang_source}) target_include_directories(cppast PUBLIC ../include) target_link_libraries(cppast PUBLIC type_safe _cppast_tiny_process _cppast_libclang) target_compile_definitions(cppast PUBLIC - CPPAST_LIBCLANG_SYSTEM_INCLUDE_DIR="${LIBCLANG_SYSTEM_INCLUDE_DIR}" - CPPAST_CLANG_BINARY="${CLANG_BINARY}" - CPPAST_CLANG_VERSION_STRING="${LLVM_VERSION}" CPPAST_VERSION_STRING="${cppast_VERSION}") if(CPPAST_ENABLE_ASSERTIONS) target_compile_definitions(cppast PUBLIC CPPAST_ENABLE_ASSERTIONS) diff --git a/src/libclang/class_parser.cpp b/src/libclang/class_parser.cpp index d3f19a5..97d68b0 100644 --- a/src/libclang/class_parser.cpp +++ b/src/libclang/class_parser.cpp @@ -137,10 +137,12 @@ std::unique_ptr detail::parse_cpp_class(const detail::parse_context& add_base_class(builder, context, child, cur); else if (kind == CXCursor_CXXFinalAttr) builder.is_final(); - else if (kind == CXCursor_TemplateTypeParameter - || kind == CXCursor_NonTypeTemplateParameter - || kind == CXCursor_TemplateTemplateParameter || kind == CXCursor_ParmDecl - || clang_isExpression(kind) || clang_isReference(kind)) + else if ( + kind == CXCursor_TemplateTypeParameter || kind == CXCursor_NonTypeTemplateParameter + || kind == CXCursor_TemplateTemplateParameter || kind == CXCursor_ParmDecl + || clang_isExpression(kind) || clang_isReference(kind) + || kind + == CXCursor_UnexposedAttr) // I have no idea what this is, but happens on Windows // other children due to templates and stuff return; else if (auto entity = parse_entity(context, child)) diff --git a/src/libclang/function_parser.cpp b/src/libclang/function_parser.cpp index 0eee2c9..80c1973 100644 --- a/src/libclang/function_parser.cpp +++ b/src/libclang/function_parser.cpp @@ -333,6 +333,8 @@ namespace else stream.bump(); } + if (stream.peek() == "{" || stream.peek() == ":" || stream.peek() == "try") + result.body_kind = cpp_function_definition; } else { @@ -356,6 +358,9 @@ namespace if (detail::skip_if(stream, "=")) parse_body(stream, result, allow_virtual); + else if (detail::skip_if(stream, "{") || detail::skip_if(stream, ":") + || detail::skip_if(stream, "try")) + result.body_kind = cpp_function_definition; } return result; diff --git a/src/libclang/libclang_visitor.hpp b/src/libclang/libclang_visitor.hpp index 431ec09..650cd5b 100644 --- a/src/libclang/libclang_visitor.hpp +++ b/src/libclang/libclang_visitor.hpp @@ -28,7 +28,10 @@ namespace cppast return CXChildVisit_Recurse; }; - clang_visitChildren(parent, recurse ? recurse_lambda : continue_lambda, &f); + if (recurse) + clang_visitChildren(parent, recurse_lambda, &f); + else + clang_visitChildren(parent, continue_lambda, &f); } // visits a translation unit diff --git a/src/libclang/preprocessor.cpp b/src/libclang/preprocessor.cpp index b39d61a..b85ff24 100644 --- a/src/libclang/preprocessor.cpp +++ b/src/libclang/preprocessor.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include @@ -650,17 +651,24 @@ detail::preprocessor_output detail::preprocess(const libclang_compile_config& co position p(ts::ref(result.source), output.c_str()); std::size_t file_depth = 0u; - ts::flag in_string(false); + ts::flag in_string(false), in_char(false); while (p) { if (starts_with(p, "\\\"")) // starts with \" p.bump(2u); - else if (starts_with(p, "\"")) // starts with " + else if (starts_with(p, "\\'")) // starts with \' + p.bump(2u); + else if (in_char == false && starts_with(p, "\"")) // starts with " { p.bump(); in_string.toggle(); } - else if (in_string == true) + else if (in_string == false && starts_with(p, "'")) + { + p.bump(); + in_char.toggle(); + } + else if (in_string == true || in_char == true) p.bump(); else if (auto macro = parse_macro(p, result, file_depth == 0u)) { diff --git a/src/libclang/tokenizer.cpp b/src/libclang/tokenizer.cpp index 3e8e83a..c127769 100644 --- a/src/libclang/tokenizer.cpp +++ b/src/libclang/tokenizer.cpp @@ -4,6 +4,8 @@ #include "tokenizer.hpp" +#include + #include "libclang_visitor.hpp" #include "parse_error.hpp" @@ -92,7 +94,7 @@ namespace auto kind = clang_getCursorKind(cur); if (cursor_is_function(kind) || cursor_is_function(clang_getTemplateCursorKind(cur))) { - auto range_shrunk = false; + auto is_definition = false; // if a function we need to remove the body // it does not need to be parsed @@ -103,11 +105,22 @@ namespace { auto child_extent = clang_getCursorExtent(child); end = clang_getRangeStart(child_extent); - range_shrunk = true; + is_definition = true; } }); - if (!range_shrunk && !token_after_is(tu, file, cur, end, ";")) + if (!is_definition) + { + // i have no idea why this is necessary + is_definition = token_after_is(tu, file, cur, end, "{") + || token_after_is(tu, file, cur, end, "try") + || token_after_is(tu, file, cur, end, ":"); + if (is_definition) + // need to extend range here to include the token + end = get_next_location(tu, file, end); + } + + if (!is_definition && !token_after_is(tu, file, cur, end, ";")) { // we do not have a body, but it is not a declaration either do diff --git a/src/libclang/type_parser.cpp b/src/libclang/type_parser.cpp index 103dbe4..91dbcbb 100644 --- a/src/libclang/type_parser.cpp +++ b/src/libclang/type_parser.cpp @@ -4,6 +4,8 @@ #include "parse_functions.hpp" +#include + #include #include #include diff --git a/test/cpp_enum.cpp b/test/cpp_enum.cpp index 53c62bb..8b70cf0 100644 --- a/test/cpp_enum.cpp +++ b/test/cpp_enum.cpp @@ -77,21 +77,29 @@ enum ns::c : int {}; ++no_vals; REQUIRE(val.value()); auto& expr = val.value().value(); - REQUIRE(expr.kind() == cpp_expression_kind::unexposed_t); - REQUIRE(static_cast(expr).expression() - == "42"); - REQUIRE(equal_types(idx, expr.type(), *cpp_builtin_type::build(cpp_uint))); + if (equal_types(idx, expr.type(), *cpp_builtin_type::build(cpp_uint))) + { + REQUIRE(expr.kind() == cpp_expression_kind::unexposed_t); + REQUIRE(static_cast(expr).expression() + == "42"); + } + else + { + REQUIRE(equal_types(idx, expr.type(), *cpp_builtin_type::build(cpp_int))); + REQUIRE(expr.kind() == cpp_expression_kind::literal_t); + REQUIRE(static_cast(expr).value() == "42"); + } } else if (val.name() == "a_d") { ++no_vals; REQUIRE(val.value()); auto& expr = val.value().value(); - // this is unexposed for some reason REQUIRE(expr.kind() == cpp_expression_kind::unexposed_t); REQUIRE(static_cast(expr).expression() == "a_a+2"); - REQUIRE(equal_types(idx, expr.type(), *cpp_builtin_type::build(cpp_uint))); + if (!equal_types(idx, expr.type(), *cpp_builtin_type::build(cpp_int))) + REQUIRE(equal_types(idx, expr.type(), *cpp_builtin_type::build(cpp_uint))); } else REQUIRE(false); diff --git a/test/cpp_preprocessor.cpp b/test/cpp_preprocessor.cpp index 23f3644..8c8945d 100644 --- a/test/cpp_preprocessor.cpp +++ b/test/cpp_preprocessor.cpp @@ -145,7 +145,10 @@ struct a {}; /// u /** b * b */ -void b(int, float); +void b(int, float) +{ + auto c = '#'; +} /** u */ //! c