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:
David Malcolm 2024-10-29 19:12:02 -04:00
parent 3d8cd34a45
commit 0b73e9382a
54 changed files with 1660 additions and 265 deletions

View File

@ -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

View File

@ -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++)

View File

@ -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);

View File

@ -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 ());

View File

@ -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. */

View File

@ -5579,14 +5579,14 @@ test_type_mismatch_range_labels ()
richloc.add_range (param, SHOW_RANGE_WITHOUT_CARET, &param_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. */

View 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

View File

@ -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.

View File

@ -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)

View File

@ -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. */

View File

@ -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;

View File

@ -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);
}

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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). */

View File

@ -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,

View File

@ -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 */

View File

@ -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

View File

@ -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;

View File

@ -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; }

View File

@ -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);

View File

@ -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"

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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. */

View File

@ -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
View 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 */

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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 ();

View File

@ -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 ();

View File

@ -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;

View File

@ -529,7 +529,7 @@ dump_refcnt_info (const hash_map<const region *, int> &region_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 &region_refcnt : region_to_refcnt)

View File

@ -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 "" } */

View File

@ -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);

View File

@ -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
{

View File

@ -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 ());

View File

@ -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" } } */

View 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" } } */

View 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}"

View 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" } }
*/

View 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'] == ';'

View File

@ -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

View File

@ -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);
}

View File

@ -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 ());
}

View File

@ -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 ()

View File

@ -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;