From db0e649bf6dcb22be530cc59afe5956937167fc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20M=C3=BCller?= Date: Thu, 16 Mar 2017 12:15:46 +0100 Subject: [PATCH] Properly handle libclang diagnostics --- include/cppast/diagnostic.hpp | 10 +++++-- include/cppast/parser.hpp | 19 +++++++++++-- src/libclang/libclang_parser.cpp | 48 +++++++++++++++++++++++++++++--- src/libclang/parse_error.hpp | 4 +-- src/libclang/type_parser.cpp | 2 +- src/parser.cpp | 7 +++++ test/cpp_function.cpp | 2 +- 7 files changed, 79 insertions(+), 13 deletions(-) diff --git a/include/cppast/diagnostic.hpp b/include/cppast/diagnostic.hpp index 24b8474..c90e9be 100644 --- a/include/cppast/diagnostic.hpp +++ b/include/cppast/diagnostic.hpp @@ -26,13 +26,19 @@ namespace cppast } /// \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}; } + /// \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. - 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}; } diff --git a/include/cppast/parser.hpp b/include/cppast/parser.hpp index 92b9498..1da2ae2 100644 --- a/include/cppast/parser.hpp +++ b/include/cppast/parser.hpp @@ -5,6 +5,8 @@ #ifndef CPPAST_PARSER_HPP_INCLUDED #define CPPAST_PARSER_HPP_INCLUDED +#include + #include #include @@ -19,7 +21,10 @@ namespace cppast class diagnostic_logger { public: - diagnostic_logger() noexcept = default; + diagnostic_logger() noexcept : error_(false) + { + } + diagnostic_logger(const diagnostic_logger&) = delete; diagnostic_logger& operator=(const diagnostic_logger&) = delete; virtual ~diagnostic_logger() noexcept = default; @@ -27,13 +32,21 @@ namespace cppast /// \effects Logs the diagnostic by invoking the `do_log()` member function. /// \returns Whether or not the diagnostic was logged. /// \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: virtual bool do_log(const char* source, const diagnostic& d) const = 0; + + mutable std::atomic error_; }; /// A [cppast::diagnostic_logger]() that logs to `stderr`. diff --git a/src/libclang/libclang_parser.cpp b/src/libclang/libclang_parser.cpp index b1980dc..a87b269 100644 --- a/src/libclang/libclang_parser.cpp +++ b/src/libclang/libclang_parser.cpp @@ -93,7 +93,7 @@ struct libclang_parser::impl { 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; } - detail::cxtranslation_unit get_cxunit(const detail::cxindex& idx, + type_safe::optional 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 std::string& source) { @@ -155,6 +194,7 @@ namespace throw libclang_error("clang_parseTranslationUnit: AST deserialization error"); } } + print_diagnostics(logger, path, tu); return detail::cxtranslation_unit(tu); } @@ -178,8 +218,8 @@ 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()); + auto tu = get_cxunit(logger(), pimpl_->index, config, path.c_str(), preprocessed.source); + auto file = clang_getFile(tu.get(), path.c_str()); cpp_file::builder builder(path); auto preprocessed_iter = preprocessed.entities.begin(); diff --git a/src/libclang/parse_error.hpp b/src/libclang/parse_error.hpp index c841506..da34438 100644 --- a/src/libclang/parse_error.hpp +++ b/src/libclang/parse_error.hpp @@ -19,12 +19,12 @@ namespace cppast { 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) { - 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 diff --git a/src/libclang/type_parser.cpp b/src/libclang/type_parser.cpp index 9396ad1..1c99c12 100644 --- a/src/libclang/type_parser.cpp +++ b/src/libclang/type_parser.cpp @@ -330,7 +330,7 @@ namespace { auto msg = detail::format("unexpected type of kind '", 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}); } // fallthrough diff --git a/src/parser.cpp b/src/parser.cpp index 1a155dc..a1ea5f6 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -11,6 +11,13 @@ 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 { std::fprintf(stderr, "[%s] [%s] %s %s\n", source, to_string(d.severity), diff --git a/test/cpp_function.cpp b/test/cpp_function.cpp index 68b2d93..5df484d 100644 --- a/test/cpp_function.cpp +++ b/test/cpp_function.cpp @@ -202,7 +202,7 @@ struct foo { static void a(); - static int b() noexcept {} + static int b() noexcept { return 0; } static constexpr char c() = delete; };