[pushed:,r15-3749] diagnostics: convert text hooks to use diagnostic_text_output_format [PR116613]

Message ID 20240920230546.1850339-1-dmalcolm@redhat.com
State Committed
Commit 7da682c896f949259eaf820be17b5781df2cbead
Headers
Series [pushed:,r15-3749] diagnostics: convert text hooks to use diagnostic_text_output_format [PR116613] |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gcc_build--master-arm fail Build failed
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 fail Build failed

Commit Message

David Malcolm Sept. 20, 2024, 11:05 p.m. UTC
  The diagnostic_starter and diagnostic_finalizer callbacks and most of
their support subroutines are only used by the "text" output format.

Emphasize this and reduce the binding with diagnostic_context
by renaming the callbacks to add "_text" in their names, and converting
the first param from diagnostic_context * to
diagnostic_text_output_output &.  Update the various subroutines used
by diagnostic starter/finalizer callbacks to also take a
diagnostic_text_output_output & rather than a diagnostic_context *.
Move m_includes and m_last_seen from the context to the text output.

Use the text_output's get_printer () rather than the context's
m_printer, which should ease the transition to multiple output sinks.

No functional change intended.

Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
Pushed to trunk as r15-3749-g7da682c896f949.

gcc/c-family/ChangeLog:
	PR other/116613
	* c-opts.cc: Include "diagnostic-format-text.h".
	(c_diagnostic_finalizer): Rename to...
	(c_diagnostic_text_finalizer): ...this.  Convert first param
	from diagnostic_context * to diagnostic_text_output_format & and
	update accordingly.
	(c_common_diagnostics_set_defaults): Update for renamings.

gcc/ChangeLog:
	PR other/116613
	* coretypes.h (class diagnostic_text_output_format): Add forward
	decl.
	* diagnostic-format-json.cc
	(json_output_format::after_diagnostic): New.
	* diagnostic-format-sarif.cc
	(sarif_output_format::after_diagnostic): New.
	* diagnostic-format-text.cc: Use pragmas to ignore -Wformat-diag.
	(diagnostic_text_output_format::~diagnostic_text_output_format):
	Use get_printer.  Clean up m_includes_seen here, rather than
	in ~diagnostic_context.
	(diagnostic_text_output_format::on_report_diagnostic):  Use
	get_printer.  Update for callback renamings and pass *this
	to them, rather than &m_context.
	(diagnostic_text_output_format::after_diagnostic): New.
	(diagnostic_text_output_format::includes_seen_p): Move here
	from diagnostic_context/diagnostic.cc.
	(diagnostic_text_output_format::get_location_text): New.
	(maybe_line_and_column): Move here from diagnostic.cc and make
	non-static.
	(diagnostic_text_output_format::report_current_module): Move
	here from diagnostic_context/diagnostic.cc.
	(default_diagnostic_text_starter): Move here from diagnostic.cc,
	renaming from default_diagnostic_starter.
	(default_diagnostic_text_finalizer): Likewise, renaming from
	default_diagnostic_finalizer.
	* diagnostic-format-text.h
	(diagnostic_text_output_format::diagnostic_text_output_format):
	Initialize m_last_module and m_includes_seen.
	(diagnostic_text_output_format::after_diagnostic): New decl.
	(diagnostic_text_output_format::build_prefix): New decl.
	(diagnostic_text_output_format::report_current_module): New decl.
	(diagnostic_text_output_format::append_note): New decl.
	(diagnostic_text_output_format::file_name_as_prefix): New decl.
	(diagnostic_text_output_format::print_path): New decl.
	(diagnostic_text_output_format::show_column_p): New decl.
	(diagnostic_text_output_format::get_location_text): New decl.
	(diagnostic_text_output_format::includes_seen_p): New decl.
	(diagnostic_text_output_format::show_any_path): New decl.
	(diagnostic_text_output_format::m_last_module): New field.
	(diagnostic_text_output_format::m_includes_seen): New field.
	* diagnostic-format.h
	(diagnostic_output_format::after_diagnostic): New vfunc.
	(diagnostic_output_format::get_context): New.
	(diagnostic_output_format::get_diagram_theme): New.
	* diagnostic-macro-unwinding.cc: Include
	"diagnostic-format-text.h".
	(maybe_unwind_expanded_macro_loc): Convert first param from
	diagnostic_context * to diagnostic_text_output_format & and update
	accordingly.
	(virt_loc_aware_diagnostic_finalizer): Likewise.
	* diagnostic-macro-unwinding.h
	(virt_loc_aware_diagnostic_finalizer): Likewise.
	(maybe_unwind_expanded_macro_loc): Likewise.
	* diagnostic-path.cc: Include "diagnostic-format-text.h".
	(path_label::path_label): Drop "ctxt" param and add "colorize"
	and "allow_emojis" params.  Update initializations.
	(path_label::get_text): Use m_colorize rather than querying
	m_ctxt.m_printer.  Use m_allow_emojis rather than querying
	m_ctxt's diagram theme.
	(path_label::m_ctxt): Drop field.
	(path_label::m_colorize): Drop field.
	(path_label::m_allow_emojis): Drop field.
	(event_range::event_range): Drop param "ctxt".  Add params
	"colorize_labels" and "allow_emojis".
	(event_range::print): Convert first param from
	diagnostic_context & to diagnostic_text_output_format & and update
	accordingly.
	(path_summary::path_summary): Likewise.
	(path_summary::print_swimlane_for_event_range): Likewise.
	(print_path_summary_as_text): Likewise for 3rd param.
	(diagnostic_context::print_path): Convert to...
	(diagnostic_text_output_format::print_path): ...this.
	(selftest::test_empty_path): Update to use a
	diagnostic_text_output_format.
	(selftest::test_intraprocedural_path): Likewise.
	(selftest::test_interprocedural_path_1): Likewise.
	(selftest::test_interprocedural_path_2): Likewise.
	(selftest::test_recursion): Likewise.
	(selftest::test_control_flow_1): Likewise.
	(selftest::test_control_flow_2): Likewise.
	(selftest::test_control_flow_3): Likewise.
	(selftest::assert_cfg_edge_path_streq): Likewise.
	(selftest::test_control_flow_5): Likewise.
	(selftest::test_control_flow_6): Likewise.
	* diagnostic.cc (file_name_as_prefix): Convert to...
	(diagnostic_text_output_format::file_name_as_prefix): ...this.
	(diagnostic_context::initialize): Update for renamings.
	Move m_last_module and m_includes_seen into text output.
	(diagnostic_context::finish): Likewise.
	(diagnostic_context::get_location_text): Add "colorize" param.
	(diagnostic_build_prefix): Convert to...
	(diagnostic_text_output_format::build_prefix): ...this.
	(diagnostic_context::includes_seen_p): Move from here to
	diagnostic_text_output_format/diagnostic-format-text.cc.
	(diagnostic_context::report_current_module): Likewise.
	(diagnostic_context::show_any_path): Convert to...
	(diagnostic_text_output_format::show_any_path): ...this.
	(default_diagnostic_starter): Rename and move to
	diagnostic-format-text.cc.
	(default_diagnostic_start_span_fn): Pass colorize bool
	to get_location_text.
	(default_diagnostic_finalizer): Rename and move to
	diagnostic-format-text.cc.
	(diagnostic_context::report_diagnostic): Replace call to
	show_any_path with call to new output format "after_diagnostic"
	vfunc, moving show_any_path call to the text output format.
	(diagnostic_append_note): Convert to...
	(diagnostic_text_output_format::append_note): ...this.
	(selftest::assert_location_text): Pass in false for colorization.
	* diagnostic.h (diagnostic_starter_fn): Rename to...
	(diagnostic_text_starter_fn): ...this.  Convert first param from
	diagnostic_context * to diagnostic_text_output_format &.
	(diagnostic_finalizer_fn, diagnostic_text_finalizer_fn): Likewise.
	(diagnostic_context): Update friends for renamings.
	(diagnostic_context::report_current_module): Move to text output
	format.
	(diagnostic_context::get_location_text): Add "colorize" bool.
	(diagnostic_context::includes_seen_p): Move to text output format.
	(diagnostic_context::show_any_path): Likewise.
	(diagnostic_context::print_path): Likewise.
	(diagnostic_context::m_text_callbacks): Update for renamings.
	(diagnostic_context::m_last_module): Move to text output format.
	(diagnostic_context::m_includes_seen): Likewise.
	(diagnostic_starter): Rename to...
	(diagnostic_text_starter): ...this and update return type.
	(diagnostic_finalizer): Rename to...
	(diagnostic_text_finalizer): ...this and update return type.
	(diagnostic_report_current_module): Drop decl in favor of a member
	function of diagnostic_text_output_format.
	(diagnostic_append_note): Likewise.
	(default_diagnostic_starter): Rename to...
	(default_diagnostic_text_starter): ...this, updating type.
	(default_diagnostic_finalizer): Rename to...
	(default_diagnostic_text_finalizer): ...this, updating type.
	(file_name_as_prefix): Drop decl.
	* langhooks-def.h (lhd_print_error_function): Convert first param
	from diagnostic_context * to diagnostic_text_output_format &.
	* langhooks.cc: Include "diagnostic-format-text.h".
	(lhd_print_error_function): Likewise.  Update accordingly
	* langhooks.h (lang_hooks::print_error_function): Convert first
	param from diagnostic_context * to
	diagnostic_text_output_format &.
	* tree-diagnostic.cc: Include "diagnostic-format-text.h".
	(diagnostic_report_current_function): Convert first param from
	diagnostic_context * to diagnostic_text_output_format & and update
	accordingly.
	(default_tree_diagnostic_starter): Rename to...
	(default_tree_diagnostic_text_starter): ...this.  Convert first
	param from diagnostic_context * to diagnostic_text_output_format &
	and update accordingly.
	(tree_diagnostics_defaults): Update for renamings.

gcc/cp/ChangeLog:
	PR other/116613
	* cp-tree.h (cxx_print_error_function): Convert first param
	from diagnostic_context * to diagnostic_text_output_format &.
	* error.cc: Include "diagnostic-format-text.h".
	(cxx_initialize_diagnostics): Update for renamings.
	(cxx_print_error_function): Convert first param from
	diagnostic_context * to diagnostic_text_output_format & and update
	accordingly
	(cp_diagnostic_starter): Rename to...
	(cp_diagnostic_text_starter): ...this.  Convert first
	param from diagnostic_context * to diagnostic_text_output_format &
	and update accordingly.
	(cp_print_error_function): Likewise.
	(print_instantiation_full_context): Likewise.
	(print_instantiation_partial_context_line): Likewise.
	(print_instantiation_partial_context): Likewise.
	(maybe_print_instantiation_context): Likewise.
	(maybe_print_constexpr_context): Likewise.
	(print_location): Likewise.
	(print_constrained_decl_info): Likewise.
	(print_concept_check_info): Likewise.
	(print_constraint_context_head): Likewise.
	(print_requires_expression_info): Likewise.
	(maybe_print_single_constraint_context): Likewise.

gcc/fortran/ChangeLog:
	PR other/116613
	* error.cc: Include "diagnostic-format-text.h".
	(gfc_diagnostic_starter): Rename to...
	(gfc_diagnostic_text_starter): ...this.  Convert first
	param from diagnostic_context * to diagnostic_text_output_format &
	and update accordingly.
	(gfc_diagnostic_finalizer, gfc_diagnostic_text_finalizer):
	Likewise.
	(gfc_diagnostics_init): Update for renamings.
	(gfc_diagnostics_finish): Likewise.

gcc/jit/ChangeLog:
	PR other/116613
	* dummy-frontend.cc: Include "diagnostic-format-text.h".
	(jit_begin_diagnostic): Convert first param from
	diagnostic_context * to diagnostic_text_output_format &
	(jit_end_diagnostic): Likewise.  Update accordingly.
	(jit_langhook_init): Update for renamings.

gcc/rust/ChangeLog:
	PR other/116613
	* resolve/rust-ast-resolve-expr.cc
	(funny_ice_finalizer): : Convert first param from
	diagnostic_context * to diagnostic_text_output_format &.
	(ResolveExpr::visit): Update for renaming.

gcc/testsuite/ChangeLog:
	PR other/116613
	* g++.dg/plugin/show_template_tree_color_plugin.c
	(noop_starter_fn): Rename to...
	(noop_text_starter_fn): ...this.  Update first param from dc to
	text_output.
	(plugin_init): Update for renamings.
	* gcc.dg/plugin/diagnostic_group_plugin.c
	(test_diagnostic_starter): Rename to...
	(test_diagnostic_text_starter): ...this.  Update first param from
	dc to text_output.
	(plugin_init): Update for renaming.
	* gcc.dg/plugin/diagnostic_plugin_test_show_locus.c: Include
	"diagnostic-format-text.h".
	(custom_diagnostic_finalizer): Rename to...
	(custom_diagnostic_text_finalizer): ...this.  Update first param
	from dc to text_output.
	(test_show_locus): Update for renamings.
	* gcc.dg/plugin/location_overflow_plugin.c: Include
	"diagnostic-format-text.h".
	(original_finalizer): Rename to...
	(original_text_finalizer): ...this and update type.
	(verify_unpacked_ranges): Update first param from dc to
	text_output.  Update for this and for renamings.
	(verify_no_columns): Likewise.
	(plugin_init): Update for renamings.

libcc1/ChangeLog:
	PR other/116613
	* context.cc: Include "diagnostic-format-text.h".
	(plugin_print_error_function): Update first param from
	diagnostic_context * to diagnostic_text_output_format &.

Signed-off-by: David Malcolm <dmalcolm@redhat.com>
---
 gcc/c-family/c-opts.cc                        |  16 +-
 gcc/coretypes.h                               |   1 +
 gcc/cp/cp-tree.h                              |   2 +-
 gcc/cp/error.cc                               | 149 ++++++------
 gcc/diagnostic-format-json.cc                 |   4 +
 gcc/diagnostic-format-sarif.cc                |   4 +
 gcc/diagnostic-format-text.cc                 | 186 ++++++++++++++-
 gcc/diagnostic-format-text.h                  |  31 ++-
 gcc/diagnostic-format.h                       |   7 +
 gcc/diagnostic-macro-unwinding.cc             |  19 +-
 gcc/diagnostic-macro-unwinding.h              |   4 +-
 gcc/diagnostic-path.cc                        | 214 ++++++++++--------
 gcc/diagnostic.cc                             | 205 +++--------------
 gcc/diagnostic.h                              |  71 ++----
 gcc/fortran/error.cc                          |  24 +-
 gcc/jit/dummy-frontend.cc                     |  11 +-
 gcc/langhooks-def.h                           |   2 +-
 gcc/langhooks.cc                              |   9 +-
 gcc/langhooks.h                               |   6 +-
 gcc/rust/resolve/rust-ast-resolve-expr.cc     |   9 +-
 .../plugin/show_template_tree_color_plugin.c  |   8 +-
 .../gcc.dg/plugin/diagnostic_group_plugin.c   |   8 +-
 .../diagnostic_plugin_test_show_locus.c       |  16 +-
 .../gcc.dg/plugin/location_overflow_plugin.c  |  27 +--
 gcc/tree-diagnostic.cc                        |  23 +-
 libcc1/context.cc                             |   6 +-
 26 files changed, 587 insertions(+), 475 deletions(-)
  

