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