public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r14-4368] diagnostics: add diagnostic_output_format class
@ 2023-10-02 16:25 David Malcolm
  0 siblings, 0 replies; only message in thread
From: David Malcolm @ 2023-10-02 16:25 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:140820265d96b0d115ade21235f8b8017fde9a5a

commit r14-4368-g140820265d96b0d115ade21235f8b8017fde9a5a
Author: David Malcolm <dmalcolm@redhat.com>
Date:   Mon Oct 2 12:16:55 2023 -0400

    diagnostics: add diagnostic_output_format class
    
    Eliminate various global variables in the json/sarif output code by
    bundling together callbacks and state into a new diagnostic_output_format
    class, with per-output-format subclasses.
    
    No functional change intended.
    
    gcc/ChangeLog:
            * diagnostic-format-json.cc (toplevel_array): Remove global in
            favor of json_output_format::m_top_level_array.
            (cur_group): Likewise, for json_output_format::m_cur_group.
            (cur_children_array): Likewise, for
            json_output_format::m_cur_children_array.
            (class json_output_format): New.
            (json_begin_diagnostic): Remove, in favor of
            json_output_format::on_begin_diagnostic.
            (json_end_diagnostic): Convert to...
            (json_output_format::on_end_diagnostic): ...this.
            (json_begin_group): Remove, in favor of
            json_output_format::on_begin_group.
            (json_end_group): Remove, in favor of
            json_output_format::on_end_group.
            (json_flush_to_file): Remove, in favor of
            json_output_format::flush_to_file.
            (json_stderr_final_cb): Remove, in favor of json_output_format
            dtor.
            (json_output_base_file_name): Remove global.
            (class json_stderr_output_format): New.
            (json_file_final_cb): Remove.
            (class json_file_output_format): New.
            (json_emit_diagram): Remove.
            (diagnostic_output_format_init_json): Update.
            (diagnostic_output_format_init_json_file): Update.
            * diagnostic-format-sarif.cc (the_builder): Remove this global,
            moving to a field of the sarif_output_format.
            (sarif_builder::maybe_make_artifact_content_object): Use the
            context's m_file_cache.
            (get_source_lines): Convert to...
            (sarif_builder::get_source_lines): ...this, using context's
            m_file_cache.
            (sarif_begin_diagnostic): Remove, in favor of
            sarif_output_format::on_begin_diagnostic.
            (sarif_end_diagnostic): Remove, in favor of
            sarif_output_format::on_end_diagnostic.
            (sarif_begin_group): Remove, in favor of
            sarif_output_format::on_begin_group.
            (sarif_end_group): Remove, in favor of
            sarif_output_format::on_end_group.
            (sarif_flush_to_file): Delete.
            (sarif_stderr_final_cb): Delete.
            (sarif_output_base_file_name): Delete.
            (sarif_file_final_cb): Delete.
            (class sarif_output_format): New.
            (sarif_emit_diagram): Delete.
            (class sarif_stream_output_format): New.
            (class sarif_file_output_format): New.
            (diagnostic_output_format_init_sarif): Update.
            (diagnostic_output_format_init_sarif_stderr): Update.
            (diagnostic_output_format_init_sarif_file): Update.
            (diagnostic_output_format_init_sarif_stream): Update.
            * diagnostic-show-locus.cc (diagnostic_show_locus): Update.
            * diagnostic.cc (default_diagnostic_final_cb): Delete, moving to
            diagnostic_text_output_format's dtor.
            (diagnostic_initialize): Update, making a new instance of
            diagnostic_text_output_format.
            (diagnostic_finish): Delete m_output_format, rather than calling
            final_cb.
            (diagnostic_report_diagnostic): Assert that m_output_format is
            non-NULL.  Replace call to begin_group_cb with call to
            m_output_format->on_begin_group.  Replace call to
            diagnostic_starter with call to
            m_output_format->on_begin_diagnostic.  Replace call to
            diagnostic_finalizer with call to
            m_output_format->on_end_diagnostic.
            (diagnostic_emit_diagram): Replace both optional call to
            m_diagrams.m_emission_cb and default implementation with call to
            m_output_format->on_diagram.  Move default implementation to
            diagnostic_text_output_format::on_diagram.
            (auto_diagnostic_group::~auto_diagnostic_group): Replace call to
            end_group_cb with call to m_output_format->on_end_group.
            (diagnostic_text_output_format::~diagnostic_text_output_format):
            New, based on default_diagnostic_final_cb.
            (diagnostic_text_output_format::on_begin_diagnostic): New, based
            on code from diagnostic_report_diagnostic.
            (diagnostic_text_output_format::on_end_diagnostic): Likewise.
            (diagnostic_text_output_format::on_diagram): New, based on code
            from diagnostic_emit_diagram.
            * diagnostic.h (class diagnostic_output_format): New.
            (class diagnostic_text_output_format): New.
            (diagnostic_context::begin_diagnostic): Move to...
            (diagnostic_context::m_text_callbacks::begin_diagnostic): ...here.
            (diagnostic_context::start_span): Move to...
            (diagnostic_context::m_text_callbacks::start_span): ...here.
            (diagnostic_context::end_diagnostic): Move to...
            (diagnostic_context::m_text_callbacks::end_diagnostic): ...here.
            (diagnostic_context::begin_group_cb): Remove, in favor of
            m_output_format->on_begin_group.
            (diagnostic_context::end_group_cb): Remove, in favor of
            m_output_format->on_end_group.
            (diagnostic_context::final_cb): Remove, in favor of
            m_output_format's dtor.
            (diagnostic_context::m_output_format): New field.
            (diagnostic_context::m_diagrams.m_emission_cb): Remove, in favor
            of m_output_format->on_diagram.
            (diagnostic_starter): Update.
            (diagnostic_finalizer): Update.
            (diagnostic_output_format_init_sarif_stream): New.
            * input.cc (location_get_source_line): Move implementation apart from
            call to diagnostic_file_cache_init to...
            (file_cache::get_source_line): ...this new function...
            (location_get_source_line): ...and reintroduce, rewritten in terms of
            file_cache::get_source_line.
            (get_source_file_content): Likewise, refactor into...
            (file_cache::get_source_file_content): ...this new function.
            * input.h (file_cache::get_source_line): New decl.
            (file_cache::get_source_file_content): New decl.
            * selftest-diagnostic.cc
            (test_diagnostic_context::test_diagnostic_context): Update.
            * tree-diagnostic-path.cc (event_range::print): Update for
            change to diagnostic_context's start_span callback.
    
    gcc/fortran/ChangeLog:
            * error.cc (gfc_diagnostics_init): Update for change to start_span.
    
    gcc/jit/ChangeLog:
            * dummy-frontend.cc (jit_langhook_init): Update for change to
            diagnostic_context callbacks.
    
    gcc/testsuite/ChangeLog:
            * gcc.dg/plugin/diagnostic_group_plugin.c
            (test_begin_group_cb, test_end_group_cb): Replace with...
            (class test_output_format): ...this new subclass.
            (plugin_init): Update.
    
    Signed-off-by: David Malcolm <dmalcolm@redhat.com>

