Properly handle libclang diagnostics
This commit is contained in:
parent
183aeaafde
commit
db0e649bf6
7 changed files with 79 additions and 13 deletions
|
|
@ -26,13 +26,19 @@ namespace cppast
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \returns A source location where only file and line information is available.
|
/// \returns A source location where only file and line information is available.
|
||||||
static source_location make(std::string file, unsigned line)
|
static source_location make_file(std::string file, unsigned line)
|
||||||
{
|
{
|
||||||
return {type_safe::nullopt, std::move(file), line};
|
return {type_safe::nullopt, std::move(file), line};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// \returns A source location where only the file is available.
|
||||||
|
static source_location make_file(std::string file)
|
||||||
|
{
|
||||||
|
return {type_safe::nullopt, std::move(file), type_safe::nullopt};
|
||||||
|
}
|
||||||
|
|
||||||
/// \returns A source location where only the entity name is available.
|
/// \returns A source location where only the entity name is available.
|
||||||
static source_location make(std::string entity)
|
static source_location make_entity(std::string entity)
|
||||||
{
|
{
|
||||||
return {std::move(entity), type_safe::nullopt, type_safe::nullopt};
|
return {std::move(entity), type_safe::nullopt, type_safe::nullopt};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@
|
||||||
#ifndef CPPAST_PARSER_HPP_INCLUDED
|
#ifndef CPPAST_PARSER_HPP_INCLUDED
|
||||||
#define CPPAST_PARSER_HPP_INCLUDED
|
#define CPPAST_PARSER_HPP_INCLUDED
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
#include <cppast/compile_config.hpp>
|
#include <cppast/compile_config.hpp>
|
||||||
#include <cppast/cpp_file.hpp>
|
#include <cppast/cpp_file.hpp>
|
||||||
|
|
||||||
|
|
@ -19,7 +21,10 @@ namespace cppast
|
||||||
class diagnostic_logger
|
class diagnostic_logger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
diagnostic_logger() noexcept = default;
|
diagnostic_logger() noexcept : error_(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
diagnostic_logger(const diagnostic_logger&) = delete;
|
diagnostic_logger(const diagnostic_logger&) = delete;
|
||||||
diagnostic_logger& operator=(const diagnostic_logger&) = delete;
|
diagnostic_logger& operator=(const diagnostic_logger&) = delete;
|
||||||
virtual ~diagnostic_logger() noexcept = default;
|
virtual ~diagnostic_logger() noexcept = default;
|
||||||
|
|
@ -27,13 +32,21 @@ namespace cppast
|
||||||
/// \effects Logs the diagnostic by invoking the `do_log()` member function.
|
/// \effects Logs the diagnostic by invoking the `do_log()` member function.
|
||||||
/// \returns Whether or not the diagnostic was logged.
|
/// \returns Whether or not the diagnostic was logged.
|
||||||
/// \notes `source` points to a string literal that gives additional context to what generates the message.
|
/// \notes `source` points to a string literal that gives additional context to what generates the message.
|
||||||
bool log(const char* source, const diagnostic& d) const
|
bool log(const char* source, const diagnostic& d) const;
|
||||||
|
|
||||||
|
/// \returns Whether or not a diagnostic of [severity::error]() was logged.
|
||||||
|
/// \notes If an error happens, the parser will still continue to build the AST,
|
||||||
|
/// unless the error is critical
|
||||||
|
/// so the result will be incomplete if this function returns `true`.
|
||||||
|
bool error_logged() const noexcept
|
||||||
{
|
{
|
||||||
return do_log(source, d);
|
return error_;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual bool do_log(const char* source, const diagnostic& d) const = 0;
|
virtual bool do_log(const char* source, const diagnostic& d) const = 0;
|
||||||
|
|
||||||
|
mutable std::atomic<bool> error_;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A [cppast::diagnostic_logger]() that logs to `stderr`.
|
/// A [cppast::diagnostic_logger]() that logs to `stderr`.
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ struct libclang_parser::impl
|
||||||
{
|
{
|
||||||
detail::cxindex index;
|
detail::cxindex index;
|
||||||
|
|
||||||
impl() : index(clang_createIndex(1, 1))
|
impl() : index(clang_createIndex(0, 0)) // no diagnostic, other one is irrelevant
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -117,7 +117,46 @@ namespace
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
detail::cxtranslation_unit get_cxunit(const detail::cxindex& idx,
|
type_safe::optional<severity> get_severity(const CXDiagnostic& diag)
|
||||||
|
{
|
||||||
|
switch (clang_getDiagnosticSeverity(diag))
|
||||||
|
{
|
||||||
|
case CXDiagnostic_Ignored:
|
||||||
|
case CXDiagnostic_Note:
|
||||||
|
case CXDiagnostic_Warning:
|
||||||
|
// ignore those diagnostics
|
||||||
|
return type_safe::nullopt;
|
||||||
|
|
||||||
|
case CXDiagnostic_Error:
|
||||||
|
return severity::error;
|
||||||
|
case CXDiagnostic_Fatal:
|
||||||
|
return severity::critical;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_UNREACHABLE(detail::assert_handler{});
|
||||||
|
return type_safe::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_diagnostics(const diagnostic_logger& logger, const char* path,
|
||||||
|
const CXTranslationUnit& tu)
|
||||||
|
{
|
||||||
|
auto no = clang_getNumDiagnostics(tu);
|
||||||
|
for (auto i = 0u; i != no; ++i)
|
||||||
|
{
|
||||||
|
auto diag = clang_getDiagnostic(tu, i);
|
||||||
|
auto sev = get_severity(diag);
|
||||||
|
if (sev)
|
||||||
|
{
|
||||||
|
auto loc = source_location::make_file(path); // line number won't help
|
||||||
|
auto text = detail::cxstring(clang_getDiagnosticSpelling(diag));
|
||||||
|
|
||||||
|
logger.log("libclang parser", diagnostic{text.c_str(), loc, sev.value()});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
detail::cxtranslation_unit get_cxunit(const diagnostic_logger& logger,
|
||||||
|
const detail::cxindex& idx,
|
||||||
const libclang_compile_config& config, const char* path,
|
const libclang_compile_config& config, const char* path,
|
||||||
const std::string& source)
|
const std::string& source)
|
||||||
{
|
{
|
||||||
|
|
@ -155,6 +194,7 @@ namespace
|
||||||
throw libclang_error("clang_parseTranslationUnit: AST deserialization error");
|
throw libclang_error("clang_parseTranslationUnit: AST deserialization error");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
print_diagnostics(logger, path, tu);
|
||||||
|
|
||||||
return detail::cxtranslation_unit(tu);
|
return detail::cxtranslation_unit(tu);
|
||||||
}
|
}
|
||||||
|
|
@ -178,8 +218,8 @@ 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(logger(), pimpl_->index, config, path.c_str(), preprocessed.source);
|
||||||
auto file = clang_getFile(tu.get(), path.c_str());
|
auto file = clang_getFile(tu.get(), path.c_str());
|
||||||
|
|
||||||
cpp_file::builder builder(path);
|
cpp_file::builder builder(path);
|
||||||
auto preprocessed_iter = preprocessed.entities.begin();
|
auto preprocessed_iter = preprocessed.entities.begin();
|
||||||
|
|
|
||||||
|
|
@ -19,12 +19,12 @@ namespace cppast
|
||||||
{
|
{
|
||||||
inline source_location make_location(const CXCursor& cur)
|
inline source_location make_location(const CXCursor& cur)
|
||||||
{
|
{
|
||||||
return source_location::make(get_display_name(cur).c_str());
|
return source_location::make_entity(get_display_name(cur).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
inline source_location make_location(const CXType& type)
|
inline source_location make_location(const CXType& type)
|
||||||
{
|
{
|
||||||
return source_location::make(cxstring(clang_getTypeSpelling(type)).c_str());
|
return source_location::make_entity(cxstring(clang_getTypeSpelling(type)).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// thrown on a parsing error
|
// thrown on a parsing error
|
||||||
|
|
|
||||||
|
|
@ -330,7 +330,7 @@ namespace
|
||||||
{
|
{
|
||||||
auto msg = detail::format("unexpected type of kind '",
|
auto msg = detail::format("unexpected type of kind '",
|
||||||
detail::get_type_kind_spelling(type).c_str(), "'");
|
detail::get_type_kind_spelling(type).c_str(), "'");
|
||||||
auto location = source_location::make(get_type_spelling(type).c_str());
|
auto location = source_location::make_entity(get_type_spelling(type).c_str());
|
||||||
context.logger->log("libclang parser", diagnostic{msg, location, severity::warning});
|
context.logger->log("libclang parser", diagnostic{msg, location, severity::warning});
|
||||||
}
|
}
|
||||||
// fallthrough
|
// fallthrough
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,13 @@
|
||||||
|
|
||||||
using namespace cppast;
|
using namespace cppast;
|
||||||
|
|
||||||
|
bool diagnostic_logger::log(const char* source, const diagnostic& d) const
|
||||||
|
{
|
||||||
|
if (d.severity == severity::error)
|
||||||
|
error_ = true;
|
||||||
|
return do_log(source, d);
|
||||||
|
}
|
||||||
|
|
||||||
bool stderr_diagnostic_logger::do_log(const char* source, const diagnostic& d) const
|
bool stderr_diagnostic_logger::do_log(const char* source, const diagnostic& d) const
|
||||||
{
|
{
|
||||||
std::fprintf(stderr, "[%s] [%s] %s %s\n", source, to_string(d.severity),
|
std::fprintf(stderr, "[%s] [%s] %s %s\n", source, to_string(d.severity),
|
||||||
|
|
|
||||||
|
|
@ -202,7 +202,7 @@ struct foo
|
||||||
{
|
{
|
||||||
static void a();
|
static void a();
|
||||||
|
|
||||||
static int b() noexcept {}
|
static int b() noexcept { return 0; }
|
||||||
|
|
||||||
static constexpr char c() = delete;
|
static constexpr char c() = delete;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue