Add parse_error handling

This commit is contained in:
Jonathan Müller 2017-02-21 20:47:15 +01:00
commit 467936cba4
5 changed files with 89 additions and 17 deletions

View file

@ -11,6 +11,11 @@
using namespace cppast; using namespace cppast;
detail::cxstring detail::get_display_name(const CXCursor& cur) noexcept
{
return cxstring(clang_getCursorDisplayName(cur));
}
namespace namespace
{ {
std::mutex mtx; std::mutex mtx;
@ -19,7 +24,7 @@ namespace
void detail::print_cursor_info(const CXCursor& cur) noexcept void detail::print_cursor_info(const CXCursor& cur) noexcept
{ {
std::lock_guard<std::mutex> lock(mtx); std::lock_guard<std::mutex> 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()); cxstring(clang_getCursorKindSpelling(cur.kind)).c_str());
} }

View file

@ -11,6 +11,8 @@ namespace cppast
{ {
namespace detail namespace detail
{ {
cxstring get_display_name(const CXCursor& cur) noexcept;
void print_cursor_info(const CXCursor& cur) noexcept; void print_cursor_info(const CXCursor& cur) noexcept;
void print_tokens(const cxtranslation_unit& tu, const CXFile& file, void print_tokens(const cxtranslation_unit& tu, const CXFile& file,

View file

@ -9,7 +9,9 @@
#include "libclang_visitor.hpp" #include "libclang_visitor.hpp"
#include "raii_wrapper.hpp" #include "raii_wrapper.hpp"
#include "parse_error.hpp"
#include "preprocessor.hpp" #include "preprocessor.hpp"
#include "tokenizer.hpp"
using namespace cppast; using namespace cppast;
@ -158,7 +160,7 @@ namespace
} }
std::unique_ptr<cpp_file> libclang_parser::do_parse(const cpp_entity_index& idx, std::string path, std::unique_ptr<cpp_file> 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{}, DEBUG_ASSERT(std::strcmp(c.name(), "libclang") == 0, detail::precondition_error_handler{},
"config has mismatched type"); "config has mismatched type");
@ -167,6 +169,7 @@ std::unique_ptr<cpp_file> libclang_parser::do_parse(const cpp_entity_index& idx,
// preprocess + parse // preprocess + parse
auto preprocessed = detail::preprocess(config, path.c_str(), logger()); auto preprocessed = detail::preprocess(config, path.c_str(), logger());
auto tu = get_cxunit(pimpl_->index, config, path.c_str(), preprocessed.source); auto tu = get_cxunit(pimpl_->index, config, path.c_str(), preprocessed.source);
auto file = clang_getFile(tu.get(), path.c_str());
// convert entity hierachies // convert entity hierachies
cpp_file::builder builder(path); cpp_file::builder builder(path);
@ -176,9 +179,12 @@ std::unique_ptr<cpp_file> libclang_parser::do_parse(const cpp_entity_index& idx,
for (auto& e : preprocessed.entities) for (auto& e : preprocessed.entities)
builder.add_child(std::move(e.entity)); 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); return builder.finish(idx);
} }
catch (detail::parse_error& ex)
{
logger().log("libclang parser", ex.get_diagnostic());
return cpp_file::builder(path).finish(idx);
}

View file

@ -0,0 +1,56 @@
// Copyright (C) 2017 Jonathan Müller <jonathanmueller.dev@gmail.com>
// 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 <stdexcept>
#include <debug_assert.hpp>
#include <cppast/diagnostic.hpp>
#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

View file

@ -5,6 +5,7 @@
#include "tokenizer.hpp" #include "tokenizer.hpp"
#include "libclang_visitor.hpp" #include "libclang_visitor.hpp"
#include "parse_error.hpp"
using namespace cppast; using namespace cppast;
@ -33,10 +34,12 @@ namespace
class simple_tokenizer class simple_tokenizer
{ {
public: 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_); 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() ~simple_tokenizer()
@ -63,12 +66,12 @@ namespace
unsigned no_; 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) const CXSourceLocation& loc, const char* token_str)
{ {
auto loc_after = get_next_location(tu, file, loc); 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])); detail::cxstring spelling(clang_getTokenSpelling(tu, tokenizer[0u]));
return spelling == token_str; return spelling == token_str;
} }
@ -103,13 +106,13 @@ namespace
return CXChildVisit_Continue; 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 // we do not have a body, but it is not a declaration either
do do
{ {
end = get_next_location(tu, file, end); 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) else if (clang_getCursorKind(cur) == CXCursor_CXXMethod)
// necessary for some reason // necessary for some reason
@ -121,7 +124,7 @@ namespace
|| clang_getCursorKind(cur) == CXCursor_ParmDecl) || clang_getCursorKind(cur) == CXCursor_ParmDecl)
{ {
if (clang_getCursorKind(cur) == CXCursor_TemplateTypeParameter 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 // if you have decltype as default argument for a type template parameter
// libclang doesn't include the parameters // libclang doesn't include the parameters
@ -130,9 +133,9 @@ namespace
for (auto paren_count = 1; paren_count != 0; for (auto paren_count = 1; paren_count != 0;
next = get_next_location(tu, file, next)) next = get_next_location(tu, file, next))
{ {
if (token_after_is(tu, file, next, "(")) if (token_after_is(tu, file, cur, next, "("))
++paren_count; ++paren_count;
else if (token_after_is(tu, file, next, ")")) else if (token_after_is(tu, file, cur, next, ")"))
--paren_count; --paren_count;
prev = next; prev = next;
} }
@ -140,13 +143,13 @@ namespace
} }
} }
else if (clang_getCursorKind(cur) == CXCursor_TypeAliasDecl 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 // type alias tokens don't include everything
do do
{ {
end = get_next_location(tu, file, end); 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); 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); 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()); tokens_.reserve(tokenizer.size());
for (auto i = 0u; i != tokenizer.size(); ++i) for (auto i = 0u; i != tokenizer.size(); ++i)
tokens_.emplace_back(tu, tokenizer[i]); tokens_.emplace_back(tu, tokenizer[i]);