Diff:
---
 gcc/diagnostic-format-json.cc                      | 230 +++++++++++----------
 gcc/diagnostic-format-sarif.cc                     | 212 ++++++++++---------
 gcc/diagnostic-show-locus.cc                       |   2 +-
 gcc/diagnostic.cc                                  | 115 ++++++-----
 gcc/diagnostic.h                                   | 109 ++++++----
 gcc/fortran/error.cc                               |   2 +-
 gcc/input.cc                                       |  26 ++-
 gcc/input.h                                        |   3 +
 gcc/jit/dummy-frontend.cc                          |   4 +-
 gcc/selftest-diagnostic.cc                         |   2 +-
 .../gcc.dg/plugin/diagnostic_group_plugin.c        |  45 ++--
 gcc/tree-diagnostic-path.cc                        |   2 +-
 12 files changed, 417 insertions(+), 335 deletions(-)

diff --git a/gcc/diagnostic-format-json.cc b/gcc/diagnostic-format-json.cc
index 539b98b5e74..346abb31ac4 100644
--- a/gcc/diagnostic-format-json.cc
+++ b/gcc/diagnostic-format-json.cc
@@ -28,18 +28,63 @@ along with GCC; see the file COPYING3.  If not see
 #include "json.h"
 #include "selftest.h"
 
-/* The top-level JSON array of pending diagnostics.  */
+/* Subclass of diagnostic_output_format for JSON output.  */
 
-static json::array *toplevel_array;
+class json_output_format : public diagnostic_output_format
+{
+public:
+  void on_begin_group () final override
+  {
+    /* No-op.  */
+  }
+  void on_end_group () final override
+  {
+    m_cur_group = nullptr;
+    m_cur_children_array = nullptr;
+  }
+  void
+  on_begin_diagnostic (diagnostic_info *) final override
+  {
+    /* No-op.  */
+  }
+  void
+  on_end_diagnostic (diagnostic_info *diagnostic,
+		     diagnostic_t orig_diag_kind) final override;
+  void on_diagram (const diagnostic_diagram &) final override
+  {
+    /* No-op.  */
+  }
 
-/* The JSON object for the current diagnostic group.  */
+protected:
+  json_output_format (diagnostic_context &context)
+  : diagnostic_output_format (context),
+    m_toplevel_array (new json::array ()),
+    m_cur_group (nullptr),
+    m_cur_children_array (nullptr)
+  {
+  }
 
-static json::object *cur_group;
+  /* Flush the top-level array to OUTF.  */
+  void
+  flush_to_file (FILE *outf)
+  {
+    m_toplevel_array->dump (outf);
+    fprintf (outf, "\n");
+    delete m_toplevel_array;
+    m_toplevel_array = nullptr;
+  }
+
+private:
+  /* The top-level JSON array of pending diagnostics.  */
+  json::array *m_toplevel_array;
 
-/* The JSON array for the "children" array within the current diagnostic
-   group.  */
+  /* The JSON object for the current diagnostic group.  */
+  json::object *m_cur_group;
 
-static json::array *cur_children_array;
+  /* The JSON array for the "children" array within the current diagnostic
+     group.  */
+  json::array *m_cur_children_array;
+};
 
 /* Generate a JSON object for LOC.  */
 
@@ -139,20 +184,13 @@ json_from_metadata (const diagnostic_metadata *metadata)
   return metadata_obj;
 }
 
-/* No-op implementation of "begin_diagnostic" for JSON output.  */
-
-static void
-json_begin_diagnostic (diagnostic_context *, diagnostic_info *)
-{
-}
-
-/* Implementation of "end_diagnostic" for JSON output.
+/* Implementation of "on_end_diagnostic" vfunc for JSON output.
    Generate a JSON object for DIAGNOSTIC, and store for output
    within current diagnostic group.  */
 