Patch

diff --git a/gcc/c-family/c-opts.cc b/gcc/c-family/c-opts.cc
index 2ccbdc84d858..429eabc847c7 100644
--- a/gcc/c-family/c-opts.cc
+++ b/gcc/c-family/c-opts.cc
@@ -43,6 +43,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "dumpfile.h"
 #include "file-prefix-map.h"    /* add_*_prefix_map()  */
 #include "context.h"
+#include "diagnostic-format-text.h"
 
 #ifndef DOLLARS_IN_IDENTIFIERS
 # define DOLLARS_IN_IDENTIFIERS true
@@ -168,18 +169,19 @@  c_common_option_lang_mask (void)
 
 /* Diagnostic finalizer for C/C++/Objective-C/Objective-C++.  */
 static void
-c_diagnostic_finalizer (diagnostic_context *context,
-			const diagnostic_info *diagnostic,
-			diagnostic_t)
+c_diagnostic_text_finalizer (diagnostic_text_output_format &text_output,
+			     const diagnostic_info *diagnostic,
+			     diagnostic_t)
 {
-  pretty_printer *const pp = context->m_printer;
+  pretty_printer *const pp = text_output.get_printer ();
   char *saved_prefix = pp_take_prefix (pp);
   pp_set_prefix (pp, NULL);
   pp_newline (pp);
-  diagnostic_show_locus (context, diagnostic->richloc, diagnostic->kind, pp);
+  diagnostic_show_locus (&text_output.get_context (),
+			 diagnostic->richloc, diagnostic->kind, pp);
   /* By default print macro expansion contexts in the diagnostic
      finalizer -- for tokens resulting from macro expansion.  */
-  virt_loc_aware_diagnostic_finalizer (context, diagnostic);
+  virt_loc_aware_diagnostic_finalizer (text_output, diagnostic);
   pp_set_prefix (pp, saved_prefix);
   pp_flush (pp);
 }
