Format preprocessor diagnostics
This commit is contained in:
parent
108fd1b2ee
commit
f66fa70b1f
3 changed files with 106 additions and 12 deletions
|
|
@ -46,7 +46,7 @@ namespace cppast
|
||||||
{
|
{
|
||||||
result += file.value() + ":";
|
result += file.value() + ":";
|
||||||
if (line)
|
if (line)
|
||||||
result += line.value() + ":";
|
result += std::to_string(line.value()) + ":";
|
||||||
if (entity)
|
if (entity)
|
||||||
result += " (" + entity.value() + "):";
|
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.
|
/// \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.
|
/// A diagnostic.
|
||||||
///
|
///
|
||||||
/// It represents an error message from a [cppast::parser]().
|
/// It represents an error message from a [cppast::parser]().
|
||||||
|
|
|
||||||
|
|
@ -36,9 +36,13 @@ namespace
|
||||||
// -CC: keep comments, even in macro
|
// -CC: keep comments, even in macro
|
||||||
// -dD: print macro definitions as well
|
// -dD: print macro definitions as well
|
||||||
// -dI: print include directives 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
|
// -Wno-pragma-once-outside-header: hide wrong warning
|
||||||
std::string cmd(detail::libclang_compile_config_access::clang_binary(c)
|
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
|
// add other flags
|
||||||
for (auto& flag : detail::libclang_compile_config_access::flags(c))
|
for (auto& flag : detail::libclang_compile_config_access::flags(c))
|
||||||
|
|
@ -53,11 +57,77 @@ namespace
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
source_location parse_source_location(const char*& ptr)
|
||||||
|
{
|
||||||
|
// format: <filename>(<line>):
|
||||||
|
// or: <filename>:
|
||||||
|
std::string filename;
|
||||||
|
while (*ptr && *ptr != ':' && *ptr != '(')
|
||||||
|
filename.push_back(*ptr++);
|
||||||
|
|
||||||
|
type_safe::optional<unsigned> 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: <severity>:
|
||||||
|
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
|
// gets the full preprocessor output
|
||||||
std::string get_full_preprocess_output(const libclang_compile_config& c, const char* full_path,
|
std::string get_full_preprocess_output(const libclang_compile_config& c, const char* full_path,
|
||||||
const diagnostic_logger& logger)
|
const diagnostic_logger& logger)
|
||||||
{
|
{
|
||||||
std::string preprocessed, diagnostics;
|
std::string preprocessed, diagnostic;
|
||||||
|
|
||||||
auto cmd = get_command(c, full_path);
|
auto cmd = get_command(c, full_path);
|
||||||
Process process(cmd, "",
|
Process process(cmd, "",
|
||||||
|
|
@ -67,20 +137,27 @@ namespace
|
||||||
if (*str != '\r')
|
if (*str != '\r')
|
||||||
preprocessed.push_back(*str);
|
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();
|
auto exit_code = process.get_exit_status();
|
||||||
|
DEBUG_ASSERT(diagnostic.empty(), detail::assert_handler{});
|
||||||
if (exit_code != 0)
|
if (exit_code != 0)
|
||||||
{
|
|
||||||
if (!diagnostics.empty())
|
|
||||||
logger.log("preprocessor",
|
|
||||||
diagnostic{std::move(diagnostics), {}, severity::critical});
|
|
||||||
throw libclang_error("preprocessor: command '" + cmd
|
throw libclang_error("preprocessor: command '" + cmd
|
||||||
+ "' exited with non-zero exit code (" + std::to_string(exit_code)
|
+ "' 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;
|
return preprocessed;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ using namespace cppast;
|
||||||
|
|
||||||
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", 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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue