mirror of
https://github.com/gcc-mirror/gcc.git
synced 2024-11-21 13:40:47 +00:00
diagnostics: support multiple output formats simultaneously [PR116613]
This patch generalizes diagnostic_context so that rather than having a single output format, it has a vector of zero or more. It adds new two options: -fdiagnostics-add-output=DIAGNOSTICS-OUTPUT-SPEC -fdiagnostics-set-output=DIAGNOSTICS-OUTPUT-SPEC which both take a new configuration syntax of the form SCHEME ("text" or "sarif"), optionally followed by ":" and one or more KEY=VALUE pairs, in this form: <SCHEME> <SCHEME>:<KEY>=<VALUE> <SCHEME>:<KEY>=<VALUE>,<KEY2>=<VALUE2> ...etc where each SCHEME supports some set of keys. For example, it's now possible to use: -fdiagnostics-add-output=sarif:version=2.1,file=foo.2.1.sarif \ -fdiagnostics-add-output=sarif:version=2.2-prerelease,file=foo.2.2.sarif to add a pair of outputs, each writing to a different file, using versions 2.1 and 2.2 of the SARIF standard respectively, whilst also emitting the classic text form of the diagnostics to stderr. I hope the new syntax gives us room to potentially add new kinds of output sink in the future (e.g. RPC notifications), and to add new key/value pairs as needed by the different sinks. Implementation-wise, the diagnostic_context's m_printer which previously was used directly by the single output format now becomes a "reference printer", created by the client (such as the frontend), with defaults modified by command-line options. Each of the multiple output sinks has its own pretty_printer instance, created by cloning the context's reference printer. gcc/ChangeLog: PR other/116613 * Makefile.in (OBJS-libcommon-target): Add opts-diagnostic.o. * common.opt (fdiagnostics-add-output=): New. (fdiagnostics-set-output=): New. (diagnostics_output_format): Drop sarif-file-2.2-prerelease from enum. * common.opt.urls: Regenerate. * diagnostic-buffer.h (diagnostic_buffer::~diagnostic_buffer): New. (diagnostic_buffer::ensure_per_format_buffer): Rename to... (diagnostic_buffer::ensure_per_format_buffers): ...this. (diagnostic_buffer::m_per_format_buffer): Replace with... (diagnostic_buffer::m_per_format_buffers): ...this, updating type. * diagnostic-format-json.cc (json_output_format::update_printer): New. (json_output_format::follows_reference_printer_p): New. (diagnostic_output_format_init_json): Drop redundant call to set_path_format, as this is not a text output format. * diagnostic-format-sarif.cc: Include "diagnostic-format-text.h". (sarif_builder::set_printer): New. (sarif_builder::sarif_builder): Add "printer" param and use it for m_printer. (sarif_builder::make_location_object::escape_nonascii_renderer::render): Rather than using dc.m_printer, create a diagnostic_text_output_format instance and use its printer. (sarif_output_format::follows_reference_printer_p): New. (sarif_output_format::update_printer): New. (sarif_output_format::sarif_output_format): Pass in correct printer to m_builder's ctor. (diagnostic_output_format_init_sarif): Drop redundant call to set_path_format, as this is not a text output format. Replace calls to pp_show_color and set_token_printer with call to update_printer. Drop redundant call to set_show_highlight_colors, as this printer does not show colors. (diagnostic_output_format_init_sarif_file): Split out file opening into... (diagnostic_output_format_open_sarif_file): ...this new function. (make_sarif_sink): New. (selftest::test_make_location_object): Provide a pp for the builder. * diagnostic-format-sarif.h (diagnostic_output_format_open_sarif_file): New decl. (make_sarif_sink): New decl. * diagnostic-format-text.cc (diagnostic_text_output_format::dump): Dump sm_follows_reference_printer. (diagnostic_text_output_format::on_report_verbatim): New. (diagnostic_text_output_format::follows_reference_printer_p): New. (diagnostic_text_output_format::update_printer): New. * diagnostic-format-text.h (diagnostic_text_output_format::diagnostic_text_output_format): Add optional "follows_reference_printer" param. (diagnostic_text_output_format::on_report_verbatim): New decl. (diagnostic_text_output_format::after_diagnostic): Drop "final". (diagnostic_text_output_format::follows_reference_printer_p): New decl. (class diagnostic_text_output_format): Convert private members to protected. (diagnostic_text_output_format::m_follows_reference_printer): New field. * diagnostic-format.h (diagnostic_output_format::on_report_verbatim): New vfunc. (diagnostic_output_format::follows_reference_printer_p): New vfunc. (diagnostic_output_format::update_printer): New vfunc. (diagnostic_output_format::get_printer): Use m_printer rather than a printer from m_context. (diagnostic_output_format::diagnostic_output_format): Initialize m_printer by cloning the context's printer. (diagnostic_output_format::m_printer): New field. * diagnostic-global-context.cc (verbatim): Reimplement in terms of global_dc->report_verbatim, moving existing implementation to diagnostic_text_output_format::on_report_verbatim. (fnotice): Support multiple output sinks by using a new global_dc->supports_fnotice_on_stderr_p. * diagnostic-output-file.h (diagnostic_output_file::diagnostic_output_file): New default ctor. (diagnostic_output_file::operator=): Implement move assignment. * diagnostic-path.cc (selftest::test_interprocedural_path_1): Pass false for new param of text_output's ctor. * diagnostic-show-locus.cc (selftest::test_layout_x_offset_display_utf8): Use reference printer. (selftest::test_layout_x_offset_display_tab): Likewise. (selftest::test_one_liner_fixit_remove): Likewise. * diagnostic.cc: Include "pretty-print-urlifier.h". (diagnostic_set_caret_max_width): Update for global_dc's m_printer becoming reference printer. (diagnostic_context::initialize): Update for m_printer becoming m_reference_printer. Use ::make_unique to create it. Update for m_output_format becoming m_output_sinks. (diagnostic_context::color_init): Update the reference printer, then update the printers for any output sinks that follow it. (diagnostic_context::urls_init): Likewise. (diagnostic_context::finish): Update comment. Update for m_output_format becoming m_output_sinks. Update for m_printer becoming m_reference_printer and use "delete" on it rather than XDELETE. (diagnostic_context::dump): Update for m_printer becoming reference printer, and for multiple output sinks. (diagnostic_context::set_output_format): Reimplement for supporting multiple output sinks. (diagnostic_context::get_output_format): Likewise. (diagnostic_context::add_sink): New. (diagnostic_context::supports_fnotice_on_stderr_p): New. (diagnostic_context::set_pretty_printer): New. (diagnostic_context::refresh_output_sinks): New. (diagnostic_context::set_format_decoder): New. (diagnostic_context::set_show_highlight_colors): New. (diagnostic_context::set_prefixing_rule): New. (diagnostic_context::report_diagnostic): Update to support multiple output sinks. (diagnostic_context::report_verbatim): New. (diagnostic_context::emit_diagram): Update to support multiple output sinks. (diagnostic_context::error_recursion): Update to use m_reference_printer. (fancy_abort): Likewise. (diagnostic_context::end_group): Update to support multiple output sinks. (diagnostic_output_format::dump): Implement. (diagnostic_output_format::on_report_verbatim): Likewise. (diagnostic_output_format_init): Drop DIAGNOSTICS_OUTPUT_FORMAT_SARIF_FILE_2_2_PRERELEASE. (diagnostic_context::set_diagnostic_buffer): Reimplement to support multiple output sinks. (diagnostic_context::clear_diagnostic_buffer): Likewise. (diagnostic_context::flush_diagnostic_buffer): Likewise. (diagnostic_buffer::diagnostic_buffer): Initialize m_per_format_buffers. (diagnostic_buffer::~diagnostic_buffer): New dtor. (diagnostic_buffer::dump): Reimplement to support multiple output sinks. (diagnostic_buffer::empty_p): Likewise. (diagnostic_buffer::move_to): Likewise. (diagnostic_buffer::ensure_per_format_buffer): Likewise, renaming to... (diagnostic_buffer::ensure_per_format_buffers): ...this. * diagnostic.h (DIAGNOSTICS_OUTPUT_FORMAT_SARIF_FILE_2_2_PRERELEASE): Delete. (class diagnostic_context): Add friend class diagnostic_buffer. (diagnostic_context::set_pretty_printer): New decl. (diagnostic_context::refresh_output_sinks): New decl. (diagnostic_context::report_verbatim): New decl. (diagnostic_context::get_output_format): Drop. (diagnostic_context::set_show_highlight_colors): Drop body. (diagnostic_context::set_format_decoder): New decl. (diagnostic_context::set_prefixing_rule): New decl. (diagnostic_context::clone_printer): Reimplement. (diagnostic_context::get_reference_printer): New accessor. (diagnostic_context::add_sink): New decl. (diagnostic_context::supports_fnotice_on_stderr_p): New decl. (diagnostic_context::m_printer): Replace with... (diagnostic_context::m_reference_printer): ...this, and make private. (diagnostic_context::m_output_format): Replace with... (diagnostic_context::m_output_sinks): ...this. (diagnostic_format_decoder): Delete. (diagnostic_prefixing_rule): Delete. (diagnostic_ready_p): Delete. * doc/invoke.texi: Document -fdiagnostics-add-output= and -fdiagnostics-set-output=. * gcc.cc: Include "opts-diagnostic.h". (driver_handle_option): Handle cases OPT_fdiagnostics_add_output_ and OPT_fdiagnostics_set_output_. * opts-diagnostic.cc: New file. * opts-diagnostic.h (handle_OPT_fdiagnostics_add_output_): New decl. (handle_OPT_fdiagnostics_set_output_): New decl. * opts-global.cc (init_options_once): Update for global_dc's m_printer becoming reference printer. Call global_dc->refresh_output_sinks. * opts.cc (common_handle_option): Replace use of diagnostic_prefixing_rule with dc->set_prefixing_rule. Handle cases OPT_fdiagnostics_add_output_ and OPT_fdiagnostics_set_output_. Update for m_printer becoming reference printer. * selftest-diagnostic.cc (selftest::test_diagnostic_context::test_diagnostic_context): Update for m_printer becoming reference printer. (test_diagnostic_context::test_show_locus): Likewise. * selftest-run-tests.cc (selftest::run_tests): Call selftest::opts_diagnostic_cc_tests. * selftest.h (selftest::opts_diagnostic_cc_tests): New decl. * simple-diagnostic-path.cc (selftest::simple_diagnostic_path_cc_tests): Use reference printer. * toplev.cc (announce_function): Update for global_dc's m_printer becoming reference printer. (toplev::main): Likewise. * tree-diagnostic.cc (tree_diagnostics_defaults): Replace use of diagnostic_format_decoder with context->set_format_decoder. * tree-diagnostic.h (tree_dump_pretty_printer::tree_dump_pretty_printer): Update for global_dc's m_printer becoming reference printer. * tree.cc (escaped_string::escape): Likewise. (selftest::test_escaped_strings): Likewise. gcc/ada/ChangeLog: PR other/116613 * gcc-interface/misc.cc (internal_error_function): Update for m_printer becoming reference printer. gcc/analyzer/ChangeLog: PR other/116613 * analyzer-language.cc (on_finish_translation_unit): Update for m_printer becoming reference printer. * engine.cc (run_checkers): Likewise. * program-point.cc (function_point::print_source_line): Likewise. gcc/c-family/ChangeLog: PR other/116613 * c-format.cc (selftest::test_type_mismatch_range_labels): Update for m_printer becoming reference printer. (selftest::test_type_mismatch_range_labels): Likewise. gcc/c/ChangeLog: PR other/116613 * c-objc-common.cc: Include "make-unique.h". (c_initialize_diagnostics): Use unique_ptr for pretty_printer. Use context->set_format_decoder. gcc/cp/ChangeLog: PR other/116613 * error.cc (cxx_initialize_diagnostics): Use unique_ptr for pretty_printer. Use context->set_format_decoder. * module.cc (noisy_p): Update for global_dc's m_printer becoming reference printer. gcc/d/ChangeLog: PR other/116613 * d-diagnostic.cc (d_diagnostic_report_diagnostic): Update for m_printer becoming reference printer. gcc/fortran/ChangeLog: PR other/116613 * error.cc (gfc_diagnostic_build_kind_prefix): Update for global_dc's m_printer becoming reference printer. (gfc_diagnostics_init): Replace usage of diagnostic_format_decoder with global_dc->set_format_decoder. gcc/jit/ChangeLog: PR other/116613 * dummy-frontend.cc: Include "make-unique.h". (class jit_diagnostic_listener): New. (jit_begin_diagnostic): Update comment. (jit_end_diagnostic): Drop call to add_diagnostic. (jit_langhook_init): Set the output format to a new jit_diagnostic_listener. * jit-playback.cc (playback::context::add_diagnostic): Add "text" param and use that rather than trying to get the text from a pretty_printer. * jit-playback.h (playback::context::add_diagnostic): Add "text" param. gcc/testsuite/ChangeLog: PR other/116613 * gcc.dg/plugin/analyzer_cpython_plugin.c (dump_refcnt_info): Update for global_dc's m_printer becoming reference printer. * gcc.dg/plugin/crash-test-ice-in-header-sarif-2.2.c: Replace usage of -fdiagnostics-format=sarif-file-2.2-prerelease with -fdiagnostics-set-output=sarif:version=2.2-prerelease. * gcc.dg/plugin/diagnostic_plugin_test_paths.c: Update for global_dc's m_printer becoming reference printer. * gcc.dg/plugin/diagnostic_plugin_xhtml_format.c: Update for changes to output formats. * gcc.dg/plugin/expensive_selftests_plugin.c: Update for global_dc's m_printer becoming reference printer. * gcc.dg/sarif-output/add-output-sarif-defaults.c: New test. * gcc.dg/sarif-output/bad-binary-op.c: New test. * gcc.dg/sarif-output/bad-binary-op.py: New support script. * gcc.dg/sarif-output/multiple-outputs.c: New test. * gcc.dg/sarif-output/multiple-outputs.py: New support script. * lib/scansarif.exp (verify-sarif-file): Add an optional second argument specifying the expected filename of the .sarif file. Signed-off-by: David Malcolm <dmalcolm@redhat.com>
This commit is contained in:
parent
3d8cd34a45
commit
0b73e9382a
@ -1849,7 +1849,8 @@ OBJS-libcommon = diagnostic-spec.o diagnostic.o diagnostic-color.o \
|
||||
# Objects in libcommon-target.a, used by drivers and by the core
|
||||
# compiler and containing target-dependent code.
|
||||
OBJS-libcommon-target = $(common_out_object_file) prefix.o \
|
||||
opts.o opts-common.o options.o vec.o hooks.o common/common-targhooks.o \
|
||||
opts.o opts-common.o opts-diagnostic.o options.o \
|
||||
vec.o hooks.o common/common-targhooks.o \
|
||||
hash-table.o file-find.o spellcheck.o selftest.o opt-suggestions.o \
|
||||
options-urls.o
|
||||
|
||||
|
@ -308,14 +308,14 @@ internal_error_function (diagnostic_context *context, const char *msgid,
|
||||
emergency_dump_function ();
|
||||
|
||||
/* Reset the pretty-printer. */
|
||||
pp_clear_output_area (context->m_printer);
|
||||
pp_clear_output_area (context->get_reference_printer ());
|
||||
|
||||
/* Format the message into the pretty-printer. */
|
||||
text_info tinfo (msgid, ap, errno);
|
||||
pp_format_verbatim (context->m_printer, &tinfo);
|
||||
pp_format_verbatim (context->get_reference_printer (), &tinfo);
|
||||
|
||||
/* Extract a (writable) pointer to the formatted text. */
|
||||
buffer = xstrdup (pp_formatted_text (context->m_printer));
|
||||
buffer = xstrdup (pp_formatted_text (context->get_reference_printer ()));
|
||||
|
||||
/* Go up to the first newline. */
|
||||
for (p = buffer; *p; p++)
|
||||
|
@ -120,7 +120,7 @@ on_finish_translation_unit (const translation_unit &tu)
|
||||
log_user the_logger (NULL);
|
||||
if (logfile)
|
||||
the_logger.set_logger (new logger (logfile, 0, 0,
|
||||
*global_dc->m_printer));
|
||||
*global_dc->get_reference_printer ()));
|
||||
stash_named_constants (the_logger.get_logger (), tu);
|
||||
|
||||
run_callbacks (the_logger.get_logger (), tu);
|
||||
|
@ -6327,7 +6327,7 @@ run_checkers ()
|
||||
get_or_create_any_logfile ();
|
||||
if (dump_fout)
|
||||
the_logger.set_logger (new logger (dump_fout, 0, 0,
|
||||
*global_dc->m_printer));
|
||||
*global_dc->get_reference_printer ()));
|
||||
LOG_SCOPE (the_logger.get_logger ());
|
||||
|
||||
impl_run_checkers (the_logger.get_logger ());
|
||||
|
@ -280,7 +280,7 @@ function_point::print_source_line (pretty_printer *pp) const
|
||||
diagnostic_source_print_policy source_policy (tmp_dc);
|
||||
gcc_assert (pp);
|
||||
source_policy.print (*pp, richloc, DK_ERROR, nullptr);
|
||||
pp_string (pp, pp_formatted_text (tmp_dc.m_printer));
|
||||
pp_string (pp, pp_formatted_text (tmp_dc.get_reference_printer ()));
|
||||
}
|
||||
|
||||
/* class program_point. */
|
||||
|
@ -5579,14 +5579,14 @@ test_type_mismatch_range_labels ()
|
||||
richloc.add_range (param, SHOW_RANGE_WITHOUT_CARET, ¶m_label);
|
||||
|
||||
test_diagnostic_context dc;
|
||||
diagnostic_show_locus (&dc, &richloc, DK_ERROR, dc.m_printer);
|
||||
diagnostic_show_locus (&dc, &richloc, DK_ERROR, dc.get_reference_printer ());
|
||||
if (c_dialect_cxx ())
|
||||
/* "char*", without a space. */
|
||||
ASSERT_STREQ (" printf (\"msg: %i\\n\", msg);\n"
|
||||
" ~^ ~~~\n"
|
||||
" | |\n"
|
||||
" char* int\n",
|
||||
pp_formatted_text (dc.m_printer));
|
||||
pp_formatted_text (dc.get_reference_printer ()));
|
||||
else
|
||||
/* "char *", with a space. */
|
||||
ASSERT_STREQ (" printf (\"msg: %i\\n\", msg);\n"
|
||||
@ -5594,7 +5594,7 @@ test_type_mismatch_range_labels ()
|
||||
" | |\n"
|
||||
" | int\n"
|
||||
" char *\n",
|
||||
pp_formatted_text (dc.m_printer));
|
||||
pp_formatted_text (dc.get_reference_printer ()));
|
||||
}
|
||||
|
||||
/* Run all of the selftests within this file. */
|
||||
|
@ -33,6 +33,7 @@ along with GCC; see the file COPYING3. If not see
|
||||
#include "stringpool.h"
|
||||
#include "attribs.h"
|
||||
#include "dwarf2.h"
|
||||
#include "make-unique.h"
|
||||
|
||||
static bool c_tree_printer (pretty_printer *, text_info *, const char *,
|
||||
int, bool, bool, bool, bool *, pp_token_list &);
|
||||
@ -412,16 +413,9 @@ has_c_linkage (const_tree decl ATTRIBUTE_UNUSED)
|
||||
void
|
||||
c_initialize_diagnostics (diagnostic_context *context)
|
||||
{
|
||||
pretty_printer *base = context->m_printer;
|
||||
c_pretty_printer *pp = XNEW (c_pretty_printer);
|
||||
context->m_printer = new (pp) c_pretty_printer ();
|
||||
|
||||
/* It is safe to free this object because it was previously XNEW()'d. */
|
||||
base->~pretty_printer ();
|
||||
XDELETE (base);
|
||||
|
||||
context->set_pretty_printer (::make_unique<c_pretty_printer> ());
|
||||
c_common_diagnostics_set_defaults (context);
|
||||
diagnostic_format_decoder (context) = &c_tree_printer;
|
||||
context->set_format_decoder (&c_tree_printer);
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -1440,6 +1440,14 @@ fdiagnostics-format=
|
||||
Common Joined RejectNegative Enum(diagnostics_output_format)
|
||||
-fdiagnostics-format=[text|sarif-stderr|sarif-file|json|json-stderr|json-file] Select output format.
|
||||
|
||||
fdiagnostics-add-output=
|
||||
Common Joined RejectNegative
|
||||
Add output format.
|
||||
|
||||
fdiagnostics-set-output=
|
||||
Common Joined RejectNegative
|
||||
Set output format.
|
||||
|
||||
fdiagnostics-escape-format=
|
||||
Common Joined RejectNegative Enum(diagnostics_escape_format)
|
||||
-fdiagnostics-escape-format=[unicode|bytes] Select how to escape non-printable-ASCII bytes in the source for diagnostics that suggest it.
|
||||
@ -1487,10 +1495,6 @@ Enum(diagnostics_output_format) String(sarif-stderr) Value(DIAGNOSTICS_OUTPUT_FO
|
||||
EnumValue
|
||||
Enum(diagnostics_output_format) String(sarif-file) Value(DIAGNOSTICS_OUTPUT_FORMAT_SARIF_FILE)
|
||||
|
||||
EnumValue
|
||||
Enum(diagnostics_output_format) String(sarif-file-2.2-prerelease) Value(DIAGNOSTICS_OUTPUT_FORMAT_SARIF_FILE_2_2_PRERELEASE)
|
||||
; undocumented
|
||||
|
||||
fdiagnostics-parseable-fixits
|
||||
Common Var(flag_diagnostics_parseable_fixits)
|
||||
Print fix-it hints in machine-readable form.
|
||||
|
@ -564,6 +564,12 @@ UrlSuffix(gcc/Diagnostic-Message-Formatting-Options.html#index-fdiagnostics-colu
|
||||
fdiagnostics-format=
|
||||
UrlSuffix(gcc/Diagnostic-Message-Formatting-Options.html#index-fdiagnostics-format)
|
||||
|
||||
fdiagnostics-add-output=
|
||||
UrlSuffix(gcc/Diagnostic-Message-Formatting-Options.html#index-fdiagnostics-add-output)
|
||||
|
||||
fdiagnostics-set-output=
|
||||
UrlSuffix(gcc/Diagnostic-Message-Formatting-Options.html#index-fdiagnostics-set-output)
|
||||
|
||||
fdiagnostics-escape-format=
|
||||
UrlSuffix(gcc/Diagnostic-Message-Formatting-Options.html#index-fdiagnostics-escape-format)
|
||||
|
||||
|
@ -275,20 +275,15 @@ cp_seen_error ()
|
||||
void
|
||||
cxx_initialize_diagnostics (diagnostic_context *context)
|
||||
{
|
||||
pretty_printer *base = context->m_printer;
|
||||
cxx_pretty_printer *pp = XNEW (cxx_pretty_printer);
|
||||
context->m_printer = new (pp) cxx_pretty_printer ();
|
||||
|
||||
/* It is safe to free this object because it was previously XNEW()'d. */
|
||||
base->~pretty_printer ();
|
||||
XDELETE (base);
|
||||
cxx_pretty_printer *pp = new cxx_pretty_printer ();
|
||||
pp_format_postprocessor (pp) = new cxx_format_postprocessor ();
|
||||
context->set_pretty_printer (std::unique_ptr<pretty_printer> (pp));
|
||||
|
||||
c_common_diagnostics_set_defaults (context);
|
||||
diagnostic_text_starter (context) = cp_diagnostic_text_starter;
|
||||
/* diagnostic_finalizer is already c_diagnostic_text_finalizer. */
|
||||
diagnostic_format_decoder (context) = cp_printer;
|
||||
context->set_format_decoder (cp_printer);
|
||||
context->m_adjust_diagnostic_info = cp_adjust_diagnostic_info;
|
||||
pp_format_postprocessor (pp) = new cxx_format_postprocessor ();
|
||||
}
|
||||
|
||||
/* Dump an '@module' name suffix for DECL, if any. */
|
||||
|
@ -4698,7 +4698,7 @@ noisy_p ()
|
||||
if (quiet_flag)
|
||||
return false;
|
||||
|
||||
pp_needs_newline (global_dc->m_printer) = true;
|
||||
pp_needs_newline (global_dc->get_reference_printer ()) = true;
|
||||
diagnostic_set_last_function (global_dc, (diagnostic_info *) NULL);
|
||||
|
||||
return true;
|
||||
|
@ -208,7 +208,7 @@ d_diagnostic_report_diagnostic (const Loc &loc, int opt, const char *format,
|
||||
/* Write verbatim messages with no location direct to stream. */
|
||||
text_info text (expand_d_format (format), &argp, errno, nullptr);
|
||||
|
||||
pretty_printer *const pp = global_dc->m_printer;
|
||||
pretty_printer *const pp = global_dc->get_reference_printer ();
|
||||
pp_format_verbatim (pp, &text);
|
||||
pp_newline_and_flush (pp);
|
||||
}
|
||||
|
@ -59,6 +59,7 @@ class diagnostic_buffer
|
||||
friend class diagnostic_context;
|
||||
|
||||
diagnostic_buffer (diagnostic_context &ctxt);
|
||||
~diagnostic_buffer ();
|
||||
|
||||
void dump (FILE *out, int indent) const;
|
||||
void DEBUG_FUNCTION dump () const { dump (stderr, 0); }
|
||||
@ -73,10 +74,10 @@ class diagnostic_buffer
|
||||
void move_to (diagnostic_buffer &dest);
|
||||
|
||||
private:
|
||||
void ensure_per_format_buffer ();
|
||||
void ensure_per_format_buffers ();
|
||||
|
||||
diagnostic_context &m_ctxt;
|
||||
std::unique_ptr<diagnostic_per_format_buffer> m_per_format_buffer;
|
||||
auto_vec<diagnostic_per_format_buffer *> *m_per_format_buffers;
|
||||
|
||||
/* The number of buffered diagnostics of each kind. */
|
||||
diagnostic_counters m_diagnostic_counters;
|
||||
|
@ -104,6 +104,15 @@ public:
|
||||
{
|
||||
/* No-op. */
|
||||
}
|
||||
void update_printer () final override
|
||||
{
|
||||
m_printer = m_context.clone_printer ();
|
||||
pp_show_color (m_printer.get ()) = false;
|
||||
}
|
||||
bool follows_reference_printer_p () const final override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
json_output_format (diagnostic_context &context,
|
||||
@ -501,9 +510,6 @@ static void
|
||||
diagnostic_output_format_init_json (diagnostic_context &context,
|
||||
std::unique_ptr<json_output_format> fmt)
|
||||
{
|
||||
/* Suppress normal textual path output. */
|
||||
context.set_path_format (DPF_NONE);
|
||||
|
||||
/* Don't colorize the text. */
|
||||
pp_show_color (fmt->get_printer ()) = false;
|
||||
context.set_show_highlight_colors (false);
|
||||
|
@ -38,6 +38,7 @@ along with GCC; see the file COPYING3. If not see
|
||||
#include "diagnostic-diagram.h"
|
||||
#include "text-art/canvas.h"
|
||||
#include "diagnostic-format-sarif.h"
|
||||
#include "diagnostic-format-text.h"
|
||||
#include "ordered-hash-map.h"
|
||||
#include "sbitmap.h"
|
||||
#include "make-unique.h"
|
||||
@ -680,12 +681,18 @@ public:
|
||||
friend class diagnostic_sarif_format_buffer;
|
||||
|
||||
sarif_builder (diagnostic_context &context,
|
||||
pretty_printer &printer,
|
||||
const line_maps *line_maps,
|
||||
const char *main_input_filename_,
|
||||
bool formatted,
|
||||
enum sarif_version version);
|
||||
~sarif_builder ();
|
||||
|
||||
void set_printer (pretty_printer &printer)
|
||||
{
|
||||
m_printer = &printer;
|
||||
}
|
||||
|
||||
void on_report_diagnostic (const diagnostic_info &diagnostic,
|
||||
diagnostic_t orig_diag_kind,
|
||||
diagnostic_sarif_format_buffer *buffer);
|
||||
@ -1540,12 +1547,13 @@ sarif_thread_flow::add_location ()
|
||||
/* sarif_builder's ctor. */
|
||||
|
||||
sarif_builder::sarif_builder (diagnostic_context &context,
|
||||
pretty_printer &printer,
|
||||
const line_maps *line_maps,
|
||||
const char *main_input_filename_,
|
||||
bool formatted,
|
||||
enum sarif_version version)
|
||||
: m_context (context),
|
||||
m_printer (context.m_printer),
|
||||
m_printer (&printer),
|
||||
m_line_maps (line_maps),
|
||||
m_token_printer (*this),
|
||||
m_version (version),
|
||||
@ -2123,11 +2131,13 @@ sarif_builder::make_location_object (sarif_location_manager &loc_mgr,
|
||||
|
||||
diagnostic_source_print_policy source_policy (dc);
|
||||
dc.set_escape_format (m_escape_format);
|
||||
source_policy.print (*dc.m_printer, my_rich_loc, DK_ERROR, nullptr);
|
||||
diagnostic_text_output_format text_output (dc);
|
||||
source_policy.print (*text_output.get_printer (),
|
||||
my_rich_loc, DK_ERROR, nullptr);
|
||||
|
||||
const char *buf = pp_formatted_text (text_output.get_printer ());
|
||||
std::unique_ptr<sarif_multiformat_message_string> result
|
||||
= builder.make_multiformat_message_string
|
||||
(pp_formatted_text (dc.m_printer));
|
||||
= builder.make_multiformat_message_string (buf);
|
||||
|
||||
diagnostic_finish (&dc);
|
||||
|
||||
@ -3423,6 +3433,28 @@ public:
|
||||
m_buffer = buffer;
|
||||
}
|
||||
|
||||
bool follows_reference_printer_p () const final override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void update_printer () final override
|
||||
{
|
||||
m_printer = m_context.clone_printer ();
|
||||
|
||||
/* Don't colorize the text. */
|
||||
pp_show_color (m_printer.get ()) = false;
|
||||
|
||||
/* No textual URLs. */
|
||||
m_printer->set_url_format (URL_FORMAT_NONE);
|
||||
|
||||
/* Use builder's token printer. */
|
||||
get_printer ()->set_token_printer (&m_builder.get_token_printer ());
|
||||
|
||||
/* Update the builder to use the new printer. */
|
||||
m_builder.set_printer (*get_printer ());
|
||||
}
|
||||
|
||||
void on_begin_group () final override
|
||||
{
|
||||
/* No-op, */
|
||||
@ -3458,7 +3490,8 @@ protected:
|
||||
bool formatted,
|
||||
enum sarif_version version)
|
||||
: diagnostic_output_format (context),
|
||||
m_builder (context, line_maps, main_input_filename_, formatted, version),
|
||||
m_builder (context, *get_printer (), line_maps, main_input_filename_,
|
||||
formatted, version),
|
||||
m_buffer (nullptr)
|
||||
{}
|
||||
|
||||
@ -3651,15 +3684,8 @@ static void
|
||||
diagnostic_output_format_init_sarif (diagnostic_context &context,
|
||||
std::unique_ptr<sarif_output_format> fmt)
|
||||
{
|
||||
/* Suppress normal textual path output. */
|
||||
context.set_path_format (DPF_NONE);
|
||||
fmt->update_printer ();
|
||||
|
||||
/* Don't colorize the text. */
|
||||
pp_show_color (fmt->get_printer ()) = false;
|
||||
context.set_show_highlight_colors (false);
|
||||
|
||||
context.m_printer->set_token_printer
|
||||
(&fmt->get_builder ().get_token_printer ());
|
||||
context.set_output_format (std::move (fmt));
|
||||
}
|
||||
|
||||
@ -3683,26 +3709,23 @@ diagnostic_output_format_init_sarif_stderr (diagnostic_context &context,
|
||||
stderr));
|
||||
}
|
||||
|
||||
/* Populate CONTEXT in preparation for SARIF output to a file named
|
||||
BASE_FILE_NAME.sarif. */
|
||||
/* Attempt to open BASE_FILE_NAME.sarif for writing.
|
||||
Return a non-null diagnostic_output_file,
|
||||
or return a null diagnostic_output_file and complain to CONTEXT
|
||||
using LINE_MAPS. */
|
||||
|
||||
void
|
||||
diagnostic_output_format_init_sarif_file (diagnostic_context &context,
|
||||
diagnostic_output_file
|
||||
diagnostic_output_format_open_sarif_file (diagnostic_context &context,
|
||||
line_maps *line_maps,
|
||||
const char *main_input_filename_,
|
||||
bool formatted,
|
||||
enum sarif_version version,
|
||||
const char *base_file_name)
|
||||
{
|
||||
gcc_assert (line_maps);
|
||||
|
||||
if (!base_file_name)
|
||||
{
|
||||
rich_location richloc (line_maps, UNKNOWN_LOCATION);
|
||||
context.emit_diagnostic_with_group
|
||||
(DK_ERROR, richloc, nullptr, 0,
|
||||
"unable to determine filename for SARIF output");
|
||||
return;
|
||||
return diagnostic_output_file ();
|
||||
}
|
||||
|
||||
label_text filename = label_text::take (concat (base_file_name,
|
||||
@ -3716,9 +3739,29 @@ diagnostic_output_format_init_sarif_file (diagnostic_context &context,
|
||||
(DK_ERROR, richloc, nullptr, 0,
|
||||
"unable to open %qs for SARIF output: %m",
|
||||
filename.get ());
|
||||
return;
|
||||
return diagnostic_output_file ();
|
||||
}
|
||||
diagnostic_output_file output_file (outf, true, std::move (filename));
|
||||
return diagnostic_output_file (outf, true, std::move (filename));
|
||||
}
|
||||
|
||||
/* Populate CONTEXT in preparation for SARIF output to a file named
|
||||
BASE_FILE_NAME.sarif. */
|
||||
|
||||
void
|
||||
diagnostic_output_format_init_sarif_file (diagnostic_context &context,
|
||||
line_maps *line_maps,
|
||||
const char *main_input_filename_,
|
||||
bool formatted,
|
||||
enum sarif_version version,
|
||||
const char *base_file_name)
|
||||
{
|
||||
gcc_assert (line_maps);
|
||||
|
||||
diagnostic_output_file output_file
|
||||
= diagnostic_output_format_open_sarif_file (context,
|
||||
line_maps,
|
||||
base_file_name);
|
||||
|
||||
diagnostic_output_format_init_sarif
|
||||
(context,
|
||||
::make_unique<sarif_file_output_format> (context,
|
||||
@ -3750,6 +3793,23 @@ diagnostic_output_format_init_sarif_stream (diagnostic_context &context,
|
||||
stream));
|
||||
}
|
||||
|
||||
std::unique_ptr<diagnostic_output_format>
|
||||
make_sarif_sink (diagnostic_context &context,
|
||||
const line_maps &line_maps,
|
||||
const char *main_input_filename_,
|
||||
enum sarif_version version,
|
||||
diagnostic_output_file output_file)
|
||||
{
|
||||
auto sink = ::make_unique<sarif_file_output_format> (context,
|
||||
&line_maps,
|
||||
main_input_filename_,
|
||||
true,
|
||||
version,
|
||||
std::move (output_file));
|
||||
sink->update_printer ();
|
||||
return sink;
|
||||
}
|
||||
|
||||
#if CHECKING_P
|
||||
|
||||
namespace selftest {
|
||||
@ -3822,8 +3882,9 @@ test_make_location_object (const line_table_case &case_,
|
||||
return;
|
||||
|
||||
test_diagnostic_context dc;
|
||||
|
||||
sarif_builder builder (dc, line_table, "MAIN_INPUT_FILENAME", true, version);
|
||||
pretty_printer pp;
|
||||
sarif_builder builder (dc, pp, line_table, "MAIN_INPUT_FILENAME",
|
||||
true, version);
|
||||
|
||||
/* These "columns" are byte offsets, whereas later on the columns
|
||||
in the generated SARIF use sarif_builder::get_sarif_column and
|
||||
|
@ -35,6 +35,11 @@ enum class sarif_version
|
||||
num_versions
|
||||
};
|
||||
|
||||
extern diagnostic_output_file
|
||||
diagnostic_output_format_open_sarif_file (diagnostic_context &context,
|
||||
line_maps *line_maps,
|
||||
const char *base_file_name);
|
||||
|
||||
extern void
|
||||
diagnostic_output_format_init_sarif_stderr (diagnostic_context &context,
|
||||
const line_maps *line_maps,
|
||||
@ -55,6 +60,12 @@ diagnostic_output_format_init_sarif_stream (diagnostic_context &context,
|
||||
bool formatted,
|
||||
enum sarif_version version,
|
||||
FILE *stream);
|
||||
extern std::unique_ptr<diagnostic_output_format>
|
||||
make_sarif_sink (diagnostic_context &context,
|
||||
const line_maps &line_maps,
|
||||
const char *main_input_filename_,
|
||||
enum sarif_version version,
|
||||
diagnostic_output_file output_file);
|
||||
|
||||
/* Concrete subclass of json::object for SARIF property bags
|
||||
(SARIF v2.1.0 section 3.8). */
|
||||
|
@ -160,6 +160,9 @@ void
|
||||
diagnostic_text_output_format::dump (FILE *out, int indent) const
|
||||
{
|
||||
fprintf (out, "%*sdiagnostic_text_output_format\n", indent, "");
|
||||
fprintf (out, "%*sm_follows_reference_printer: %s\n",
|
||||
indent, "",
|
||||
m_follows_reference_printer ? "true" : "false");
|
||||
diagnostic_output_format::dump (out, indent);
|
||||
fprintf (out, "%*ssaved_output_buffer:\n", indent + 2, "");
|
||||
if (m_saved_output_buffer)
|
||||
@ -222,6 +225,13 @@ on_report_diagnostic (const diagnostic_info &diagnostic,
|
||||
orig_diag_kind);
|
||||
}
|
||||
|
||||
void
|
||||
diagnostic_text_output_format::on_report_verbatim (text_info &text)
|
||||
{
|
||||
pp_format_verbatim (get_printer (), &text);
|
||||
pp_newline_and_flush (get_printer ());
|
||||
}
|
||||
|
||||
void
|
||||
diagnostic_text_output_format::on_diagram (const diagnostic_diagram &diagram)
|
||||
{
|
||||
@ -315,6 +325,30 @@ diagnostic_text_output_format::append_note (location_t location,
|
||||
va_end (ap);
|
||||
}
|
||||
|
||||
bool
|
||||
diagnostic_text_output_format::follows_reference_printer_p () const
|
||||
{
|
||||
return m_follows_reference_printer;
|
||||
}
|
||||
|
||||
void
|
||||
diagnostic_text_output_format::
|
||||
update_printer ()
|
||||
{
|
||||
pretty_printer *copy_from_pp
|
||||
= (m_follows_reference_printer
|
||||
? get_context ().get_reference_printer ()
|
||||
: m_printer.get ());
|
||||
const bool show_color = pp_show_color (copy_from_pp);
|
||||
const diagnostic_url_format url_format = copy_from_pp->get_url_format ();
|
||||
|
||||
m_printer = get_context ().clone_printer ();
|
||||
|
||||
pp_show_color (m_printer.get ()) = show_color;
|
||||
m_printer->set_url_format (url_format);
|
||||
// ...etc
|
||||
}
|
||||
|
||||
/* If DIAGNOSTIC has a CWE identifier, print it.
|
||||
|
||||
For example, if the diagnostic metadata associates it with CWE-119,
|
||||
|
@ -32,12 +32,14 @@ along with GCC; see the file COPYING3. If not see
|
||||
class diagnostic_text_output_format : public diagnostic_output_format
|
||||
{
|
||||
public:
|
||||
diagnostic_text_output_format (diagnostic_context &context)
|
||||
diagnostic_text_output_format (diagnostic_context &context,
|
||||
bool follows_reference_printer = false)
|
||||
: diagnostic_output_format (context),
|
||||
m_saved_output_buffer (nullptr),
|
||||
m_column_policy (context),
|
||||
m_last_module (nullptr),
|
||||
m_includes_seen (nullptr)
|
||||
m_includes_seen (nullptr),
|
||||
m_follows_reference_printer (follows_reference_printer)
|
||||
{}
|
||||
~diagnostic_text_output_format ();
|
||||
|
||||
@ -51,12 +53,16 @@ public:
|
||||
void on_end_group () override {}
|
||||
void on_report_diagnostic (const diagnostic_info &,
|
||||
diagnostic_t orig_diag_kind) override;
|
||||
void on_report_verbatim (text_info &) final override;
|
||||
void on_diagram (const diagnostic_diagram &diagram) override;
|
||||
void after_diagnostic (const diagnostic_info &) final override;
|
||||
void after_diagnostic (const diagnostic_info &) override;
|
||||
bool machine_readable_stderr_p () const final override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bool follows_reference_printer_p () const final override;
|
||||
|
||||
void update_printer () override;
|
||||
|
||||
/* Helpers for writing lang-specific starters/finalizers for text output. */
|
||||
char *build_prefix (const diagnostic_info &) const;
|
||||
@ -77,7 +83,7 @@ public:
|
||||
}
|
||||
diagnostic_location_print_policy get_location_print_policy () const;
|
||||
|
||||
private:
|
||||
protected:
|
||||
void print_any_cwe (const diagnostic_info &diagnostic);
|
||||
void print_any_rules (const diagnostic_info &diagnostic);
|
||||
void print_option_information (const diagnostic_info &diagnostic,
|
||||
@ -98,6 +104,14 @@ private:
|
||||
/* Include files that report_current_module has already listed the
|
||||
include path for. */
|
||||
hash_set<location_t, false, location_hash> *m_includes_seen;
|
||||
|
||||
/* If true, this is the initial default text output format created
|
||||
when the diagnostic_context was created, and, in particular, before
|
||||
initializations of color and m_url_format. Hence this should follow
|
||||
the dc's reference printer for these.
|
||||
If false, this text output was created after the dc was created, and
|
||||
thus tracks its own values for color and m_url_format. */
|
||||
bool m_follows_reference_printer;
|
||||
};
|
||||
|
||||
#endif /* ! GCC_DIAGNOSTIC_FORMAT_TEXT_H */
|
||||
|
@ -55,12 +55,21 @@ public:
|
||||
virtual void on_report_diagnostic (const diagnostic_info &,
|
||||
diagnostic_t orig_diag_kind) = 0;
|
||||
|
||||
virtual void on_report_verbatim (text_info &);
|
||||
|
||||
virtual void on_diagram (const diagnostic_diagram &diagram) = 0;
|
||||
virtual void after_diagnostic (const diagnostic_info &) = 0;
|
||||
virtual bool machine_readable_stderr_p () const = 0;
|
||||
virtual bool follows_reference_printer_p () const = 0;
|
||||
|
||||
/* Vfunc called when the diagnostic_context changes its
|
||||
reference printer (either to a new subclass of pretty_printer
|
||||
or when color/url options change).
|
||||
Subclasses should update their m_printer accordingly. */
|
||||
virtual void update_printer () = 0;
|
||||
|
||||
diagnostic_context &get_context () const { return m_context; }
|
||||
pretty_printer *get_printer () const { return m_context.m_printer; }
|
||||
pretty_printer *get_printer () const { return m_printer.get (); }
|
||||
|
||||
text_art::theme *get_diagram_theme () const
|
||||
{
|
||||
@ -71,10 +80,13 @@ public:
|
||||
|
||||
protected:
|
||||
diagnostic_output_format (diagnostic_context &context)
|
||||
: m_context (context)
|
||||
: m_context (context),
|
||||
m_printer (context.clone_printer ())
|
||||
{}
|
||||
|
||||
protected:
|
||||
diagnostic_context &m_context;
|
||||
std::unique_ptr<pretty_printer> m_printer;
|
||||
};
|
||||
|
||||
extern void
|
||||
|
@ -37,7 +37,8 @@ diagnostic_context *global_dc = &global_diagnostic_context;
|
||||
/* Standard error reporting routines in increasing order of severity. */
|
||||
|
||||
/* Text to be emitted verbatim to the error message stream; this
|
||||
produces no prefix and disables line-wrapping. Use rarely. */
|
||||
produces no prefix and disables line-wrapping. Use rarely.
|
||||
It is ignored for machine-readable output formats. */
|
||||
void
|
||||
verbatim (const char *gmsgid, ...)
|
||||
{
|
||||
@ -45,8 +46,7 @@ verbatim (const char *gmsgid, ...)
|
||||
|
||||
va_start (ap, gmsgid);
|
||||
text_info text (_(gmsgid), &ap, errno);
|
||||
pp_format_verbatim (global_dc->m_printer, &text);
|
||||
pp_newline_and_flush (global_dc->m_printer);
|
||||
global_dc->report_verbatim (text);
|
||||
va_end (ap);
|
||||
}
|
||||
|
||||
@ -551,9 +551,7 @@ fnotice (FILE *file, const char *cmsgid, ...)
|
||||
emitting free-form text on stderr will lead to corrupt output.
|
||||
Skip the message for such cases. */
|
||||
if (file == stderr && global_dc)
|
||||
if (const diagnostic_output_format *output_format
|
||||
= global_dc->get_output_format ())
|
||||
if (output_format->machine_readable_stderr_p ())
|
||||
if (!global_dc->supports_fnotice_on_stderr_p ())
|
||||
return;
|
||||
|
||||
va_list ap;
|
||||
|
@ -27,6 +27,12 @@ along with GCC; see the file COPYING3. If not see
|
||||
class diagnostic_output_file
|
||||
{
|
||||
public:
|
||||
diagnostic_output_file ()
|
||||
: m_outf (nullptr),
|
||||
m_owned (false),
|
||||
m_filename ()
|
||||
{
|
||||
}
|
||||
diagnostic_output_file (FILE *outf, bool owned, label_text filename)
|
||||
: m_outf (outf),
|
||||
m_owned (owned),
|
||||
@ -60,7 +66,26 @@ public:
|
||||
diagnostic_output_file &
|
||||
operator= (const diagnostic_output_file &other) = delete;
|
||||
diagnostic_output_file &
|
||||
operator= (diagnostic_output_file &&other) = delete;
|
||||
operator= (diagnostic_output_file &&other)
|
||||
{
|
||||
if (m_owned)
|
||||
{
|
||||
gcc_assert (m_outf);
|
||||
fclose (m_outf);
|
||||
}
|
||||
|
||||
m_outf = other.m_outf;
|
||||
other.m_outf = nullptr;
|
||||
|
||||
m_owned = other.m_owned;
|
||||
other.m_owned = false;
|
||||
|
||||
m_filename = std::move (other.m_filename);
|
||||
|
||||
if (m_owned)
|
||||
gcc_assert (m_outf);
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator bool () const { return m_outf != nullptr; }
|
||||
FILE *get_open_file () const { return m_outf; }
|
||||
|
@ -1297,7 +1297,7 @@ test_interprocedural_path_1 (pretty_printer *event_pp)
|
||||
|
||||
{
|
||||
test_diagnostic_context dc;
|
||||
diagnostic_text_output_format text_output (dc);
|
||||
diagnostic_text_output_format text_output (dc, false);
|
||||
path_print_policy policy (text_output);
|
||||
path_summary summary (policy, path, false);
|
||||
ASSERT_EQ (summary.get_num_ranges (), 9);
|
||||
|
@ -3582,7 +3582,7 @@ test_layout_x_offset_display_utf8 (const line_table_case &case_)
|
||||
linemap_position_for_column (line_table,
|
||||
emoji_col));
|
||||
layout test_layout (policy, richloc, nullptr);
|
||||
layout_printer lp (*dc.m_printer, test_layout, richloc, DK_ERROR);
|
||||
layout_printer lp (*dc.get_reference_printer (), test_layout, richloc, DK_ERROR);
|
||||
lp.print (policy);
|
||||
ASSERT_STREQ (" | 1 \n"
|
||||
" | 1 \n"
|
||||
@ -3591,7 +3591,7 @@ test_layout_x_offset_display_utf8 (const line_table_case &case_)
|
||||
"that occupies 8 bytes and 4 display columns, starting at "
|
||||
"column #102.\n"
|
||||
" | ^\n",
|
||||
pp_formatted_text (dc.m_printer));
|
||||
pp_formatted_text (dc.get_reference_printer ()));
|
||||
}
|
||||
|
||||
/* Similar to the previous example, but now the offset called for would split
|
||||
@ -3609,7 +3609,7 @@ test_layout_x_offset_display_utf8 (const line_table_case &case_)
|
||||
linemap_position_for_column (line_table,
|
||||
emoji_col + 2));
|
||||
layout test_layout (dc, richloc, nullptr);
|
||||
layout_printer lp (*dc.m_printer, test_layout, richloc, DK_ERROR);
|
||||
layout_printer lp (*dc.get_reference_printer (), test_layout, richloc, DK_ERROR);
|
||||
lp.print (policy);
|
||||
ASSERT_STREQ (" | 1 1 \n"
|
||||
" | 1 2 \n"
|
||||
@ -3618,7 +3618,7 @@ test_layout_x_offset_display_utf8 (const line_table_case &case_)
|
||||
"that occupies 8 bytes and 4 display columns, starting at "
|
||||
"column #102.\n"
|
||||
" | ^\n",
|
||||
pp_formatted_text (dc.m_printer));
|
||||
pp_formatted_text (dc.get_reference_printer ()));
|
||||
}
|
||||
|
||||
}
|
||||
@ -3690,9 +3690,9 @@ test_layout_x_offset_display_tab (const line_table_case &case_)
|
||||
dc.m_tabstop = tabstop;
|
||||
diagnostic_source_print_policy policy (dc);
|
||||
layout test_layout (policy, richloc, nullptr);
|
||||
layout_printer lp (*dc.m_printer, test_layout, richloc, DK_ERROR);
|
||||
layout_printer lp (*dc.get_reference_printer (), test_layout, richloc, DK_ERROR);
|
||||
lp.print (policy);
|
||||
const char *out = pp_formatted_text (dc.m_printer);
|
||||
const char *out = pp_formatted_text (dc.get_reference_printer ());
|
||||
ASSERT_EQ (NULL, strchr (out, '\t'));
|
||||
const char *left_quote = strchr (out, '`');
|
||||
const char *right_quote = strchr (out, '\'');
|
||||
@ -3715,7 +3715,7 @@ test_layout_x_offset_display_tab (const line_table_case &case_)
|
||||
dc.m_source_printing.show_line_numbers_p = true;
|
||||
diagnostic_source_print_policy policy (dc);
|
||||
layout test_layout (policy, richloc, nullptr);
|
||||
layout_printer lp (*dc.m_printer, test_layout, richloc, DK_ERROR);
|
||||
layout_printer lp (*dc.get_reference_printer (), test_layout, richloc, DK_ERROR);
|
||||
lp.print (policy);
|
||||
|
||||
/* We have arranged things so that two columns will be printed before
|
||||
@ -3731,7 +3731,7 @@ test_layout_x_offset_display_tab (const line_table_case &case_)
|
||||
"display columns, starting at column #103.\n"
|
||||
" | ^\n";
|
||||
const char *expected_output = (extra_width[tabstop] ? output1 : output2);
|
||||
ASSERT_STREQ (expected_output, pp_formatted_text (dc.m_printer));
|
||||
ASSERT_STREQ (expected_output, pp_formatted_text (dc.get_reference_printer ()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -3887,8 +3887,8 @@ test_one_liner_fixit_remove ()
|
||||
/* Test of adding a prefix. */
|
||||
{
|
||||
test_diagnostic_context dc;
|
||||
pp_prefixing_rule (dc.m_printer) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
|
||||
pp_set_prefix (dc.m_printer, xstrdup ("TEST PREFIX:"));
|
||||
pp_prefixing_rule (dc.get_reference_printer ()) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
|
||||
pp_set_prefix (dc.get_reference_printer (), xstrdup ("TEST PREFIX:"));
|
||||
ASSERT_STREQ ("TEST PREFIX: foo = bar.field;\n"
|
||||
"TEST PREFIX: ^~~~~~\n"
|
||||
"TEST PREFIX: ------\n",
|
||||
@ -3914,8 +3914,8 @@ test_one_liner_fixit_remove ()
|
||||
test_diagnostic_context dc;
|
||||
dc.m_source_printing.show_ruler_p = true;
|
||||
dc.m_source_printing.max_width = 50;
|
||||
pp_prefixing_rule (dc.m_printer) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
|
||||
pp_set_prefix (dc.m_printer, xstrdup ("TEST PREFIX:"));
|
||||
pp_prefixing_rule (dc.get_reference_printer ()) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
|
||||
pp_set_prefix (dc.get_reference_printer (), xstrdup ("TEST PREFIX:"));
|
||||
ASSERT_STREQ ("TEST PREFIX: 1 2 3 4 5\n"
|
||||
"TEST PREFIX: 12345678901234567890123456789012345678901234567890\n"
|
||||
"TEST PREFIX: foo = bar.field;\n"
|
||||
@ -3930,8 +3930,8 @@ test_one_liner_fixit_remove ()
|
||||
dc.m_source_printing.show_ruler_p = true;
|
||||
dc.m_source_printing.max_width = 50;
|
||||
dc.m_source_printing.show_line_numbers_p = true;
|
||||
pp_prefixing_rule (dc.m_printer) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
|
||||
pp_set_prefix (dc.m_printer, xstrdup ("TEST PREFIX:"));
|
||||
pp_prefixing_rule (dc.get_reference_printer ()) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
|
||||
pp_set_prefix (dc.get_reference_printer (), xstrdup ("TEST PREFIX:"));
|
||||
ASSERT_STREQ ("TEST PREFIX: | 1 2 3 4 5\n"
|
||||
"TEST PREFIX: | 12345678901234567890123456789012345678901234567890\n"
|
||||
"TEST PREFIX: 1 | foo = bar.field;\n"
|
||||
|
@ -50,6 +50,7 @@ along with GCC; see the file COPYING3. If not see
|
||||
#include "pretty-print-urlifier.h"
|
||||
#include "logical-location.h"
|
||||
#include "diagnostic-buffer.h"
|
||||
#include "make-unique.h"
|
||||
|
||||
#ifdef HAVE_TERMIOS_H
|
||||
# include <termios.h>
|
||||
@ -118,7 +119,7 @@ diagnostic_set_caret_max_width (diagnostic_context *context, int value)
|
||||
{
|
||||
/* One minus to account for the leading empty space. */
|
||||
value = value ? value - 1
|
||||
: (isatty (fileno (pp_buffer (context->m_printer)->m_stream))
|
||||
: (isatty (fileno (pp_buffer (context->get_reference_printer ())->m_stream))
|
||||
? get_terminal_width () - 1 : INT_MAX);
|
||||
|
||||
if (value <= 0)
|
||||
@ -223,8 +224,7 @@ diagnostic_context::initialize (int n_opts)
|
||||
{
|
||||
/* Allocate a basic pretty-printer. Clients will replace this a
|
||||
much more elaborated pretty-printer if they wish. */
|
||||
m_printer = XNEW (pretty_printer);
|
||||
new (m_printer) pretty_printer ();
|
||||
m_reference_printer = ::make_unique<pretty_printer> ().release ();
|
||||
|
||||
m_file_cache = new file_cache ();
|
||||
m_diagnostic_counters.clear ();
|
||||
@ -232,7 +232,7 @@ diagnostic_context::initialize (int n_opts)
|
||||
m_n_opts = n_opts;
|
||||
m_option_classifier.init (n_opts);
|
||||
m_source_printing.enabled = false;
|
||||
diagnostic_set_caret_max_width (this, pp_line_cutoff (m_printer));
|
||||
diagnostic_set_caret_max_width (this, pp_line_cutoff (get_reference_printer ()));
|
||||
for (int i = 0; i < rich_location::STATICALLY_ALLOCATED_RANGES; i++)
|
||||
m_source_printing.caret_chars[i] = '^';
|
||||
m_show_cwe = false;
|
||||
@ -283,7 +283,7 @@ diagnostic_context::initialize (int n_opts)
|
||||
m_edit_context_ptr = nullptr;
|
||||
m_diagnostic_groups.m_nesting_depth = 0;
|
||||
m_diagnostic_groups.m_emission_count = 0;
|
||||
m_output_format = new diagnostic_text_output_format (*this);
|
||||
m_output_sinks.safe_push (new diagnostic_text_output_format (*this, true));
|
||||
m_set_locations_cb = nullptr;
|
||||
m_client_data_hooks = nullptr;
|
||||
m_diagrams.m_theme = nullptr;
|
||||
@ -326,8 +326,12 @@ diagnostic_context::color_init (int value)
|
||||
else
|
||||
value = DIAGNOSTICS_COLOR_DEFAULT;
|
||||
}
|
||||
pp_show_color (m_printer)
|
||||
pp_show_color (m_reference_printer)
|
||||
= colorize_init ((diagnostic_color_rule_t) value);
|
||||
for (auto sink : m_output_sinks)
|
||||
if (sink->follows_reference_printer_p ())
|
||||
pp_show_color (sink->get_printer ())
|
||||
= pp_show_color (m_reference_printer);
|
||||
}
|
||||
|
||||
/* Initialize URL support within this context based on VALUE,
|
||||
@ -354,8 +358,12 @@ diagnostic_context::urls_init (int value)
|
||||
value = DIAGNOSTICS_URLS_DEFAULT;
|
||||
}
|
||||
|
||||
m_printer->set_url_format
|
||||
m_reference_printer->set_url_format
|
||||
(determine_url_format ((diagnostic_url_rule_t) value));
|
||||
for (auto sink : m_output_sinks)
|
||||
if (sink->follows_reference_printer_p ())
|
||||
sink->get_printer ()->set_url_format
|
||||
(m_reference_printer->get_url_format ());
|
||||
}
|
||||
|
||||
/* Create the file_cache, if not already created, and tell it how to
|
||||
@ -375,7 +383,7 @@ diagnostic_context::finish ()
|
||||
{
|
||||
/* We might be handling a fatal error.
|
||||
Close any active diagnostic groups, which may trigger flushing
|
||||
the output format. */
|
||||
sinks. */
|
||||
while (m_diagnostic_groups.m_nesting_depth > 0)
|
||||
end_group ();
|
||||
|
||||
@ -383,8 +391,8 @@ diagnostic_context::finish ()
|
||||
|
||||
/* Clean ups. */
|
||||
|
||||
delete m_output_format;
|
||||
m_output_format= nullptr;
|
||||
while (!m_output_sinks.is_empty ())
|
||||
delete m_output_sinks.pop ();
|
||||
|
||||
if (m_diagrams.m_theme)
|
||||
{
|
||||
@ -397,11 +405,8 @@ diagnostic_context::finish ()
|
||||
|
||||
m_option_classifier.fini ();
|
||||
|
||||
/* diagnostic_context::initialize allocates this->printer using XNEW
|
||||
and placement-new. */
|
||||
m_printer->~pretty_printer ();
|
||||
XDELETE (m_printer);
|
||||
m_printer = nullptr;
|
||||
delete m_reference_printer;
|
||||
m_reference_printer = nullptr;
|
||||
|
||||
if (m_edit_context_ptr)
|
||||
{
|
||||
@ -429,10 +434,13 @@ diagnostic_context::dump (FILE *out) const
|
||||
{
|
||||
fprintf (out, "diagnostic_context:\n");
|
||||
m_diagnostic_counters.dump (out, 2);
|
||||
fprintf (out, " output format:\n");
|
||||
m_output_format->dump (out, 4);
|
||||
fprintf (out, " printer:\n");
|
||||
m_printer->dump (out, 4);
|
||||
fprintf (out, " reference printer:\n");
|
||||
m_reference_printer->dump (out, 4);
|
||||
for (unsigned i = 0; i < m_output_sinks.length (); ++i)
|
||||
{
|
||||
fprintf (out, " sink %i:\n", i);
|
||||
m_output_sinks[i]->dump (out, 4);
|
||||
}
|
||||
fprintf (out, " diagnostic buffer:\n");
|
||||
if (m_diagnostic_buffer)
|
||||
m_diagnostic_buffer->dump (out, 4);
|
||||
@ -457,9 +465,34 @@ void
|
||||
diagnostic_context::
|
||||
set_output_format (std::unique_ptr<diagnostic_output_format> output_format)
|
||||
{
|
||||
delete m_output_format;
|
||||
/* Ideally this field would be a std::unique_ptr. */
|
||||
m_output_format = output_format.release ();
|
||||
while (!m_output_sinks.is_empty ())
|
||||
delete m_output_sinks.pop ();
|
||||
m_output_sinks.safe_push (output_format.release ());
|
||||
}
|
||||
|
||||
diagnostic_output_format &
|
||||
diagnostic_context::get_output_format (size_t idx) const
|
||||
{
|
||||
gcc_assert (idx < m_output_sinks.length ());
|
||||
gcc_assert (m_output_sinks[idx]);
|
||||
return *m_output_sinks[idx];
|
||||
}
|
||||
|
||||
void
|
||||
diagnostic_context::add_sink (std::unique_ptr<diagnostic_output_format> sink)
|
||||
{
|
||||
m_output_sinks.safe_push (sink.release ());
|
||||
}
|
||||
|
||||
/* Return true if there are no machine-readable formats writing to stderr. */
|
||||
|
||||
bool
|
||||
diagnostic_context::supports_fnotice_on_stderr_p () const
|
||||
{
|
||||
for (auto sink : m_output_sinks)
|
||||
if (sink->machine_readable_stderr_p ())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
@ -502,6 +535,55 @@ diagnostic_context::set_urlifier (std::unique_ptr<urlifier> urlifier)
|
||||
m_urlifier = urlifier.release ();
|
||||
}
|
||||
|
||||
/* Set PP as the reference printer for this context.
|
||||
Refresh all output sinks. */
|
||||
|
||||
void
|
||||
diagnostic_context::set_pretty_printer (std::unique_ptr<pretty_printer> pp)
|
||||
{
|
||||
delete m_reference_printer;
|
||||
m_reference_printer = pp.release ();
|
||||
refresh_output_sinks ();
|
||||
}
|
||||
|
||||
/* Give all output sinks a chance to rebuild their pretty_printer. */
|
||||
|
||||
void
|
||||
diagnostic_context::refresh_output_sinks ()
|
||||
{
|
||||
for (auto sink : m_output_sinks)
|
||||
sink->update_printer ();
|
||||
}
|
||||
|
||||
/* Set FORMAT_DECODER on the reference printer and on the pretty_printer
|
||||
of all output sinks. */
|
||||
|
||||
void
|
||||
diagnostic_context::set_format_decoder (printer_fn format_decoder)
|
||||
{
|
||||
pp_format_decoder (m_reference_printer) = format_decoder;
|
||||
for (auto sink : m_output_sinks)
|
||||
pp_format_decoder (sink->get_printer ()) = format_decoder;
|
||||
}
|
||||
|
||||
void
|
||||
diagnostic_context::set_show_highlight_colors (bool val)
|
||||
{
|
||||
pp_show_highlight_colors (m_reference_printer) = val;
|
||||
for (auto sink : m_output_sinks)
|
||||
if (sink->follows_reference_printer_p ())
|
||||
pp_show_highlight_colors (sink->get_printer ()) = val;
|
||||
}
|
||||
|
||||
void
|
||||
diagnostic_context::set_prefixing_rule (diagnostic_prefixing_rule_t rule)
|
||||
{
|
||||
pp_prefixing_rule (m_reference_printer) = rule;
|
||||
for (auto sink : m_output_sinks)
|
||||
if (sink->follows_reference_printer_p ())
|
||||
pp_prefixing_rule (sink->get_printer ()) = rule;
|
||||
}
|
||||
|
||||
void
|
||||
diagnostic_context::create_edit_context ()
|
||||
{
|
||||
@ -1234,8 +1316,6 @@ diagnostic_context::report_diagnostic (diagnostic_info *diagnostic)
|
||||
{
|
||||
diagnostic_t orig_diag_kind = diagnostic->kind;
|
||||
|
||||
gcc_assert (m_output_format);
|
||||
|
||||
/* Every call to report_diagnostic should be within a
|
||||
begin_group/end_group pair so that output formats can reliably
|
||||
flush diagnostics with on_end_group when the topmost group is ended. */
|
||||
@ -1269,7 +1349,7 @@ diagnostic_context::report_diagnostic (diagnostic_info *diagnostic)
|
||||
through. Don't do this more than once. */
|
||||
if ((diagnostic->kind == DK_ICE || diagnostic->kind == DK_ICE_NOBT)
|
||||
&& m_lock == 1)
|
||||
pp_newline_and_flush (m_printer);
|
||||
pp_newline_and_flush (m_reference_printer);
|
||||
else
|
||||
error_recursion ();
|
||||
}
|
||||
@ -1341,20 +1421,38 @@ diagnostic_context::report_diagnostic (diagnostic_info *diagnostic)
|
||||
|
||||
/* Is this the initial diagnostic within the stack of groups? */
|
||||
if (m_diagnostic_groups.m_emission_count == 0)
|
||||
m_output_format->on_begin_group ();
|
||||
for (auto sink : m_output_sinks)
|
||||
sink->on_begin_group ();
|
||||
m_diagnostic_groups.m_emission_count++;
|
||||
|
||||
/* Run phases 1 and 2 of formatting the message.
|
||||
In particular, some format codes may have side-effects here which need to
|
||||
happen before sending the diagnostic to the output format.
|
||||
va_list *orig_args = diagnostic->message.m_args_ptr;
|
||||
for (auto sink : m_output_sinks)
|
||||
{
|
||||
/* Formatting the message is done per-output-format,
|
||||
so that each output format gets its own set of pp_token_lists
|
||||
to work with.
|
||||
|
||||
Run phases 1 and 2 of formatting the message before calling
|
||||
the format's on_report_diagnostic.
|
||||
In particular, some format codes may have side-effects here which
|
||||
need to happen before sending the diagnostic to the output format.
|
||||
For example, Fortran's %C and %L formatting codes populate the
|
||||
rich_location. */
|
||||
pp_format (m_printer, &diagnostic->message);
|
||||
rich_location.
|
||||
Such side-effects must be idempotent, since they are run per
|
||||
output-format.
|
||||
|
||||
Make a duplicate of the varargs for each call to pp_format,
|
||||
so that each has its own set to consume. */
|
||||
va_list copied_args;
|
||||
va_copy (copied_args, *orig_args);
|
||||
diagnostic->message.m_args_ptr = &copied_args;
|
||||
pp_format (sink->get_printer (), &diagnostic->message);
|
||||
va_end (copied_args);
|
||||
|
||||
/* Call vfunc in the output format. This is responsible for
|
||||
phase 3 of formatting, and for printing the result. */
|
||||
m_output_format->on_report_diagnostic (*diagnostic, orig_diag_kind);
|
||||
sink->on_report_diagnostic (*diagnostic, orig_diag_kind);
|
||||
}
|
||||
|
||||
switch (m_extra_output_kind)
|
||||
{
|
||||
@ -1362,17 +1460,17 @@ diagnostic_context::report_diagnostic (diagnostic_info *diagnostic)
|
||||
break;
|
||||
case EXTRA_DIAGNOSTIC_OUTPUT_fixits_v1:
|
||||
print_parseable_fixits (get_file_cache (),
|
||||
m_printer, diagnostic->richloc,
|
||||
m_reference_printer, diagnostic->richloc,
|
||||
DIAGNOSTICS_COLUMN_UNIT_BYTE,
|
||||
m_tabstop);
|
||||
pp_flush (m_printer);
|
||||
pp_flush (m_reference_printer);
|
||||
break;
|
||||
case EXTRA_DIAGNOSTIC_OUTPUT_fixits_v2:
|
||||
print_parseable_fixits (get_file_cache (),
|
||||
m_printer, diagnostic->richloc,
|
||||
m_reference_printer, diagnostic->richloc,
|
||||
DIAGNOSTICS_COLUMN_UNIT_DISPLAY,
|
||||
m_tabstop);
|
||||
pp_flush (m_printer);
|
||||
pp_flush (m_reference_printer);
|
||||
break;
|
||||
}
|
||||
if (m_diagnostic_buffer == nullptr
|
||||
@ -1389,11 +1487,26 @@ diagnostic_context::report_diagnostic (diagnostic_info *diagnostic)
|
||||
m_lock--;
|
||||
|
||||
if (!m_diagnostic_buffer)
|
||||
m_output_format->after_diagnostic (*diagnostic);
|
||||
for (auto sink : m_output_sinks)
|
||||
sink->after_diagnostic (*diagnostic);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
diagnostic_context::report_verbatim (text_info &text)
|
||||
{
|
||||
va_list *orig_args = text.m_args_ptr;
|
||||
for (auto sink : m_output_sinks)
|
||||
{
|
||||
va_list copied_args;
|
||||
va_copy (copied_args, *orig_args);
|
||||
text.m_args_ptr = &copied_args;
|
||||
sink->on_report_verbatim (text);
|
||||
va_end (copied_args);
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the number of digits in the decimal representation of VALUE. */
|
||||
|
||||
int
|
||||
@ -1511,8 +1624,8 @@ diagnostic_context::emit_diagram (const diagnostic_diagram &diagram)
|
||||
if (m_diagrams.m_theme == nullptr)
|
||||
return;
|
||||
|
||||
gcc_assert (m_output_format);
|
||||
m_output_format->on_diagram (diagram);
|
||||
for (auto sink : m_output_sinks)
|
||||
sink->on_diagram (diagram);
|
||||
}
|
||||
|
||||
/* Inform the user that an error occurred while trying to report some
|
||||
@ -1524,7 +1637,7 @@ void
|
||||
diagnostic_context::error_recursion ()
|
||||
{
|
||||
if (m_lock < 3)
|
||||
pp_newline_and_flush (m_printer);
|
||||
pp_newline_and_flush (m_reference_printer);
|
||||
|
||||
fnotice (stderr,
|
||||
"internal compiler error: error reporting routines re-entered.\n");
|
||||
@ -1554,7 +1667,7 @@ fancy_abort (const char *file, int line, const char *function)
|
||||
initialized yet, or might be in use by another thread).
|
||||
Handle such cases as gracefully as possible by falling back to a
|
||||
minimal abort handler that only relies on i18n. */
|
||||
if (global_dc->m_printer == nullptr)
|
||||
if (global_dc->get_reference_printer () == nullptr)
|
||||
{
|
||||
/* Print the error message. */
|
||||
fnotice (stderr, diagnostic_kind_text[DK_ICE]);
|
||||
@ -1597,15 +1710,23 @@ diagnostic_context::end_group ()
|
||||
If any diagnostics were emitted, give the context a chance
|
||||
to do something. */
|
||||
if (m_diagnostic_groups.m_emission_count > 0)
|
||||
m_output_format->on_end_group ();
|
||||
for (auto sink : m_output_sinks)
|
||||
sink->on_end_group ();
|
||||
m_diagnostic_groups.m_emission_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
diagnostic_output_format::dump (FILE *, int) const
|
||||
diagnostic_output_format::dump (FILE *out, int indent) const
|
||||
{
|
||||
/* No-op for now. */
|
||||
fprintf (out, "%*sprinter:\n", indent, "");
|
||||
m_printer->dump (out, indent + 2);
|
||||
}
|
||||
|
||||
void
|
||||
diagnostic_output_format::on_report_verbatim (text_info &)
|
||||
{
|
||||
/* No-op. */
|
||||
}
|
||||
|
||||
/* Set the output format for CONTEXT to FORMAT, using BASE_FILE_NAME for
|
||||
@ -1653,15 +1774,6 @@ diagnostic_output_format_init (diagnostic_context &context,
|
||||
sarif_version::v2_1_0,
|
||||
base_file_name);
|
||||
break;
|
||||
case DIAGNOSTICS_OUTPUT_FORMAT_SARIF_FILE_2_2_PRERELEASE:
|
||||
diagnostic_output_format_init_sarif_file
|
||||
(context,
|
||||
line_table,
|
||||
main_input_filename_,
|
||||
json_formatting,
|
||||
sarif_version::v2_2_prerelease_2024_08_08,
|
||||
base_file_name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1713,15 +1825,22 @@ diagnostic_context::set_diagnostic_buffer (diagnostic_buffer *buffer)
|
||||
|
||||
m_diagnostic_buffer = buffer;
|
||||
|
||||
gcc_assert (m_output_format);
|
||||
if (buffer)
|
||||
{
|
||||
buffer->ensure_per_format_buffer ();
|
||||
gcc_assert (buffer->m_per_format_buffer);
|
||||
m_output_format->set_buffer (buffer->m_per_format_buffer.get ());
|
||||
buffer->ensure_per_format_buffers ();
|
||||
gcc_assert (buffer->m_per_format_buffers);
|
||||
gcc_assert (buffer->m_per_format_buffers->length ()
|
||||
== m_output_sinks.length ());
|
||||
for (unsigned idx = 0; idx < m_output_sinks.length (); ++idx)
|
||||
{
|
||||
auto sink = m_output_sinks[idx];
|
||||
auto per_format_buffer = (*buffer->m_per_format_buffers)[idx];
|
||||
sink->set_buffer (per_format_buffer);
|
||||
}
|
||||
}
|
||||
else
|
||||
m_output_format->set_buffer (nullptr);
|
||||
for (auto sink : m_output_sinks)
|
||||
sink->set_buffer (nullptr);
|
||||
}
|
||||
|
||||
/* Clear BUFFER without flushing it. */
|
||||
@ -1729,8 +1848,10 @@ diagnostic_context::set_diagnostic_buffer (diagnostic_buffer *buffer)
|
||||
void
|
||||
diagnostic_context::clear_diagnostic_buffer (diagnostic_buffer &buffer)
|
||||
{
|
||||
if (buffer.m_per_format_buffer)
|
||||
buffer.m_per_format_buffer->clear ();
|
||||
if (buffer.m_per_format_buffers)
|
||||
for (auto per_format_buffer : *buffer.m_per_format_buffers)
|
||||
per_format_buffer->clear ();
|
||||
|
||||
buffer.m_diagnostic_counters.clear ();
|
||||
|
||||
/* We need to reset last_location, otherwise we may skip caret lines
|
||||
@ -1746,8 +1867,9 @@ diagnostic_context::flush_diagnostic_buffer (diagnostic_buffer &buffer)
|
||||
bool had_errors
|
||||
= (buffer.m_diagnostic_counters.m_count_for_kind[DK_ERROR] > 0
|
||||
|| buffer.m_diagnostic_counters.m_count_for_kind[DK_WERROR] > 0);
|
||||
if (buffer.m_per_format_buffer)
|
||||
buffer.m_per_format_buffer->flush ();
|
||||
if (buffer.m_per_format_buffers)
|
||||
for (auto per_format_buffer : *buffer.m_per_format_buffers)
|
||||
per_format_buffer->flush ();
|
||||
buffer.m_diagnostic_counters.move_to (m_diagnostic_counters);
|
||||
|
||||
action_after_output (had_errors ? DK_ERROR : DK_WARNING);
|
||||
@ -1796,17 +1918,29 @@ diagnostic_counters::clear ()
|
||||
/* class diagnostic_buffer. */
|
||||
|
||||
diagnostic_buffer::diagnostic_buffer (diagnostic_context &ctxt)
|
||||
: m_ctxt (ctxt)
|
||||
: m_ctxt (ctxt),
|
||||
m_per_format_buffers (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
diagnostic_buffer::~diagnostic_buffer ()
|
||||
{
|
||||
if (m_per_format_buffers)
|
||||
{
|
||||
for (auto iter : *m_per_format_buffers)
|
||||
delete iter;
|
||||
delete m_per_format_buffers;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
diagnostic_buffer::dump (FILE *out, int indent) const
|
||||
{
|
||||
fprintf (out, "%*sm_per_format_buffer:\n", indent, "");
|
||||
m_diagnostic_counters.dump (out, indent + 2);
|
||||
if (m_per_format_buffer)
|
||||
m_per_format_buffer->dump (out, indent + 2);
|
||||
fprintf (out, "%*sm_per_format_buffers:\n", indent, "");
|
||||
if (m_per_format_buffers)
|
||||
for (auto per_format_buffer : *m_per_format_buffers)
|
||||
per_format_buffer->dump (out, indent + 2);
|
||||
else
|
||||
fprintf (out, "%*s(none)\n", indent + 2, "");
|
||||
}
|
||||
@ -1814,33 +1948,67 @@ diagnostic_buffer::dump (FILE *out, int indent) const
|
||||
bool
|
||||
diagnostic_buffer::empty_p () const
|
||||
{
|
||||
if (m_per_format_buffer)
|
||||
return m_per_format_buffer->empty_p ();
|
||||
else
|
||||
if (m_per_format_buffers)
|
||||
for (auto per_format_buffer : *m_per_format_buffers)
|
||||
/* Query initial buffer. */
|
||||
return per_format_buffer->empty_p ();
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
diagnostic_buffer::move_to (diagnostic_buffer &dest)
|
||||
{
|
||||
ensure_per_format_buffer ();
|
||||
dest.ensure_per_format_buffer ();
|
||||
m_per_format_buffer->move_to (*dest.m_per_format_buffer);
|
||||
/* Bail if there's nothing to move. */
|
||||
if (!m_per_format_buffers)
|
||||
return;
|
||||
|
||||
m_diagnostic_counters.move_to (dest.m_diagnostic_counters);
|
||||
|
||||
if (!dest.m_per_format_buffers)
|
||||
{
|
||||
/* Optimization for the "move to empty" case:
|
||||
simply move the vec to the dest. */
|
||||
dest.m_per_format_buffers = m_per_format_buffers;
|
||||
m_per_format_buffers = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Lazily get output format to create its own kind of buffer. */
|
||||
dest.ensure_per_format_buffers ();
|
||||
gcc_assert (m_per_format_buffers);
|
||||
gcc_assert (m_per_format_buffers->length ()
|
||||
== m_ctxt.m_output_sinks.length ());
|
||||
gcc_assert (dest.m_per_format_buffers);
|
||||
gcc_assert (dest.m_per_format_buffers->length ()
|
||||
== m_ctxt.m_output_sinks.length ());
|
||||
for (unsigned idx = 0; idx < m_ctxt.m_output_sinks.length (); ++idx)
|
||||
{
|
||||
auto per_format_buffer_src = (*m_per_format_buffers)[idx];
|
||||
auto per_format_buffer_dest = (*dest.m_per_format_buffers)[idx];
|
||||
per_format_buffer_src->move_to (*per_format_buffer_dest);
|
||||
}
|
||||
}
|
||||
|
||||
/* Lazily get the output formats to create their own kind of buffers.
|
||||
We can't change the output sinks on a context once this has been called
|
||||
on any diagnostic_buffer instances for that context, since there's no
|
||||
way to update all diagnostic_buffer instances for that context. */
|
||||
|
||||
void
|
||||
diagnostic_buffer::ensure_per_format_buffer ()
|
||||
diagnostic_buffer::ensure_per_format_buffers ()
|
||||
{
|
||||
if (!m_per_format_buffer)
|
||||
if (!m_per_format_buffers)
|
||||
{
|
||||
gcc_assert (m_ctxt.get_output_format ());
|
||||
m_per_format_buffer
|
||||
= m_ctxt.get_output_format ()->make_per_format_buffer ();
|
||||
m_per_format_buffers = new auto_vec<diagnostic_per_format_buffer *> ();
|
||||
for (unsigned idx = 0; idx < m_ctxt.m_output_sinks.length (); ++idx)
|
||||
{
|
||||
auto sink = m_ctxt.m_output_sinks[idx];
|
||||
auto per_format_buffer = sink->make_per_format_buffer ();
|
||||
m_per_format_buffers->safe_push (per_format_buffer.release ());
|
||||
}
|
||||
gcc_assert (m_per_format_buffer);
|
||||
}
|
||||
gcc_assert (m_per_format_buffers);
|
||||
gcc_assert (m_per_format_buffers->length ()
|
||||
== m_ctxt.m_output_sinks.length ());
|
||||
}
|
||||
|
||||
/* Really call the system 'abort'. This has to go right at the end of
|
||||
|
@ -84,11 +84,7 @@ enum diagnostics_output_format
|
||||
DIAGNOSTICS_OUTPUT_FORMAT_SARIF_STDERR,
|
||||
|
||||
/* SARIF-based output, to a file. */
|
||||
DIAGNOSTICS_OUTPUT_FORMAT_SARIF_FILE,
|
||||
|
||||
/* Undocumented, for use by test suite.
|
||||
SARIF-based output, to a file, using a prerelease of the 2.2 schema. */
|
||||
DIAGNOSTICS_OUTPUT_FORMAT_SARIF_FILE_2_2_PRERELEASE
|
||||
DIAGNOSTICS_OUTPUT_FORMAT_SARIF_FILE
|
||||
};
|
||||
|
||||
/* An enum for controlling how diagnostic_paths should be printed. */
|
||||
@ -494,6 +490,7 @@ public:
|
||||
|
||||
friend class diagnostic_source_print_policy;
|
||||
friend class diagnostic_text_output_format;
|
||||
friend class diagnostic_buffer;
|
||||
|
||||
typedef void (*set_locations_callback_t) (diagnostic_context *,
|
||||
diagnostic_info *);
|
||||
@ -501,6 +498,8 @@ public:
|
||||
void initialize (int n_opts);
|
||||
void color_init (int value);
|
||||
void urls_init (int value);
|
||||
void set_pretty_printer (std::unique_ptr<pretty_printer> pp);
|
||||
void refresh_output_sinks ();
|
||||
|
||||
void finish ();
|
||||
|
||||
@ -548,6 +547,7 @@ public:
|
||||
ATTRIBUTE_GCC_DIAG(6,0);
|
||||
|
||||
bool report_diagnostic (diagnostic_info *);
|
||||
void report_verbatim (text_info &);
|
||||
|
||||
diagnostic_t
|
||||
classify_diagnostic (diagnostic_option_id option_id,
|
||||
@ -576,11 +576,6 @@ public:
|
||||
|
||||
void emit_diagram (const diagnostic_diagram &diagram);
|
||||
|
||||
diagnostic_output_format *get_output_format () const
|
||||
{
|
||||
return m_output_format;
|
||||
}
|
||||
|
||||
/* Various setters for use by option-handling logic. */
|
||||
void set_output_format (std::unique_ptr<diagnostic_output_format> output_format);
|
||||
void set_text_art_charset (enum diagnostic_text_art_charset charset);
|
||||
@ -598,10 +593,7 @@ public:
|
||||
}
|
||||
void set_show_cwe (bool val) { m_show_cwe = val; }
|
||||
void set_show_rules (bool val) { m_show_rules = val; }
|
||||
void set_show_highlight_colors (bool val)
|
||||
{
|
||||
pp_show_highlight_colors (m_printer) = val;
|
||||
}
|
||||
void set_show_highlight_colors (bool val);
|
||||
void set_path_format (enum diagnostic_path_format val)
|
||||
{
|
||||
m_path_format = val;
|
||||
@ -614,12 +606,16 @@ public:
|
||||
m_escape_format = val;
|
||||
}
|
||||
|
||||
void set_format_decoder (printer_fn format_decoder);
|
||||
void set_prefixing_rule (diagnostic_prefixing_rule_t rule);
|
||||
|
||||
/* Various accessors. */
|
||||
bool warning_as_error_requested_p () const
|
||||
{
|
||||
return m_warning_as_error_requested;
|
||||
}
|
||||
bool show_path_depths_p () const { return m_show_path_depths; }
|
||||
diagnostic_output_format &get_output_format (size_t idx) const;
|
||||
enum diagnostic_path_format get_path_format () const { return m_path_format; }
|
||||
enum diagnostics_escape_format get_escape_format () const
|
||||
{
|
||||
@ -719,9 +715,19 @@ public:
|
||||
|
||||
std::unique_ptr<pretty_printer> clone_printer () const
|
||||
{
|
||||
return m_printer->clone ();
|
||||
return m_reference_printer->clone ();
|
||||
}
|
||||
|
||||
pretty_printer *get_reference_printer () const
|
||||
{
|
||||
return m_reference_printer;
|
||||
}
|
||||
|
||||
void
|
||||
add_sink (std::unique_ptr<diagnostic_output_format>);
|
||||
|
||||
bool supports_fnotice_on_stderr_p () const;
|
||||
|
||||
private:
|
||||
void error_recursion () ATTRIBUTE_NORETURN;
|
||||
|
||||
@ -735,13 +741,14 @@ private:
|
||||
/* Data members.
|
||||
Ideally, all of these would be private. */
|
||||
|
||||
public:
|
||||
/* Where most of the diagnostic formatting work is done.
|
||||
private:
|
||||
/* A reference instance of pretty_printer created by the client
|
||||
and owned by the context. Used for cloning when creating/adding
|
||||
output formats.
|
||||
Owned by the context; this would be a std::unique_ptr if
|
||||
diagnostic_context had a proper ctor. */
|
||||
pretty_printer *m_printer;
|
||||
pretty_printer *m_reference_printer;
|
||||
|
||||
private:
|
||||
/* Cache of source code.
|
||||
Owned by the context; this would be a std::unique_ptr if
|
||||
diagnostic_context had a proper ctor. */
|
||||
@ -902,11 +909,12 @@ private:
|
||||
int m_emission_count;
|
||||
} m_diagnostic_groups;
|
||||
|
||||
/* How to output diagnostics (text vs a structured format such as JSON).
|
||||
Must be non-NULL; owned by context.
|
||||
This would be a std::unique_ptr if diagnostic_context had a proper
|
||||
ctor. */
|
||||
diagnostic_output_format *m_output_format;
|
||||
/* The various sinks to which diagnostics are to be outputted
|
||||
(text vs structured formats such as SARIF).
|
||||
The sinks are owned by the context; this would be a
|
||||
std::vector<std::unique_ptr> if diagnostic_context had a
|
||||
proper ctor. */
|
||||
auto_vec<diagnostic_output_format *> m_output_sinks;
|
||||
|
||||
/* Callback to set the locations of call sites along the inlining
|
||||
stack corresponding to a diagnostic location. Needed to traverse
|
||||
@ -982,12 +990,6 @@ diagnostic_text_finalizer (diagnostic_context *context)
|
||||
#define diagnostic_context_auxiliary_data(DC) (DC)->m_client_aux_data
|
||||
#define diagnostic_info_auxiliary_data(DI) (DI)->x_data
|
||||
|
||||
/* Same as pp_format_decoder. Works on 'diagnostic_context *'. */
|
||||
#define diagnostic_format_decoder(DC) pp_format_decoder ((DC)->m_printer)
|
||||
|
||||
/* Same as pp_prefixing_rule. Works on 'diagnostic_context *'. */
|
||||
#define diagnostic_prefixing_rule(DC) pp_prefixing_rule ((DC)->m_printer)
|
||||
|
||||
/* Raise SIGABRT on any diagnostic of severity DK_ERROR or higher. */
|
||||
inline void
|
||||
diagnostic_abort_on_error (diagnostic_context *context)
|
||||
@ -1000,14 +1002,6 @@ diagnostic_abort_on_error (diagnostic_context *context)
|
||||
and similar functions. */
|
||||
extern diagnostic_context *global_dc;
|
||||
|
||||
/* Returns whether the diagnostic framework has been intialized already and is
|
||||
ready for use. */
|
||||
inline bool
|
||||
diagnostic_ready_p ()
|
||||
{
|
||||
return global_dc->m_printer != nullptr;
|
||||
}
|
||||
|
||||
/* The number of errors that have been issued so far. Ideally, these
|
||||
would take a diagnostic_context as an argument. */
|
||||
#define errorcount global_dc->diagnostic_count (DK_ERROR)
|
||||
|
@ -307,6 +307,8 @@ Objective-C and Objective-C++ Dialects}.
|
||||
-fdiagnostics-color=@r{[}auto@r{|}never@r{|}always@r{]}
|
||||
-fdiagnostics-urls=@r{[}auto@r{|}never@r{|}always@r{]}
|
||||
-fdiagnostics-format=@r{[}text@r{|}sarif-stderr@r{|}sarif-file@r{|}json@r{|}json-stderr@r{|}json-file@r{]}
|
||||
-fdiagnostics-add-output=@var{DIAGNOSTICS-OUTPUT-SPEC}
|
||||
-fdiagnostics-set-output=@var{DIAGNOSTICS-OUTPUT-SPEC}
|
||||
-fno-diagnostics-json-formatting
|
||||
-fno-diagnostics-show-option -fno-diagnostics-show-caret
|
||||
-fno-diagnostics-show-event-links
|
||||
@ -5902,6 +5904,10 @@ Select a different format for printing diagnostics.
|
||||
@var{FORMAT} is @samp{text}, @samp{sarif-stderr}, @samp{sarif-file},
|
||||
@samp{json}, @samp{json-stderr}, or @samp{json-file}.
|
||||
|
||||
Using this option replaces any additional ``output sinks'' added by
|
||||
@option{-fdiagnostics-add-output=}, or that set by
|
||||
@option{-fdiagnostics-set-output=}.
|
||||
|
||||
The default is @samp{text}.
|
||||
|
||||
The @samp{sarif-stderr} and @samp{sarif-file} formats both emit
|
||||
@ -5916,6 +5922,97 @@ where the JSON is emitted to. With @samp{json-stderr}, the JSON is emitted
|
||||
to stderr, whereas with @samp{json-file} it is written to
|
||||
@file{@var{source}.gcc.json}.
|
||||
|
||||
@opindex fdiagnostics-add-output
|
||||
@item -fdiagnostics-add-output=@var{DIAGNOSTICS-OUTPUT-SPEC}
|
||||
Add an additional ``output sink'' for emitting diagnostics.
|
||||
|
||||
@var{DIAGNOSTICS-OUTPUT-SPEC} should specify a scheme, optionally followed
|
||||
by @code{:} and one or more @var{KEY}=@var{VALUE} pairs, in this form:
|
||||
|
||||
@smallexample
|
||||
@var{SCHEME}
|
||||
@var{SCHEME}:@var{KEY}=@var{VALUE}
|
||||
@var{SCHEME}:@var{KEY}=@var{VALUE},@var{KEY2}=@var{VALUE2}
|
||||
@end smallexample
|
||||
|
||||
etc.
|
||||
|
||||
@var{SCHEME} can be
|
||||
|
||||
@table @gcctabopt
|
||||
|
||||
@item text
|
||||
Emit diagnostics to stderr using GCC's classic text output format.
|
||||
|
||||
Supported keys are:
|
||||
|
||||
@table @gcctabopt
|
||||
|
||||
@item color=@r{[}yes@r{|}no@r{]}
|
||||
Override colorization settings from @option{-fdiagnostics-color} for this
|
||||
text output.
|
||||
|
||||
@end table
|
||||
|
||||
@item sarif
|
||||
Emit diagnostics to a file in SARIF format.
|
||||
|
||||
Supported keys are:
|
||||
|
||||
@table @gcctabopt
|
||||
|
||||
@item file=@var{FILENAME}
|
||||
Specify the filename to write the SARIF output to, potentially with a
|
||||
leading absolute or relative path. If not specified, it defaults to
|
||||
@file{@var{source}.sarif}.
|
||||
|
||||
@item version=@r{[}2.1@r{|}2.2-prerelease@r{]}
|
||||
Specify the version of SARIF to use for the output. If not specified,
|
||||
defaults to 2.1. @code{2.2-prerelease} uses an unofficial draft of the
|
||||
future SARIF 2.2 specification and should only be used for experimentation
|
||||
in this release.
|
||||
|
||||
@end table
|
||||
|
||||
@end table
|
||||
|
||||
For example,
|
||||
|
||||
@smallexample
|
||||
-fdiagnostics-add-output=sarif:version=2.1,file=foo.2.1.sarif
|
||||
-fdiagnostics-add-output=sarif:version=2.2-prerelease,file=foo.2.2.sarif
|
||||
@end smallexample
|
||||
|
||||
would add a pair of outputs, each writing to a different file, using
|
||||
versions 2.1 and 2.2 of the SARIF standard respectively.
|
||||
|
||||
In EBNF:
|
||||
|
||||
@smallexample
|
||||
|
||||
@var{diagnostics-output-specifier} = @var{diagnostics-output-name}
|
||||
| @var{diagnostics-output-name}, ":", @var{key-value-pairs};
|
||||
|
||||
@var{diagnostics-output-name} = "text" | "sarif";
|
||||
|
||||
@var{key-value-pairs} = @var{key-value-pair}
|
||||
| @var{key-value-pair} "," @var{key-value-pairs};
|
||||
|
||||
@var{key-value-pair} = @var{key} "=" @var{value};
|
||||
|
||||
@var{key} = ? string without a '=' ? ;
|
||||
@var{value} = ? string without a ',' ? ;
|
||||
|
||||
@end smallexample
|
||||
|
||||
@opindex fdiagnostics-set-output
|
||||
@item -fdiagnostics-set-output=@var{DIAGNOSTICS-OUTPUT-SPEC}
|
||||
This works in a similar way to @option{-fdiagnostics-add-output=} except
|
||||
that instead of adding an additional ``output sink'' for diagnostics, it
|
||||
replaces all existing output sinks, such as from @option{-fdiagnostics-format=},
|
||||
@option{-fdiagnostics-add-output=}, or a prior call to
|
||||
@option{-fdiagnostics-set-output=}.
|
||||
|
||||
@opindex fno-diagnostics-json-formatting
|
||||
@opindex fdiagnostics-json-formatting
|
||||
@item -fno-diagnostics-json-formatting
|
||||
|
@ -479,7 +479,7 @@ gfc_diagnostic_build_kind_prefix (diagnostic_context *context,
|
||||
gcc_assert (diagnostic->kind < DK_LAST_DIAGNOSTIC_KIND);
|
||||
const char *text = _(diagnostic_kind_text[diagnostic->kind]);
|
||||
const char *text_cs = "", *text_ce = "";
|
||||
pretty_printer *const pp = context->m_printer;
|
||||
pretty_printer *const pp = context->get_reference_printer ();
|
||||
|
||||
if (diagnostic_kind_color[diagnostic->kind])
|
||||
{
|
||||
@ -951,7 +951,7 @@ gfc_diagnostics_init (void)
|
||||
diagnostic_text_starter (global_dc) = gfc_diagnostic_text_starter;
|
||||
diagnostic_start_span (global_dc) = gfc_diagnostic_start_span;
|
||||
diagnostic_text_finalizer (global_dc) = gfc_diagnostic_text_finalizer;
|
||||
diagnostic_format_decoder (global_dc) = gfc_format_decoder;
|
||||
global_dc->set_format_decoder (gfc_format_decoder);
|
||||
global_dc->m_source_printing.caret_chars[0] = '1';
|
||||
global_dc->m_source_printing.caret_chars[1] = '2';
|
||||
pp_warning_buffer = new diagnostic_buffer (*global_dc);
|
||||
|
@ -50,6 +50,7 @@ compilation is specified by a string called a "spec". */
|
||||
#include "opts-jobserver.h"
|
||||
#include "common/common-target.h"
|
||||
#include "gcc-urlifier.h"
|
||||
#include "opts-diagnostic.h"
|
||||
|
||||
#ifndef MATH_LIBRARY
|
||||
#define MATH_LIBRARY "m"
|
||||
@ -4369,6 +4370,14 @@ driver_handle_option (struct gcc_options *opts,
|
||||
break;
|
||||
}
|
||||
|
||||
case OPT_fdiagnostics_add_output_:
|
||||
handle_OPT_fdiagnostics_add_output_ (*opts, *dc, arg, loc);
|
||||
break;
|
||||
|
||||
case OPT_fdiagnostics_set_output_:
|
||||
handle_OPT_fdiagnostics_set_output_ (*opts, *dc, arg, loc);
|
||||
break;
|
||||
|
||||
case OPT_fdiagnostics_text_art_charset_:
|
||||
dc->set_text_art_charset ((enum diagnostic_text_art_charset)value);
|
||||
break;
|
||||
|
@ -33,6 +33,7 @@ along with GCC; see the file COPYING3. If not see
|
||||
#include "cgraph.h"
|
||||
#include "target.h"
|
||||
#include "diagnostic-format-text.h"
|
||||
#include "make-unique.h"
|
||||
|
||||
#include <mpfr.h>
|
||||
|
||||
@ -981,6 +982,49 @@ struct ggc_root_tab jit_root_tab[] =
|
||||
LAST_GGC_ROOT_TAB
|
||||
};
|
||||
|
||||
/* Subclass of diagnostic_output_format for libgccjit: like text
|
||||
output, but capture the message and call add_diagnostic with it
|
||||
on the active playback context. */
|
||||
|
||||
class jit_diagnostic_listener : public diagnostic_text_output_format
|
||||
{
|
||||
public:
|
||||
jit_diagnostic_listener (diagnostic_context &dc,
|
||||
gcc::jit::playback::context &playback_ctxt)
|
||||
: diagnostic_text_output_format (dc),
|
||||
m_playback_ctxt (playback_ctxt)
|
||||
{
|
||||
}
|
||||
|
||||
void dump (FILE *out, int indent) const final override
|
||||
{
|
||||
fprintf (out, "%*sjit_diagnostic_listener\n", indent, "");
|
||||
fprintf (out, "%*sm_playback_context: %p\n",
|
||||
indent + 2, "",
|
||||
(void *)&m_playback_ctxt);
|
||||
}
|
||||
|
||||
void on_report_diagnostic (const diagnostic_info &info,
|
||||
diagnostic_t orig_diag_kind)
|
||||
{
|
||||
JIT_LOG_SCOPE (gcc::jit::active_playback_ctxt->get_logger ());
|
||||
|
||||
/* Let the text output format do most of the work. */
|
||||
diagnostic_text_output_format::on_report_diagnostic (info, orig_diag_kind);
|
||||
|
||||
const char *text = pp_formatted_text (get_printer ());
|
||||
|
||||
/* Delegate to the playback context (and thence to the
|
||||
recording context). */
|
||||
gcc::jit::active_playback_ctxt->add_diagnostic (text, info);
|
||||
|
||||
pp_clear_output_area (get_printer ());
|
||||
}
|
||||
|
||||
private:
|
||||
gcc::jit::playback::context &m_playback_ctxt;
|
||||
};
|
||||
|
||||
/* JIT-specific implementation of diagnostic callbacks. */
|
||||
|
||||
/* Implementation of "begin_diagnostic". */
|
||||
@ -992,24 +1036,22 @@ jit_begin_diagnostic (diagnostic_text_output_format &,
|
||||
gcc_assert (gcc::jit::active_playback_ctxt);
|
||||
JIT_LOG_SCOPE (gcc::jit::active_playback_ctxt->get_logger ());
|
||||
|
||||
/* No-op (apart from logging); the real error-handling is done in the
|
||||
"end_diagnostic" hook. */
|
||||
/* No-op (apart from logging); the real error-handling is done by the
|
||||
jit_diagnostic_listener. */
|
||||
}
|
||||
|
||||
/* Implementation of "end_diagnostic". */
|
||||
|
||||
static void
|
||||
jit_end_diagnostic (diagnostic_text_output_format &text_output,
|
||||
const diagnostic_info *diagnostic,
|
||||
jit_end_diagnostic (diagnostic_text_output_format &,
|
||||
const diagnostic_info *,
|
||||
diagnostic_t)
|
||||
{
|
||||
gcc_assert (gcc::jit::active_playback_ctxt);
|
||||
JIT_LOG_SCOPE (gcc::jit::active_playback_ctxt->get_logger ());
|
||||
|
||||
/* Delegate to the playback context (and thence to the
|
||||
recording context). */
|
||||
gcc_assert (diagnostic);
|
||||
gcc::jit::active_playback_ctxt->add_diagnostic (&text_output.get_context (), *diagnostic); // FIXME
|
||||
/* No-op (apart from logging); the real error-handling is done by the
|
||||
jit_diagnostic_listener. */
|
||||
}
|
||||
|
||||
/* Language hooks. */
|
||||
@ -1030,6 +1072,10 @@ jit_langhook_init (void)
|
||||
gcc_assert (global_dc);
|
||||
diagnostic_text_starter (global_dc) = jit_begin_diagnostic;
|
||||
diagnostic_text_finalizer (global_dc) = jit_end_diagnostic;
|
||||
auto sink
|
||||
= ::make_unique<jit_diagnostic_listener> (*global_dc,
|
||||
*gcc::jit::active_playback_ctxt);
|
||||
global_dc->set_output_format (std::move (sink));
|
||||
|
||||
build_common_tree_nodes (false);
|
||||
|
||||
|
@ -3687,14 +3687,9 @@ add_error_va (location *loc, const char *fmt, va_list ap)
|
||||
|
||||
void
|
||||
playback::context::
|
||||
add_diagnostic (diagnostic_context *diag_context,
|
||||
add_diagnostic (const char *text,
|
||||
const diagnostic_info &diagnostic)
|
||||
{
|
||||
/* At this point the text has been formatted into the pretty-printer's
|
||||
output buffer. */
|
||||
pretty_printer *pp = diag_context->m_printer;
|
||||
const char *text = pp_formatted_text (pp);
|
||||
|
||||
/* Get location information (if any) from the diagnostic.
|
||||
The recording::context::add_error[_va] methods require a
|
||||
recording::location. We can't lookup the playback::location
|
||||
@ -3714,7 +3709,6 @@ add_diagnostic (diagnostic_context *diag_context,
|
||||
}
|
||||
|
||||
m_recording_ctxt->add_error (rec_loc, "%s", text);
|
||||
pp_clear_output_area (pp);
|
||||
}
|
||||
|
||||
/* Dealing with the linemap API. */
|
||||
|
@ -276,7 +276,7 @@ public:
|
||||
get_first_error () const;
|
||||
|
||||
void
|
||||
add_diagnostic (diagnostic_context *context,
|
||||
add_diagnostic (const char *text,
|
||||
const diagnostic_info &diagnostic);
|
||||
|
||||
void
|
||||
|
680
gcc/opts-diagnostic.cc
Normal file
680
gcc/opts-diagnostic.cc
Normal file
@ -0,0 +1,680 @@
|
||||
/* Support for -fdiagnostics-add-output= and -fdiagnostics-set-output=.
|
||||
Copyright (C) 2024 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GCC.
|
||||
|
||||
GCC is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation; either version 3, or (at your option) any later
|
||||
version.
|
||||
|
||||
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GCC; see the file COPYING3. If not see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
|
||||
/* This file implements the options -fdiagnostics-add-output=,
|
||||
-fdiagnostics-set-output=, and their domain-specific language. */
|
||||
|
||||
#include "config.h"
|
||||
#define INCLUDE_ARRAY
|
||||
#define INCLUDE_MEMORY
|
||||
#define INCLUDE_STRING
|
||||
#define INCLUDE_VECTOR
|
||||
#include "system.h"
|
||||
#include "coretypes.h"
|
||||
#include "version.h"
|
||||
#include "intl.h"
|
||||
#include "diagnostic.h"
|
||||
#include "diagnostic-color.h"
|
||||
#include "diagnostic-format.h"
|
||||
#include "diagnostic-format-text.h"
|
||||
#include "diagnostic-format-sarif.h"
|
||||
#include "selftest.h"
|
||||
#include "selftest-diagnostic.h"
|
||||
#include "pretty-print-markup.h"
|
||||
#include "opts.h"
|
||||
#include "options.h"
|
||||
#include "make-unique.h"
|
||||
|
||||
/* A namespace for handling the DSL of the arguments of
|
||||
-fdiagnostics-add-output= and -fdiagnostics-set-output=. */
|
||||
|
||||
namespace gcc {
|
||||
namespace diagnostics_output_spec {
|
||||
|
||||
/* Decls. */
|
||||
|
||||
struct context
|
||||
{
|
||||
public:
|
||||
context (const gcc_options &opts,
|
||||
diagnostic_context &dc,
|
||||
line_maps *location_mgr,
|
||||
location_t loc,
|
||||
const char *option_name)
|
||||
: m_opts (opts), m_dc (dc), m_location_mgr (location_mgr), m_loc (loc),
|
||||
m_option_name (option_name)
|
||||
{}
|
||||
|
||||
void
|
||||
report_error (const char *gmsgid, ...) const
|
||||
ATTRIBUTE_GCC_DIAG(2,3);
|
||||
|
||||
void
|
||||
report_unknown_key (const char *unparsed_arg,
|
||||
const std::string &key,
|
||||
const std::string &format_name,
|
||||
auto_vec<const char *> &known_keys) const;
|
||||
|
||||
void
|
||||
report_missing_key (const char *unparsed_arg,
|
||||
const std::string &key,
|
||||
const std::string &format_name,
|
||||
const char *metavar) const;
|
||||
|
||||
diagnostic_output_file
|
||||
open_output_file (label_text &&filename) const;
|
||||
|
||||
const gcc_options &m_opts;
|
||||
diagnostic_context &m_dc;
|
||||
line_maps *m_location_mgr;
|
||||
location_t m_loc;
|
||||
const char *m_option_name;
|
||||
};
|
||||
|
||||
struct name_and_params
|
||||
{
|
||||
std::string m_format;
|
||||
std::vector<std::pair<std::string, std::string>> m_kvs;
|
||||
};
|
||||
|
||||
static std::unique_ptr<name_and_params>
|
||||
parse (const context &ctxt, const char *unparsed_arg);
|
||||
|
||||
/* Class for parsing the arguments of -fdiagnostics-add-output= and
|
||||
-fdiagnostics-set-output=, and making diagnostic_output_format
|
||||
instances (or issuing errors). */
|
||||
|
||||
class output_factory
|
||||
{
|
||||
public:
|
||||
class handler
|
||||
{
|
||||
public:
|
||||
handler (std::string name) : m_name (name) {}
|
||||
virtual ~handler () {}
|
||||
|
||||
const std::string &get_name () const { return m_name; }
|
||||
|
||||
virtual std::unique_ptr<diagnostic_output_format>
|
||||
make_sink (const context &ctxt,
|
||||
const char *unparsed_arg,
|
||||
const name_and_params &parsed_arg) const = 0;
|
||||
|
||||
protected:
|
||||
bool
|
||||
parse_bool_value (const context &ctxt,
|
||||
const char *unparsed_arg,
|
||||
const std::string &key,
|
||||
const std::string &value,
|
||||
bool &out) const
|
||||
{
|
||||
if (value == "yes")
|
||||
{
|
||||
out = true;
|
||||
return true;
|
||||
}
|
||||
else if (value == "no")
|
||||
{
|
||||
out = false;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ctxt.report_error
|
||||
("%<%s%s%>:"
|
||||
" unexpected value %qs for key %qs; expected %qs or %qs",
|
||||
ctxt.m_option_name, unparsed_arg,
|
||||
value.c_str (),
|
||||
key.c_str (),
|
||||
"yes", "no");
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
template <typename EnumType, size_t NumValues>
|
||||
bool
|
||||
parse_enum_value (const context &ctxt,
|
||||
const char *unparsed_arg,
|
||||
const std::string &key,
|
||||
const std::string &value,
|
||||
const std::array<std::pair<const char *, EnumType>, NumValues> &value_names,
|
||||
EnumType &out) const
|
||||
{
|
||||
for (auto &iter : value_names)
|
||||
if (value == iter.first)
|
||||
{
|
||||
out = iter.second;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto_vec<const char *> known_values;
|
||||
for (auto iter : value_names)
|
||||
known_values.safe_push (iter.first);
|
||||
pp_markup::comma_separated_quoted_strings e (known_values);
|
||||
ctxt.report_error
|
||||
("%<%s%s%>:"
|
||||
" unexpected value %qs for key %qs; known values: %e",
|
||||
ctxt.m_option_name, unparsed_arg,
|
||||
value.c_str (),
|
||||
key.c_str (),
|
||||
&e);
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
const std::string m_name;
|
||||
};
|
||||
|
||||
output_factory ();
|
||||
|
||||
std::unique_ptr<diagnostic_output_format>
|
||||
make_sink (const context &ctxt,
|
||||
const char *unparsed_arg,
|
||||
const name_and_params &parsed_arg);
|
||||
|
||||
const handler *get_handler (std::string name);
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<handler>> m_handlers;
|
||||
};
|
||||
|
||||
class text_handler : public output_factory::handler
|
||||
{
|
||||
public:
|
||||
text_handler () : handler ("text") {}
|
||||
|
||||
std::unique_ptr<diagnostic_output_format>
|
||||
make_sink (const context &ctxt,
|
||||
const char *unparsed_arg,
|
||||
const name_and_params &parsed_arg) const final override;
|
||||
};
|
||||
|
||||
class sarif_handler : public output_factory::handler
|
||||
{
|
||||
public:
|
||||
sarif_handler () : handler ("sarif") {}
|
||||
|
||||
std::unique_ptr<diagnostic_output_format>
|
||||
make_sink (const context &ctxt,
|
||||
const char *unparsed_arg,
|
||||
const name_and_params &parsed_arg) const final override;
|
||||
};
|
||||
|
||||
/* struct context. */
|
||||
|
||||
void
|
||||
context::report_error (const char *gmsgid, ...) const
|
||||
{
|
||||
m_dc.begin_group ();
|
||||
va_list ap;
|
||||
va_start (ap, gmsgid);
|
||||
rich_location richloc (m_location_mgr, m_loc);
|
||||
m_dc.diagnostic_impl (&richloc, nullptr, -1, gmsgid, &ap, DK_ERROR);
|
||||
va_end (ap);
|
||||
m_dc.end_group ();
|
||||
}
|
||||
|
||||
void
|
||||
context::report_unknown_key (const char *unparsed_arg,
|
||||
const std::string &key,
|
||||
const std::string &format_name,
|
||||
auto_vec<const char *> &known_keys) const
|
||||
{
|
||||
pp_markup::comma_separated_quoted_strings e (known_keys);
|
||||
report_error
|
||||
("%<%s%s%>:"
|
||||
" unknown key %qs for format %qs; known keys: %e",
|
||||
m_option_name, unparsed_arg,
|
||||
key.c_str (), format_name.c_str (), &e);
|
||||
}
|
||||
|
||||
void
|
||||
context::report_missing_key (const char *unparsed_arg,
|
||||
const std::string &key,
|
||||
const std::string &format_name,
|
||||
const char *metavar) const
|
||||
{
|
||||
report_error
|
||||
("%<%s%s%>:"
|
||||
" missing required key %qs for format %qs;"
|
||||
" try %<%s%s:%s=%s%>",
|
||||
m_option_name, unparsed_arg,
|
||||
key.c_str (), format_name.c_str (),
|
||||
m_option_name, format_name.c_str (), key.c_str (), metavar);
|
||||
}
|
||||
|
||||
std::unique_ptr<name_and_params>
|
||||
parse (const context &ctxt, const char *unparsed_arg)
|
||||
{
|
||||
name_and_params result;
|
||||
if (const char *const colon = strchr (unparsed_arg, ':'))
|
||||
{
|
||||
result.m_format = std::string (unparsed_arg, colon - unparsed_arg);
|
||||
/* Expect zero of more of KEY=VALUE,KEY=VALUE, etc .*/
|
||||
const char *iter = colon + 1;
|
||||
const char *last_separator = ":";
|
||||
while (iter)
|
||||
{
|
||||
/* Look for a non-empty key string followed by '='. */
|
||||
const char *eq = strchr (iter, '=');
|
||||
if (eq == nullptr || eq == iter)
|
||||
{
|
||||
/* Missing '='. */
|
||||
ctxt.report_error
|
||||
("%<%s%s%>:"
|
||||
" expected KEY=VALUE-style parameter for format %qs"
|
||||
" after %qs;"
|
||||
" got %qs",
|
||||
ctxt.m_option_name, unparsed_arg,
|
||||
result.m_format.c_str (),
|
||||
last_separator,
|
||||
iter);
|
||||
return nullptr;
|
||||
}
|
||||
std::string key = std::string (iter, eq - iter);
|
||||
std::string value;
|
||||
const char *comma = strchr (iter, ',');
|
||||
if (comma)
|
||||
{
|
||||
value = std::string (eq + 1, comma - (eq + 1));
|
||||
iter = comma + 1;
|
||||
last_separator = ",";
|
||||
}
|
||||
else
|
||||
{
|
||||
value = std::string (eq + 1);
|
||||
iter = nullptr;
|
||||
}
|
||||
result.m_kvs.push_back ({std::move (key), std::move (value)});
|
||||
}
|
||||
}
|
||||
else
|
||||
result.m_format = unparsed_arg;
|
||||
return ::make_unique<name_and_params> (std::move (result));
|
||||
}
|
||||
|
||||
/* class output_factory::handler. */
|
||||
|
||||
/* class output_factory. */
|
||||
|
||||
output_factory::output_factory ()
|
||||
{
|
||||
m_handlers.push_back (::make_unique<text_handler> ());
|
||||
m_handlers.push_back (::make_unique<sarif_handler> ());
|
||||
}
|
||||
|
||||
const output_factory::handler *
|
||||
output_factory::get_handler (std::string name)
|
||||
{
|
||||
for (auto &iter : m_handlers)
|
||||
if (iter->get_name () == name)
|
||||
return iter.get ();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<diagnostic_output_format>
|
||||
output_factory::make_sink (const context &ctxt,
|
||||
const char *unparsed_arg,
|
||||
const name_and_params &parsed_arg)
|
||||
{
|
||||
auto handler = get_handler (parsed_arg.m_format);
|
||||
if (!handler)
|
||||
{
|
||||
auto_vec<const char *> strings;
|
||||
for (auto &iter : m_handlers)
|
||||
strings.safe_push (iter->get_name ().c_str ());
|
||||
pp_markup::comma_separated_quoted_strings e (strings);
|
||||
ctxt.report_error ("%<%s%s%>:"
|
||||
" unrecognized format %qs; known formats: %e",
|
||||
ctxt.m_option_name, unparsed_arg,
|
||||
parsed_arg.m_format.c_str (), &e);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return handler->make_sink (ctxt, unparsed_arg, parsed_arg);
|
||||
}
|
||||
|
||||
/* class text_handler : public output_factory::handler. */
|
||||
|
||||
std::unique_ptr<diagnostic_output_format>
|
||||
text_handler::make_sink (const context &ctxt,
|
||||
const char *unparsed_arg,
|
||||
const name_and_params &parsed_arg) const
|
||||
{
|
||||
bool show_color = pp_show_color (ctxt.m_dc.get_reference_printer ());
|
||||
for (auto& iter : parsed_arg.m_kvs)
|
||||
{
|
||||
const std::string &key = iter.first;
|
||||
const std::string &value = iter.second;
|
||||
if (key == "color")
|
||||
{
|
||||
if (!parse_bool_value (ctxt, unparsed_arg, key, value, show_color))
|
||||
return nullptr;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Key not found. */
|
||||
auto_vec<const char *> known_keys;
|
||||
known_keys.safe_push ("color");
|
||||
ctxt.report_unknown_key (unparsed_arg, key, get_name (), known_keys);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<diagnostic_output_format> sink;
|
||||
sink = ::make_unique<diagnostic_text_output_format> (ctxt.m_dc);
|
||||
return sink;
|
||||
}
|
||||
|
||||
diagnostic_output_file
|
||||
context::open_output_file (label_text &&filename) const
|
||||
{
|
||||
FILE *outf = fopen (filename.get (), "w");
|
||||
if (!outf)
|
||||
{
|
||||
rich_location richloc (m_location_mgr, m_loc);
|
||||
m_dc.emit_diagnostic_with_group
|
||||
(DK_ERROR, richloc, nullptr, 0,
|
||||
"unable to open %qs: %m", filename.get ());
|
||||
return diagnostic_output_file (nullptr, false, std::move (filename));
|
||||
}
|
||||
return diagnostic_output_file (outf, true, std::move (filename));
|
||||
}
|
||||
|
||||
/* class sarif_handler : public output_factory::handler. */
|
||||
|
||||
std::unique_ptr<diagnostic_output_format>
|
||||
sarif_handler::make_sink (const context &ctxt,
|
||||
const char *unparsed_arg,
|
||||
const name_and_params &parsed_arg) const
|
||||
{
|
||||
enum sarif_version version = sarif_version::v2_1_0;
|
||||
label_text filename;
|
||||
for (auto& iter : parsed_arg.m_kvs)
|
||||
{
|
||||
const std::string &key = iter.first;
|
||||
const std::string &value = iter.second;
|
||||
if (key == "version")
|
||||
{
|
||||
static const std::array<std::pair<const char *, enum sarif_version>,
|
||||
(size_t)sarif_version::num_versions> value_names
|
||||
{{{"2.1", sarif_version::v2_1_0},
|
||||
{"2.2-prerelease", sarif_version::v2_2_prerelease_2024_08_08}}};
|
||||
|
||||
if (!parse_enum_value<enum sarif_version> (ctxt, unparsed_arg,
|
||||
key, value,
|
||||
value_names,
|
||||
version))
|
||||
return nullptr;
|
||||
continue;
|
||||
}
|
||||
if (key == "file")
|
||||
{
|
||||
filename = label_text::take (xstrdup (value.c_str ()));
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Key not found. */
|
||||
auto_vec<const char *> known_keys;
|
||||
known_keys.safe_push ("file");
|
||||
known_keys.safe_push ("version");
|
||||
ctxt.report_unknown_key (unparsed_arg, key, get_name (), known_keys);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
diagnostic_output_file output_file;
|
||||
if (filename.get ())
|
||||
output_file = ctxt.open_output_file (std::move (filename));
|
||||
else
|
||||
// Default filename
|
||||
{
|
||||
const char *basename = (ctxt.m_opts.x_dump_base_name
|
||||
? ctxt.m_opts.x_dump_base_name
|
||||
: ctxt.m_opts.x_main_input_basename);
|
||||
output_file = diagnostic_output_format_open_sarif_file (ctxt.m_dc,
|
||||
line_table,
|
||||
basename);
|
||||
}
|
||||
if (!output_file)
|
||||
return nullptr;
|
||||
|
||||
auto sink = make_sarif_sink (ctxt.m_dc,
|
||||
*line_table,
|
||||
ctxt.m_opts.x_main_input_filename,
|
||||
version,
|
||||
std::move (output_file));
|
||||
return sink;
|
||||
}
|
||||
|
||||
} // namespace diagnostics_output_spec
|
||||
} // namespace gcc
|
||||
|
||||
void
|
||||
handle_OPT_fdiagnostics_add_output_ (const gcc_options &opts,
|
||||
diagnostic_context &dc,
|
||||
const char *arg,
|
||||
location_t loc)
|
||||
{
|
||||
gcc_assert (arg);
|
||||
gcc_assert (line_table);
|
||||
|
||||
const char *const option_name = "-fdiagnostics-add-output=";
|
||||
gcc::diagnostics_output_spec::context ctxt (opts, dc, line_table, loc,
|
||||
option_name);
|
||||
auto result = gcc::diagnostics_output_spec::parse (ctxt, arg);
|
||||
if (!result)
|
||||
return;
|
||||
|
||||
gcc::diagnostics_output_spec::output_factory factory;
|
||||
auto sink = factory.make_sink (ctxt, arg, *result);
|
||||
if (!sink)
|
||||
return;
|
||||
|
||||
dc.add_sink (std::move (sink));
|
||||
}
|
||||
|
||||
void
|
||||
handle_OPT_fdiagnostics_set_output_ (const gcc_options &opts,
|
||||
diagnostic_context &dc,
|
||||
const char *arg,
|
||||
location_t loc)
|
||||
{
|
||||
gcc_assert (arg);
|
||||
gcc_assert (line_table);
|
||||
|
||||
const char *const option_name = "-fdiagnostics-set-output=";
|
||||
gcc::diagnostics_output_spec::context ctxt (opts, dc, line_table, loc,
|
||||
option_name);
|
||||
auto result = gcc::diagnostics_output_spec::parse (ctxt, arg);
|
||||
if (!result)
|
||||
return;
|
||||
|
||||
gcc::diagnostics_output_spec::output_factory factory;
|
||||
auto sink = factory.make_sink (ctxt, arg, *result);
|
||||
if (!sink)
|
||||
return;
|
||||
|
||||
dc.set_output_format (std::move (sink));
|
||||
}
|
||||
|
||||
#if CHECKING_P
|
||||
|
||||
namespace selftest {
|
||||
|
||||
/* RAII class to temporarily override "progname" to the
|
||||
string "PROGNAME". */
|
||||
|
||||
class auto_fix_progname
|
||||
{
|
||||
public:
|
||||
auto_fix_progname ()
|
||||
{
|
||||
m_old_progname = progname;
|
||||
progname = "PROGNAME";
|
||||
}
|
||||
|
||||
~auto_fix_progname ()
|
||||
{
|
||||
progname = m_old_progname;
|
||||
}
|
||||
|
||||
private:
|
||||
const char *m_old_progname;
|
||||
};
|
||||
|
||||
struct parser_test
|
||||
{
|
||||
parser_test ()
|
||||
: m_opts (),
|
||||
m_dc (),
|
||||
m_ctxt (m_opts, m_dc, line_table, UNKNOWN_LOCATION, "-fOPTION="),
|
||||
m_fmt (m_dc.get_output_format (0))
|
||||
{
|
||||
pp_buffer (m_fmt.get_printer ())->m_flush_p = false;
|
||||
}
|
||||
|
||||
std::unique_ptr<gcc::diagnostics_output_spec::name_and_params>
|
||||
parse (const char *unparsed_arg)
|
||||
{
|
||||
return gcc::diagnostics_output_spec::parse (m_ctxt, unparsed_arg);
|
||||
}
|
||||
|
||||
bool execution_failed_p () const
|
||||
{
|
||||
return m_dc.execution_failed_p ();
|
||||
}
|
||||
|
||||
const char *
|
||||
get_diagnostic_text () const
|
||||
{
|
||||
return pp_formatted_text (m_fmt.get_printer ());
|
||||
}
|
||||
|
||||
private:
|
||||
const gcc_options m_opts;
|
||||
test_diagnostic_context m_dc;
|
||||
gcc::diagnostics_output_spec::context m_ctxt;
|
||||
diagnostic_output_format &m_fmt;
|
||||
};
|
||||
|
||||
/* Selftests. */
|
||||
|
||||
static void
|
||||
test_output_arg_parsing ()
|
||||
{
|
||||
auto_fix_quotes fix_quotes;
|
||||
auto_fix_progname fix_progname;
|
||||
|
||||
/* Minimal correct example. */
|
||||
{
|
||||
parser_test pt;
|
||||
auto result = pt.parse ("foo");
|
||||
ASSERT_EQ (result->m_format, "foo");
|
||||
ASSERT_EQ (result->m_kvs.size (), 0);
|
||||
ASSERT_FALSE (pt.execution_failed_p ());
|
||||
}
|
||||
|
||||
/* Stray trailing colon with no key/value pairs. */
|
||||
{
|
||||
parser_test pt;
|
||||
auto result = pt.parse ("foo:");
|
||||
ASSERT_EQ (result, nullptr);
|
||||
ASSERT_TRUE (pt.execution_failed_p ());
|
||||
ASSERT_STREQ (pt.get_diagnostic_text (),
|
||||
"PROGNAME: error: `-fOPTION=foo:':"
|
||||
" expected KEY=VALUE-style parameter for format `foo'"
|
||||
" after `:';"
|
||||
" got `'\n");
|
||||
}
|
||||
|
||||
/* No key before '='. */
|
||||
{
|
||||
parser_test pt;
|
||||
auto result = pt.parse ("foo:=");
|
||||
ASSERT_EQ (result, nullptr);
|
||||
ASSERT_TRUE (pt.execution_failed_p ());
|
||||
ASSERT_STREQ (pt.get_diagnostic_text (),
|
||||
"PROGNAME: error: `-fOPTION=foo:=':"
|
||||
" expected KEY=VALUE-style parameter for format `foo'"
|
||||
" after `:';"
|
||||
" got `='\n");
|
||||
}
|
||||
|
||||
/* No value for key. */
|
||||
{
|
||||
parser_test pt;
|
||||
auto result = pt.parse ("foo:key,");
|
||||
ASSERT_EQ (result, nullptr);
|
||||
ASSERT_TRUE (pt.execution_failed_p ());
|
||||
ASSERT_STREQ (pt.get_diagnostic_text (),
|
||||
"PROGNAME: error: `-fOPTION=foo:key,':"
|
||||
" expected KEY=VALUE-style parameter for format `foo'"
|
||||
" after `:';"
|
||||
" got `key,'\n");
|
||||
}
|
||||
|
||||
/* Correct example, with one key/value pair. */
|
||||
{
|
||||
parser_test pt;
|
||||
auto result = pt.parse ("foo:key=value");
|
||||
ASSERT_EQ (result->m_format, "foo");
|
||||
ASSERT_EQ (result->m_kvs.size (), 1);
|
||||
ASSERT_EQ (result->m_kvs[0].first, "key");
|
||||
ASSERT_EQ (result->m_kvs[0].second, "value");
|
||||
ASSERT_FALSE (pt.execution_failed_p ());
|
||||
}
|
||||
|
||||
/* Stray trailing comma. */
|
||||
{
|
||||
parser_test pt;
|
||||
auto result = pt.parse ("foo:key=value,");
|
||||
ASSERT_EQ (result, nullptr);
|
||||
ASSERT_TRUE (pt.execution_failed_p ());
|
||||
ASSERT_STREQ (pt.get_diagnostic_text (),
|
||||
"PROGNAME: error: `-fOPTION=foo:key=value,':"
|
||||
" expected KEY=VALUE-style parameter for format `foo'"
|
||||
" after `,';"
|
||||
" got `'\n");
|
||||
}
|
||||
|
||||
/* Correct example, with two key/value pairs. */
|
||||
{
|
||||
parser_test pt;
|
||||
auto result = pt.parse ("foo:color=red,shape=circle");
|
||||
ASSERT_EQ (result->m_format, "foo");
|
||||
ASSERT_EQ (result->m_kvs.size (), 2);
|
||||
ASSERT_EQ (result->m_kvs[0].first, "color");
|
||||
ASSERT_EQ (result->m_kvs[0].second, "red");
|
||||
ASSERT_EQ (result->m_kvs[1].first, "shape");
|
||||
ASSERT_EQ (result->m_kvs[1].second, "circle");
|
||||
ASSERT_FALSE (pt.execution_failed_p ());
|
||||
}
|
||||
}
|
||||
|
||||
/* Run all of the selftests within this file. */
|
||||
|
||||
void
|
||||
opts_diagnostic_cc_tests ()
|
||||
{
|
||||
test_output_arg_parsing ();
|
||||
}
|
||||
|
||||
} // namespace selftest
|
||||
|
||||
#endif /* #if CHECKING_P */
|
@ -59,4 +59,15 @@ private:
|
||||
void *m_opts;
|
||||
};
|
||||
|
||||
extern void
|
||||
handle_OPT_fdiagnostics_add_output_ (const gcc_options &opts,
|
||||
diagnostic_context &dc,
|
||||
const char *arg,
|
||||
location_t loc);
|
||||
|
||||
extern void
|
||||
handle_OPT_fdiagnostics_set_output_ (const gcc_options &opts,
|
||||
diagnostic_context &dc,
|
||||
const char *arg,
|
||||
location_t loc);
|
||||
#endif
|
||||
|
@ -258,7 +258,7 @@ init_options_once (void)
|
||||
initial_lang_mask = lang_hooks.option_lang_mask ();
|
||||
|
||||
const bool show_highlight_colors
|
||||
= pp_show_highlight_colors (global_dc->m_printer);
|
||||
= pp_show_highlight_colors (global_dc->get_reference_printer ());
|
||||
|
||||
lang_hooks.initialize_diagnostics (global_dc);
|
||||
/* ??? Ideally, we should do this earlier and the FEs will override
|
||||
@ -269,6 +269,7 @@ init_options_once (void)
|
||||
|
||||
diagnostic_color_init (global_dc);
|
||||
diagnostic_urls_init (global_dc);
|
||||
global_dc->refresh_output_sinks ();
|
||||
}
|
||||
|
||||
/* Decode command-line options to an array, like
|
||||
|
12
gcc/opts.cc
12
gcc/opts.cc
@ -2936,7 +2936,7 @@ common_handle_option (struct gcc_options *opts,
|
||||
break;
|
||||
|
||||
case OPT_fdiagnostics_show_location_:
|
||||
diagnostic_prefixing_rule (dc) = (diagnostic_prefixing_rule_t) value;
|
||||
dc->set_prefixing_rule ((diagnostic_prefixing_rule_t) value);
|
||||
break;
|
||||
|
||||
case OPT_fdiagnostics_show_caret:
|
||||
@ -2975,6 +2975,14 @@ common_handle_option (struct gcc_options *opts,
|
||||
break;
|
||||
}
|
||||
|
||||
case OPT_fdiagnostics_add_output_:
|
||||
handle_OPT_fdiagnostics_add_output_ (*opts, *dc, arg, loc);
|
||||
break;
|
||||
|
||||
case OPT_fdiagnostics_set_output_:
|
||||
handle_OPT_fdiagnostics_set_output_ (*opts, *dc, arg, loc);
|
||||
break;
|
||||
|
||||
case OPT_fdiagnostics_text_art_charset_:
|
||||
dc->set_text_art_charset ((enum diagnostic_text_art_charset)value);
|
||||
break;
|
||||
@ -3059,7 +3067,7 @@ common_handle_option (struct gcc_options *opts,
|
||||
break;
|
||||
|
||||
case OPT_fmessage_length_:
|
||||
pp_set_line_maximum_length (dc->m_printer, value);
|
||||
pp_set_line_maximum_length (dc->get_reference_printer (), value);
|
||||
diagnostic_set_caret_max_width (dc, value);
|
||||
break;
|
||||
|
||||
|
@ -37,7 +37,7 @@ namespace selftest {
|
||||
test_diagnostic_context::test_diagnostic_context ()
|
||||
{
|
||||
diagnostic_initialize (this, 0);
|
||||
pp_show_color (m_printer) = false;
|
||||
pp_show_color (get_reference_printer ()) = false;
|
||||
m_source_printing.enabled = true;
|
||||
m_source_printing.show_labels_p = true;
|
||||
m_show_column = true;
|
||||
@ -86,10 +86,11 @@ test_diagnostic_context::report (diagnostic_t kind,
|
||||
const char *
|
||||
test_diagnostic_context::test_show_locus (rich_location &richloc)
|
||||
{
|
||||
gcc_assert (m_printer);
|
||||
pretty_printer *pp = get_reference_printer ();
|
||||
gcc_assert (pp);
|
||||
diagnostic_source_print_policy source_policy (*this);
|
||||
source_policy.print (*m_printer, richloc, DK_ERROR, nullptr);
|
||||
return pp_formatted_text (m_printer);
|
||||
source_policy.print (*pp, richloc, DK_ERROR, nullptr);
|
||||
return pp_formatted_text (pp);
|
||||
}
|
||||
|
||||
} // namespace selftest
|
||||
|
@ -106,6 +106,7 @@ selftest::run_tests ()
|
||||
diagnostic_path_cc_tests ();
|
||||
simple_diagnostic_path_cc_tests ();
|
||||
attribs_cc_tests ();
|
||||
opts_diagnostic_cc_tests ();
|
||||
|
||||
/* This one relies on most of the above. */
|
||||
function_tests_cc_tests ();
|
||||
|
@ -241,6 +241,7 @@ extern void input_cc_tests ();
|
||||
extern void json_cc_tests ();
|
||||
extern void optinfo_emit_json_cc_tests ();
|
||||
extern void opts_cc_tests ();
|
||||
extern void opts_diagnostic_cc_tests ();
|
||||
extern void ordered_hash_map_tests_cc_tests ();
|
||||
extern void predict_cc_tests ();
|
||||
extern void pretty_print_cc_tests ();
|
||||
|
@ -226,7 +226,7 @@ simple_diagnostic_path_cc_tests ()
|
||||
{
|
||||
/* In a few places we use the global dc's printer to determine
|
||||
colorization so ensure this off during the tests. */
|
||||
pretty_printer *global_pp = global_dc->m_printer;
|
||||
pretty_printer *global_pp = global_dc->get_reference_printer ();
|
||||
const bool saved_show_color = pp_show_color (global_pp);
|
||||
pp_show_color (global_pp) = false;
|
||||
|
||||
|
@ -529,7 +529,7 @@ dump_refcnt_info (const hash_map<const region *, int> ®ion_to_refcnt,
|
||||
region_model_manager *mgr = model->get_manager ();
|
||||
pretty_printer pp;
|
||||
pp_format_decoder (&pp) = default_tree_printer;
|
||||
pp_show_color (&pp) = pp_show_color (global_dc->m_printer);
|
||||
pp_show_color (&pp) = pp_show_color (global_dc->get_reference_printer ());
|
||||
pp.set_output_stream (stderr);
|
||||
|
||||
for (const auto ®ion_refcnt : region_to_refcnt)
|
||||
|
@ -1,7 +1,7 @@
|
||||
/* Test of an ICE triggered within a header file with SARIF 2.2 */
|
||||
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-fdiagnostics-format=sarif-file-2.2-prerelease" } */
|
||||
/* { dg-options "-fdiagnostics-set-output=sarif:version=2.2-prerelease" } */
|
||||
/* { dg-additional-options "-fno-report-bug" } */
|
||||
|
||||
#include "crash-test-ice-in-header.h" /* { dg-ice "" } */
|
||||
|
@ -147,7 +147,7 @@ example_1 ()
|
||||
{
|
||||
auto_diagnostic_group d;
|
||||
gcc_rich_location richloc (gimple_location (call_to_PyList_Append));
|
||||
simple_diagnostic_path path (global_dc->m_printer);
|
||||
simple_diagnostic_path path (global_dc->get_reference_printer ());
|
||||
diagnostic_event_id_t alloc_event_id
|
||||
= path.add_event (gimple_location (call_to_PyList_New),
|
||||
example_a_fun->decl, 0,
|
||||
@ -335,7 +335,7 @@ example_2 ()
|
||||
auto_diagnostic_group d;
|
||||
|
||||
gcc_rich_location richloc (call_to_free.m_loc);
|
||||
test_diagnostic_path path (global_dc->m_printer);
|
||||
test_diagnostic_path path (global_dc->get_reference_printer ());
|
||||
path.add_entry (entry_to_test, 0, "test");
|
||||
path.add_call (call_to_make_boxed_int, 0,
|
||||
entry_to_make_boxed_int, "make_boxed_int");
|
||||
@ -420,7 +420,7 @@ example_3 ()
|
||||
auto_diagnostic_group d;
|
||||
|
||||
gcc_rich_location richloc (call_to_fprintf.m_loc);
|
||||
test_diagnostic_path path (global_dc->m_printer);
|
||||
test_diagnostic_path path (global_dc->get_reference_printer ());
|
||||
path.add_entry (entry_to_test, 1, "test");
|
||||
path.add_call (call_to_register_handler, 1,
|
||||
entry_to_register_handler, "register_handler");
|
||||
@ -495,7 +495,7 @@ example_4 ()
|
||||
auto_diagnostic_group d;
|
||||
|
||||
gcc_rich_location richloc (call_to_acquire_lock_a_in_bar.m_loc);
|
||||
test_diagnostic_path path (global_dc->m_printer);
|
||||
test_diagnostic_path path (global_dc->get_reference_printer ());
|
||||
diagnostic_thread_id_t thread_1 = path.add_thread ("Thread 1");
|
||||
diagnostic_thread_id_t thread_2 = path.add_thread ("Thread 2");
|
||||
path.add_entry (entry_to_foo, 0, "foo", thread_1);
|
||||
|
@ -316,13 +316,18 @@ public:
|
||||
|
||||
const xml::document &get_document () const { return *m_document; }
|
||||
|
||||
void set_printer (pretty_printer &pp)
|
||||
{
|
||||
m_printer = &pp;
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<xml::element>
|
||||
make_element_for_diagnostic (const diagnostic_info &diagnostic,
|
||||
diagnostic_t orig_diag_kind);
|
||||
|
||||
diagnostic_context &m_context;
|
||||
pretty_printer &m_printer;
|
||||
pretty_printer *m_printer;
|
||||
const line_maps *m_line_maps;
|
||||
|
||||
std::unique_ptr<xml::document> m_document;
|
||||
@ -400,7 +405,7 @@ xhtml_builder::xhtml_builder (diagnostic_context &context,
|
||||
pretty_printer &pp,
|
||||
const line_maps *line_maps)
|
||||
: m_context (context),
|
||||
m_printer (pp),
|
||||
m_printer (&pp),
|
||||
m_line_maps (line_maps)
|
||||
{
|
||||
gcc_assert (m_line_maps);
|
||||
@ -565,10 +570,10 @@ xhtml_builder::make_element_for_diagnostic (const diagnostic_info &diagnostic,
|
||||
|
||||
auto message_span = make_span (label_text::borrow ("gcc-message"));
|
||||
xhtml_token_printer tok_printer (*this, *message_span.get ());
|
||||
m_printer.set_token_printer (&tok_printer);
|
||||
pp_output_formatted_text (&m_printer, m_context.get_urlifier ());
|
||||
m_printer.set_token_printer (nullptr);
|
||||
pp_clear_output_area (&m_printer);
|
||||
m_printer->set_token_printer (&tok_printer);
|
||||
pp_output_formatted_text (m_printer, m_context.get_urlifier ());
|
||||
m_printer->set_token_printer (nullptr);
|
||||
pp_clear_output_area (m_printer);
|
||||
diag_element->add_child (std::move (message_span));
|
||||
|
||||
if (diagnostic.metadata)
|
||||
@ -626,10 +631,10 @@ xhtml_builder::make_element_for_diagnostic (const diagnostic_info &diagnostic,
|
||||
pre->set_attr ("class", label_text::borrow ("gcc-annotated-source"));
|
||||
// TODO: ideally we'd like to capture elements within the following:
|
||||
diagnostic_show_locus (&m_context, diagnostic.richloc, diagnostic.kind,
|
||||
&m_printer);
|
||||
m_printer);
|
||||
pre->add_text
|
||||
(label_text::take (xstrdup (pp_formatted_text (&m_printer))));
|
||||
pp_clear_output_area (&m_printer);
|
||||
(label_text::take (xstrdup (pp_formatted_text (m_printer))));
|
||||
pp_clear_output_area (m_printer);
|
||||
diag_element->add_child (std::move (pre));
|
||||
}
|
||||
|
||||
@ -723,6 +728,23 @@ public:
|
||||
{
|
||||
/* No-op, but perhaps could show paths here. */
|
||||
}
|
||||
bool follows_reference_printer_p () const final override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
void update_printer () final override
|
||||
{
|
||||
m_printer = m_context.clone_printer ();
|
||||
|
||||
/* Don't colorize the text. */
|
||||
pp_show_color (m_printer.get ()) = false;
|
||||
|
||||
/* No textual URLs. */
|
||||
m_printer->set_url_format (URL_FORMAT_NONE);
|
||||
|
||||
/* Update the builder to use the new printer. */
|
||||
m_builder.set_printer (*get_printer ());
|
||||
}
|
||||
|
||||
const xml::document &get_document () const
|
||||
{
|
||||
|
@ -48,7 +48,7 @@ test_richloc (rich_location *richloc)
|
||||
{
|
||||
/* Run the diagnostic and fix-it printing code. */
|
||||
test_diagnostic_context dc;
|
||||
diagnostic_show_locus (&dc, richloc, DK_ERROR, dc.m_printer);
|
||||
diagnostic_show_locus (&dc, richloc, DK_ERROR, dc.get_reference_printer ());
|
||||
|
||||
/* Generate a diff. */
|
||||
edit_context ec (global_dc->get_file_cache ());
|
||||
|
@ -0,0 +1,16 @@
|
||||
/* Verify using -fdiagnostics-add-output=sarif with the defaults. */
|
||||
|
||||
/* { dg-do compile } */
|
||||
/* { dg-additional-options "-fdiagnostics-add-output=sarif" } */
|
||||
|
||||
/* Verify that SARIF output can capture secondary locations
|
||||
relating to a diagnostic. */
|
||||
|
||||
int missing_semicolon (void)
|
||||
{
|
||||
return 42 /* { dg-error "expected ';' before '.' token" } */
|
||||
}
|
||||
|
||||
/* Verify that JSON was written to the output file with the
|
||||
expected version and expected name:
|
||||
{ dg-final { verify-sarif-file "2.1" } } */
|
30
gcc/testsuite/gcc.dg/sarif-output/bad-binary-op.c
Normal file
30
gcc/testsuite/gcc.dg/sarif-output/bad-binary-op.c
Normal file
@ -0,0 +1,30 @@
|
||||
/* { dg-options "-fdiagnostics-show-caret -fdiagnostics-add-output=sarif" } */
|
||||
|
||||
struct s {};
|
||||
struct t {};
|
||||
|
||||
typedef struct s S;
|
||||
typedef struct t T;
|
||||
|
||||
extern S callee_4a (void);
|
||||
extern T callee_4b (void);
|
||||
|
||||
int test_4 (void)
|
||||
{
|
||||
return callee_4a () + callee_4b (); /* { dg-error "invalid operands to binary \+" } */
|
||||
|
||||
/* { dg-begin-multiline-output "" }
|
||||
return callee_4a () + callee_4b ();
|
||||
~~~~~~~~~~~~ ^ ~~~~~~~~~~~~
|
||||
| |
|
||||
| T {aka struct t}
|
||||
S {aka struct s}
|
||||
{ dg-end-multiline-output "" } */
|
||||
}
|
||||
|
||||
/* Verify that some JSON was written to a file with the expected name. */
|
||||
/* { dg-final { verify-sarif-file } } */
|
||||
|
||||
/* Use a Python script to verify various properties about the generated
|
||||
.sarif file:
|
||||
{ dg-final { run-sarif-pytest bad-binary-op.c "bad-binary-op.py" } } */
|
70
gcc/testsuite/gcc.dg/sarif-output/bad-binary-op.py
Normal file
70
gcc/testsuite/gcc.dg/sarif-output/bad-binary-op.py
Normal file
@ -0,0 +1,70 @@
|
||||
from sarif import *
|
||||
|
||||
import pytest
|
||||
|
||||
@pytest.fixture(scope='function', autouse=True)
|
||||
def sarif():
|
||||
return sarif_from_env()
|
||||
|
||||
def test_basics(sarif):
|
||||
schema = sarif['$schema']
|
||||
assert schema == "https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json"
|
||||
|
||||
version = sarif['version']
|
||||
assert version == "2.1.0"
|
||||
|
||||
def test_execution_unsuccessful(sarif):
|
||||
runs = sarif['runs']
|
||||
run = runs[0]
|
||||
|
||||
invocations = run['invocations']
|
||||
assert len(invocations) == 1
|
||||
invocation = invocations[0]
|
||||
|
||||
# We expect the 'error' to make executionSuccessful be false
|
||||
assert invocation['executionSuccessful'] == False
|
||||
|
||||
def test_error_location(sarif):
|
||||
runs = sarif['runs']
|
||||
run = runs[0]
|
||||
results = run['results']
|
||||
|
||||
# We expect a single error with annotations.
|
||||
#
|
||||
# The textual form of the diagnostic looks like this:
|
||||
# . PATH/bad-binary-ops.c: In function 'test_4':
|
||||
# . PATH/bad-binary-ops.c:64:23: error: invalid operands to binary + (have 'S' {aka 'struct s'} and 'T' {aka 'struct t'})
|
||||
# . return callee_4a () + callee_4b (); /* { dg-error "invalid operands to binary \+" } */
|
||||
# . ~~~~~~~~~~~~ ^ ~~~~~~~~~~~~
|
||||
# . | |
|
||||
# . | T {aka struct t}
|
||||
# . S {aka struct s}
|
||||
assert len(results) == 1
|
||||
|
||||
result = results[0]
|
||||
assert result['level'] == 'error'
|
||||
|
||||
assert result['message']['text'] \
|
||||
== "invalid operands to binary + (have 'S' {aka 'struct s'} and 'T' {aka 'struct t'})"
|
||||
locations = result['locations']
|
||||
assert len(locations) == 1
|
||||
|
||||
location = locations[0]
|
||||
assert get_location_artifact_uri(location).endswith('bad-binary-op.c')
|
||||
assert get_location_snippet_text(location) \
|
||||
== " return callee_4a () + callee_4b (); /* { dg-error \"invalid operands to binary \\+\" } */\n"
|
||||
EXPECTED_LINE = 14
|
||||
assert get_location_physical_region(location)['startLine'] == EXPECTED_LINE
|
||||
assert get_location_physical_region(location)['startColumn'] == 23
|
||||
assert get_location_physical_region(location)['endColumn'] == 24
|
||||
|
||||
annotations = location['annotations']
|
||||
assert len(annotations) == 2
|
||||
assert annotations[0]['startLine'] == EXPECTED_LINE
|
||||
assert annotations[0]['startColumn'] == 10
|
||||
assert annotations[0]['endColumn'] == 22
|
||||
assert annotations[0]['message']['text'] == "S {aka struct s}"
|
||||
assert annotations[1]['startLine'] == EXPECTED_LINE
|
||||
assert annotations[1]['startColumn'] == 25
|
||||
assert annotations[1]['endColumn'] == 37
|
||||
assert annotations[1]['message']['text'] == "T {aka struct t}"
|
27
gcc/testsuite/gcc.dg/sarif-output/multiple-outputs.c
Normal file
27
gcc/testsuite/gcc.dg/sarif-output/multiple-outputs.c
Normal file
@ -0,0 +1,27 @@
|
||||
/* Verify that we can output multiple different versions of SARIF
|
||||
with -fdiagnostics-add-output (specifying version and filename),
|
||||
as well as usual text output. */
|
||||
|
||||
/* { dg-do compile } */
|
||||
/* { dg-additional-options "-fdiagnostics-add-output=sarif:file=multiple-outputs-c.2.1.sarif,version=2.1" } */
|
||||
/* { dg-additional-options "-fdiagnostics-add-output=sarif:file=multiple-outputs-c.2.2.sarif,version=2.2-prerelease" } */
|
||||
|
||||
/* Verify that SARIF output can capture secondary locations
|
||||
relating to a diagnostic. */
|
||||
|
||||
int missing_semicolon (void)
|
||||
{
|
||||
return 42 /* { dg-error "expected ';' before '.' token" } */
|
||||
}
|
||||
|
||||
/* Verify that JSON was written to the output files with the
|
||||
expected version and expected names:
|
||||
{ dg-final { verify-sarif-file "2.1" "multiple-outputs-c.2.1.sarif" } }
|
||||
{ dg-final { verify-sarif-file "2.2" "multiple-outputs-c.2.2.sarif" } }
|
||||
*/
|
||||
|
||||
/* Use a Python script to verify various properties about the generated
|
||||
.sarif files:
|
||||
{ dg-final { run-sarif-pytest multiple-outputs-c.2.1 "multiple-outputs.py" } }
|
||||
{ dg-final { run-sarif-pytest multiple-outputs-c.2.2 "multiple-outputs.py" } }
|
||||
*/
|
50
gcc/testsuite/gcc.dg/sarif-output/multiple-outputs.py
Normal file
50
gcc/testsuite/gcc.dg/sarif-output/multiple-outputs.py
Normal file
@ -0,0 +1,50 @@
|
||||
from sarif import *
|
||||
|
||||
import pytest
|
||||
|
||||
@pytest.fixture(scope='function', autouse=True)
|
||||
def sarif():
|
||||
return sarif_from_env()
|
||||
|
||||
def test_execution_unsuccessful(sarif):
|
||||
runs = sarif['runs']
|
||||
run = runs[0]
|
||||
|
||||
invocations = run['invocations']
|
||||
assert len(invocations) == 1
|
||||
invocation = invocations[0]
|
||||
|
||||
# We expect the 'error' to make executionSuccessful be false
|
||||
assert invocation['executionSuccessful'] == False
|
||||
|
||||
def test_result(sarif):
|
||||
runs = sarif['runs']
|
||||
run = runs[0]
|
||||
results = run['results']
|
||||
|
||||
# We expect a single error with a secondary location and a fix-it hint.
|
||||
#
|
||||
# The textual form of the diagnostic would look like this:
|
||||
# . PATH/missing-semicolon.c: In function 'missing_semicolon':
|
||||
# . PATH/missing-semicolon.c:19:12: error: expected ';' before '}' token
|
||||
# . 19 | return 42
|
||||
# . | ^
|
||||
# . | ;
|
||||
# . 20 | }
|
||||
# . | ~
|
||||
assert len(results) == 1
|
||||
|
||||
result = results[0]
|
||||
assert result['level'] == 'error'
|
||||
assert result['message']['text'] == "expected ';' before '}' token"
|
||||
locations = result['locations']
|
||||
assert len(locations) == 1
|
||||
|
||||
assert len(result['fixes']) == 1
|
||||
assert len(result['fixes'][0]['artifactChanges']) == 1
|
||||
change = result['fixes'][0]['artifactChanges'][0]
|
||||
assert len(change['replacements']) == 1
|
||||
replacement = change['replacements'][0]
|
||||
assert replacement['deletedRegion']['startColumn'] == 12
|
||||
assert replacement['deletedRegion']['endColumn'] == 12
|
||||
assert replacement['insertedContent']['text'] == ';'
|
@ -63,17 +63,24 @@ proc scan-sarif-file-not { args } {
|
||||
# The first argument is the version of the SARIF schema to validate against
|
||||
# If present can be "2.1" or "2.2"
|
||||
# If absent, validate against 2.1
|
||||
#
|
||||
# If present, the second argument is the expected filename of the .sarif file
|
||||
|
||||
proc verify-sarif-file { args } {
|
||||
global srcdir subdir
|
||||
|
||||
set testcase [testname-for-summary]
|
||||
set filename [lindex $testcase 0]
|
||||
set output_file "[file tail $filename].sarif"
|
||||
|
||||
set version [lindex $args 0]
|
||||
verbose "sarif version: $version" 2
|
||||
|
||||
set output_file [lindex $args 1]
|
||||
verbose "output_file: $output_file" 2
|
||||
if { $output_file == "" } {
|
||||
set output_file "[file tail $filename].sarif"
|
||||
}
|
||||
|
||||
if { ![check_effective_target_recent_python3] } {
|
||||
unsupported "$testcase verify-sarif-file: python3 is missing"
|
||||
return
|
||||
|
@ -231,7 +231,7 @@ announce_function (tree decl)
|
||||
fprintf (stderr, " %s",
|
||||
identifier_to_locale (lang_hooks.decl_printable_name (decl, 2)));
|
||||
fflush (stderr);
|
||||
pp_needs_newline (global_dc->m_printer) = true;
|
||||
pp_needs_newline (global_dc->get_reference_printer ()) = true;
|
||||
diagnostic_set_last_function (global_dc, (diagnostic_info *) NULL);
|
||||
}
|
||||
}
|
||||
@ -2389,7 +2389,7 @@ toplev::main (int argc, char **argv)
|
||||
if (auto edit_context_ptr = global_dc->get_edit_context ())
|
||||
{
|
||||
pretty_printer pp;
|
||||
pp_show_color (&pp) = pp_show_color (global_dc->m_printer);
|
||||
pp_show_color (&pp) = pp_show_color (global_dc->get_reference_printer ());
|
||||
edit_context_ptr->print_diff (&pp, true);
|
||||
pp_flush (&pp);
|
||||
}
|
||||
|
@ -180,7 +180,7 @@ tree_diagnostics_defaults (diagnostic_context *context)
|
||||
{
|
||||
diagnostic_text_starter (context) = default_tree_diagnostic_text_starter;
|
||||
diagnostic_text_finalizer (context) = default_diagnostic_text_finalizer;
|
||||
diagnostic_format_decoder (context) = default_tree_printer;
|
||||
context->set_format_decoder (default_tree_printer);
|
||||
context->set_set_locations_callback (set_inlining_locations);
|
||||
context->set_client_data_hooks (make_compiler_data_hooks ());
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ public:
|
||||
{
|
||||
pp_format_decoder (this) = default_tree_printer;
|
||||
if (outf == stderr)
|
||||
pp_show_color (this) = pp_show_color (global_dc->m_printer);
|
||||
pp_show_color (this) = pp_show_color (global_dc->get_reference_printer ());
|
||||
set_output_stream (outf);
|
||||
}
|
||||
~tree_dump_pretty_printer ()
|
||||
|
@ -12429,7 +12429,7 @@ escaped_string::escape (const char *unescaped)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c != '\n' || !pp_is_wrapping_line (global_dc->m_printer))
|
||||
if (c != '\n' || !pp_is_wrapping_line (global_dc->get_reference_printer ()))
|
||||
{
|
||||
if (escaped == NULL)
|
||||
{
|
||||
@ -15901,7 +15901,7 @@ test_escaped_strings (void)
|
||||
ASSERT_STREQ ("foobar", (const char *) msg);
|
||||
|
||||
/* Ensure that we have -fmessage-length set to 0. */
|
||||
pretty_printer *pp = global_dc->m_printer;
|
||||
pretty_printer *pp = global_dc->get_reference_printer ();
|
||||
saved_cutoff = pp_line_cutoff (pp);
|
||||
pp_line_cutoff (pp) = 0;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user