@@ -188,7 +190,7 @@  c_diagnostic_finalizer (diagnostic_context *context,
 void
 c_common_diagnostics_set_defaults (diagnostic_context *context)
 {
-  diagnostic_finalizer (context) = c_diagnostic_finalizer;
+  diagnostic_text_finalizer (context) = c_diagnostic_text_finalizer;
   context->m_opt_permissive = OPT_fpermissive;
 }
 
diff --git a/gcc/coretypes.h b/gcc/coretypes.h
index 0544bb8fd976..eb3072fabbae 100644
--- a/gcc/coretypes.h
+++ b/gcc/coretypes.h
@@ -159,6 +159,7 @@  struct cl_decoded_option;
 struct cl_option_handlers;
 class rich_location;
 class diagnostic_context;
+class diagnostic_text_output_format;
 class pretty_printer;
 class diagnostic_event_id_t;
 typedef const char * (*diagnostic_input_charset_callback)(const char *);
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 7baa2ccbe1eb..b9d03b8ff294 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -8170,7 +8170,7 @@  extern void cxx_print_xnode			(FILE *, tree, int);
 extern void cxx_print_decl			(FILE *, tree, int);
 extern void cxx_print_type			(FILE *, tree, int);
 extern void cxx_print_identifier		(FILE *, tree, int);
-extern void cxx_print_error_function		(diagnostic_context *,
+extern void cxx_print_error_function		(diagnostic_text_output_format &,
 						 const char *,
 						 const diagnostic_info *);
 
diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc
index c241c3e13bb0..90e3630e3aa6 100644
--- a/gcc/cp/error.cc
+++ b/gcc/cp/error.cc
@@ -40,6 +40,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "attribs.h"
 #include "pretty-print-format-impl.h"
 #include "make-unique.h"
+#include "diagnostic-format-text.h"
 
 #define pp_separate_with_comma(PP) pp_cxx_separate_with (PP, ',')
 #define pp_separate_with_semicolon(PP) pp_cxx_separate_with (PP, ';')
@@ -98,16 +99,16 @@  static void dump_scope (cxx_pretty_printer *, tree, int);
 static void dump_template_parms (cxx_pretty_printer *, tree, int, int);
 static int get_non_default_template_args_count (tree, int);
 static const char *function_category (tree);
-static void maybe_print_constexpr_context (diagnostic_context *);
-static void maybe_print_instantiation_context (diagnostic_context *);
-static void print_instantiation_full_context (diagnostic_context *);
-static void print_instantiation_partial_context (diagnostic_context *,
+static void maybe_print_constexpr_context (diagnostic_text_output_format &);
+static void maybe_print_instantiation_context (diagnostic_text_output_format &);
+static void print_instantiation_full_context (diagnostic_text_output_format &);
+static void print_instantiation_partial_context (diagnostic_text_output_format &,
 						 struct tinst_level *,
 						 location_t);
-static void maybe_print_constraint_context (diagnostic_context *);
-static void cp_diagnostic_starter (diagnostic_context *,
-				   const diagnostic_info *);
-static void cp_print_error_function (diagnostic_context *,
+static void maybe_print_constraint_context (diagnostic_text_output_format &);
+static void cp_diagnostic_text_starter (diagnostic_text_output_format &,
+					const diagnostic_info *);
+static void cp_print_error_function (diagnostic_text_output_format &,
 				     const diagnostic_info *);
 
 static bool cp_printer (pretty_printer *, text_info *, const char *,
@@ -284,8 +285,8 @@  cxx_initialize_diagnostics (diagnostic_context *context)
   XDELETE (base);
 
   c_common_diagnostics_set_defaults (context);
-  diagnostic_starter (context) = cp_diagnostic_starter;
-  /* diagnostic_finalizer is already c_diagnostic_finalizer.  */
+  diagnostic_text_starter (context) = cp_diagnostic_text_starter;
+  /* diagnostic_finalizer is already c_diagnostic_text_finalizer.  */
   diagnostic_format_decoder (context) = cp_printer;
   context->m_adjust_diagnostic_info = cp_adjust_diagnostic_info;
   pp_format_postprocessor (pp) = new cxx_format_postprocessor ();
@@ -3665,7 +3666,8 @@  eh_spec_to_string (tree p, int /*v*/)
 
 /* Langhook for print_error_function.  */
 void
-cxx_print_error_function (diagnostic_context *context, const char *file,
+cxx_print_error_function (diagnostic_text_output_format &text_output,
+			  const char *file,
 			  const diagnostic_info *diagnostic)
 {
   char *prefix;
@@ -3673,28 +3675,29 @@  cxx_print_error_function (diagnostic_context *context, const char *file,
     prefix = xstrdup (file);
   else
     prefix = NULL;
-  lhd_print_error_function (context, file, diagnostic);
-  pp_set_prefix (context->m_printer, prefix);
-  maybe_print_instantiation_context (context);
+  lhd_print_error_function (text_output, file, diagnostic);
+
+  pp_set_prefix (text_output.get_printer (), prefix);
+  maybe_print_instantiation_context (text_output);
 }
 
 static void
-cp_diagnostic_starter (diagnostic_context *context,
-		       const diagnostic_info *diagnostic)
+cp_diagnostic_text_starter (diagnostic_text_output_format &text_output,
+			    const diagnostic_info *diagnostic)
 {
-  diagnostic_report_current_module (context, diagnostic_location (diagnostic));
-  cp_print_error_function (context, diagnostic);
-  maybe_print_instantiation_context (context);
-  maybe_print_constexpr_context (context);
-  maybe_print_constraint_context (context);
-  pp_set_prefix (context->m_printer, diagnostic_build_prefix (context,
-							      diagnostic));
+  text_output.report_current_module (diagnostic_location (diagnostic));
+  cp_print_error_function (text_output, diagnostic);
+  maybe_print_instantiation_context (text_output);
+  maybe_print_constexpr_context (text_output);
+  maybe_print_constraint_context (text_output);
+  pp_set_prefix (text_output.get_printer (),
+		 text_output.build_prefix (*diagnostic));
 }
 
 /* Print current function onto BUFFER, in the process of reporting
    a diagnostic message.  Called from cp_diagnostic_starter.  */
 static void
-cp_print_error_function (diagnostic_context *context,
+cp_print_error_function (diagnostic_text_output_format &text_output,
 			 const diagnostic_info *diagnostic)
 {
   /* If we are in an instantiation context, current_function_decl is likely
@@ -3704,14 +3707,15 @@  cp_print_error_function (diagnostic_context *context,
   /* The above is true for constraint satisfaction also.  */
   if (current_failed_constraint)
     return;
+  diagnostic_context *const context = &text_output.get_context ();
   if (diagnostic_last_function_changed (context, diagnostic))
     {
-      pretty_printer *const pp = context->m_printer;
+      pretty_printer *const pp = text_output.get_printer ();
       char *old_prefix = pp_take_prefix (pp);
       const char *file = LOCATION_FILE (diagnostic_location (diagnostic));
       tree abstract_origin = diagnostic_abstract_origin (diagnostic);
       char *new_prefix = (file && abstract_origin == NULL)
-			 ? file_name_as_prefix (context, file) : NULL;
+			 ? text_output.file_name_as_prefix (file) : NULL;
 
       pp_set_prefix (pp, new_prefix);
 
@@ -3773,7 +3777,7 @@  cp_print_error_function (diagnostic_context *context,
 		  pp_newline (pp);
 		  if (s.file != NULL)
 		    {
-		      if (context->m_show_column && s.column != 0)
+		      if (text_output.show_column_p () && s.column != 0)
 			pp_printf (pp,
 				   _("    inlined from %qD at %r%s:%d:%d%R"),
 				   fndecl,
@@ -3841,14 +3845,14 @@  function_category (tree fn)
 /* Report the full context of a current template instantiation,
    onto BUFFER.  */
 static void
-print_instantiation_full_context (diagnostic_context *context)
+print_instantiation_full_context (diagnostic_text_output_format &text_output)
 {
   struct tinst_level *p = current_instantiation ();
   location_t location = input_location;
 
   if (p)
     {
-      pp_verbatim (context->m_printer,
+      pp_verbatim (text_output.get_printer (),
 		   p->list_p ()
 		   ? _("%s: In substitution of %qS:\n")
 		   : _("%s: In instantiation of %q#D:\n"),
@@ -3859,14 +3863,14 @@  print_instantiation_full_context (diagnostic_context *context)
       p = p->next;
     }
 
-  print_instantiation_partial_context (context, p, location);
+  print_instantiation_partial_context (text_output, p, location);
 }
 
 /* Helper function of print_instantiation_partial_context() that
    prints a single line of instantiation context.  */
 
 static void
-print_instantiation_partial_context_line (diagnostic_context *context,
+print_instantiation_partial_context_line (diagnostic_text_output_format &text_output,
 					  struct tinst_level *t,
 					  location_t loc, bool recursive_p)
 {
@@ -3875,9 +3879,9 @@  print_instantiation_partial_context_line (diagnostic_context *context,
 
   expanded_location xloc = expand_location (loc);
 
-  pretty_printer *const pp = context->m_printer;
+  pretty_printer *const pp = text_output.get_printer ();
 
-  if (context->m_show_column)
+  if (text_output.show_column_p ())
     pp_verbatim (pp, _("%r%s:%d:%d:%R   "),
 		 "locus", xloc.file, xloc.line, xloc.column);
   else
@@ -3908,14 +3912,14 @@  print_instantiation_partial_context_line (diagnostic_context *context,
     }
   gcc_rich_location rich_loc (loc);
   char *saved_prefix = pp_take_prefix (pp);
-  diagnostic_show_locus (context, &rich_loc, DK_NOTE);
+  diagnostic_show_locus (&text_output.get_context (), &rich_loc, DK_NOTE, pp);
   pp_set_prefix (pp, saved_prefix);
 }
 
 /* Same as print_instantiation_full_context but less verbose.  */
 
 static void
-print_instantiation_partial_context (diagnostic_context *context,
+print_instantiation_partial_context (diagnostic_text_output_format &text_output,
 				     struct tinst_level *t0, location_t loc)
 {
   struct tinst_level *t;
@@ -3949,7 +3953,7 @@  print_instantiation_partial_context (diagnostic_context *context,
 	{
 	  gcc_assert (t != NULL);
 	  if (loc != t->locus)
-	    print_instantiation_partial_context_line (context, t, loc,
+	    print_instantiation_partial_context_line (text_output, t, loc,
 						      /*recursive_p=*/false);
 	  loc = t->locus;
 	  t = t->next;
@@ -3958,8 +3962,8 @@  print_instantiation_partial_context (diagnostic_context *context,
 	{
 	  expanded_location xloc;
 	  xloc = expand_location (loc);
-	  pretty_printer *const pp = context->m_printer;
-	  if (context->m_show_column)
+	  pretty_printer *const pp = text_output.get_printer ();
+	  if (text_output.show_column_p ())
 	    pp_verbatim (pp,
 			 _("%r%s:%d:%d:%R   [ skipping %d instantiation "
 			   "contexts, use -ftemplate-backtrace-limit=0 to "
@@ -3986,30 +3990,30 @@  print_instantiation_partial_context (diagnostic_context *context,
 	  loc = t->locus;
 	  t = t->next;
 	}
-      print_instantiation_partial_context_line (context, t, loc,
+      print_instantiation_partial_context_line (text_output, t, loc,
 						t->locus == loc);
       loc = t->locus;
       t = t->next;
     }
-  print_instantiation_partial_context_line (context, NULL, loc,
+  print_instantiation_partial_context_line (text_output, NULL, loc,
 					    /*recursive_p=*/false);
 }
 
 /* Called from cp_thing to print the template context for an error.  */
 static void
-maybe_print_instantiation_context (diagnostic_context *context)
+maybe_print_instantiation_context (diagnostic_text_output_format &text_output)
 {
   if (!problematic_instantiation_changed () || current_instantiation () == 0)
     return;
 
   record_last_problematic_instantiation ();
-  print_instantiation_full_context (context);
+  print_instantiation_full_context (text_output);
 }
 
 /* Report what constexpr call(s) we're trying to expand, if any.  */
 
 void
-maybe_print_constexpr_context (diagnostic_context *context)
+maybe_print_constexpr_context (diagnostic_text_output_format &text_output)
 {
   vec<tree> call_stack = cx_error_context ();
   unsigned ix;
@@ -4019,8 +4023,8 @@  maybe_print_constexpr_context (diagnostic_context *context)
     {
       expanded_location xloc = expand_location (EXPR_LOCATION (t));
       const char *s = expr_as_string (t, 0);
-      pretty_printer *const pp = context->m_printer;
-      if (context->m_show_column)
+      pretty_printer *const pp = text_output.get_printer ();
+      if (text_output.show_column_p ())
 	pp_verbatim (pp,
 		     _("%r%s:%d:%d:%R   in %<constexpr%> expansion of %qs"),
 		     "locus", xloc.file, xloc.line, xloc.column, s);
@@ -4034,11 +4038,12 @@  maybe_print_constexpr_context (diagnostic_context *context)
 
 
 static void
-print_location (diagnostic_context *context, location_t loc)
+print_location (diagnostic_text_output_format &text_output,
+		location_t loc)
 {
   expanded_location xloc = expand_location (loc);
-  pretty_printer *const pp = context->m_printer;
-  if (context->m_show_column)
+  pretty_printer *const pp = text_output.get_printer ();
+  if (text_output.show_column_p ())
     pp_verbatim (pp, _("%r%s:%d:%d:%R   "),
                  "locus", xloc.file, xloc.line, xloc.column);
   else
@@ -4047,23 +4052,26 @@  print_location (diagnostic_context *context, location_t loc)
 }
 
 static void
-print_constrained_decl_info (diagnostic_context *context, tree decl)
+print_constrained_decl_info (diagnostic_text_output_format &text_output,
+			     tree decl)
 {
-  print_location (context, DECL_SOURCE_LOCATION (decl));
-  pretty_printer *const pp = context->m_printer;
+  print_location (text_output, DECL_SOURCE_LOCATION (decl));
+  pretty_printer *const pp = text_output.get_printer ();
   pp_verbatim (pp, "required by the constraints of %q#D\n", decl);
 }
 
 static void
-print_concept_check_info (diagnostic_context *context, tree expr, tree map, tree args)
+print_concept_check_info (diagnostic_text_output_format &text_output,
+			  tree expr, tree map, tree args)
 {
   gcc_assert (concept_check_p (expr));
 
   tree tmpl = TREE_OPERAND (expr, 0);
 
-  print_location (context, DECL_SOURCE_LOCATION (tmpl));
+  print_location (text_output, DECL_SOURCE_LOCATION (tmpl));
 
-  cxx_pretty_printer *const pp = (cxx_pretty_printer *)context->m_printer;
+  cxx_pretty_printer *const pp
+    = (cxx_pretty_printer *)text_output.get_printer ();
   pp_verbatim (pp, "required for the satisfaction of %qE", expr);
   if (map && map != error_mark_node)
     {
@@ -4078,30 +4086,32 @@  print_concept_check_info (diagnostic_context *context, tree expr, tree map, tree
    context, if any.  */
 
 static tree
-print_constraint_context_head (diagnostic_context *context, tree cxt, tree args)
+print_constraint_context_head (diagnostic_text_output_format &text_output,
+			       tree cxt, tree args)
 {
   tree src = TREE_VALUE (cxt);
   if (!src)
     {
-      print_location (context, input_location);
-      pretty_printer *const pp = context->m_printer;
+      print_location (text_output, input_location);
+      pretty_printer *const pp = text_output.get_printer ();
       pp_verbatim (pp, "required for constraint satisfaction\n");
       return NULL_TREE;
     }
   if (DECL_P (src))
     {
-      print_constrained_decl_info (context, src);
+      print_constrained_decl_info (text_output, src);
       return NULL_TREE;
     }
   else
     {
-      print_concept_check_info (context, src, TREE_PURPOSE (cxt), args);
+      print_concept_check_info (text_output, src, TREE_PURPOSE (cxt), args);
       return TREE_CHAIN (cxt);
     }
 }
 
 static void
-print_requires_expression_info (diagnostic_context *context, tree constr, tree args)
+print_requires_expression_info (diagnostic_text_output_format &text_output,
+				tree constr, tree args)
 {
 
   tree expr = ATOMIC_CONSTR_EXPR (constr);
@@ -4110,9 +4120,9 @@  print_requires_expression_info (diagnostic_context *context, tree constr, tree a
   if (map == error_mark_node)
     return;
 
-  print_location (context, cp_expr_loc_or_input_loc (expr));
+  print_location (text_output, cp_expr_loc_or_input_loc (expr));
   cxx_pretty_printer *const pp
-    = static_cast <cxx_pretty_printer *> (context->m_printer);
+    = static_cast <cxx_pretty_printer *> (text_output.get_printer ());
   pp_verbatim (pp, "in requirements ");
 
   tree parms = TREE_OPERAND (expr, 0);
@@ -4131,7 +4141,8 @@  print_requires_expression_info (diagnostic_context *context, tree constr, tree a
 }
 
 void
-maybe_print_single_constraint_context (diagnostic_context *context, tree failed)
+maybe_print_single_constraint_context (diagnostic_text_output_format &text_output,
+				       tree failed)
 {
   if (!failed)
     return;
@@ -4145,23 +4156,23 @@  maybe_print_single_constraint_context (diagnostic_context *context, tree failed)
   tree args = TREE_PURPOSE (failed);
 
   /* Print the stack of requirements.  */
-  cxt = print_constraint_context_head (context, cxt, args);
+  cxt = print_constraint_context_head (text_output, cxt, args);
   while (cxt && !DECL_P (TREE_VALUE (cxt)))
     {
       tree expr = TREE_VALUE (cxt);
       tree map = TREE_PURPOSE (cxt);
-      print_concept_check_info (context, expr, map, args);
+      print_concept_check_info (text_output, expr, map, args);
       cxt = TREE_CHAIN (cxt);
     }
 
   /* For certain constraints, we can provide additional context.  */
   if (TREE_CODE (constr) == ATOMIC_CONSTR
       && TREE_CODE (ATOMIC_CONSTR_EXPR (constr)) == REQUIRES_EXPR)
-    print_requires_expression_info (context, constr, args);
+    print_requires_expression_info (text_output, constr, args);
 }
 
 void
-maybe_print_constraint_context (diagnostic_context *context)
+maybe_print_constraint_context (diagnostic_text_output_format &text_output)
 {
   if (!current_failed_constraint)
     return;
@@ -4171,10 +4182,10 @@  maybe_print_constraint_context (diagnostic_context *context)
   /* Recursively print nested contexts.  */
   current_failed_constraint = TREE_CHAIN (current_failed_constraint);
   if (current_failed_constraint)
-    maybe_print_constraint_context (context);
+    maybe_print_constraint_context (text_output);
 
   /* Print this context.  */
-  maybe_print_single_constraint_context (context, cur);
+  maybe_print_single_constraint_context (text_output, cur);
 }
 
 /* Return true iff TYPE_A and TYPE_B are template types that are
diff --git a/gcc/diagnostic-format-json.cc b/gcc/diagnostic-format-json.cc
index a98269d5d48b..448b6cb54eee 100644
--- a/gcc/diagnostic-format-json.cc
+++ b/gcc/diagnostic-format-json.cc
@@ -54,6 +54,10 @@  public:
   {
     /* No-op.  */
   }
+  void after_diagnostic (const diagnostic_info &) final override
+  {
+    /* No-op.  */
+  }
 
 protected:
   json_output_format (diagnostic_context &context,
diff --git a/gcc/diagnostic-format-sarif.cc b/gcc/diagnostic-format-sarif.cc
index 33d60a680beb..eda67a6f6583 100644
--- a/gcc/diagnostic-format-sarif.cc
+++ b/gcc/diagnostic-format-sarif.cc
@@ -3139,6 +3139,10 @@  public:
   {
     m_builder.emit_diagram (m_context, diagram);
   }
+  void after_diagnostic (const diagnostic_info &) final override
+  {
+    /* No-op.  */
+  }
 
   sarif_builder &get_builder () { return m_builder; }
 
diff --git a/gcc/diagnostic-format-text.cc b/gcc/diagnostic-format-text.cc
index f87addc44b24..16bf34130475 100644
--- a/gcc/diagnostic-format-text.cc
+++ b/gcc/diagnostic-format-text.cc
@@ -34,6 +34,13 @@  along with GCC; see the file COPYING3.  If not see
 #include "diagnostic-format-text.h"
 #include "text-art/theme.h"
 
+/* Disable warnings about quoting issues in the pp_xxx calls below
+   that (intentionally) don't follow GCC diagnostic conventions.  */
+#if __GNUC__ >= 10
+#  pragma GCC diagnostic push
+#  pragma GCC diagnostic ignored "-Wformat-diag"
+#endif
+
 /* class diagnostic_text_output_format : public diagnostic_output_format.  */
 
 diagnostic_text_output_format::~diagnostic_text_output_format ()
@@ -41,7 +48,7 @@  diagnostic_text_output_format::~diagnostic_text_output_format ()
   /* Some of the errors may actually have been warnings.  */
   if (m_context.diagnostic_count (DK_WERROR))
     {
-      pretty_printer *pp = m_context.m_printer;
+      pretty_printer *pp = get_printer ();
       /* -Werror was given.  */
       if (m_context.warning_as_error_requested_p ())
 	pp_verbatim (pp,
@@ -54,6 +61,12 @@  diagnostic_text_output_format::~diagnostic_text_output_format ()
 		     progname);
       pp_newline_and_flush (pp);
     }
+
+  if (m_includes_seen)
+    {
+      delete m_includes_seen;
+      m_includes_seen = nullptr;
+    }
 }
 
 /* Implementation of diagnostic_output_format::on_report_diagnostic vfunc
@@ -64,9 +77,9 @@  diagnostic_text_output_format::
 on_report_diagnostic (const diagnostic_info &diagnostic,
 		      diagnostic_t orig_diag_kind)
 {
-  pretty_printer *pp = m_context.m_printer;
+  pretty_printer *pp = get_printer ();
 
-  (*diagnostic_starter (&m_context)) (&m_context, &diagnostic);
+  (*diagnostic_text_starter (&m_context)) (*this, &diagnostic);
 
   pp_output_formatted_text (pp, m_context.get_urlifier ());
 
@@ -79,9 +92,9 @@  on_report_diagnostic (const diagnostic_info &diagnostic,
   if (m_context.m_show_option_requested)
     print_option_information (diagnostic, orig_diag_kind);
 
-  (*diagnostic_finalizer (&m_context)) (&m_context,
-					&diagnostic,
-					orig_diag_kind);
+  (*diagnostic_text_finalizer (&m_context)) (*this,
+					     &diagnostic,
+					     orig_diag_kind);
 }
 
 void
@@ -100,6 +113,13 @@  diagnostic_text_output_format::on_diagram (const diagnostic_diagram &diagram)
   pp_flush (pp);
 }
 
+void
+diagnostic_text_output_format::
+after_diagnostic (const diagnostic_info &diagnostic)
+{
+  show_any_path (diagnostic);
+}
+
 /* If DIAGNOSTIC has a CWE identifier, print it.
 
    For example, if the diagnostic metadata associates it with CWE-119,
@@ -213,3 +233,157 @@  print_option_information (const diagnostic_info &diagnostic,
       free (option_text);
     }
 }
+
+/* Only dump the "In file included from..." stack once for each file.  */
+
+bool
+diagnostic_text_output_format::includes_seen_p (const line_map_ordinary *map)
+{
+  /* No include path for main.  */
+  if (MAIN_FILE_P (map))
+    return true;
+
+  /* Always identify C++ modules, at least for now.  */
+  auto probe = map;
+  if (linemap_check_ordinary (map)->reason == LC_RENAME)
+    /* The module source file shows up as LC_RENAME inside LC_MODULE.  */
+    probe = linemap_included_from_linemap (line_table, map);
+  if (MAP_MODULE_P (probe))
+    return false;
+
+  if (!m_includes_seen)
+    m_includes_seen = new hash_set<location_t, false, location_hash>;
+
+  /* Hash the location of the #include directive to better handle files
+     that are included multiple times with different macros defined.  */
+  return m_includes_seen->add (linemap_included_from (map));
+}
+
+label_text
+diagnostic_text_output_format::
+get_location_text (const expanded_location &s) const
+{
+  return get_context ().get_location_text (s, pp_show_color (get_printer ()));
+}
+
+
+/* Helpers for writing lang-specific starters/finalizers for text output.  */
+
+/* Return a formatted line and column ':%line:%column'.  Elided if
+   line == 0 or col < 0.  (A column of 0 may be valid due to the
+   -fdiagnostics-column-origin option.)
+   The result is a statically allocated buffer.  */
+
+const char *
+maybe_line_and_column (int line, int col)
+{
+  static char result[32];
+
+  if (line)
+    {
+      size_t l
+	= snprintf (result, sizeof (result),
+		    col >= 0 ? ":%d:%d" : ":%d", line, col);
+      gcc_checking_assert (l < sizeof (result));
+    }
+  else
+    result[0] = 0;
+  return result;
+}
+
+void
+diagnostic_text_output_format::report_current_module (location_t where)
+{
+  const diagnostic_context &context = get_context ();
+  pretty_printer *pp = get_printer ();
+  const line_map_ordinary *map = NULL;
+
+  if (pp_needs_newline (pp))
+    {
+      pp_newline (pp);
+      pp_needs_newline (pp) = false;
+    }
+
+  if (where <= BUILTINS_LOCATION)
+    return;
+
+  linemap_resolve_location (line_table, where,
+			    LRK_MACRO_DEFINITION_LOCATION,
+			    &map);
+
+  if (map && m_last_module != map)
+    {
+      m_last_module = map;
+      if (!includes_seen_p (map))
+	{
+	  bool first = true, need_inc = true, was_module = MAP_MODULE_P (map);
+	  expanded_location s = {};
+	  do
+	    {
+	      where = linemap_included_from (map);
+	      map = linemap_included_from_linemap (line_table, map);
+	      bool is_module = MAP_MODULE_P (map);
+	      s.file = LINEMAP_FILE (map);
+	      s.line = SOURCE_LINE (map, where);
+	      int col = -1;
+	      if (first && show_column_p ())
+		{
+		  s.column = SOURCE_COLUMN (map, where);
+		  col = context.converted_column (s);
+		}
+	      const char *line_col = maybe_line_and_column (s.line, col);
+	      static const char *const msgs[] =
+		{
+		 NULL,
+		 N_("                 from"),
+		 N_("In file included from"),	/* 2 */
+		 N_("        included from"),
+		 N_("In module"),		/* 4 */
+		 N_("of module"),
+		 N_("In module imported at"),	/* 6 */
+		 N_("imported at"),
+		};
+
+	      unsigned index = (was_module ? 6 : is_module ? 4
+				: need_inc ? 2 : 0) + !first;
+
+	      pp_verbatim (pp, "%s%s %r%s%s%R",
+			   first ? "" : was_module ? ", " : ",\n",
+			   _(msgs[index]),
+			   "locus", s.file, line_col);
+	      first = false, need_inc = was_module, was_module = is_module;
+	    }
+	  while (!includes_seen_p (map));
+	  pp_verbatim (pp, ":");
+	  pp_newline (pp);
+	}
+    }
+}
+
+void
+default_diagnostic_text_starter (diagnostic_text_output_format &text_output,
+				 const diagnostic_info *diagnostic)
+{
+  text_output.report_current_module (diagnostic_location (diagnostic));
+  pretty_printer *const pp = text_output.get_printer ();
+  pp_set_prefix (pp, text_output.build_prefix (*diagnostic));
+}
+
+void
+default_diagnostic_text_finalizer (diagnostic_text_output_format &text_output,
+				   const diagnostic_info *diagnostic,
+				   diagnostic_t)
+{
+  pretty_printer *const pp = text_output.get_printer ();
+  char *saved_prefix = pp_take_prefix (pp);
+  pp_set_prefix (pp, NULL);
+  pp_newline (pp);
+  diagnostic_show_locus (&text_output.get_context (),
+			 diagnostic->richloc, diagnostic->kind, pp);
+  pp_set_prefix (pp, saved_prefix);
+  pp_flush (pp);
+}
+
+#if __GNUC__ >= 10
+#  pragma GCC diagnostic pop
+#endif
diff --git a/gcc/diagnostic-format-text.h b/gcc/diagnostic-format-text.h
index 2db365e5d182..5361687e7203 100644
--- a/gcc/diagnostic-format-text.h
+++ b/gcc/diagnostic-format-text.h
@@ -33,7 +33,9 @@  class diagnostic_text_output_format : public diagnostic_output_format
 {
 public:
   diagnostic_text_output_format (diagnostic_context &context)
-  : diagnostic_output_format (context)
+  : diagnostic_output_format (context),
+    m_last_module (nullptr),
+    m_includes_seen (nullptr)
   {}
   ~diagnostic_text_output_format ();
   void on_begin_group () override {}
@@ -41,16 +43,43 @@  public:
   void on_report_diagnostic (const diagnostic_info &,
 			     diagnostic_t orig_diag_kind) override;
   void on_diagram (const diagnostic_diagram &diagram) override;
+  void after_diagnostic (const diagnostic_info &) final override;
   bool machine_readable_stderr_p () const final override
   {
     return false;
   }
 
+  /* Helpers for writing lang-specific starters/finalizers for text output.  */
+  char *build_prefix (const diagnostic_info &) const;
+  void report_current_module (location_t where);
+  void append_note (location_t location,
+		    const char * gmsgid, ...) ATTRIBUTE_GCC_DIAG(3,4);
+
+
+  char *file_name_as_prefix (const char *) const;
+
+  void print_path (const diagnostic_path &path);
+
+  bool show_column_p () const { return get_context ().m_show_column; }
+
 private:
   void print_any_cwe (const diagnostic_info &diagnostic);
   void print_any_rules (const diagnostic_info &diagnostic);
   void print_option_information (const diagnostic_info &diagnostic,
 				 diagnostic_t orig_diag_kind);
+
+  label_text get_location_text (const expanded_location &s) const;
+  bool includes_seen_p (const line_map_ordinary *map);
+
+  void show_any_path (const diagnostic_info &diagnostic);
+
+  /* Used to detect when the input file stack has changed since last
+     described.  */
+  const line_map_ordinary *m_last_module;
+
+  /* Include files that report_current_module has already listed the
+     include path for.  */
+  hash_set<location_t, false, location_hash> *m_includes_seen;
 };
 
 #endif /* ! GCC_DIAGNOSTIC_FORMAT_TEXT_H */
diff --git a/gcc/diagnostic-format.h b/gcc/diagnostic-format.h
index 688ddcaaf430..fd937ef0e3f9 100644
--- a/gcc/diagnostic-format.h
+++ b/gcc/diagnostic-format.h
@@ -39,10 +39,17 @@  public:
 				     diagnostic_t orig_diag_kind) = 0;
 
   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;
 
+  diagnostic_context &get_context () const { return m_context; }
   pretty_printer *get_printer () const { return m_context.m_printer; }
 
+  text_art::theme *get_diagram_theme () const
+  {
+    return m_context.get_diagram_theme ();
+  }
+
 protected:
   diagnostic_output_format (diagnostic_context &context)
   : m_context (context)
diff --git a/gcc/diagnostic-macro-unwinding.cc b/gcc/diagnostic-macro-unwinding.cc
index 3056d8c8afb1..a9838ffb52ca 100644
--- a/gcc/diagnostic-macro-unwinding.cc
+++ b/gcc/diagnostic-macro-unwinding.cc
@@ -23,6 +23,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "tree.h"
 #include "diagnostic.h"
 #include "diagnostic-macro-unwinding.h"
+#include "diagnostic-format-text.h"
 #include "intl.h"
 
 /* This is a pair made of a location and the line map it originated
@@ -72,7 +73,7 @@  struct loc_map_pair
    function.  */
 
 void
-maybe_unwind_expanded_macro_loc (diagnostic_context *context,
+maybe_unwind_expanded_macro_loc (diagnostic_text_output_format &text_output,
                                  location_t where)
 {
   const struct line_map *map;
@@ -177,9 +178,9 @@  maybe_unwind_expanded_macro_loc (diagnostic_context *context,
 	const int resolved_def_loc_line = SOURCE_LINE (m, l0);
         if (ix == 0 && saved_location_line != resolved_def_loc_line)
           {
-            diagnostic_append_note (context, resolved_def_loc, 
-                                    "in definition of macro %qs",
-                                    linemap_map_get_macro_name (iter->map));
+	    text_output.append_note (resolved_def_loc,
+				     "in definition of macro %qs",
+				     linemap_map_get_macro_name (iter->map));
             /* At this step, as we've printed the context of the macro
                definition, we don't want to print the context of its
                expansion, otherwise, it'd be redundant.  */
@@ -194,9 +195,9 @@  maybe_unwind_expanded_macro_loc (diagnostic_context *context,
                                     iter->map->get_expansion_point_location (),
                                     LRK_MACRO_DEFINITION_LOCATION, NULL);
 
-        diagnostic_append_note (context, resolved_exp_loc, 
-                                "in expansion of macro %qs",
-                                linemap_map_get_macro_name (iter->map));
+	text_output.append_note (resolved_exp_loc,
+				 "in expansion of macro %qs",
+				 linemap_map_get_macro_name (iter->map));
       }
 }
 
@@ -214,8 +215,8 @@  maybe_unwind_expanded_macro_loc (diagnostic_context *context,
     that is similar to what is done for function call stacks, or
     template instantiation contexts.  */
 void
-virt_loc_aware_diagnostic_finalizer (diagnostic_context *context,
+virt_loc_aware_diagnostic_finalizer (diagnostic_text_output_format &text_output,
 				     const diagnostic_info *diagnostic)
 {
-  maybe_unwind_expanded_macro_loc (context, diagnostic_location (diagnostic));
+  maybe_unwind_expanded_macro_loc (text_output, diagnostic_location (diagnostic));
 }
diff --git a/gcc/diagnostic-macro-unwinding.h b/gcc/diagnostic-macro-unwinding.h
index 74083cd56ded..c825b6e524ba 100644
--- a/gcc/diagnostic-macro-unwinding.h
+++ b/gcc/diagnostic-macro-unwinding.h
@@ -20,10 +20,10 @@  along with GCC; see the file COPYING3.  If not see
 #ifndef GCC_DIAGNOSTIC_MACRO_UNWINDING_H
 #define GCC_DIAGNOSTIC_MACRO_UNWINDING_H
 
-void virt_loc_aware_diagnostic_finalizer (diagnostic_context *,
+void virt_loc_aware_diagnostic_finalizer (diagnostic_text_output_format &,
 					  const diagnostic_info *);
 
-extern void maybe_unwind_expanded_macro_loc (diagnostic_context *context,
+extern void maybe_unwind_expanded_macro_loc (diagnostic_text_output_format &,
 					     location_t where);
 
 #endif /* ! GCC_DIAGNOSTIC_MACRO_UNWINDING_H */
diff --git a/gcc/diagnostic-path.cc b/gcc/diagnostic-path.cc
index abe427d3e52d..feee054ea4df 100644
--- a/gcc/diagnostic-path.cc
+++ b/gcc/diagnostic-path.cc
@@ -37,6 +37,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "selftest-diagnostic.h"
 #include "selftest-diagnostic-path.h"
 #include "text-art/theme.h"
+#include "diagnostic-format-text.h"
 
 /* Disable warnings about missing quoting in GCC diagnostics for the print
    calls below.  */
@@ -224,10 +225,12 @@  namespace {
 class path_label : public range_label
 {
  public:
-  path_label (const diagnostic_context &ctxt,
-	      const diagnostic_path &path,
-	      unsigned start_idx)
-  : m_ctxt (ctxt), m_path (path), m_start_idx (start_idx), m_effects (*this)
+  path_label (const diagnostic_path &path,
+	      unsigned start_idx,
+	      bool colorize,
+	      bool allow_emojis)
+  : m_path (path), m_start_idx (start_idx), m_effects (*this),
+    m_colorize (colorize), m_allow_emojis (allow_emojis)
   {}
 
   label_text get_text (unsigned range_idx) const final override
@@ -238,33 +241,31 @@  class path_label : public range_label
     /* Get the description of the event, perhaps with colorization:
        normally, we don't colorize within a range_label, but this
        is special-cased for diagnostic paths.  */
-    const bool colorize = pp_show_color (m_ctxt.m_printer);
-    label_text event_text (event.get_desc (colorize));
+    label_text event_text (event.get_desc (m_colorize));
     gcc_assert (event_text.get ());
 
     const diagnostic_event::meaning meaning (event.get_meaning ());
 
     pretty_printer pp;
-    pp_show_color (&pp) = colorize;
+    pp_show_color (&pp) = m_colorize;
     diagnostic_event_id_t event_id (event_idx);
 
     pp_printf (&pp, "%@", &event_id);
     pp_space (&pp);
 
-    if (meaning.m_verb == diagnostic_event::VERB_danger)
-      if (text_art::theme *theme = m_ctxt.get_diagram_theme ())
-	if (theme->emojis_p ())
-	  {
-	    pp_unicode_character (&pp, 0x26A0); /* U+26A0 WARNING SIGN.  */
-	    /* Append U+FE0F VARIATION SELECTOR-16 to select the emoji
-	       variation of the char.  */
-	    pp_unicode_character (&pp, 0xFE0F);
-	    /* U+26A0 WARNING SIGN has East_Asian_Width == Neutral, but in its
-	       emoji variant is printed (by vte at least) with a 2nd half
-	       overlapping the next char.  Hence we add two spaces here: a space
-	       to be covered by this overlap, plus another space of padding.  */
-	    pp_string (&pp, "  ");
-	  }
+    if (meaning.m_verb == diagnostic_event::VERB_danger
+	&& m_allow_emojis)
+      {
+	pp_unicode_character (&pp, 0x26A0); /* U+26A0 WARNING SIGN.  */
+	/* Append U+FE0F VARIATION SELECTOR-16 to select the emoji
+	   variation of the char.  */
+	pp_unicode_character (&pp, 0xFE0F);
+	/* U+26A0 WARNING SIGN has East_Asian_Width == Neutral, but in its
+	   emoji variant is printed (by vte at least) with a 2nd half
+	   overlapping the next char.  Hence we add two spaces here: a space
+	   to be covered by this overlap, plus another space of padding.  */
+	pp_string (&pp, "  ");
+      }
 
     pp_printf (&pp, "%s", event_text.get ());
 
@@ -316,10 +317,11 @@  class path_label : public range_label
     return &m_path.get_event (event_idx);
   }
 
-  const diagnostic_context &m_ctxt;
   const diagnostic_path &m_path;
   unsigned m_start_idx;
   path_label_effects m_effects;
+  const bool m_colorize;
+  const bool m_allow_emojis;
 };
 
 /* Return true if E1 and E2 can be consolidated into the same run of events
@@ -507,17 +509,18 @@  struct event_range
     int m_max_label_source_column;
   };
 
-  event_range (const diagnostic_context &ctxt,
-	       const diagnostic_path &path, unsigned start_idx,
+  event_range (const diagnostic_path &path, unsigned start_idx,
 	       const diagnostic_event &initial_event,
 	       per_thread_summary &t,
-	       bool show_event_links)
+	       bool show_event_links,
+	       bool colorize_labels,
+	       bool allow_emojis)
   : m_path (path),
     m_initial_event (initial_event),
     m_logical_loc (initial_event.get_logical_location ()),
     m_stack_depth (initial_event.get_stack_depth ()),
     m_start_idx (start_idx), m_end_idx (start_idx),
-    m_path_label (ctxt, path, start_idx),
+    m_path_label (path, start_idx, colorize_labels, allow_emojis),
     m_richloc (initial_event.get_location (), &m_path_label, nullptr),
     m_thread_id (initial_event.get_thread_id ()),
     m_per_thread_summary (t),
@@ -601,14 +604,17 @@  struct event_range
     return true;
   }
 
-  /* Print the events in this range to DC, typically as a single
+  /* Print the events in this range to PP, typically as a single
      call to the printer's diagnostic_show_locus.  */
 
-  void print (diagnostic_context &dc, pretty_printer *pp,
+  void print (diagnostic_text_output_format &text_output,
+	      pretty_printer *pp,
 	      diagnostic_source_effect_info *effect_info)
   {
     location_t initial_loc = m_initial_event.get_location ();
 
+    diagnostic_context &dc = text_output.get_context ();
+
     /* Emit a span indicating the filename (and line/column) if the
        line has changed relative to the last call to
        diagnostic_show_locus.  */
@@ -649,7 +655,7 @@  struct event_range
     if (linemap_location_from_macro_expansion_p (line_table, initial_loc))
       {
 	gcc_assert (m_start_idx == m_end_idx);
-	maybe_unwind_expanded_macro_loc (&dc, initial_loc);
+	maybe_unwind_expanded_macro_loc (text_output, initial_loc);
       }
   }
 
@@ -674,7 +680,7 @@  struct event_range
 
 struct path_summary
 {
-  path_summary (const diagnostic_context &ctxt,
+  path_summary (diagnostic_text_output_format &text_output,
 		const diagnostic_path &path,
 		bool check_rich_locations,
 		bool show_event_links = true);
@@ -736,7 +742,7 @@  per_thread_summary::interprocedural_p () const
 
 /* path_summary's ctor.  */
 
-path_summary::path_summary (const diagnostic_context &ctxt,
+path_summary::path_summary (diagnostic_text_output_format &text_output,
 			    const diagnostic_path &path,
 			    bool check_rich_locations,
 			    bool show_event_links)
@@ -754,12 +760,18 @@  path_summary::path_summary (const diagnostic_context &ctxt,
       pts.update_depth_limits (event.get_stack_depth ());
 
       if (cur_event_range)
-	if (cur_event_range->maybe_add_event (ctxt, event,
+	if (cur_event_range->maybe_add_event (text_output.get_context (),
+					      event,
 					      idx, check_rich_locations))
 	  continue;
 
-      cur_event_range = new event_range (ctxt, path, idx, event, pts,
-					 show_event_links);
+      const bool colorize = pp_show_color (text_output.get_printer ());
+      auto theme = text_output.get_diagram_theme ();
+      const bool allow_emojis = theme ? theme->emojis_p (): false;
+      cur_event_range = new event_range (path, idx, event, pts,
+					 show_event_links,
+					 colorize,
+					 allow_emojis);
       m_ranges.safe_push (cur_event_range);
       pts.m_event_ranges.safe_push (cur_event_range);
       pts.m_last_event = &event;
@@ -812,7 +824,7 @@  public:
   }
 
   void
-  print_swimlane_for_event_range (diagnostic_context &dc,
+  print_swimlane_for_event_range (diagnostic_text_output_format &text_output,
 				  pretty_printer *pp,
 				  event_range *range,
 				  diagnostic_source_effect_info *effect_info)
@@ -823,7 +835,7 @@  public:
     const char *end_line_color = colorize_stop (pp_show_color (pp));
 
     text_art::ascii_theme fallback_theme;
-    text_art::theme *theme = dc.get_diagram_theme ();
+    text_art::theme *theme = text_output.get_diagram_theme ();
     if (!theme)
       theme = &fallback_theme;
 
@@ -894,7 +906,7 @@  public:
 	}
 	pp_set_prefix (pp, prefix);
 	pp_prefixing_rule (pp) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
-	range->print (dc, pp, effect_info);
+	range->print (text_output, pp, effect_info);
 	pp_set_prefix (pp, saved_prefix);
 
 	write_indent (pp, m_cur_indent + per_frame_indent);
@@ -904,7 +916,7 @@  public:
 	pp_newline (pp);
       }
     else
-      range->print (dc, pp, effect_info);
+      range->print (text_output, pp, effect_info);
 
     if (const event_range *next_range = get_any_next_range ())
       {
@@ -989,8 +1001,8 @@  private:
   unsigned  m_num_printed;
 };
 
-/* Print path_summary PS to DC, giving an overview of the interprocedural
-   calls and returns.
+/* Print path_summary PS to TEXT_OUTPUT, giving an overview of the
+   interprocedural calls and returns.
 
    Print the event descriptions in a nested form, printing the event
    descriptions within calls to diagnostic_show_locus, using labels to
@@ -1024,10 +1036,10 @@  private:
 
 static void
 print_path_summary_as_text (const path_summary &ps,
-			    diagnostic_context &dc,
+			    diagnostic_text_output_format &text_output,
 			    bool show_depths)
 {
-  pretty_printer *const pp = dc.m_printer;
+  pretty_printer *const pp = text_output.get_printer ();
 
   std::vector<thread_event_printer> thread_event_printers;
   for (auto t : ps.m_per_thread_summary)
@@ -1054,21 +1066,21 @@  print_path_summary_as_text (const path_summary &ps,
 	 of this range.  */
       diagnostic_source_effect_info effect_info;
       effect_info.m_leading_in_edge_column = last_out_edge_column;
-      tep.print_swimlane_for_event_range (dc, pp, range, &effect_info);
+      tep.print_swimlane_for_event_range (text_output, pp, range, &effect_info);
       last_out_edge_column = effect_info.m_trailing_out_edge_column;
     }
 }
 
 } /* end of anonymous namespace for path-printing code.  */
 
-/* Print PATH according to this context's path_format.  */
+/* Print PATH according to the context's path_format.  */
 
 void
-diagnostic_context::print_path (const diagnostic_path &path)
+diagnostic_text_output_format::print_path (const diagnostic_path &path)
 {
   const unsigned num_events = path.num_events ();
 
-  switch (get_path_format ())
+  switch (get_context ().get_path_format ())
     {
     case DPF_NONE:
       /* Do nothing.  */
@@ -1083,7 +1095,7 @@  diagnostic_context::print_path (const diagnostic_path &path)
 	    label_text event_text (event.get_desc (false));
 	    gcc_assert (event_text.get ());
 	    diagnostic_event_id_t event_id (i);
-	    if (this->show_path_depths_p ())
+	    if (get_context ().show_path_depths_p ())
 	      {
 		int stack_depth = event.get_stack_depth ();
 		/* -fdiagnostics-path-format=separate-events doesn't print
@@ -1115,13 +1127,14 @@  diagnostic_context::print_path (const diagnostic_path &path)
       {
 	/* Consolidate related events.  */
 	path_summary summary (*this, path, true,
-			      m_source_printing.show_event_links_p);
-	char *saved_prefix = pp_take_prefix (m_printer);
-	pp_set_prefix (m_printer, NULL);
+			      get_context ().m_source_printing.show_event_links_p);
+	pretty_printer *const pp = get_printer ();
+	char *saved_prefix = pp_take_prefix (pp);
+	pp_set_prefix (pp, NULL);
 	print_path_summary_as_text (summary, *this,
-				    show_path_depths_p ());
-	pp_flush (m_printer);
-	pp_set_prefix (m_printer, saved_prefix);
+				    get_context ().show_path_depths_p ());
+	pp_flush (pp);
+	pp_set_prefix (pp, saved_prefix);
       }
       break;
     }
@@ -1161,10 +1174,11 @@  test_empty_path (pretty_printer *event_pp)
   ASSERT_FALSE (path.interprocedural_p ());
 
   test_diagnostic_context dc;
-  path_summary summary (dc, path, false);
+  diagnostic_text_output_format text_output (dc);
+  path_summary summary (text_output, path, false);
   ASSERT_EQ (summary.get_num_ranges (), 0);
 
-  print_path_summary_as_text (summary, dc, true);
+  print_path_summary_as_text (summary, text_output, true);
   ASSERT_STREQ ("",
 		pp_formatted_text (dc.m_printer));
 }
@@ -1182,10 +1196,11 @@  test_intraprocedural_path (pretty_printer *event_pp)
   ASSERT_FALSE (path.interprocedural_p ());
 
   test_diagnostic_context dc;
-  path_summary summary (dc, path, false);
+  diagnostic_text_output_format text_output (dc);
+  path_summary summary (text_output, path, false);
   ASSERT_EQ (summary.get_num_ranges (), 1);
 
-  print_path_summary_as_text (summary, dc, true);
+  print_path_summary_as_text (summary, text_output, true);
   ASSERT_STREQ ("  `foo': events 1-2 (depth 0)\n"
 		" (1): first `free'\n"
 		" (2): double `free'\n",
@@ -1217,11 +1232,12 @@  test_interprocedural_path_1 (pretty_printer *event_pp)
 
   {
     test_diagnostic_context dc;
-    path_summary summary (dc, path, false);
+    diagnostic_text_output_format text_output (dc);
+    path_summary summary (text_output, path, false);
     ASSERT_EQ (summary.get_num_ranges (), 9);
 
     dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
-    print_path_summary_as_text (summary, dc, true);
+    print_path_summary_as_text (summary, text_output, true);
     ASSERT_STREQ
       ("  `test': events 1-2 (depth 0)\n"
        "    |\n"
@@ -1277,8 +1293,9 @@  test_interprocedural_path_1 (pretty_printer *event_pp)
   {
     test_diagnostic_context dc;
     dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_UNICODE);
-    path_summary summary (dc, path, false);
-    print_path_summary_as_text (summary, dc, true);
+    diagnostic_text_output_format text_output (dc);
+    path_summary summary (text_output, path, false);
+    print_path_summary_as_text (summary, text_output, true);
     ASSERT_STREQ
       ("  `test': events 1-2 (depth 0)\n"
        "    │\n"
@@ -1352,10 +1369,11 @@  test_interprocedural_path_2 (pretty_printer *event_pp)
 
   {
     test_diagnostic_context dc;
-    path_summary summary (dc, path, false);
+    diagnostic_text_output_format text_output (dc);
+    path_summary summary (text_output, path, false);
     ASSERT_EQ (summary.get_num_ranges (), 5);
     dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
-    print_path_summary_as_text (summary, dc, true);
+    print_path_summary_as_text (summary, text_output, true);
     ASSERT_STREQ
       ("  `foo': events 1-2 (depth 0)\n"
        "    |\n"
@@ -1387,8 +1405,9 @@  test_interprocedural_path_2 (pretty_printer *event_pp)
   {
     test_diagnostic_context dc;
     dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_UNICODE);
-    path_summary summary (dc, path, false);
-    print_path_summary_as_text (summary, dc, true);
+    diagnostic_text_output_format text_output (dc);
+    path_summary summary (text_output, path, false);
+    print_path_summary_as_text (summary, text_output, true);
     ASSERT_STREQ
       ("  `foo': events 1-2 (depth 0)\n"
        "    │\n"
@@ -1437,10 +1456,11 @@  test_recursion (pretty_printer *event_pp)
     test_diagnostic_context dc;
     dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
 
-    path_summary summary (dc, path, false);
+    diagnostic_text_output_format text_output (dc);
+    path_summary summary (text_output, path, false);
     ASSERT_EQ (summary.get_num_ranges (), 4);
 
-    print_path_summary_as_text (summary, dc, true);
+    print_path_summary_as_text (summary, text_output, true);
     ASSERT_STREQ
       ("  `factorial': events 1-2 (depth 0)\n"
        "    |\n"
@@ -1467,8 +1487,9 @@  test_recursion (pretty_printer *event_pp)
     test_diagnostic_context dc;
     dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_UNICODE);
 
-    path_summary summary (dc, path, false);
-    print_path_summary_as_text (summary, dc, true);
+    diagnostic_text_output_format text_output (dc);
+    path_summary summary (text_output, path, false);
+    print_path_summary_as_text (summary, text_output, true);
     ASSERT_STREQ
       ("  `factorial': events 1-2 (depth 0)\n"
        "    │\n"
@@ -1587,8 +1608,9 @@  test_control_flow_1 (const line_table_case &case_,
     test_diagnostic_context dc;
     dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
     dc.m_source_printing.show_event_links_p = true;
-    path_summary summary (dc, path, true);
-    print_path_summary_as_text (summary, dc, false);
+    diagnostic_text_output_format text_output (dc);
+    path_summary summary (text_output, path, true);
+    print_path_summary_as_text (summary, text_output, false);
     ASSERT_STREQ
       ("  events 1-3\n"
        "FILENAME:3:7:\n"
@@ -1611,8 +1633,9 @@  test_control_flow_1 (const line_table_case &case_,
     test_diagnostic_context dc;
     dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
     dc.m_source_printing.show_event_links_p = false;
-    path_summary summary (dc, path, true);
-    print_path_summary_as_text (summary, dc, false);
+    diagnostic_text_output_format text_output (dc);
+    path_summary summary (text_output, path, true);
+    print_path_summary_as_text (summary, text_output, false);
     ASSERT_STREQ
       ("  events 1-3\n"
        "FILENAME:3:7:\n"
@@ -1633,8 +1656,9 @@  test_control_flow_1 (const line_table_case &case_,
     dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
     dc.m_source_printing.show_line_numbers_p = true;
     dc.m_source_printing.show_event_links_p = true;
-    path_summary summary (dc, path, true);
-    print_path_summary_as_text (summary, dc, false);
+    diagnostic_text_output_format text_output (dc);
+    path_summary summary (text_output, path, true);
+    print_path_summary_as_text (summary, text_output, false);
     ASSERT_STREQ
       ("  events 1-3\n"
        "FILENAME:3:7:\n"
@@ -1658,8 +1682,9 @@  test_control_flow_1 (const line_table_case &case_,
     dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
     dc.m_source_printing.show_line_numbers_p = true;
     dc.m_source_printing.show_event_links_p = false;
-    path_summary summary (dc, path, true);
-    print_path_summary_as_text (summary, dc, false);
+    diagnostic_text_output_format text_output (dc);
+    path_summary summary (text_output, path, true);
+    print_path_summary_as_text (summary, text_output, false);
     ASSERT_STREQ
       ("  events 1-3\n"
        "FILENAME:3:7:\n"
@@ -1679,8 +1704,9 @@  test_control_flow_1 (const line_table_case &case_,
     test_diagnostic_context dc;
     dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_UNICODE);
     dc.m_source_printing.show_event_links_p = true;
-    path_summary summary (dc, path, true);
-    print_path_summary_as_text (summary, dc, false);
+    diagnostic_text_output_format text_output (dc);
+    path_summary summary (text_output, path, true);
+    print_path_summary_as_text (summary, text_output, false);
     ASSERT_STREQ
       ("  events 1-3\n"
        "FILENAME:3:7:\n"
@@ -1704,8 +1730,9 @@  test_control_flow_1 (const line_table_case &case_,
     dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_UNICODE);
     dc.m_source_printing.show_event_links_p = true;
     dc.m_source_printing.show_line_numbers_p = true;
-    path_summary summary (dc, path, true);
-    print_path_summary_as_text (summary, dc, false);
+    diagnostic_text_output_format text_output (dc);
+    path_summary summary (text_output, path, true);
+    print_path_summary_as_text (summary, text_output, false);
     ASSERT_STREQ
       ("  events 1-3\n"
        "FILENAME:3:7:\n"
@@ -1773,8 +1800,9 @@  test_control_flow_2 (const line_table_case &case_,
     dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
     dc.m_source_printing.show_event_links_p = true;
     dc.m_source_printing.show_line_numbers_p = true;
-    path_summary summary (dc, path, true);
-    print_path_summary_as_text (summary, dc, false);
+    diagnostic_text_output_format text_output (dc);
+    path_summary summary (text_output, path, true);
+    print_path_summary_as_text (summary, text_output, false);
     ASSERT_STREQ
       ("  events 1-3\n"
        "FILENAME:4:31:\n"
@@ -1858,8 +1886,9 @@  test_control_flow_3 (const line_table_case &case_,
     dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
     dc.m_source_printing.show_event_links_p = true;
     dc.m_source_printing.show_line_numbers_p = true;
-    path_summary summary (dc, path, true);
-    print_path_summary_as_text (summary, dc, false);
+    diagnostic_text_output_format text_output (dc);
+    path_summary summary (text_output, path, true);
+    print_path_summary_as_text (summary, text_output, false);
     ASSERT_STREQ
       ("  events 1-2\n"
        "FILENAME:3:19:\n"
@@ -1914,8 +1943,9 @@  assert_cfg_edge_path_streq (const location &loc,
   dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
   dc.m_source_printing.show_event_links_p = true;
   dc.m_source_printing.show_line_numbers_p = true;
-  path_summary summary (dc, path, true);
-  print_path_summary_as_text (summary, dc, false);
+  diagnostic_text_output_format text_output (dc);
+  path_summary summary (text_output, path, true);
+  print_path_summary_as_text (summary, text_output, false);
   ASSERT_STREQ_AT (loc, expected_str,
 		   pp_formatted_text (dc.m_printer));
 }
@@ -2236,8 +2266,9 @@  test_control_flow_5 (const line_table_case &case_,
     dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
     dc.m_source_printing.show_event_links_p = true;
     dc.m_source_printing.show_line_numbers_p = true;
-    path_summary summary (dc, path, true);
-    print_path_summary_as_text (summary, dc, false);
+    diagnostic_text_output_format text_output (dc);
+    path_summary summary (text_output, path, true);
+    print_path_summary_as_text (summary, text_output, false);
     ASSERT_STREQ
       ("  events 1-5\n"
        "FILENAME:1:6:\n"
@@ -2323,8 +2354,9 @@  test_control_flow_6 (const line_table_case &case_,
     dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
     dc.m_source_printing.show_event_links_p = true;
     dc.m_source_printing.show_line_numbers_p = true;
-    path_summary summary (dc, path, true);
-    print_path_summary_as_text (summary, dc, false);
+    diagnostic_text_output_format text_output (dc);
+    path_summary summary (text_output, path, true);
+    print_path_summary_as_text (summary, text_output, false);
     ASSERT_STREQ
       ("  events 1-3\n"
        "FILENAME:6:25:\n"
diff --git a/gcc/diagnostic.cc b/gcc/diagnostic.cc
index 03fc346501e4..152eb9d35a8e 100644
--- a/gcc/diagnostic.cc
+++ b/gcc/diagnostic.cc
@@ -85,11 +85,11 @@  build_message_string (const char *msg, ...)
   return str;
 }
 
-/* Same as diagnostic_build_prefix, but only the source FILE is given.  */
+/* Same as build_prefix, but only the source FILE is given.  */
 char *
-file_name_as_prefix (diagnostic_context *context, const char *f)
+diagnostic_text_output_format::file_name_as_prefix (const char *f) const
 {
-  pretty_printer *const pp = context->m_printer;
+  pretty_printer *const pp = get_printer ();
   const char *locus_cs
     = colorize_start (pp_show_color (pp), "locus");
   const char *locus_ce = colorize_stop (pp_show_color (pp));
@@ -223,13 +223,12 @@  diagnostic_context::initialize (int n_opts)
   m_max_errors = 0;
   m_internal_error = nullptr;
   m_adjust_diagnostic_info = nullptr;
-  m_text_callbacks.m_begin_diagnostic = default_diagnostic_starter;
+  m_text_callbacks.m_begin_diagnostic = default_diagnostic_text_starter;
   m_text_callbacks.m_start_span = default_diagnostic_start_span_fn;
-  m_text_callbacks.m_end_diagnostic = default_diagnostic_finalizer;
+  m_text_callbacks.m_end_diagnostic = default_diagnostic_text_finalizer;
   m_option_mgr = nullptr;
   m_urlifier = nullptr;
   m_last_location = UNKNOWN_LOCATION;
-  m_last_module = nullptr;
   m_client_aux_data = nullptr;
   m_lock = 0;
   m_inhibit_notes_p = false;
@@ -259,7 +258,6 @@  diagnostic_context::initialize (int n_opts)
   m_output_format = new diagnostic_text_output_format (*this);
   m_set_locations_cb = nullptr;
   m_ice_handler_cb = nullptr;
-  m_includes_seen = nullptr;
   m_client_data_hooks = nullptr;
   m_diagrams.m_theme = nullptr;
   m_original_argv = nullptr;
@@ -381,12 +379,6 @@  diagnostic_context::finish ()
       m_edit_context_ptr = nullptr;
     }
 
-  if (m_includes_seen)
-    {
-      delete m_includes_seen;
-      m_includes_seen = nullptr;
-    }
-
   if (m_client_data_hooks)
     {
       delete m_client_data_hooks;
@@ -554,36 +546,14 @@  diagnostic_context::converted_column (expanded_location s) const
   return one_based_col + (m_column_origin - 1);
 }
 
-/* Return a formatted line and column ':%line:%column'.  Elided if
-   line == 0 or col < 0.  (A column of 0 may be valid due to the
-   -fdiagnostics-column-origin option.)
-   The result is a statically allocated buffer.  */
-
-static const char *
-maybe_line_and_column (int line, int col)
-{
-  static char result[32];
-
-  if (line)
-    {
-      size_t l
-	= snprintf (result, sizeof (result),
-		    col >= 0 ? ":%d:%d" : ":%d", line, col);
-      gcc_checking_assert (l < sizeof (result));
-    }
-  else
-    result[0] = 0;
-  return result;
-}
-
 /* Return a string describing a location e.g. "foo.c:42:10".  */
 
 label_text
-diagnostic_context::get_location_text (const expanded_location &s) const
+diagnostic_context::get_location_text (const expanded_location &s,
+				       bool colorize) const
 {
-  pretty_printer *pp = m_printer;
-  const char *locus_cs = colorize_start (pp_show_color (pp), "locus");
-  const char *locus_ce = colorize_stop (pp_show_color (pp));
+  const char *locus_cs = colorize_start (colorize, "locus");
+  const char *locus_ce = colorize_stop (colorize);
   const char *file = s.file ? s.file : progname;
   int line = 0;
   int col = -1;
@@ -618,24 +588,24 @@  get_diagnostic_kind_text (diagnostic_t kind)
    diagnostic, e.g. "foo.c:42:10: error: ".  The caller is responsible for
    freeing the memory.  */
 char *
-diagnostic_build_prefix (diagnostic_context *context,
-			 const diagnostic_info *diagnostic)
+diagnostic_text_output_format::
+build_prefix (const diagnostic_info &diagnostic) const
 {
-  gcc_assert (diagnostic->kind < DK_LAST_DIAGNOSTIC_KIND);
+  gcc_assert (diagnostic.kind < DK_LAST_DIAGNOSTIC_KIND);
 
-  const char *text = _(diagnostic_kind_text[diagnostic->kind]);
+  const char *text = _(diagnostic_kind_text[diagnostic.kind]);
   const char *text_cs = "", *text_ce = "";
-  pretty_printer *pp = context->m_printer;
+  pretty_printer *pp = get_printer ();
 
-  if (diagnostic_kind_color[diagnostic->kind])
+  if (diagnostic_kind_color[diagnostic.kind])
     {
       text_cs = colorize_start (pp_show_color (pp),
-				diagnostic_kind_color[diagnostic->kind]);
+				diagnostic_kind_color[diagnostic.kind]);
       text_ce = colorize_stop (pp_show_color (pp));
     }
 
-  const expanded_location s = diagnostic_expand_location (diagnostic);
-  label_text location_text = context->get_location_text (s);
+  const expanded_location s = diagnostic_expand_location (&diagnostic);
+  label_text location_text = get_location_text (s);
 
   char *result = build_message_string ("%s %s%s%s", location_text.get (),
 				       text_cs, text, text_ce);
@@ -837,104 +807,11 @@  diagnostic_context::action_after_output (diagnostic_t diag_kind)
     }
 }
 
-/* Only dump the "In file included from..." stack once for each file.  */
-
-bool
-diagnostic_context::includes_seen_p (const line_map_ordinary *map)
-{
-  /* No include path for main.  */
-  if (MAIN_FILE_P (map))
-    return true;
-
-  /* Always identify C++ modules, at least for now.  */
-  auto probe = map;
-  if (linemap_check_ordinary (map)->reason == LC_RENAME)
-    /* The module source file shows up as LC_RENAME inside LC_MODULE.  */
-    probe = linemap_included_from_linemap (line_table, map);
-  if (MAP_MODULE_P (probe))
-    return false;
-
-  if (!m_includes_seen)
-    m_includes_seen = new hash_set<location_t, false, location_hash>;
-
-  /* Hash the location of the #include directive to better handle files
-     that are included multiple times with different macros defined.  */
-  return m_includes_seen->add (linemap_included_from (map));
-}
-
-void
-diagnostic_context::report_current_module (location_t where)
-{
-  pretty_printer *pp = m_printer;
-  const line_map_ordinary *map = NULL;
-
-  if (pp_needs_newline (m_printer))
-    {
-      pp_newline (pp);
-      pp_needs_newline (pp) = false;
-    }
-
-  if (where <= BUILTINS_LOCATION)
-    return;
-
-  linemap_resolve_location (line_table, where,
-			    LRK_MACRO_DEFINITION_LOCATION,
-			    &map);
-
-  if (map && m_last_module != map)
-    {
-      m_last_module = map;
-      if (!includes_seen_p (map))
-	{
-	  bool first = true, need_inc = true, was_module = MAP_MODULE_P (map);
-	  expanded_location s = {};
-	  do
-	    {
-	      where = linemap_included_from (map);
-	      map = linemap_included_from_linemap (line_table, map);
-	      bool is_module = MAP_MODULE_P (map);
-	      s.file = LINEMAP_FILE (map);
-	      s.line = SOURCE_LINE (map, where);
-	      int col = -1;
-	      if (first && m_show_column)
-		{
-		  s.column = SOURCE_COLUMN (map, where);
-		  col = converted_column (s);
-		}
-	      const char *line_col = maybe_line_and_column (s.line, col);
-	      static const char *const msgs[] =
-		{
-		 NULL,
-		 N_("                 from"),
-		 N_("In file included from"),	/* 2 */
-		 N_("        included from"),
-		 N_("In module"),		/* 4 */
-		 N_("of module"),
-		 N_("In module imported at"),	/* 6 */
-		 N_("imported at"),
-		};
-
-	      unsigned index = (was_module ? 6 : is_module ? 4
-				: need_inc ? 2 : 0) + !first;
-
-	      pp_verbatim (pp, "%s%s %r%s%s%R",
-			   first ? "" : was_module ? ", " : ",\n",
-			   _(msgs[index]),
-			   "locus", s.file, line_col);
-	      first = false, need_inc = was_module, was_module = is_module;
-	    }
-	  while (!includes_seen_p (map));
-	  pp_verbatim (pp, ":");
-	  pp_newline (pp);
-	}
-    }
-}
-
 /* If DIAGNOSTIC has a diagnostic_path and this context supports
    printing paths, print the path.  */
 
 void
-diagnostic_context::show_any_path (const diagnostic_info &diagnostic)
+diagnostic_text_output_format::show_any_path (const diagnostic_info &diagnostic)
 {
   const diagnostic_path *path = diagnostic.richloc->get_path ();
   if (!path)
@@ -969,39 +846,16 @@  logical_location::function_p () const
     }
 }
 
-void
-default_diagnostic_starter (diagnostic_context *context,
-			    const diagnostic_info *diagnostic)
-{
-  diagnostic_report_current_module (context, diagnostic_location (diagnostic));
-  pretty_printer *const pp = context->m_printer;
-  pp_set_prefix (pp, diagnostic_build_prefix (context, diagnostic));
-}
-
 void
 default_diagnostic_start_span_fn (diagnostic_context *context,
 				  expanded_location exploc)
 {
-  label_text text = context->get_location_text (exploc);
   pretty_printer *pp = context->m_printer;
+  label_text text = context->get_location_text (exploc, pp_show_color (pp));
   pp_string (pp, text.get ());
   pp_newline (pp);
 }
 
-void
-default_diagnostic_finalizer (diagnostic_context *context,
-			      const diagnostic_info *diagnostic,
-			      diagnostic_t)
-{
-  pretty_printer *const pp = context->m_printer;
-  char *saved_prefix = pp_take_prefix (pp);
-  pp_set_prefix (pp, NULL);
-  pp_newline (pp);
-  diagnostic_show_locus (context, diagnostic->richloc, diagnostic->kind, pp);
-  pp_set_prefix (pp, saved_prefix);
-  pp_flush (pp);
-}
-
 /* Interface to specify diagnostic kind overrides.  Returns the
    previous setting, or DK_UNSPECIFIED if the parameters are out of
    range.  If OPTION_ID is zero, the new setting is for all the
@@ -1436,7 +1290,7 @@  diagnostic_context::report_diagnostic (diagnostic_info *diagnostic)
 
   m_lock--;
 
-  show_any_path (*diagnostic);
+  m_output_format->after_diagnostic (*diagnostic);
 
   return true;
 }
@@ -1492,12 +1346,14 @@  trim_filename (const char *name)
   return p;
 }
 
-/* Add a note with text GMSGID and with LOCATION to the diagnostic CONTEXT.  */
+/* Add a purely textual note with text GMSGID and with LOCATION.  */
+
 void
-diagnostic_append_note (diagnostic_context *context,
-                        location_t location,
-                        const char * gmsgid, ...)
+diagnostic_text_output_format::append_note (location_t location,
+					    const char * gmsgid, ...)
 {
+  diagnostic_context *context = &get_context ();
+
   diagnostic_info diagnostic;
   va_list ap;
   rich_location richloc (line_table, location);
@@ -1509,10 +1365,9 @@  diagnostic_append_note (diagnostic_context *context,
       va_end (ap);
       return;
     }
-  pretty_printer *pp = context->m_printer;
+  pretty_printer *pp = get_printer ();
   char *saved_prefix = pp_take_prefix (pp);
-  pp_set_prefix (pp,
-                 diagnostic_build_prefix (context, &diagnostic));
+  pp_set_prefix (pp, build_prefix (diagnostic));
   pp_format (pp, &diagnostic.message);
   pp_output_formatted_text (pp);
   pp_destroy_prefix (pp);
@@ -1983,7 +1838,7 @@  assert_location_text (const char *expected_loc_text,
   xloc.data = NULL;
   xloc.sysp = false;
 
-  label_text actual_loc_text = dc.get_location_text (xloc);
+  label_text actual_loc_text = dc.get_location_text (xloc, false);
   ASSERT_STREQ (expected_loc_text, actual_loc_text.get ());
 }
 
diff --git a/gcc/diagnostic.h b/gcc/diagnostic.h
index b7c38267bd08..d5b4d610e6cf 100644
--- a/gcc/diagnostic.h
+++ b/gcc/diagnostic.h
@@ -171,15 +171,15 @@  struct diagnostic_info
 };
 
 /*  Forward declarations.  */
-typedef void (*diagnostic_starter_fn) (diagnostic_context *,
-				       const diagnostic_info *);
+typedef void (*diagnostic_text_starter_fn) (diagnostic_text_output_format &,
+					    const diagnostic_info *);
 
 typedef void (*diagnostic_start_span_fn) (diagnostic_context *,
 					  expanded_location);
 
-typedef void (*diagnostic_finalizer_fn) (diagnostic_context *,
-					 const diagnostic_info *,
-					 diagnostic_t);
+typedef void (*diagnostic_text_finalizer_fn) (diagnostic_text_output_format &,
+					      const diagnostic_info *,
+					      diagnostic_t);
 
 /* Abstract base class for the diagnostic subsystem to make queries
    about command-line options.  */
@@ -352,12 +352,12 @@  class diagnostic_context
 {
 public:
   /* Give access to m_text_callbacks.  */
-  friend diagnostic_starter_fn &
-  diagnostic_starter (diagnostic_context *context);
+  friend diagnostic_text_starter_fn &
+  diagnostic_text_starter (diagnostic_context *context);
   friend diagnostic_start_span_fn &
   diagnostic_start_span (diagnostic_context *context);
-  friend diagnostic_finalizer_fn &
-  diagnostic_finalizer (diagnostic_context *context);
+  friend diagnostic_text_finalizer_fn &
+  diagnostic_text_finalizer (diagnostic_context *context);
 
   friend class diagnostic_text_output_format;
 
@@ -400,8 +400,6 @@  public:
 
   bool report_diagnostic (diagnostic_info *);
 
-  void report_current_module (location_t where);
-
   void check_max_errors (bool flush);
   void action_after_output (diagnostic_t diag_kind);
 
@@ -546,7 +544,8 @@  public:
     return m_lang_mask;
   }
 
-  label_text get_location_text (const expanded_location &s) const;
+  label_text get_location_text (const expanded_location &s,
+				bool colorize) const;
 
   bool diagnostic_impl (rich_location *, const diagnostic_metadata *,
 			diagnostic_option_id, const char *,
@@ -557,10 +556,6 @@  public:
 			  diagnostic_t) ATTRIBUTE_GCC_DIAG(7,0);
 
 private:
-  bool includes_seen_p (const line_map_ordinary *map);
-
-  void show_any_path (const diagnostic_info &diagnostic);
-
   void error_recursion () ATTRIBUTE_NORETURN;
 
   bool diagnostic_enabled (diagnostic_info *diagnostic);
@@ -572,8 +567,6 @@  private:
 		   pretty_printer *pp,
 		   diagnostic_source_effect_info *effect_info);
 
-  void print_path (const diagnostic_path &path);
-
   /* Data members.
      Ideally, all of these would be private.  */
 
@@ -654,7 +647,7 @@  private:
        from "/home/gdr/src/nifty_printer.h:56:
        ...
     */
-    diagnostic_starter_fn m_begin_diagnostic;
+    diagnostic_text_starter_fn m_begin_diagnostic;
 
     /* This function is called by diagnostic_show_locus in between
        disjoint spans of source code, so that the context can print
@@ -662,7 +655,7 @@  private:
     diagnostic_start_span_fn m_start_span;
 
     /* This function is called after the diagnostic message is printed.  */
-    diagnostic_finalizer_fn m_end_diagnostic;
+    diagnostic_text_finalizer_fn m_end_diagnostic;
   } m_text_callbacks;
 
 public:
@@ -689,10 +682,6 @@  public:
   location_t m_last_location;
 
 private:
-  /* Used to detect when the input file stack has changed since last
-     described.  */
-  const line_map_ordinary *m_last_module;
-
   int m_lock;
 
 public:
@@ -751,10 +740,6 @@  private:
   /* Optional callback for attempting to handle ICEs gracefully.  */
   ice_handler_callback_t m_ice_handler_cb;
 
-  /* Include files that diagnostic_report_current_module has already listed the
-     include path for.  */
-  hash_set<location_t, false, location_hash> *m_includes_seen;
-
   /* A bundle of hooks for providing data to the context about its client
      e.g. version information, plugins, etc.
      Used by SARIF output to give metadata about the client that's
@@ -783,8 +768,8 @@  diagnostic_inhibit_notes (diagnostic_context * context)
 
 /* Client supplied function to announce a diagnostic
    (for text-based diagnostic output).  */
-inline diagnostic_starter_fn &
-diagnostic_starter (diagnostic_context *context)
+inline diagnostic_text_starter_fn &
+diagnostic_text_starter (diagnostic_context *context)
 {
   return context->m_text_callbacks.m_begin_diagnostic;
 }
@@ -800,8 +785,8 @@  diagnostic_start_span (diagnostic_context *context)
 
 /* Client supplied function called after a diagnostic message is
    displayed (for text-based diagnostic output).  */
-inline diagnostic_finalizer_fn &
-diagnostic_finalizer (diagnostic_context *context)
+inline diagnostic_text_finalizer_fn &
+diagnostic_text_finalizer (diagnostic_context *context)
 {
   return context->m_text_callbacks.m_end_diagnostic;
 }
@@ -887,13 +872,6 @@  diagnostic_finish (diagnostic_context *context)
   context->finish ();
 }
 
-inline void
-diagnostic_report_current_module (diagnostic_context *context,
-				  location_t where)
-{
-  context->report_current_module (where);
-}
-
 inline void
 diagnostic_show_locus (diagnostic_context *context,
 		       rich_location *richloc,
@@ -975,16 +953,14 @@  extern void diagnostic_set_info_translated (diagnostic_info *, const char *,
 					    va_list *, rich_location *,
 					    diagnostic_t)
      ATTRIBUTE_GCC_DIAG(2,0);
-extern void diagnostic_append_note (diagnostic_context *, location_t,
-                                    const char *, ...) ATTRIBUTE_GCC_DIAG(3,4);
 #endif
-extern char *diagnostic_build_prefix (diagnostic_context *, const diagnostic_info *);
-void default_diagnostic_starter (diagnostic_context *, const diagnostic_info *);
+void default_diagnostic_text_starter (diagnostic_text_output_format &,
+				      const diagnostic_info *);
 void default_diagnostic_start_span_fn (diagnostic_context *,
 				       expanded_location);
-void default_diagnostic_finalizer (diagnostic_context *,
-				   const diagnostic_info *,
-				   diagnostic_t);
+void default_diagnostic_text_finalizer (diagnostic_text_output_format &,
+					const diagnostic_info *,
+					diagnostic_t);
 void diagnostic_set_caret_max_width (diagnostic_context *context, int value);
 
 inline void
@@ -1050,7 +1026,6 @@  diagnostic_same_line (const diagnostic_context *context,
 extern const char *diagnostic_get_color_for_kind (diagnostic_t kind);
 
 /* Pure text formatting support functions.  */
-extern char *file_name_as_prefix (diagnostic_context *, const char *);
 
 extern char *build_message_string (const char *, ...) ATTRIBUTE_PRINTF_1;
 
@@ -1073,4 +1048,6 @@  extern char *get_cwe_url (int cwe);
 
 extern const char *get_diagnostic_kind_text (diagnostic_t kind);
 
+const char *maybe_line_and_column (int line, int col);
+
 #endif /* ! GCC_DIAGNOSTIC_H */
diff --git a/gcc/fortran/error.cc b/gcc/fortran/error.cc
index 6a7f0b052bc5..cad0e699f0a2 100644
--- a/gcc/fortran/error.cc
+++ b/gcc/fortran/error.cc
@@ -33,6 +33,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "diagnostic.h"
 #include "diagnostic-color.h"
 #include "tree-diagnostic.h" /* tree_diagnostics_defaults */
+#include "diagnostic-format-text.h"
 
 static int suppress_errors = 0;
 
@@ -1266,10 +1267,11 @@  gfc_diagnostic_build_locus_prefix (diagnostic_context *context,
        [locus of primary range]: Error: Some error at (1) and (2)
 */
 static void
-gfc_diagnostic_starter (diagnostic_context *context,
-			const diagnostic_info *diagnostic)
+gfc_diagnostic_text_starter (diagnostic_text_output_format &text_output,
+			     const diagnostic_info *diagnostic)
 {
-  pretty_printer *const pp = context->m_printer;
+  diagnostic_context *const context = &text_output.get_context ();
+  pretty_printer *const pp = text_output.get_printer ();
   char * kind_prefix = gfc_diagnostic_build_kind_prefix (context, diagnostic);
 
   expanded_location s1 = diagnostic_expand_location (diagnostic);
@@ -1345,11 +1347,11 @@  gfc_diagnostic_start_span (diagnostic_context *context,
 
 
 static void
-gfc_diagnostic_finalizer (diagnostic_context *context,
-			  const diagnostic_info *diagnostic ATTRIBUTE_UNUSED,
-			  diagnostic_t orig_diag_kind ATTRIBUTE_UNUSED)
+gfc_diagnostic_text_finalizer (diagnostic_text_output_format &text_output,
+			       const diagnostic_info *diagnostic ATTRIBUTE_UNUSED,
+			       diagnostic_t orig_diag_kind ATTRIBUTE_UNUSED)
 {
-  pretty_printer *const pp = context->m_printer;
+  pretty_printer *const pp = text_output.get_printer ();
   pp_destroy_prefix (pp);
   pp_newline_and_flush (pp);
 }
@@ -1704,9 +1706,9 @@  gfc_errors_to_warnings (bool f)
 void
 gfc_diagnostics_init (void)
 {
-  diagnostic_starter (global_dc) = gfc_diagnostic_starter;
+  diagnostic_text_starter (global_dc) = gfc_diagnostic_text_starter;
   diagnostic_start_span (global_dc) = gfc_diagnostic_start_span;
-  diagnostic_finalizer (global_dc) = gfc_diagnostic_finalizer;
+  diagnostic_text_finalizer (global_dc) = gfc_diagnostic_text_finalizer;
   diagnostic_format_decoder (global_dc) = gfc_format_decoder;
   global_dc->m_source_printing.caret_chars[0] = '1';
   global_dc->m_source_printing.caret_chars[1] = '2';
@@ -1724,8 +1726,8 @@  gfc_diagnostics_finish (void)
   tree_diagnostics_defaults (global_dc);
   /* We still want to use the gfc starter and finalizer, not the tree
      defaults.  */
-  diagnostic_starter (global_dc) = gfc_diagnostic_starter;
-  diagnostic_finalizer (global_dc) = gfc_diagnostic_finalizer;
+  diagnostic_text_starter (global_dc) = gfc_diagnostic_text_starter;
+  diagnostic_text_finalizer (global_dc) = gfc_diagnostic_text_finalizer;
   global_dc->m_source_printing.caret_chars[0] = '^';
   global_dc->m_source_printing.caret_chars[1] = '^';
 }
diff --git a/gcc/jit/dummy-frontend.cc b/gcc/jit/dummy-frontend.cc
index dbeeacd17a86..17f37a3f405a 100644
--- a/gcc/jit/dummy-frontend.cc
+++ b/gcc/jit/dummy-frontend.cc
@@ -31,6 +31,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "attribs.h"
 #include "cgraph.h"
 #include "target.h"
+#include "diagnostic-format-text.h"
 
 #include <mpfr.h>
 
@@ -984,7 +985,7 @@  struct ggc_root_tab jit_root_tab[] =
 /* Implementation of "begin_diagnostic".  */
 
 static void
-jit_begin_diagnostic (diagnostic_context */*context*/,
+jit_begin_diagnostic (diagnostic_text_output_format &,
 		      const diagnostic_info */*diagnostic*/)
 {
   gcc_assert (gcc::jit::active_playback_ctxt);
@@ -997,7 +998,7 @@  jit_begin_diagnostic (diagnostic_context */*context*/,
 /* Implementation of "end_diagnostic".  */
 
 static void
-jit_end_diagnostic (diagnostic_context *context,
+jit_end_diagnostic (diagnostic_text_output_format &text_output,
 		    const diagnostic_info *diagnostic,
 		    diagnostic_t)
 {
@@ -1007,7 +1008,7 @@  jit_end_diagnostic (diagnostic_context *context,
   /* Delegate to the playback context (and thence to the
      recording context).  */
   gcc_assert (diagnostic);
-  gcc::jit::active_playback_ctxt->add_diagnostic (context, *diagnostic);
+  gcc::jit::active_playback_ctxt->add_diagnostic (&text_output.get_context (), *diagnostic); // FIXME
 }
 
 /* Language hooks.  */
@@ -1026,8 +1027,8 @@  jit_langhook_init (void)
     }
 
   gcc_assert (global_dc);
-  diagnostic_starter (global_dc) = jit_begin_diagnostic;
-  diagnostic_finalizer (global_dc) = jit_end_diagnostic;
+  diagnostic_text_starter (global_dc) = jit_begin_diagnostic;
+  diagnostic_text_finalizer (global_dc) = jit_end_diagnostic;
 
   build_common_tree_nodes (false);
 
diff --git a/gcc/langhooks-def.h b/gcc/langhooks-def.h
index f5c67b6823cb..179ed393fd29 100644
--- a/gcc/langhooks-def.h
+++ b/gcc/langhooks-def.h
@@ -49,7 +49,7 @@  extern void lhd_print_tree_nothing (FILE *, tree, int);
 extern const char *lhd_decl_printable_name (tree, int);
 extern const char *lhd_dwarf_name (tree, int);
 extern int lhd_types_compatible_p (tree, tree);
-extern void lhd_print_error_function (diagnostic_context *,
+extern void lhd_print_error_function (diagnostic_text_output_format &,
 				      const char *,
 				      const struct diagnostic_info *);
 extern void lhd_set_decl_assembler_name (tree decl);
diff --git a/gcc/langhooks.cc b/gcc/langhooks.cc
index 8614f44f187c..b28c01deed21 100644
--- a/gcc/langhooks.cc
+++ b/gcc/langhooks.cc
@@ -38,6 +38,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "stor-layout.h"
 #include "cgraph.h"
 #include "debug.h"
+#include "diagnostic-format-text.h"
 
 /* Do nothing; in many cases the default hook.  */
 
@@ -368,16 +369,18 @@  lhd_handle_option (size_t code ATTRIBUTE_UNUSED,
 /* The default function to print out name of current function that caused
    an error.  */
 void
-lhd_print_error_function (diagnostic_context *context, const char *file,
+lhd_print_error_function (diagnostic_text_output_format &text_output,
+			  const char *file,
 			  const diagnostic_info *diagnostic)
 {
+  diagnostic_context *const context = &text_output.get_context ();
   if (diagnostic_last_function_changed (context, diagnostic))
     {
-      pretty_printer *const pp = context->m_printer;
+      pretty_printer *const pp = text_output.get_printer ();
       char *old_prefix = pp_take_prefix (pp);
       tree abstract_origin = diagnostic_abstract_origin (diagnostic);
       char *new_prefix = (file && abstract_origin == NULL)
-			 ? file_name_as_prefix (context, file) : NULL;
+			 ? text_output.file_name_as_prefix (file) : NULL;
 
       pp_set_prefix (pp, new_prefix);
 
diff --git a/gcc/langhooks.h b/gcc/langhooks.h
index 5a4dfb6ef62c..cd944673cd62 100644
--- a/gcc/langhooks.h
+++ b/gcc/langhooks.h
@@ -514,8 +514,10 @@  struct lang_hooks
      in contexts where erroneously returning 0 causes problems.  */
   int (*types_compatible_p) (tree x, tree y);
 
-  /* Called by report_error_function to print out function name.  */
-  void (*print_error_function) (diagnostic_context *, const char *,
+  /* Called by diagnostic_report_current_function to print out function name
+     for textual diagnostic output.  */
+  void (*print_error_function) (diagnostic_text_output_format &,
+				const char *,
 				const struct diagnostic_info *);
 
   /* Convert a character from the host's to the target's character
diff --git a/gcc/rust/resolve/rust-ast-resolve-expr.cc b/gcc/rust/resolve/rust-ast-resolve-expr.cc
index 1bb3fc601539..5520dc235017 100644
--- a/gcc/rust/resolve/rust-ast-resolve-expr.cc
+++ b/gcc/rust/resolve/rust-ast-resolve-expr.cc
@@ -131,11 +131,12 @@  ResolveExpr::visit (AST::AssignmentExpr &expr)
    the default bug reporting instructions, as there is no bug to report.  */
 
 static void ATTRIBUTE_NORETURN
-funny_ice_finalizer (diagnostic_context *context,
-		     const diagnostic_info *diagnostic, diagnostic_t diag_kind)
+funny_ice_text_finalizer (diagnostic_text_output_format &text_output,
+			  const diagnostic_info *diagnostic,
+			  diagnostic_t diag_kind)
 {
   gcc_assert (diag_kind == DK_ICE_NOBT);
-  default_diagnostic_finalizer (context, diagnostic, diag_kind);
+  default_diagnostic_text_finalizer (text_output, diagnostic, diag_kind);
   fnotice (stderr, "You have broken GCC Rust. This is a feature.\n");
   exit (ICE_EXIT_CODE);
 }
@@ -161,7 +162,7 @@  ResolveExpr::visit (AST::IdentifierExpr &expr)
 	 resolve.  Emit a funny ICE.  We set the finalizer to our custom one,
 	 and use the lower-level emit_diagnostic () instead of the more common
 	 internal_error_no_backtrace () in order to pass our locus.  */
-      diagnostic_finalizer (global_dc) = funny_ice_finalizer;
+      diagnostic_text_finalizer (global_dc) = funny_ice_text_finalizer;
       emit_diagnostic (DK_ICE_NOBT, expr.get_locus (), -1,
 		       "are you trying to break %s? how dare you?",
 		       expr.as_string ().c_str ());
diff --git a/gcc/testsuite/g++.dg/plugin/show_template_tree_color_plugin.c b/gcc/testsuite/g++.dg/plugin/show_template_tree_color_plugin.c
index e1af2cf25a4e..6151b6ebe79f 100644
--- a/gcc/testsuite/g++.dg/plugin/show_template_tree_color_plugin.c
+++ b/gcc/testsuite/g++.dg/plugin/show_template_tree_color_plugin.c
@@ -3,8 +3,8 @@ 
    The color codes in the generated messages would also need escaping
    for use within dg-error.
 
-   Hence the simplest approach is to provide a custom diagnostic_starter_fn,
-   which does nothing.
+   Hence the simplest approach is to provide a custom
+   diagnostic_text_starter_fn, which does nothing.
 
    The resulting messages lack the "FILENAME:LINE:COL: error: " prefix
    and can thus be tested using dg-begin/end-multiline-output.  */
@@ -21,7 +21,7 @@ 
 int plugin_is_GPL_compatible;
 
 void
-noop_starter_fn (diagnostic_context *, const diagnostic_info *)
+noop_text_starter_fn (diagnostic_text_output_format &, const diagnostic_info *)
 {
 }
 
@@ -32,7 +32,7 @@  plugin_init (struct plugin_name_args *plugin_info,
   if (!plugin_default_version_check (version, &gcc_version))
     return 1;
 
-  diagnostic_starter (global_dc) = noop_starter_fn;
+  diagnostic_text_starter (global_dc) = noop_text_starter_fn;
 
   return 0;
 }
diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic_group_plugin.c b/gcc/testsuite/gcc.dg/plugin/diagnostic_group_plugin.c
index 047ba3626d8a..b4e876bc3a0c 100644
--- a/gcc/testsuite/gcc.dg/plugin/diagnostic_group_plugin.c
+++ b/gcc/testsuite/gcc.dg/plugin/diagnostic_group_plugin.c
@@ -165,10 +165,10 @@  pass_test_groups::execute (function *fun)
    expected output.  */
 
 void
-test_diagnostic_starter (diagnostic_context *context,
-			 const diagnostic_info *diagnostic)
+test_diagnostic_text_starter (diagnostic_text_output_format &text_output,
+			      const diagnostic_info *diagnostic)
 {
-  pp_set_prefix (context->m_printer, xstrdup ("PREFIX: "));
+  pp_set_prefix (text_output.get_printer (), xstrdup ("PREFIX: "));
 }
 
 /* Custom diagnostic callback, to avoid having the path in the
@@ -224,7 +224,7 @@  plugin_init (struct plugin_name_args *plugin_info,
   if (!plugin_default_version_check (version, &gcc_version))
     return 1;
 
-  diagnostic_starter (global_dc) = test_diagnostic_starter;
+  diagnostic_text_starter (global_dc) = test_diagnostic_text_starter;
   diagnostic_start_span (global_dc) = test_diagnostic_start_span_fn;
   global_dc->set_output_format (new test_output_format (*global_dc));
 
diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_show_locus.c b/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_show_locus.c
index ec6f0ec44523..95d5b1a04480 100644
--- a/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_show_locus.c
+++ b/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_show_locus.c
@@ -62,6 +62,7 @@ 
 #include "print-tree.h"
 #include "gcc-rich-location.h"
 #include "text-range-label.h"
+#include "diagnostic-format-text.h"
 
 int plugin_is_GPL_compatible;
 
@@ -124,23 +125,24 @@  static bool force_show_locus_color = false;
 /* We want to verify the colorized output of diagnostic_show_locus,
    but turning on colorization for everything confuses "dg-warning" etc.
    Hence we special-case it within this plugin by using this modified
-   version of default_diagnostic_finalizer, which, if "color" is
+   version of default_diagnostic_text_finalizer, which, if "color" is
    passed in as a plugin argument turns on colorization, but just
    for diagnostic_show_locus.  */
 
 static void
-custom_diagnostic_finalizer (diagnostic_context *context,
-			     const diagnostic_info *diagnostic,
-			     diagnostic_t)
+custom_diagnostic_text_finalizer (diagnostic_text_output_format &text_output,
+				  const diagnostic_info *diagnostic,
+				  diagnostic_t)
 {
-  pretty_printer *const pp = context->m_printer;
+  pretty_printer *const pp = text_output.get_printer ();
   bool old_show_color = pp_show_color (pp);
   if (force_show_locus_color)
     pp_show_color (pp) = true;
   char *saved_prefix = pp_take_prefix (pp);
   pp_set_prefix (pp, NULL);
   pp_newline (pp);
-  diagnostic_show_locus (context, diagnostic->richloc, diagnostic->kind);
+  diagnostic_show_locus (&text_output.get_context (),
+			 diagnostic->richloc, diagnostic->kind, pp);
   pp_show_color (pp) = old_show_color;
   pp_set_prefix (pp, saved_prefix);
   pp_flush (pp);
@@ -173,7 +175,7 @@  test_show_locus (function *fun)
   location_t fnstart = fun->function_start_locus;
   int fnstart_line = LOCATION_LINE (fnstart);
 
-  diagnostic_finalizer (global_dc) = custom_diagnostic_finalizer;
+  diagnostic_text_finalizer (global_dc) = custom_diagnostic_text_finalizer;
 
   /* Hardcode the "terminal width", to verify the behavior of
      very wide lines.  */
diff --git a/gcc/testsuite/gcc.dg/plugin/location_overflow_plugin.c b/gcc/testsuite/gcc.dg/plugin/location_overflow_plugin.c
index d0a6b0755648..45a01b5f9178 100644
--- a/gcc/testsuite/gcc.dg/plugin/location_overflow_plugin.c
+++ b/gcc/testsuite/gcc.dg/plugin/location_overflow_plugin.c
@@ -7,6 +7,7 @@ 
 #include "coretypes.h"
 #include "spellcheck.h"
 #include "diagnostic.h"
+#include "diagnostic-format-text.h"
 
 int plugin_is_GPL_compatible;
 
@@ -36,12 +37,12 @@  on_pragma_registration (void */*gcc_data*/, void */*user_data*/)
 }
 
 /* We add some extra testing during diagnostics by chaining up
-   to the finalizer.  */
+   to the text finalizer.  */
 
-static diagnostic_finalizer_fn original_finalizer = NULL;
+static diagnostic_text_finalizer_fn original_text_finalizer = NULL;
 
 static void
-verify_unpacked_ranges  (diagnostic_context *context,
+verify_unpacked_ranges  (diagnostic_text_output_format &text_output,
 			 const diagnostic_info *diagnostic,
 			 diagnostic_t orig_diag_kind)
 {
@@ -49,13 +50,13 @@  verify_unpacked_ranges  (diagnostic_context *context,
   location_t loc = diagnostic_location (diagnostic);
   gcc_assert (IS_ADHOC_LOC (loc));
 
-  /* We're done testing; chain up to original finalizer.  */
-  gcc_assert (original_finalizer);
-  original_finalizer (context, diagnostic, orig_diag_kind);
+  /* We're done testing; chain up to original text finalizer.  */
+  gcc_assert (original_text_finalizer);
+  original_text_finalizer (text_output, diagnostic, orig_diag_kind);
 }
 
 static void
-verify_no_columns  (diagnostic_context *context,
+verify_no_columns  (diagnostic_text_output_format &text_output,
 		    const diagnostic_info *diagnostic,
 		    diagnostic_t orig_diag_kind)
 {
@@ -63,9 +64,9 @@  verify_no_columns  (diagnostic_context *context,
   location_t loc = diagnostic_location (diagnostic);
   gcc_assert (LOCATION_COLUMN (loc) == 0);
 
-  /* We're done testing; chain up to original finalizer.  */
-  gcc_assert (original_finalizer);
-  original_finalizer (context, diagnostic, orig_diag_kind);
+  /* We're done testing; chain up to original text finalizer.  */
+  gcc_assert (original_text_finalizer);
+  original_text_finalizer (text_output, diagnostic, orig_diag_kind);
 }
 
 int
@@ -89,15 +90,15 @@  plugin_init (struct plugin_name_args *plugin_info,
 		     NULL); /* void *user_data */
 
   /* Hack in additional testing, based on the exact value supplied.  */
-  original_finalizer = diagnostic_finalizer (global_dc);
+  original_text_finalizer = diagnostic_text_finalizer (global_dc);
   switch (base_location)
     {
     case LINE_MAP_MAX_LOCATION_WITH_PACKED_RANGES + 1:
-      diagnostic_finalizer (global_dc) = verify_unpacked_ranges;
+      diagnostic_text_finalizer (global_dc) = verify_unpacked_ranges;
       break;
 
     case LINE_MAP_MAX_LOCATION_WITH_COLS + 1:
-      diagnostic_finalizer (global_dc) = verify_no_columns;
+      diagnostic_text_finalizer (global_dc) = verify_no_columns;
       break;
 
     default:
diff --git a/gcc/tree-diagnostic.cc b/gcc/tree-diagnostic.cc
index b9b2704b909d..39471021d0f4 100644
--- a/gcc/tree-diagnostic.cc
+++ b/gcc/tree-diagnostic.cc
@@ -30,25 +30,26 @@  along with GCC; see the file COPYING3.  If not see
 #include "diagnostic-client-data-hooks.h"
 #include "langhooks.h"
 #include "intl.h"
+#include "diagnostic-format-text.h"
 
 /* Prints out, if necessary, the name of the current function
-   that caused an error.  Called from all error and warning functions.  */
+   that caused an error.  */
 void
-diagnostic_report_current_function (diagnostic_context *context,
+diagnostic_report_current_function (diagnostic_text_output_format &text_output,
 				    const diagnostic_info *diagnostic)
 {
   location_t loc = diagnostic_location (diagnostic);
-  diagnostic_report_current_module (context, loc);
-  lang_hooks.print_error_function (context, LOCATION_FILE (loc), diagnostic);
+  text_output.report_current_module (loc);
+  lang_hooks.print_error_function (text_output, LOCATION_FILE (loc), diagnostic);
 }
 
 static void
-default_tree_diagnostic_starter (diagnostic_context *context,
-				 const diagnostic_info *diagnostic)
+default_tree_diagnostic_text_starter (diagnostic_text_output_format &text_output,
+				      const diagnostic_info *diagnostic)
 {
-  pretty_printer *pp = context->m_printer;
-  diagnostic_report_current_function (context, diagnostic);
-  pp_set_prefix (pp, diagnostic_build_prefix (context, diagnostic));
+  pretty_printer *const pp = text_output.get_printer ();
+  diagnostic_report_current_function (text_output, diagnostic);
+  pp_set_prefix (pp, text_output.build_prefix (*diagnostic));
 }
 
 /* Default tree printer.   Handles declarations only.  */
@@ -176,8 +177,8 @@  set_inlining_locations (diagnostic_context *,
 void
 tree_diagnostics_defaults (diagnostic_context *context)
 {
-  diagnostic_starter (context) = default_tree_diagnostic_starter;
-  diagnostic_finalizer (context) = default_diagnostic_finalizer;
+  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_set_locations_callback (set_inlining_locations);
   context->set_client_data_hooks (make_compiler_data_hooks ());
diff --git a/libcc1/context.cc b/libcc1/context.cc
index b4f3fd1fb33a..052047299b3a 100644
--- a/libcc1/context.cc
+++ b/libcc1/context.cc
@@ -39,6 +39,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "diagnostic.h"
 #include "langhooks.h"
 #include "langhooks-def.h"
+#include "diagnostic-format-text.h"
 
 #include "gcc-interface.h"
 
@@ -62,7 +63,8 @@  cc1_plugin::plugin_context *cc1_plugin::current_context;
 // This is put into the lang hooks when the plugin starts.
 
 static void
-plugin_print_error_function (diagnostic_context *context, const char *file,
+plugin_print_error_function (diagnostic_text_output_format &text_output,
+			     const char *file,
 			     const diagnostic_info *diagnostic)
 {
   if (current_function_decl != NULL_TREE
@@ -70,7 +72,7 @@  plugin_print_error_function (diagnostic_context *context, const char *file,
       && strcmp (IDENTIFIER_POINTER (DECL_NAME (current_function_decl)),
 		 GCC_FE_WRAPPER_FUNCTION) == 0)
     return;
-  lhd_print_error_function (context, file, diagnostic);
+  lhd_print_error_function (text_output, file, diagnostic);
 }