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;
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<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());
}

View file

@ -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,

View file

@ -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<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{},
"config has mismatched type");
@ -167,6 +169,7 @@ std::unique_ptr<cpp_file> 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<cpp_file> 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);
}

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 "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]);