-static void
-json_end_diagnostic (diagnostic_context *context, diagnostic_info *diagnostic,
-		     diagnostic_t orig_diag_kind)
+void
+json_output_format::on_end_diagnostic (diagnostic_info *diagnostic,
+				       diagnostic_t orig_diag_kind)
 {
   json::object *diag_obj = new json::object ();
 
@@ -178,22 +216,22 @@ json_end_diagnostic (diagnostic_context *context, diagnostic_info *diagnostic,
 
   // FIXME: encoding of the message (json::string requires UTF-8)
   diag_obj->set ("message",
-		 new json::string (pp_formatted_text (context->printer)));
-  pp_clear_output_area (context->printer);
+		 new json::string (pp_formatted_text (m_context.printer)));
+  pp_clear_output_area (m_context.printer);
 
   char *option_text;
-  option_text = context->option_name (context, diagnostic->option_index,
-				      orig_diag_kind, diagnostic->kind);
+  option_text = m_context.option_name (&m_context, diagnostic->option_index,
+				       orig_diag_kind, diagnostic->kind);
   if (option_text)
     {
       diag_obj->set ("option", new json::string (option_text));
       free (option_text);
     }
 
-  if (context->get_option_url)
+  if (m_context.get_option_url)
     {
-      char *option_url = context->get_option_url (context,
-						  diagnostic->option_index);
+      char *option_url = m_context.get_option_url (&m_context,
+						   diagnostic->option_index);
       if (option_url)
 	{
 	  diag_obj->set ("option_url", new json::string (option_url));
@@ -203,21 +241,21 @@ json_end_diagnostic (diagnostic_context *context, diagnostic_info *diagnostic,
 
   /* If we've already emitted a diagnostic within this auto_diagnostic_group,
      then add diag_obj to its "children" array.  */
-  if (cur_group)
+  if (m_cur_group)
     {
-      gcc_assert (cur_children_array);
-      cur_children_array->append (diag_obj);
+      gcc_assert (m_cur_children_array);
+      m_cur_children_array->append (diag_obj);
     }
   else
     {
       /* Otherwise, make diag_obj be the top-level object within the group;
 	 add a "children" array and record the column origin.  */
-      toplevel_array->append (diag_obj);
-      cur_group = diag_obj;
-      cur_children_array = new json::array ();
-      diag_obj->set ("children", cur_children_array);
+      m_toplevel_array->append (diag_obj);
+      m_cur_group = diag_obj;
+      m_cur_children_array = new json::array ();
+      diag_obj->set ("children", m_cur_children_array);
       diag_obj->set ("column-origin",
-		     new json::integer_number (context->column_origin));
+		     new json::integer_number (m_context.column_origin));
     }
 
   const rich_location *richloc = diagnostic->richloc;
@@ -228,7 +266,8 @@ json_end_diagnostic (diagnostic_context *context, diagnostic_info *diagnostic,
   for (unsigned int i = 0; i < richloc->get_num_locations (); i++)
     {
       const location_range *loc_range = richloc->get_range (i);
-      json::object *loc_obj = json_from_location_range (context, loc_range, i);
+      json::object *loc_obj
+	= json_from_location_range (&m_context, loc_range, i);
       if (loc_obj)
 	loc_array->append (loc_obj);
     }
@@ -240,7 +279,7 @@ json_end_diagnostic (diagnostic_context *context, diagnostic_info *diagnostic,
       for (unsigned int i = 0; i < richloc->get_num_fixit_hints (); i++)
 	{
 	  const fixit_hint *hint = richloc->get_fixit_hint (i);
-	  json::object *fixit_obj = json_from_fixit_hint (context, hint);
+	  json::object *fixit_obj = json_from_fixit_hint (&m_context, hint);
 	  fixit_array->append (fixit_obj);
 	}
     }
@@ -257,9 +296,9 @@ json_end_diagnostic (diagnostic_context *context, diagnostic_info *diagnostic,
     }
 
   const diagnostic_path *path = richloc->get_path ();
-  if (path && context->make_json_for_path)
+  if (path && m_context.make_json_for_path)
     {
-      json::value *path_value = context->make_json_for_path (context, path);
+      json::value *path_value = m_context.make_json_for_path (&m_context, path);
       diag_obj->set ("path", path_value);
     }
 
@@ -267,71 +306,51 @@ json_end_diagnostic (diagnostic_context *context, diagnostic_info *diagnostic,
 		 new json::literal (richloc->escape_on_output_p ()));
 }
 
-/* No-op implementation of "begin_group_cb" for JSON output.  */
-
-static void
-json_begin_group (diagnostic_context *)
-{
-}
-
-/* Implementation of "end_group_cb" for JSON output.  */
-
-static void
-json_end_group (diagnostic_context *)
+class json_stderr_output_format : public json_output_format
 {
-  cur_group = NULL;
-  cur_children_array = NULL;
-}
-
-/* Flush the top-level array to OUTF.  */
-
-static void
-json_flush_to_file (FILE *outf)
-{
-  toplevel_array->dump (outf);
-  fprintf (outf, "\n");
-  delete toplevel_array;
-  toplevel_array = NULL;
-}
-
-/* Callback for final cleanup for JSON output to stderr.  */
-
-static void
-json_stderr_final_cb (diagnostic_context *)
-{
-  json_flush_to_file (stderr);
-}
-
-static char *json_output_base_file_name;
-
-/* Callback for final cleanup for JSON output to a file.  */
+public:
+  json_stderr_output_format (diagnostic_context &context)
+  : json_output_format (context)
+  {
+  }
+  ~json_stderr_output_format ()
+  {
+    flush_to_file (stderr);
+  }
+};
 
-static void
-json_file_final_cb (diagnostic_context *)
+class json_file_output_format : public json_output_format
 {
-  char *filename = concat (json_output_base_file_name, ".gcc.json", NULL);
-  FILE *outf = fopen (filename, "w");
-  if (!outf)
-    {
-      const char *errstr = xstrerror (errno);
-      fnotice (stderr, "error: unable to open '%s' for writing: %s\n",
-	       filename, errstr);
-      free (filename);
-      return;
-    }
-  json_flush_to_file (outf);
-  fclose (outf);
-  free (filename);
-}
+public:
+  json_file_output_format (diagnostic_context &context,
+			   const char *base_file_name)
+  : json_output_format (context),
+    m_base_file_name (xstrdup (base_file_name))
+  {
+  }
 
-/* Callback for diagnostic_context::m_diagrams.m_emission_cb.  */
+  ~json_file_output_format ()
+  {
+    char *filename = concat (m_base_file_name, ".gcc.json", NULL);
+    free (m_base_file_name);
+    m_base_file_name = nullptr;
+    FILE *outf = fopen (filename, "w");
+    if (!outf)
+      {
+	const char *errstr = xstrerror (errno);
+	fnotice (stderr, "error: unable to open '%s' for writing: %s\n",
+		 filename, errstr);
+	free (filename);
+	return;
+      }
+    flush_to_file (outf);
+    fclose (outf);
+    free (filename);
+  }
 
-static void
-json_emit_diagram (diagnostic_context *,
-		   const diagnostic_diagram &)
-{
-  /* No-op.  */
-}
+private:
+  char *m_base_file_name;
+};
 
 /* Populate CONTEXT in preparation for JSON output (either to stderr, or
    to a file).  */
@@ -339,17 +358,8 @@ json_emit_diagram (diagnostic_context *,
 static void
 diagnostic_output_format_init_json (diagnostic_context *context)
 {
-  /* Set up top-level JSON array.  */
-  if (toplevel_array == NULL)
-    toplevel_array = new json::array ();
-
   /* Override callbacks.  */
-  context->begin_diagnostic = json_begin_diagnostic;
-  context->end_diagnostic = json_end_diagnostic;
-  context->begin_group_cb = json_begin_group;
-  context->end_group_cb =  json_end_group;
   context->print_path = NULL; /* handled in json_end_diagnostic.  */
-  context->m_diagrams.m_emission_cb = json_emit_diagram;
 
   /* The metadata is handled in JSON format, rather than as text.  */
   context->show_cwe = false;
@@ -368,7 +378,8 @@ void
 diagnostic_output_format_init_json_stderr (diagnostic_context *context)
 {
   diagnostic_output_format_init_json (context);
-  context->final_cb = json_stderr_final_cb;
+  delete context->m_output_format;
+  context->m_output_format = new json_stderr_output_format (*context);
 }
 
 /* Populate CONTEXT in preparation for JSON output to a file named
@@ -379,8 +390,9 @@ diagnostic_output_format_init_json_file (diagnostic_context *context,
 					 const char *base_file_name)
 {
   diagnostic_output_format_init_json (context);
-  context->final_cb = json_file_final_cb;
-  json_output_base_file_name = xstrdup (base_file_name);
+  delete context->m_output_format;
+  context->m_output_format = new json_file_output_format (*context,
+							  base_file_name);
 }
 
 #if CHECKING_P
diff --git a/gcc/diagnostic-format-sarif.cc b/gcc/diagnostic-format-sarif.cc
index f56c4ce033d..d8cca21d834 100644
--- a/gcc/diagnostic-format-sarif.cc
+++ b/gcc/diagnostic-format-sarif.cc
@@ -215,6 +215,9 @@ private:
   json::object *
   make_reporting_descriptor_reference_object_for_cwe_id (int cwe_id);
   json::object *make_artifact_object (const char *filename);
+  char *get_source_lines (const char *filename,
+			  int start_line,
+			  int end_line) const;
   json::object *maybe_make_artifact_content_object (const char *filename) const;
   json::object *maybe_make_artifact_content_object (const char *filename,
 						    int start_line,
@@ -248,8 +251,6 @@ private:
   int m_tabstop;
 };
 
-static sarif_builder *the_builder;
-
 /* class sarif_object : public json::object.  */
 
 sarif_property_bag &
@@ -1540,7 +1541,8 @@ json::object *
 sarif_builder::maybe_make_artifact_content_object (const char *filename) const
 {
   /* Let input.cc handle any charset conversion.  */
-  char_span utf8_content = get_source_file_content (filename);
+  char_span utf8_content
+    = m_context->m_file_cache->get_source_file_content (filename);
   if (!utf8_content)
     return NULL;
 
@@ -1558,16 +1560,17 @@ sarif_builder::maybe_make_artifact_content_object (const char *filename) const
 /* Attempt to read the given range of lines from FILENAME; return
    a freshly-allocated 0-terminated buffer containing them, or NULL.  */
 
-static char *
-get_source_lines (const char *filename,
-		  int start_line,
-		  int end_line)
+char *
+sarif_builder::get_source_lines (const char *filename,
+				 int start_line,
+				 int end_line) const
 {
   auto_vec<char> result;
 
   for (int line = start_line; line <= end_line; line++)
     {
-      char_span line_content = location_get_source_line (filename, line);
+      char_span line_content
+	= m_context->m_file_cache->get_source_line (filename, line);
       if (!line_content.get_buffer ())
 	return NULL;
       result.reserve (line_content.length () + 1);
@@ -1680,82 +1683,6 @@ sarif_builder::make_artifact_content_object (const char *text) const
   return content_obj;
 }
 
-/* No-op implementation of "begin_diagnostic" for SARIF output.  */
-
-static void
-sarif_begin_diagnostic (diagnostic_context *, diagnostic_info *)
-{
-}
-
-/* Implementation of "end_diagnostic" for SARIF output.  */
-
-static void
-sarif_end_diagnostic (diagnostic_context *context, diagnostic_info *diagnostic,
-		      diagnostic_t orig_diag_kind)
-{
-  gcc_assert (the_builder);
-  the_builder->end_diagnostic (context, diagnostic, orig_diag_kind);
-}
-
-/* No-op implementation of "begin_group_cb" for SARIF output.  */
-
-static void
-sarif_begin_group (diagnostic_context *)
-{
-}
-
-/* Implementation of "end_group_cb" for SARIF output.  */
-
-static void
-sarif_end_group (diagnostic_context *)
-{
-  gcc_assert (the_builder);
-  the_builder->end_group ();
-}
-
-/* Flush the top-level array to OUTF.  */
-
-static void
-sarif_flush_to_file (FILE *outf)
-{
-  gcc_assert (the_builder);
-  the_builder->flush_to_file (outf);
-  delete the_builder;
-  the_builder = NULL;
-}
-
-/* Callback for final cleanup for SARIF output to stderr.  */
-
-static void
-sarif_stderr_final_cb (diagnostic_context *)
-{
-  gcc_assert (the_builder);
-  sarif_flush_to_file (stderr);
-}
-
-static char *sarif_output_base_file_name;
-
-/* Callback for final cleanup for SARIF output to a file.  */
-
-static void
-sarif_file_final_cb (diagnostic_context *)
-{
-  char *filename = concat (sarif_output_base_file_name, ".sarif", NULL);
-  FILE *outf = fopen (filename, "w");
-  if (!outf)
-    {
-      const char *errstr = xstrerror (errno);
-      fnotice (stderr, "error: unable to open '%s' for writing: %s\n",
-	       filename, errstr);
-      free (filename);
-      return;
-    }
-  gcc_assert (the_builder);
-  sarif_flush_to_file (outf);
-  fclose (outf);
-  free (filename);
-}
-
 /* Callback for diagnostic_context::ice_handler_cb for when an ICE
    occurs.  */
 
@@ -1773,15 +1700,89 @@ sarif_ice_handler (diagnostic_context *context)
   fnotice (stderr, "Internal compiler error:\n");
 }
 
-/* Callback for diagnostic_context::m_diagrams.m_emission_cb.  */
+class sarif_output_format : public diagnostic_output_format
+{
+public:
+  void on_begin_group () final override
+  {
+    /* No-op,  */
+  }
+  void on_end_group () final override
+  {
+    m_builder.end_group ();
+  }
+  void
+  on_begin_diagnostic (diagnostic_info *) final override
+  {
+    /* No-op,  */
+  }
+  void
+  on_end_diagnostic (diagnostic_info *diagnostic,
+		     diagnostic_t orig_diag_kind) final override
+  {
+    m_builder.end_diagnostic (&m_context, diagnostic, orig_diag_kind);
+  }
+  void on_diagram (const diagnostic_diagram &diagram) final override
+  {
+    m_builder.emit_diagram (&m_context, diagram);
+  }
 
-static void
-sarif_emit_diagram (diagnostic_context *context,
-		    const diagnostic_diagram &diagram)
+protected:
+  sarif_output_format (diagnostic_context &context)
+  : diagnostic_output_format (context),
+    m_builder (&context)
+  {}
+
+  sarif_builder m_builder;
+};
+
+class sarif_stream_output_format : public sarif_output_format
 {
-  gcc_assert (the_builder);
-  the_builder->emit_diagram (context, diagram);
-}
+public:
+  sarif_stream_output_format (diagnostic_context &context, FILE *stream)
+  : sarif_output_format (context),
+    m_stream (stream)
+  {
+  }
+  ~sarif_stream_output_format ()
+  {
+    m_builder.flush_to_file (m_stream);
+  }
+private:
+  FILE *m_stream;
+};
+
+class sarif_file_output_format : public sarif_output_format
+{
+public:
+  sarif_file_output_format (diagnostic_context &context,
+			   const char *base_file_name)
+  : sarif_output_format (context),
+    m_base_file_name (xstrdup (base_file_name))
+  {
+  }
+  ~sarif_file_output_format ()
+  {
+    char *filename = concat (m_base_file_name, ".sarif", NULL);
+    free (m_base_file_name);
+    m_base_file_name = nullptr;
+    FILE *outf = fopen (filename, "w");
+    if (!outf)
+      {
+	const char *errstr = xstrerror (errno);
+	fnotice (stderr, "error: unable to open '%s' for writing: %s\n",
+		 filename, errstr);
+	free (filename);
+	return;
+      }
+    m_builder.flush_to_file (outf);
+    fclose (outf);
+    free (filename);
+  }
+
+private:
+  char *m_base_file_name;
+};
 
 /* Populate CONTEXT in preparation for SARIF output (either to stderr, or
    to a file).  */
@@ -1789,16 +1790,9 @@ sarif_emit_diagram (diagnostic_context *context,
 static void
 diagnostic_output_format_init_sarif (diagnostic_context *context)
 {
-  the_builder = new sarif_builder (context);
-
   /* Override callbacks.  */
-  context->begin_diagnostic = sarif_begin_diagnostic;
-  context->end_diagnostic = sarif_end_diagnostic;
-  context->begin_group_cb = sarif_begin_group;
-  context->end_group_cb =  sarif_end_group;
   context->print_path = NULL; /* handled in sarif_end_diagnostic.  */
   context->ice_handler_cb = sarif_ice_handler;
-  context->m_diagrams.m_emission_cb = sarif_emit_diagram;
 
   /* The metadata is handled in SARIF format, rather than as text.  */
   context->show_cwe = false;
@@ -1817,7 +1811,8 @@ void
 diagnostic_output_format_init_sarif_stderr (diagnostic_context *context)
 {
   diagnostic_output_format_init_sarif (context);
-  context->final_cb = sarif_stderr_final_cb;
+  delete context->m_output_format;
+  context->m_output_format = new sarif_stream_output_format (*context, stderr);
 }
 
 /* Populate CONTEXT in preparation for SARIF output to a file named
@@ -1825,9 +1820,22 @@ diagnostic_output_format_init_sarif_stderr (diagnostic_context *context)
 
 void
 diagnostic_output_format_init_sarif_file (diagnostic_context *context,
-					 const char *base_file_name)
+					  const char *base_file_name)
+{
+  diagnostic_output_format_init_sarif (context);
+  delete context->m_output_format;
+  context->m_output_format = new sarif_file_output_format (*context,
+							   base_file_name);
+}
+
+/* Populate CONTEXT in preparation for SARIF output to STREAM.  */
+
+void
+diagnostic_output_format_init_sarif_stream (diagnostic_context *context,
+					    FILE *stream)
 {
   diagnostic_output_format_init_sarif (context);
-  context->final_cb = sarif_file_final_cb;
-  sarif_output_base_file_name = xstrdup (base_file_name);
+  delete context->m_output_format;
+  context->m_output_format = new sarif_stream_output_format (*context,
+							     stream);
 }
diff --git a/gcc/diagnostic-show-locus.cc b/gcc/diagnostic-show-locus.cc
index 0778223ccb5..4439d9a4804 100644
--- a/gcc/diagnostic-show-locus.cc
+++ b/gcc/diagnostic-show-locus.cc
@@ -2876,7 +2876,7 @@ diagnostic_show_locus (diagnostic_context * context,
 	    {
 	      expanded_location exploc
 		= layout.get_expanded_location (line_span);
-	      context->start_span (context, exploc);
+	      context->m_text_callbacks.start_span (context, exploc);
 	    }
 	}
       /* Iterate over the lines within this span (using linenum_arith_t to
diff --git a/gcc/diagnostic.cc b/gcc/diagnostic.cc
index b4bbd60590b..65a78840a66 100644
--- a/gcc/diagnostic.cc
+++ b/gcc/diagnostic.cc
@@ -150,28 +150,6 @@ diagnostic_set_caret_max_width (diagnostic_context *context, int value)
   context->m_source_printing.max_width = value;
 }
 
-/* Default implementation of final_cb.  */
-
-static void
-default_diagnostic_final_cb (diagnostic_context *context)
-{
-  /* Some of the errors may actually have been warnings.  */
-  if (diagnostic_kind_count (context, DK_WERROR))
-    {
-      /* -Werror was given.  */
-      if (context->warning_as_error_requested)
-	pp_verbatim (context->printer,
-		     _("%s: all warnings being treated as errors"),
-		     progname);
-      /* At least one -Werror= was given.  */
-      else
-	pp_verbatim (context->printer,
-		     _("%s: some warnings being treated as errors"),
-		     progname);
-      pp_newline_and_flush (context->printer);
-    }
-}
-
 /* Initialize the diagnostic message outputting machinery.  */
 void
 diagnostic_initialize (diagnostic_context *context, int n_opts)
@@ -209,7 +187,7 @@ diagnostic_initialize (diagnostic_context *context, int n_opts)
   context->max_errors = 0;
   context->internal_error = NULL;
   diagnostic_starter (context) = default_diagnostic_starter;
-  context->start_span = default_diagnostic_start_span_fn;
+  context->m_text_callbacks.start_span = default_diagnostic_start_span_fn;
   diagnostic_finalizer (context) = default_diagnostic_finalizer;
   context->option_enabled = NULL;
   context->option_state = NULL;
@@ -242,15 +220,12 @@ diagnostic_initialize (diagnostic_context *context, int n_opts)
   context->edit_context_ptr = NULL;
   context->diagnostic_group_nesting_depth = 0;
   context->diagnostic_group_emission_count = 0;
-  context->begin_group_cb = NULL;
-  context->end_group_cb = NULL;
-  context->final_cb = default_diagnostic_final_cb;
+  context->m_output_format = new diagnostic_text_output_format (*context);
   context->set_locations_cb = nullptr;
   context->ice_handler_cb = NULL;
   context->includes_seen = NULL;
   context->m_client_data_hooks = NULL;
   context->m_diagrams.m_theme = NULL;
-  context->m_diagrams.m_emission_cb = NULL;
   diagnostics_text_art_charset_init (context,
 				     DIAGNOSTICS_TEXT_ART_CHARSET_DEFAULT);
 }
@@ -326,8 +301,8 @@ void diagnostic_initialize_input_context (diagnostic_context *context,
 void
 diagnostic_finish (diagnostic_context *context)
 {
-  if (context->final_cb)
-    context->final_cb (context);
+  delete context->m_output_format;
+  context->m_output_format= nullptr;
 
   if (context->m_diagrams.m_theme)
     {
@@ -1508,6 +1483,8 @@ diagnostic_report_diagnostic (diagnostic_context *context,
   location_t location = diagnostic_location (diagnostic);
   diagnostic_t orig_diag_kind = diagnostic->kind;
 
+  gcc_assert (context->m_output_format);
+
   /* Give preference to being able to inhibit warnings, before they
      get reclassified to something else.  */
   bool report_warning_p = true;
@@ -1598,14 +1575,11 @@ diagnostic_report_diagnostic (diagnostic_context *context,
 
   /* Is this the initial diagnostic within the stack of groups?  */
   if (context->diagnostic_group_emission_count == 0)
-    {
-      if (context->begin_group_cb)
-	context->begin_group_cb (context);
-    }
+    context->m_output_format->on_begin_group ();
   context->diagnostic_group_emission_count++;
 
   pp_format (context->printer, &diagnostic->message);
-  (*diagnostic_starter (context)) (context, diagnostic);
+  context->m_output_format->on_begin_diagnostic (diagnostic);
   pp_output_formatted_text (context->printer);
   if (context->show_cwe)
     print_any_cwe (context, diagnostic);
@@ -1613,7 +1587,7 @@ diagnostic_report_diagnostic (diagnostic_context *context,
     print_any_rules (context, diagnostic);
   if (context->show_option_requested)
     print_option_information (context, diagnostic, orig_diag_kind);
-  (*diagnostic_finalizer (context)) (context, diagnostic, orig_diag_kind);
+  context->m_output_format->on_end_diagnostic (diagnostic, orig_diag_kind);
   switch (context->extra_output_kind)
     {
     default:
@@ -2214,22 +2188,8 @@ diagnostic_emit_diagram (diagnostic_context *context,
   if (context->m_diagrams.m_theme == nullptr)
     return;
 
-  if (context->m_diagrams.m_emission_cb)
-    {
-      context->m_diagrams.m_emission_cb (context, diagram);
-      return;
-    }
-
-  /* Default implementation.  */
-  char *saved_prefix = pp_take_prefix (context->printer);
-  pp_set_prefix (context->printer, NULL);
-  /* Use a newline before and after and a two-space indent
-     to make the diagram stand out a little from the wall of text.  */
-  pp_newline (context->printer);
-  diagram.get_canvas ().print_to_pp (context->printer, "  ");
-  pp_newline (context->printer);
-  pp_set_prefix (context->printer, saved_prefix);
-  pp_flush (context->printer);
+  gcc_assert (context->m_output_format);
+  context->m_output_format->on_diagram (diagram);
 }
 \f
 /* Special case error functions.  Most are implemented in terms of the
@@ -2331,14 +2291,59 @@ auto_diagnostic_group::~auto_diagnostic_group ()
 	 If any diagnostics were emitted, give the context a chance
 	 to do something.  */
       if (global_dc->diagnostic_group_emission_count > 0)
-	{
-	  if (global_dc->end_group_cb)
-	    global_dc->end_group_cb (global_dc);
-	}
+	global_dc->m_output_format->on_end_group ();
       global_dc->diagnostic_group_emission_count = 0;
     }
 }
 
+/* class diagnostic_text_output_format : public diagnostic_output_format.  */
+
+diagnostic_text_output_format::~diagnostic_text_output_format ()
+{
+  /* Some of the errors may actually have been warnings.  */
+  if (diagnostic_kind_count (&m_context, DK_WERROR))
+    {
+      /* -Werror was given.  */
+      if (m_context.warning_as_error_requested)
+	pp_verbatim (m_context.printer,
+		     _("%s: all warnings being treated as errors"),
+		     progname);
+      /* At least one -Werror= was given.  */
+      else
+	pp_verbatim (m_context.printer,
+		     _("%s: some warnings being treated as errors"),
+		     progname);
+      pp_newline_and_flush (m_context.printer);
+    }
+}
+
+void
+diagnostic_text_output_format::on_begin_diagnostic (diagnostic_info *diagnostic)
+{
+  (*diagnostic_starter (&m_context)) (&m_context, diagnostic);
+}
+
+void
+diagnostic_text_output_format::on_end_diagnostic (diagnostic_info *diagnostic,
+						  diagnostic_t orig_diag_kind)
+{
+  (*diagnostic_finalizer (&m_context)) (&m_context, diagnostic, orig_diag_kind);
+}
+
+void
+diagnostic_text_output_format::on_diagram (const diagnostic_diagram &diagram)
+{
+  char *saved_prefix = pp_take_prefix (m_context.printer);
+  pp_set_prefix (m_context.printer, NULL);
+  /* Use a newline before and after and a two-space indent
+     to make the diagram stand out a little from the wall of text.  */
+  pp_newline (m_context.printer);
+  diagram.get_canvas ().print_to_pp (m_context.printer, "  ");
+  pp_newline (m_context.printer);
+  pp_set_prefix (m_context.printer, saved_prefix);
+  pp_flush (m_context.printer);
+}
+
 /* Set the output format for CONTEXT to FORMAT, using BASE_FILE_NAME for
    file-based output formats.  */
 
diff --git a/gcc/diagnostic.h b/gcc/diagnostic.h
index 1f2d93c3d00..a2c8740cbd0 100644
--- a/gcc/diagnostic.h
+++ b/gcc/diagnostic.h
@@ -177,6 +177,51 @@ class diagnostic_client_data_hooks;
 class logical_location;
 class diagnostic_diagram;
 
+/* Abstract base class for a particular output format for diagnostics;
+   each value of -fdiagnostics-output-format= will have its own
+   implementation.  */
+
+class diagnostic_output_format
+{
+public:
+  virtual ~diagnostic_output_format () {}
+
+  virtual void on_begin_group () = 0;
+  virtual void on_end_group () = 0;
+  virtual void on_begin_diagnostic (diagnostic_info *) = 0;
+  virtual void on_end_diagnostic (diagnostic_info *,
+				  diagnostic_t orig_diag_kind) = 0;
+  virtual void on_diagram (const diagnostic_diagram &diagram) = 0;
+
+protected:
+  diagnostic_output_format (diagnostic_context &context)
+  : m_context (context)
+  {}
+
+  diagnostic_context &m_context;
+};
+
+/* Subclass of diagnostic_output_format for classic text-based output
+   to stderr.
+
+   Uses diagnostic_context.m_text_callbacks to provide client-specific
+   textual output (e.g. include paths, macro expansions, etc).  */
+
+class diagnostic_text_output_format : public diagnostic_output_format
+{
+public:
+  diagnostic_text_output_format (diagnostic_context &context)
+  : diagnostic_output_format (context)
+  {}
+  ~diagnostic_text_output_format ();
+  void on_begin_group () override {}
+  void on_end_group () override {}
+  void on_begin_diagnostic (diagnostic_info *) override;
+  void on_end_diagnostic (diagnostic_info *,
+			  diagnostic_t orig_diag_kind) override;
+  void on_diagram (const diagnostic_diagram &diagram) override;
+};
+
 /* This data structure bundles altogether any information relevant to
    the context of a diagnostic message.  */
 struct diagnostic_context
@@ -264,22 +309,25 @@ struct diagnostic_context
   /* Maximum number of errors to report.  */
   int max_errors;
 
-  /* This function is called before any message is printed out.  It is
-     responsible for preparing message prefix and such.  For example, it
-     might say:
-     In file included from "/usr/local/include/curses.h:5:
-                      from "/home/gdr/src/nifty_printer.h:56:
-                      ...
-  */
-  diagnostic_starter_fn begin_diagnostic;
-
-  /* This function is called by diagnostic_show_locus in between
-     disjoint spans of source code, so that the context can print
-     something to indicate that a new span of source code has begun.  */
-  diagnostic_start_span_fn start_span;
-
-  /* This function is called after the diagnostic message is printed.  */
-  diagnostic_finalizer_fn end_diagnostic;
+  /* Client-supplied callbacks for use in text output.  */
+  struct {
+    /* This function is called before any message is printed out.  It is
+       responsible for preparing message prefix and such.  For example, it
+       might say:
+       In file included from "/usr/local/include/curses.h:5:
+       from "/home/gdr/src/nifty_printer.h:56:
+       ...
+    */
+    diagnostic_starter_fn begin_diagnostic;
+
+    /* This function is called by diagnostic_show_locus in between
+       disjoint spans of source code, so that the context can print
+       something to indicate that a new span of source code has begun.  */
+    diagnostic_start_span_fn start_span;
+
+    /* This function is called after the diagnostic message is printed.  */
+    diagnostic_finalizer_fn end_diagnostic;
+  } m_text_callbacks;
 
   /* Client hook to report an internal error.  */
   void (*internal_error) (diagnostic_context *, const char *, va_list *);
@@ -402,18 +450,9 @@ struct diagnostic_context
      diagnostic_group was pushed.  */
   int diagnostic_group_emission_count;
 
-  /* Optional callbacks for handling diagnostic groups.  */
-
-  /* If non-NULL, this will be called immediately before the first
-     time a diagnostic is emitted within a stack of groups.  */
-  void (*begin_group_cb) (diagnostic_context * context);
-
-  /* If non-NULL, this will be called when a stack of groups is
-     popped if any diagnostics were emitted within that group.  */
-  void (*end_group_cb) (diagnostic_context * context);
-
-  /* Callback for final cleanup.  */
-  void (*final_cb) (diagnostic_context *context);
+  /* How to output diagnostics (text vs a structured format such as JSON).
+     Must be non-NULL; owned by context.  */
+  diagnostic_output_format *m_output_format;
 
   /* Callback to set the locations of call sites along the inlining
      stack corresponding to a diagnostic location.  Needed to traverse
@@ -441,9 +480,6 @@ struct diagnostic_context
        Can be NULL (if text art is disabled).  */
     text_art::theme *m_theme;
 
-    /* Callback for emitting diagrams.  */
-    void (*m_emission_cb) (diagnostic_context *context,
-			   const diagnostic_diagram &diagram);
   } m_diagrams;
 };
 
@@ -454,12 +490,13 @@ diagnostic_inhibit_notes (diagnostic_context * context)
 }
 
 
-/* Client supplied function to announce a diagnostic.  */
-#define diagnostic_starter(DC) (DC)->begin_diagnostic
+/* Client supplied function to announce a diagnostic
+   (for text-based diagnostic output).  */
+#define diagnostic_starter(DC) (DC)->m_text_callbacks.begin_diagnostic
 
 /* Client supplied function called after a diagnostic message is
-   displayed.  */
-#define diagnostic_finalizer(DC) (DC)->end_diagnostic
+   displayed (for text-based diagnostic output).  */
+#define diagnostic_finalizer(DC) (DC)->m_text_callbacks.end_diagnostic
 
 /* Extension hooks for client.  */
 #define diagnostic_context_auxiliary_data(DC) (DC)->x_data
@@ -638,6 +675,8 @@ extern void diagnostic_output_format_init_json_file (diagnostic_context *context
 extern void diagnostic_output_format_init_sarif_stderr (diagnostic_context *context);
 extern void diagnostic_output_format_init_sarif_file (diagnostic_context *context,
 						      const char *base_file_name);
+extern void diagnostic_output_format_init_sarif_stream (diagnostic_context *context,
+							FILE *stream);
 
 /* Compute the number of digits in the decimal representation of an integer.  */
 extern int num_digits (int);
diff --git a/gcc/fortran/error.cc b/gcc/fortran/error.cc
index 378f7393133..0b6c850e6d9 100644
--- a/gcc/fortran/error.cc
+++ b/gcc/fortran/error.cc
@@ -1637,7 +1637,7 @@ void
 gfc_diagnostics_init (void)
 {
   diagnostic_starter (global_dc) = gfc_diagnostic_starter;
-  global_dc->start_span = gfc_diagnostic_start_span;
+  global_dc->m_text_callbacks.start_span = gfc_diagnostic_start_span;
   diagnostic_finalizer (global_dc) = gfc_diagnostic_finalizer;
   diagnostic_format_decoder (global_dc) = gfc_format_decoder;
   global_dc->m_source_printing.caret_chars[0] = '1';
diff --git a/gcc/input.cc b/gcc/input.cc
index eaf301ec7c1..0b00b8923db 100644
--- a/gcc/input.cc
+++ b/gcc/input.cc
@@ -946,7 +946,7 @@ file_cache_slot::read_line_num (size_t line_num,
    If the function fails, a NULL char_span is returned.  */
 
 char_span
-location_get_source_line (const char *file_path, int line)
+file_cache::get_source_line (const char *file_path, int line)
 {
   char *buffer = NULL;
   ssize_t len;
@@ -957,9 +957,7 @@ location_get_source_line (const char *file_path, int line)
   if (file_path == NULL)
     return char_span (NULL, 0);
 
-  diagnostic_file_cache_init ();
-
-  file_cache_slot *c = global_dc->m_file_cache->lookup_or_add_file (file_path);
+  file_cache_slot *c = lookup_or_add_file (file_path);
   if (c == NULL)
     return char_span (NULL, 0);
 
@@ -970,6 +968,13 @@ location_get_source_line (const char *file_path, int line)
   return char_span (buffer, len);
 }
 
+char_span
+location_get_source_line (const char *file_path, int line)
+{
+  diagnostic_file_cache_init ();
+  return global_dc->m_file_cache->get_source_line (file_path, line);
+}
+
 /* Return a NUL-terminated copy of the source text between two locations, or
    NULL if the arguments are invalid.  The caller is responsible for freeing
    the return value.  */
@@ -1062,6 +1067,15 @@ get_source_text_between (location_t start, location_t end)
   return xstrdup (buf);
 }
 
+
+char_span
+file_cache::get_source_file_content (const char *file_path)
+{
+  file_cache_slot *c = lookup_or_add_file (file_path);
+  return c->get_full_file_content ();
+}
+
+
 /* Get a borrowed char_span to the full content of FILE_PATH
    as decoded according to the input charset, encoded as UTF-8.  */
 
@@ -1069,9 +1083,7 @@ char_span
 get_source_file_content (const char *file_path)
 {
   diagnostic_file_cache_init ();
-
-  file_cache_slot *c = global_dc->m_file_cache->lookup_or_add_file (file_path);
-  return c->get_full_file_content ();
+  return global_dc->m_file_cache->get_source_file_content (file_path);
 }
 
 /* Determine if FILE_PATH missing a trailing newline on its final line.
diff --git a/gcc/input.h b/gcc/input.h
index d1087b7a9e8..bfd71dfd619 100644
--- a/gcc/input.h
+++ b/gcc/input.h
@@ -150,6 +150,9 @@ class file_cache
   void initialize_input_context (diagnostic_input_charset_callback ccb,
 				 bool should_skip_bom);
 
+  char_span get_source_file_content (const char *file_path);
+  char_span get_source_line (const char *file_path, int line);
+
  private:
   file_cache_slot *evicted_cache_tab_entry (unsigned *highest_use_count);
   file_cache_slot *add_file (const char *file_path);
diff --git a/gcc/jit/dummy-frontend.cc b/gcc/jit/dummy-frontend.cc
index 8b7294e32cc..a729086bafb 100644
--- a/gcc/jit/dummy-frontend.cc
+++ b/gcc/jit/dummy-frontend.cc
@@ -589,8 +589,8 @@ jit_langhook_init (void)
     }
 
   gcc_assert (global_dc);
-  global_dc->begin_diagnostic = jit_begin_diagnostic;
-  global_dc->end_diagnostic = jit_end_diagnostic;
+  diagnostic_starter (global_dc) = jit_begin_diagnostic;
+  diagnostic_finalizer (global_dc) = jit_end_diagnostic;
 
   build_common_tree_nodes (false);
 
diff --git a/gcc/selftest-diagnostic.cc b/gcc/selftest-diagnostic.cc
index 5469e555fc0..d632c51b2d9 100644
--- a/gcc/selftest-diagnostic.cc
+++ b/gcc/selftest-diagnostic.cc
@@ -39,7 +39,7 @@ test_diagnostic_context::test_diagnostic_context ()
   m_source_printing.enabled = true;
   m_source_printing.show_labels_p = true;
   show_column = true;
-  start_span = start_span_cb;
+  m_text_callbacks.start_span = start_span_cb;
   m_source_printing.min_margin_width = 6;
   m_source_printing.max_width = 80;
 }
diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic_group_plugin.c b/gcc/testsuite/gcc.dg/plugin/diagnostic_group_plugin.c
index 3396b384163..0a4f25efe24 100644
--- a/gcc/testsuite/gcc.dg/plugin/diagnostic_group_plugin.c
+++ b/gcc/testsuite/gcc.dg/plugin/diagnostic_group_plugin.c
@@ -181,27 +181,31 @@ test_diagnostic_start_span_fn (diagnostic_context *context,
   pp_newline (context->printer);
 }
 
-/* Custom diagnostic callback: loudly announce a new diagnostic group.  */
+/* Custom output format subclass.  */
 
-static void
-test_begin_group_cb (diagnostic_context * context)
+class test_output_format : public diagnostic_text_output_format
 {
-  pp_string (context->printer,
-	     "================================= BEGIN GROUP ==============================");
-  pp_newline (context->printer);
-}
-
-/* Custom diagnostic callback: loudly announce the end of a
-   diagnostic group.  */
+ public:
+  test_output_format (diagnostic_context &context)
+  : diagnostic_text_output_format (context)
+  {}
 
-static void
-test_end_group_cb (diagnostic_context * context)
-{
-  pp_set_prefix (context->printer, NULL);
-  pp_string (context->printer,
-	     "---------------------------------- END GROUP -------------------------------");
-  pp_newline_and_flush (context->printer);
-}
+  void on_begin_group () final override
+  {
+    /* Loudly announce a new diagnostic group.  */
+    pp_string (m_context.printer,
+	       "================================= BEGIN GROUP ==============================");
+    pp_newline (m_context.printer);
+  }
+  void on_end_group () final override
+  {
+    /* Loudly announce the end of a diagnostic group.  */
+    pp_set_prefix (m_context.printer, NULL);
+    pp_string (m_context.printer,
+	       "---------------------------------- END GROUP -------------------------------");
+    pp_newline_and_flush (m_context.printer);
+  }
+};
 
 /* Entrypoint for the plugin.
    Install custom callbacks into the global_dc.
@@ -220,9 +224,8 @@ plugin_init (struct plugin_name_args *plugin_info,
     return 1;
 
   diagnostic_starter (global_dc) = test_diagnostic_starter;
-  global_dc->start_span = test_diagnostic_start_span_fn;
-  global_dc->begin_group_cb = test_begin_group_cb;
-  global_dc->end_group_cb = test_end_group_cb;
+  global_dc->m_text_callbacks.start_span = test_diagnostic_start_span_fn;
+  global_dc->m_output_format = new test_output_format (*global_dc);
 
   pass_info.pass = new pass_test_groups (g);
   pass_info.reference_pass_name = "*warn_function_noreturn";
diff --git a/gcc/tree-diagnostic-path.cc b/gcc/tree-diagnostic-path.cc
index b1b483a27aa..97ee0f04091 100644
--- a/gcc/tree-diagnostic-path.cc
+++ b/gcc/tree-diagnostic-path.cc
@@ -206,7 +206,7 @@ struct event_range
 	  = linemap_client_expand_location_to_spelling_point
 	  (initial_loc, LOCATION_ASPECT_CARET);
 	if (exploc.file != LOCATION_FILE (dc->last_location))
-	  dc->start_span (dc, exploc);
+	  dc->m_text_callbacks.start_span (dc, exploc);
       }
 
     /* If we have an UNKNOWN_LOCATION (or BUILTINS_LOCATION) as the

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2023-10-02 16:25 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-10-02 16:25 [gcc r14-4368] diagnostics: add diagnostic_output_format class David Malcolm

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).