From f66fa70b1f5d7c957d8dabde612b5caa00789c0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20M=C3=BCller?= Date: Sun, 19 Feb 2017 20:51:08 +0100 Subject: [PATCH] Format preprocessor diagnostics --- include/cppast/diagnostic.hpp | 18 ++++++- src/libclang/preprocessor.cpp | 97 +++++++++++++++++++++++++++++++---- src/parser.cpp | 3 +- 3 files changed, 106 insertions(+), 12 deletions(-) diff --git a/include/cppast/diagnostic.hpp b/include/cppast/diagnostic.hpp index b6348b8..24b8474 100644 --- a/include/cppast/diagnostic.hpp +++ b/include/cppast/diagnostic.hpp @@ -46,7 +46,7 @@ namespace cppast { result += file.value() + ":"; if (line) - result += line.value() + ":"; + result += std::to_string(line.value()) + ":"; if (entity) result += " (" + entity.value() + "):"; } @@ -66,6 +66,22 @@ namespace cppast /// \notes This will usually result in an exception being thrown after the diagnostic has been displayed. }; + /// \returns A human-readable string describing the severity. + inline const char* to_string(severity s) noexcept + { + switch (s) + { + case severity::warning: + return "warning"; + case severity::error: + return "error"; + case severity::critical: + return "critical"; + } + + return "programmer error"; + } + /// A diagnostic. /// /// It represents an error message from a [cppast::parser](). diff --git a/src/libclang/preprocessor.cpp b/src/libclang/preprocessor.cpp index e6c0a8b..8dd1ac8 100644 --- a/src/libclang/preprocessor.cpp +++ b/src/libclang/preprocessor.cpp @@ -36,9 +36,13 @@ namespace // -CC: keep comments, even in macro // -dD: print macro definitions as well // -dI: print include directives as well + // -fno-caret-diagnostics: don't show the source extract in diagnostics + // -fno-show-column: don't show the column number + // -fdiagnostics-format=msvc: use easier to parse MSVC format // -Wno-pragma-once-outside-header: hide wrong warning std::string cmd(detail::libclang_compile_config_access::clang_binary(c) - + " -E -CC -dD -dI -Wno-pragma-once-outside-header "); + + " -E -CC -dD -dI -fno-caret-diagnostics -fno-show-column " + "-fdiagnostics-format=msvc -Wno-pragma-once-outside-header "); // add other flags for (auto& flag : detail::libclang_compile_config_access::flags(c)) @@ -53,11 +57,77 @@ namespace return cmd; } + source_location parse_source_location(const char*& ptr) + { + // format: (): + // or: : + std::string filename; + while (*ptr && *ptr != ':' && *ptr != '(') + filename.push_back(*ptr++); + + type_safe::optional line; + if (*ptr == '(') + { + ++ptr; + + std::string str; + while (*ptr != ')') + str.push_back(*ptr++); + ++ptr; + + line = std::stoi(str); + } + + DEBUG_ASSERT(*ptr == ':', detail::assert_handler{}); + ++ptr; + + return {type_safe::nullopt, std::move(filename), std::move(line)}; + } + + severity parse_severity(const char*& ptr) + { + // format: : + std::string sev; + while (*ptr && *ptr != ':') + sev.push_back(*ptr++); + ++ptr; + + if (sev == "warning") + return severity::warning; + else if (sev == "error") + return severity::error; + else if (sev == "fatal error") + return severity::critical; + else + DEBUG_UNREACHABLE(detail::assert_handler{}); + return severity::error; + } + + // parse and log diagnostic + void log_diagnostic(const diagnostic_logger& logger, const std::string& msg) + { + auto ptr = msg.c_str(); + + auto loc = parse_source_location(ptr); + while (*ptr == ' ') + ++ptr; + + auto sev = parse_severity(ptr); + while (*ptr == ' ') + ++ptr; + + std::string message; + while (*ptr && *ptr != '\n') + message.push_back(*ptr++); + + logger.log("preprocessor", diagnostic{std::move(message), std::move(loc), sev}); + } + // gets the full preprocessor output std::string get_full_preprocess_output(const libclang_compile_config& c, const char* full_path, const diagnostic_logger& logger) { - std::string preprocessed, diagnostics; + std::string preprocessed, diagnostic; auto cmd = get_command(c, full_path); Process process(cmd, "", @@ -67,20 +137,27 @@ namespace if (*str != '\r') preprocessed.push_back(*str); }, - [&](const char* str, std::size_t n) { diagnostics.append(str, n); }); + [&](const char* str, std::size_t n) { + diagnostic.reserve(diagnostic.size() + n); + for (auto end = str + n; str != end; ++str) + if (*str == '\r') + continue; + else if (*str == '\n') + { + // consume current diagnostic + log_diagnostic(logger, diagnostic); + diagnostic.clear(); + } + else + diagnostic.push_back(*str); + }); auto exit_code = process.get_exit_status(); + DEBUG_ASSERT(diagnostic.empty(), detail::assert_handler{}); if (exit_code != 0) - { - if (!diagnostics.empty()) - logger.log("preprocessor", - diagnostic{std::move(diagnostics), {}, severity::critical}); throw libclang_error("preprocessor: command '" + cmd + "' exited with non-zero exit code (" + std::to_string(exit_code) + ")"); - } - else if (!diagnostics.empty()) - logger.log("preprocessor", diagnostic{std::move(diagnostics), {}, severity::error}); return preprocessed; } diff --git a/src/parser.cpp b/src/parser.cpp index f632576..1a155dc 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -13,6 +13,7 @@ using namespace cppast; bool stderr_diagnostic_logger::do_log(const char* source, const diagnostic& d) const { - std::fprintf(stderr, "[%s] %s %s", source, d.location.to_string().c_str(), d.message.c_str()); + std::fprintf(stderr, "[%s] [%s] %s %s\n", source, to_string(d.severity), + d.location.to_string().c_str(), d.message.c_str()); return true; }