diff --git a/src/libclang/debug_helper.cpp b/src/libclang/debug_helper.cpp index 316e0df..78af7c1 100644 --- a/src/libclang/debug_helper.cpp +++ b/src/libclang/debug_helper.cpp @@ -11,6 +11,11 @@ using namespace cppast; +detail::cxstring detail::get_display_name(const CXCursor& cur) noexcept +{ + return cxstring(clang_getCursorDisplayName(cur)); +} + namespace { std::mutex mtx; @@ -19,7 +24,7 @@ namespace void detail::print_cursor_info(const CXCursor& cur) noexcept { std::lock_guard lock(mtx); - std::printf("[debug] cursor '%s' (%s)\n", cxstring(clang_getCursorDisplayName(cur)).c_str(), + std::printf("[debug] cursor '%s' (%s)\n", get_display_name(cur).c_str(), cxstring(clang_getCursorKindSpelling(cur.kind)).c_str()); } diff --git a/src/libclang/debug_helper.hpp b/src/libclang/debug_helper.hpp index 946775b..8c1d99d 100644 --- a/src/libclang/debug_helper.hpp +++ b/src/libclang/debug_helper.hpp @@ -11,6 +11,8 @@ namespace cppast { namespace detail { + cxstring get_display_name(const CXCursor& cur) noexcept; + void print_cursor_info(const CXCursor& cur) noexcept; void print_tokens(const cxtranslation_unit& tu, const CXFile& file, diff --git a/src/libclang/libclang_parser.cpp b/src/libclang/libclang_parser.cpp index 83697b7..fe13c9e 100644 --- a/src/libclang/libclang_parser.cpp +++ b/src/libclang/libclang_parser.cpp @@ -9,7 +9,9 @@ #include "libclang_visitor.hpp" #include "raii_wrapper.hpp" +#include "parse_error.hpp" #include "preprocessor.hpp" +#include "tokenizer.hpp" using namespace cppast; @@ -158,7 +160,7 @@ namespace } std::unique_ptr libclang_parser::do_parse(const cpp_entity_index& idx, std::string path, - const compile_config& c) const + const compile_config& c) const try { DEBUG_ASSERT(std::strcmp(c.name(), "libclang") == 0, detail::precondition_error_handler{}, "config has mismatched type"); @@ -167,6 +169,7 @@ std::unique_ptr libclang_parser::do_parse(const cpp_entity_index& idx, // preprocess + parse auto preprocessed = detail::preprocess(config, path.c_str(), logger()); auto tu = get_cxunit(pimpl_->index, config, path.c_str(), preprocessed.source); + auto file = clang_getFile(tu.get(), path.c_str()); // convert entity hierachies cpp_file::builder builder(path); @@ -176,9 +179,12 @@ std::unique_ptr libclang_parser::do_parse(const cpp_entity_index& idx, for (auto& e : preprocessed.entities) builder.add_child(std::move(e.entity)); - detail::visit_tu(tu, path.c_str(), [&](const CXCursor&) { - - }); + detail::visit_tu(tu, path.c_str(), [&](const CXCursor&) {}); return builder.finish(idx); } +catch (detail::parse_error& ex) +{ + logger().log("libclang parser", ex.get_diagnostic()); + return cpp_file::builder(path).finish(idx); +} diff --git a/src/libclang/parse_error.hpp b/src/libclang/parse_error.hpp new file mode 100644 index 0000000..8398360 --- /dev/null +++ b/src/libclang/parse_error.hpp @@ -0,0 +1,56 @@ +// Copyright (C) 2017 Jonathan Müller +// This file is subject to the license terms in the LICENSE file +// found in the top-level directory of this distribution. + +#ifndef CPPAST_PARSE_ERROR_HPP_INCLUDED +#define CPPAST_PARSE_ERROR_HPP_INCLUDED + +#include + +#include +#include + +#include "debug_helper.hpp" + +namespace cppast +{ + namespace detail + { + // thrown on a parsing error + // not meant to escape to the user + class parse_error : public std::logic_error + { + public: + parse_error(source_location loc, std::string message) + : std::logic_error(std::move(message)), location_(std::move(loc)) + { + } + + parse_error(const CXCursor& cur, std::string message) + : parse_error(source_location::make(get_display_name(cur).c_str()), std::move(message)) + { + } + + diagnostic get_diagnostic() const + { + return diagnostic{what(), location_, severity::error}; + } + + private: + source_location location_; + }; + + // DEBUG_ASSERT handler for parse errors + // throws a parse_error exception + struct parse_error_handler : debug_assert::set_level<1>, debug_assert::allow_exception + { + static void handle(const debug_assert::source_location&, const char*, + const CXCursor& cur, const char* message) + { + throw parse_error(cur, message); + } + }; + } +} // namespace cppast::detail + +#endif // CPPAST_PARSE_ERROR_HPP_INCLUDED diff --git a/src/libclang/tokenizer.cpp b/src/libclang/tokenizer.cpp index c3001cd..43830a4 100644 --- a/src/libclang/tokenizer.cpp +++ b/src/libclang/tokenizer.cpp @@ -5,6 +5,7 @@ #include "tokenizer.hpp" #include "libclang_visitor.hpp" +#include "parse_error.hpp" using namespace cppast; @@ -33,10 +34,12 @@ namespace class simple_tokenizer { public: - explicit simple_tokenizer(const CXTranslationUnit& tu, const CXSourceRange& range) : tu_(tu) + explicit simple_tokenizer(const CXTranslationUnit& tu, const CXSourceRange& range, + const CXCursor& cur) + : tu_(tu) { clang_tokenize(tu, range, &tokens_, &no_); - DEBUG_ASSERT(no_ >= 1u, detail::assert_handler{}); + DEBUG_ASSERT(no_ >= 1u, detail::parse_error_handler{}, cur, "no tokens available"); } ~simple_tokenizer() @@ -63,12 +66,12 @@ namespace unsigned no_; }; - bool token_after_is(const CXTranslationUnit& tu, const CXFile& file, + bool token_after_is(const CXTranslationUnit& tu, const CXFile& file, const CXCursor& cur, const CXSourceLocation& loc, const char* token_str) { auto loc_after = get_next_location(tu, file, loc); - simple_tokenizer tokenizer(tu, clang_getRange(loc, loc_after)); + simple_tokenizer tokenizer(tu, clang_getRange(loc, loc_after), cur); detail::cxstring spelling(clang_getTokenSpelling(tu, tokenizer[0u])); return spelling == token_str; } @@ -103,13 +106,13 @@ namespace return CXChildVisit_Continue; }); - if (!range_shrunk && !token_after_is(tu, file, end, ";")) + if (!range_shrunk && !token_after_is(tu, file, cur, end, ";")) { // we do not have a body, but it is not a declaration either do { end = get_next_location(tu, file, end); - } while (!token_after_is(tu, file, end, ";")); + } while (!token_after_is(tu, file, cur, end, ";")); } else if (clang_getCursorKind(cur) == CXCursor_CXXMethod) // necessary for some reason @@ -121,7 +124,7 @@ namespace || clang_getCursorKind(cur) == CXCursor_ParmDecl) { if (clang_getCursorKind(cur) == CXCursor_TemplateTypeParameter - && token_after_is(tu, file, end, "(")) + && token_after_is(tu, file, cur, end, "(")) { // if you have decltype as default argument for a type template parameter // libclang doesn't include the parameters @@ -130,9 +133,9 @@ namespace for (auto paren_count = 1; paren_count != 0; next = get_next_location(tu, file, next)) { - if (token_after_is(tu, file, next, "(")) + if (token_after_is(tu, file, cur, next, "(")) ++paren_count; - else if (token_after_is(tu, file, next, ")")) + else if (token_after_is(tu, file, cur, next, ")")) --paren_count; prev = next; } @@ -140,13 +143,13 @@ namespace } } else if (clang_getCursorKind(cur) == CXCursor_TypeAliasDecl - && !token_after_is(tu, file, end, ";")) + && !token_after_is(tu, file, cur, end, ";")) { // type alias tokens don't include everything do { end = get_next_location(tu, file, end); - } while (!token_after_is(tu, file, end, ";")); + } while (!token_after_is(tu, file, cur, end, ";")); end = get_next_location(tu, file, end); } @@ -159,7 +162,7 @@ detail::tokenizer::tokenizer(const detail::cxtranslation_unit& tu, const CXFile& { auto extent = get_extent(tu.get(), file, cur); - simple_tokenizer tokenizer(tu.get(), extent); + simple_tokenizer tokenizer(tu.get(), extent, cur); tokens_.reserve(tokenizer.size()); for (auto i = 0u; i != tokenizer.size(); ++i) tokens_.emplace_back(tu, tokenizer[i]);