[committed] diagnostics: add labeling of source ranges

Eric Gallager egall@gwmail.gwu.edu
Wed Aug 15 23:50:00 GMT 2018


On 8/15/18, David Malcolm <dmalcolm@redhat.com> wrote:
> This patch adds the ability to label source ranges within a rich_location,
> to be printed by diagnostic_show_locus.
>
> For example:
>
> pr69554-1.c:11:18: error: invalid operands to binary + (have 'const char *'
> and 'const char *')
> 11 |   return (p + 1) + (q + 1);
>    |          ~~~~~~~ ^ ~~~~~~~
>    |             |         |
>    |             |         const char *
>    |             const char *
>
> The patch implements labels for various type mismatch errors in the C and
> C++ frontends, and in -Wformat.  I implemented it wherever accurate
> location
> information was guaranteed (there are other places that could benefit, but
> we need better location information in those places).
>
> The labels can be disabled via -fno-diagnostics-show-labels.
>
> Similarly:
>
> param-type-mismatch.C: In function 'int test_1(int, int, float)':
> param-type-mismatch.C:11:27: error: invalid conversion from 'int' to 'const
> char*' [-fpermissive]
> 11 |   return callee_1 (first, second, third);
>    |                           ^~~~~~
>    |                           |
>    |                           int

To me this seems more like it's saying "second" SHOULD be of type int,
rather than "second" IS of type int.

> param-type-mismatch.C:7:43: note:   initializing argument 2 of 'int
> callee_1(int, const char*, float)'
> 7 | extern int callee_1 (int one, const char *two, float three);
>   |                               ~~~~~~~~~~~~^~~
>

...but I guess that confusion would get quickly cleared up when
looking at the note that goes with it.

> where the first "error" describing the bad argument gets a label
> describing the type inline (since it's non-obvious from "second").
> The "note" describing the type of the param of the callee *doesn't*
> get a label, since that information is explicit there in the
> source ("const char *two").
>
> The idea is that in any diagnostic where two aspects of the source aren't
> in sync it ought to be easier for the user if we directly show them the
> mismatching aspects inline (e.g. types).
>
> As well as type mismatch errors, perhaps labels could also be used for
> buffer overflow warnings, for describing the capacity of the destination
> buffer vs the size of what's being written:
>
>   sprintf (buf, "filename: %s\n", file);
>            ^~~   ~~~~~~~~~~~^~~
>            |                |
>            capacity: 32     10 + strlen(file) + 2
>
> or somesuch.  Another idea might be for macro expansion warnings:
>
> warning: repeated side effects in macro expansion...
>    x = MIN (p++, q++);
>        ~~~~^~~~~~~~~~
> note: ...expanded here as
>  #define MIN(X,Y) (X<Y?X:Y)
>          ^~~ ~ ~   ~ ~ ~ ~
>              | |   | | | |
>              | |   | | | q++
>              | |   | | p++
>              | |   | q++
>              | q++ p++
>              p++
>

This would be a good thing to warn about separately from the addition
of labels to things; I just checked (with gcc 8) and code like that
currently gets no warnings regardless of the discussion of labels.

> The patch removes some logic from multiline.exp which special-cased
> lines ending with a '|' character (thus complicating testing of this
> patch).  I believe that this was a vestige from experiments I did to
> support strippng dg directives from the output; it was present in the
> earliest version of multiline.exp I posted:
>   "[RFC, stage1] Richer source location information for gcc 6 (location
> ranges etc)"
>     https://gcc.gnu.org/ml/gcc-patches/2015-03/msg00837.html
> and I believe was neved used.
>
> Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
>
> I manually verified that "make selftest-valgrind" and
> "make selftest-c++-valgrind" are clean.
>
> Committed to trunk as r263564.
>
> gcc/c-family/ChangeLog:
> 	* c-format.c: Include "selftest-diagnostic.h" and
> 	"gcc-rich-location.h".
> 	(format_warning_at_char): Pass NULL for new label params of
> 	format_warning_va.
> 	(class indirection_suffix): New class.
> 	(class range_label_for_format_type_mismatch): New class.
> 	(format_type_warning): Move logic for generating "*" suffix to
> 	class indirection_suffix.  Create "fmt_label" and "param_label"
> 	to show their types, and pass them to the
> 	format_warning_at_substring calls.
> 	(selftest::test_type_mismatch_range_labels): New test.
> 	(selftest::c_format_c_tests): Call it.
>
> gcc/c/ChangeLog:
> 	* c-objc-common.c: Include "gcc-rich-location.h".
> 	(c_tree_printer): Move implemenation of '%T' to...
> 	(print_type): ...this new function.
> 	(range_label_for_type_mismatch::get_text): New function.
> 	* c-typeck.c (convert_for_assignment): Add type labels to the rhs
> 	range for the various ic_argpass cases.
> 	(class maybe_range_label_for_tree_type_mismatch): New class.
> 	(build_binary_op): Use it when calling binary_op_error.
>
> gcc/cp/ChangeLog:
> 	* call.c: Include "gcc-rich-location.h".
> 	(convert_like_real): Add range label for "invalid conversion"
> 	diagnostic.
> 	(perform_implicit_conversion_flags): Add type label to the
> 	"could not convert" error.
> 	* error.c: Include "gcc-rich-location.h".
> 	(range_label_for_type_mismatch::get_text): New function.
> 	* typeck.c (convert_for_assignment): Add type label to
> 	the "cannot convert" error if a location is available.
>
> gcc/ChangeLog:
> 	* common.opt (fdiagnostics-show-labels): New option.
> 	* diagnostic-show-locus.c (class layout_range): Add field
> 	"m_label".
> 	(class layout): Add field "m_show_labels_p".
> 	(layout_range::layout_range): Add param "label" and use it to
> 	initialize m_label.
> 	(make_range): Pass in NULL for new "label" param of layout_range's
> 	ctor.
> 	(layout::layout): Initialize m_show_labels_p.
> 	(layout::maybe_add_location_range): Pass in loc_range->m_label
> 	when constructing layout_range instances.
> 	(struct line_label): New struct.
> 	(layout::print_any_labels): New member function.
> 	(layout::print_line): Call it if label-printing is enabled.
> 	(selftest::test_one_liner_labels): New test.
> 	(selftest::test_diagnostic_show_locus_one_liner): Call it.
> 	* diagnostic.c (diagnostic_initialize): Initialize
> 	context->show_labels_p.
> 	* diagnostic.h (struct diagnostic_context): Add field
> 	"show_labels_p".
> 	* doc/invoke.texi (Diagnostic Message Formatting Options): Add
> 	-fno-diagnostics-show-labels.
> 	* dwarf2out.c (gen_producer_string): Add
> 	OPT_fdiagnostics_show_labels to the ignored options.
> 	* gcc-rich-location.c (gcc_rich_location::add_expr): Add "label"
> 	param.
> 	(gcc_rich_location::maybe_add_expr): Likewise.
> 	* gcc-rich-location.h (gcc_rich_location::gcc_rich_location): Add
> 	label" param, defaulting to NULL.
> 	(gcc_rich_location::add_expr): Add "label" param.
> 	(gcc_rich_location::maybe_add_expr): Likewise.
> 	(class text_range_label): New class.
> 	(class range_label_for_type_mismatch): New class.
> 	* gimple-ssa-sprintf.c (fmtwarn): Pass NULL for new label params
> 	of format_warning_va.
> 	(fmtwarn_n): Likewise for new params of format_warning_n_va.
> 	* lto-wrapper.c (merge_and_complain): Add
> 	OPT_fdiagnostics_show_labels to the "pick one setting" options.
> 	(append_compiler_options): Likewise to the dropped options.
> 	(append_diag_options): Likewise to the passed-on options.
> 	* opts.c (common_handle_option): Handle the new option.
> 	* selftest-diagnostic.c
> 	(test_diagnostic_context::test_diagnostic_context): Enable
> 	show_labels_p.
> 	* substring-locations.c: Include "gcc-rich-location.h".
> 	(format_warning_n_va): Add "fmt_label" and "param_label" params
> 	and use them as appropriate.
> 	(format_warning_va): Add "fmt_label" and "param_label" params,
> 	passing them on to format_warning_n_va.
> 	(format_warning_at_substring): Likewise.
> 	(format_warning_at_substring_n): Likewise.
> 	* substring-locations.h (format_warning_va): Add "fmt_label" and
> 	"param_label" params.
> 	(format_warning_n_va): Likewise.
> 	(format_warning_at_substring): Likewise.
> 	(format_warning_at_substring_n): Likewise.
> 	* toplev.c (general_init): Initialize global_dc->show_labels_p.
>
> gcc/testsuite/ChangeLog:
> 	* g++.dg/diagnostic/aka3.C: New test.
> 	* g++.dg/diagnostic/param-type-mismatch-2.C: Update expected
> 	output to show range labels.
> 	* g++.dg/diagnostic/param-type-mismatch.C: Likewise.
> 	* g++.dg/plugin/plugin.exp (plugin_test_list): Add...
> 	* g++.dg/plugin/show-template-tree-color-labels.C: New test.
> 	* gcc.dg/bad-binary-ops.c: Update expected output to show range
> 	labels.  Add an "aka" example.
> 	* gcc.dg/cpp/pr66415-1.c: Update expected output to show range
> 	labels.
> 	* gcc.dg/format/diagnostic-ranges.c: Likewise.
> 	* gcc.dg/format/pr72858.c: Likewise.
> 	* gcc.dg/format/pr78498.c: Likewise.
> 	* gcc.dg/param-type-mismatch.c: Add "-Wpointer-sign" to options.
> 	Update expected output to show range labels.  Add examples of
> 	-Wincompatible-pointer-types and -Wpointer-sign for parameters.
> 	* gcc.dg/plugin/diagnostic-test-show-locus-bw-line-numbers.c:
> 	Update expected output to show range labels.
> 	* gcc.dg/plugin/diagnostic-test-show-locus-bw.c: Likewise.
> 	(test_very_wide_line): Adjust so that label is at left-clipping
> 	boundary.
> 	(test_very_wide_line_2): New test.
> 	* gcc.dg/plugin/diagnostic-test-show-locus-color-line-numbers.c:
> 	Update expected output to show range labels.
> 	* gcc.dg/plugin/diagnostic-test-show-locus-color.c: Likewise.
> 	* gcc.dg/plugin/diagnostic-test-show-locus-no-labels.c: New test.
> 	* gcc.dg/plugin/diagnostic_plugin_show_trees.c (show_tree): Update
> 	for new param to gcc_rich_location::add_expr.
> 	* gcc.dg/plugin/diagnostic_plugin_test_show_locus.c (add_range):
> 	Add "label" param.
> 	(test_show_locus): Add examples of labels to various tests.  Tweak
> 	the "very wide_line" test case and duplicate it, to cover the
> 	boundary values for clipping of labels against the left-margin.
> 	* gcc.dg/plugin/plugin.exp (plugin_test_list): Add
> 	diagnostic-test-show-locus-no-labels.c.
> 	* gcc.dg/pr69554-1.c: Update expected output to show range labels.
> 	Update line numbers of dg-locus directives.
> 	* gcc.dg/pr69627.c:  Update expected output to show range labels.
> 	* lib/multiline.exp (proc _build_multiline_regex): Remove
> 	special-case handling of lines with trailing '|'.
>
> libcpp/ChangeLog:
> 	* include/line-map.h (struct location_range): Add "m_label" field.
> 	(class rich_location): Add description of labels to leading
> 	comment.
> 	(rich_location::rich_location): Add "label" param, defaulting to
> 	NULL.
> 	(rich_location::add_range): Likewise.
> 	(struct label_text): New struct.
> 	(class range_label): New abstract base class.
> 	* line-map.c (rich_location::rich_location): Add "label" param;
> 	use it.
> 	(rich_location::add_range): Likewise.
> ---
>  gcc/c-family/c-format.c                            | 179 +++++++++--
>  gcc/c/c-objc-common.c                              | 124 +++++---
>  gcc/c/c-typeck.c                                   | 108 +++++--
>  gcc/common.opt                                     |   4 +
>  gcc/cp/call.c                                      |  18 +-
>  gcc/cp/error.c                                     |  28 ++
>  gcc/cp/typeck.c                                    |  11 +-
>  gcc/diagnostic-show-locus.c                        | 346
> ++++++++++++++++++++-
>  gcc/diagnostic.c                                   |   1 +
>  gcc/diagnostic.h                                   |   3 +
>  gcc/doc/invoke.texi                                |  19 +-
>  gcc/dwarf2out.c                                    |   1 +
>  gcc/gcc-rich-location.c                            |  14 +-
>  gcc/gcc-rich-location.h                            |  71 ++++-
>  gcc/gimple-ssa-sprintf.c                           |   7 +-
>  gcc/lto-wrapper.c                                  |   3 +
>  gcc/opts.c                                         |   4 +
>  gcc/selftest-diagnostic.c                          |   1 +
>  gcc/substring-locations.c                          |  53 +++-
>  gcc/substring-locations.h                          |  16 +-
>  gcc/testsuite/g++.dg/diagnostic/aka3.C             |  25 ++
>  .../g++.dg/diagnostic/param-type-mismatch-2.C      |   6 +
>  .../g++.dg/diagnostic/param-type-mismatch.C        |  20 ++
>  gcc/testsuite/g++.dg/plugin/plugin.exp             |   1 +
>  .../plugin/show-template-tree-color-labels.C       |  38 +++
>  gcc/testsuite/gcc.dg/bad-binary-ops.c              |  26 ++
>  gcc/testsuite/gcc.dg/cpp/pr66415-1.c               |   2 +
>  gcc/testsuite/gcc.dg/format/diagnostic-ranges.c    |  55 ++++
>  gcc/testsuite/gcc.dg/format/pr72858.c              | 108 +++++++
>  gcc/testsuite/gcc.dg/format/pr78498.c              |   2 +
>  gcc/testsuite/gcc.dg/param-type-mismatch.c         |  56 +++-
>  .../diagnostic-test-show-locus-bw-line-numbers.c   |  14 +-
>  .../gcc.dg/plugin/diagnostic-test-show-locus-bw.c  |  72 ++++-
>  ...diagnostic-test-show-locus-color-line-numbers.c |   2 +
>  .../plugin/diagnostic-test-show-locus-color.c      |  15 +-
>  .../plugin/diagnostic-test-show-locus-no-labels.c  |  27 ++
>  .../gcc.dg/plugin/diagnostic_plugin_show_trees.c   |   2 +-
>  .../plugin/diagnostic_plugin_test_show_locus.c     |  51 ++-
>  gcc/testsuite/gcc.dg/plugin/plugin.exp             |   1 +
>  gcc/testsuite/gcc.dg/pr69554-1.c                   |  29 +-
>  gcc/testsuite/gcc.dg/pr69627.c                     |   4 +
>  gcc/testsuite/lib/multiline.exp                    |  20 --
>  gcc/toplev.c                                       |   2 +
>  libcpp/include/line-map.h                          |  78 ++++-
>  libcpp/line-map.c                                  |   9 +-
>  45 files changed, 1482 insertions(+), 194 deletions(-)
>  create mode 100644 gcc/testsuite/g++.dg/diagnostic/aka3.C
>  create mode 100644
> gcc/testsuite/g++.dg/plugin/show-template-tree-color-labels.C
>  create mode 100644
> gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-no-labels.c
>
> diff --git a/gcc/c-family/c-format.c b/gcc/c-family/c-format.c
> index dc0e756..5a04f05 100644
> --- a/gcc/c-family/c-format.c
> +++ b/gcc/c-family/c-format.c
> @@ -32,8 +32,10 @@ along with GCC; see the file COPYING3.  If not see
>  #include "diagnostic.h"
>  #include "substring-locations.h"
>  #include "selftest.h"
> +#include "selftest-diagnostic.h"
>  #include "builtins.h"
>  #include "attribs.h"
> +#include "gcc-rich-location.h"
>
>  /* Handle attributes associated with format checking.  */
>
> @@ -97,8 +99,8 @@ format_warning_at_char (location_t fmt_string_loc, tree
> format_string_cst,
>
>    substring_loc fmt_loc (fmt_string_loc, string_type, char_idx, char_idx,
>  			 char_idx);
> -  bool warned = format_warning_va (fmt_loc, UNKNOWN_LOCATION, NULL, opt,
> -				   gmsgid, &ap);
> +  bool warned = format_warning_va (fmt_loc, NULL, UNKNOWN_LOCATION, NULL,
> +				   NULL, opt, gmsgid, &ap);
>    va_end (ap);
>
>    return warned;
> @@ -3510,6 +3512,82 @@ get_corrected_substring (const substring_loc
> &fmt_loc,
>    return result;
>  }
>
> +/* Helper class for adding zero or more trailing '*' to types.
> +
> +   The format type and name exclude any '*' for pointers, so those
> +   must be formatted manually.  For all the types we currently have,
> +   this is adequate, but formats taking pointers to functions or
> +   arrays would require the full type to be built up in order to
> +   print it with %T.  */
> +
> +class indirection_suffix
> +{
> + public:
> +  indirection_suffix (int pointer_count) : m_pointer_count (pointer_count)
> {}
> +
> +  /* Determine the size of the buffer (including NUL-terminator).  */
> +
> +  size_t get_buffer_size () const
> +  {
> +    return m_pointer_count + 2;
> +  }
> +
> +  /* Write the '*' to DST and add a NUL-terminator.  */
> +
> +  void fill_buffer (char *dst) const
> +  {
> +    if (m_pointer_count == 0)
> +      dst[0] = 0;
> +    else if (c_dialect_cxx ())
> +      {
> +	memset (dst, '*', m_pointer_count);
> +	dst[m_pointer_count] = 0;
> +      }
> +    else
> +      {
> +	dst[0] = ' ';
> +	memset (dst + 1, '*', m_pointer_count);
> +	dst[m_pointer_count + 1] = 0;
> +      }
> +  }
> +
> + private:
> +  int m_pointer_count;
> +};
> +
> +/* Subclass of range_label for labelling the range in the format string
> +   with the type in question, adding trailing '*' for pointer_count.  */
> +
> +class range_label_for_format_type_mismatch
> +  : public range_label_for_type_mismatch
> +{
> + public:
> +  range_label_for_format_type_mismatch (tree labelled_type, tree
> other_type,
> +					int pointer_count)
> +  : range_label_for_type_mismatch (labelled_type, other_type),
> +    m_pointer_count (pointer_count)
> +  {
> +  }
> +
> +  label_text get_text () const FINAL OVERRIDE
> +  {
> +    label_text text = range_label_for_type_mismatch::get_text ();
> +    if (text.m_buffer == NULL)
> +      return text;
> +
> +    indirection_suffix suffix (m_pointer_count);
> +    char *p = (char *) alloca (suffix.get_buffer_size ());
> +    suffix.fill_buffer (p);
> +
> +    char *result = concat (text.m_buffer, p, NULL);
> +    text.maybe_free ();
> +    return label_text (result, true);
> +  }
> +
> + private:
> +  int m_pointer_count;
> +};
> +
>  /* Give a warning about a format argument of different type from that
> expected.
>     The range of the diagnostic is taken from WHOLE_FMT_LOC; the caret
> location
>     is based on the location of the char at TYPE->offset_loc.
> @@ -3558,7 +3636,6 @@ format_type_warning (const substring_loc
> &whole_fmt_loc,
>    int pointer_count = type->pointer_count;
>    int arg_num = type->arg_num;
>
> -  char *p;
>    /* If ARG_TYPE is a typedef with a misleading name (for example,
>       size_t but not the standard size_t expected by printf %zu), avoid
>       printing the typedef name.  */
> @@ -3570,25 +3647,10 @@ format_type_warning (const substring_loc
> &whole_fmt_loc,
>        && !strcmp (wanted_type_name,
>  		  lang_hooks.decl_printable_name (TYPE_NAME (arg_type), 2)))
>      arg_type = TYPE_MAIN_VARIANT (arg_type);
> -  /* The format type and name exclude any '*' for pointers, so those
> -     must be formatted manually.  For all the types we currently have,
> -     this is adequate, but formats taking pointers to functions or
> -     arrays would require the full type to be built up in order to
> -     print it with %T.  */
> -  p = (char *) alloca (pointer_count + 2);
> -  if (pointer_count == 0)
> -    p[0] = 0;
> -  else if (c_dialect_cxx ())
> -    {
> -      memset (p, '*', pointer_count);
> -      p[pointer_count] = 0;
> -    }
> -  else
> -    {
> -      p[0] = ' ';
> -      memset (p + 1, '*', pointer_count);
> -      p[pointer_count + 1] = 0;
> -    }
> +
> +  indirection_suffix suffix (pointer_count);
> +  char *p = (char *) alloca (suffix.get_buffer_size ());
> +  suffix.fill_buffer (p);
>
>    /* WHOLE_FMT_LOC has the caret at the end of the range.
>       Set the caret to be at the offset from TYPE.  Subtract one
> @@ -3596,6 +3658,10 @@ format_type_warning (const substring_loc
> &whole_fmt_loc,
>    substring_loc fmt_loc (whole_fmt_loc);
>    fmt_loc.set_caret_index (type->offset_loc - 1);
>
> +  range_label_for_format_type_mismatch fmt_label (wanted_type, arg_type,
> +						  pointer_count);
> +  range_label_for_type_mismatch param_label (arg_type, wanted_type);
> +
>    /* Get a string for use as a replacement fix-it hint for the range in
>       fmt_loc, or NULL.  */
>    char *corrected_substring
> @@ -3606,7 +3672,7 @@ format_type_warning (const substring_loc
> &whole_fmt_loc,
>      {
>        if (arg_type)
>  	format_warning_at_substring
> -	  (fmt_loc, param_loc,
> +	  (fmt_loc, &fmt_label, param_loc, &param_label,
>  	   corrected_substring, OPT_Wformat_,
>  	   "%s %<%s%.*s%> expects argument of type %<%s%s%>, "
>  	   "but argument %d has type %qT",
> @@ -3616,7 +3682,7 @@ format_type_warning (const substring_loc
> &whole_fmt_loc,
>  	   wanted_type_name, p, arg_num, arg_type);
>        else
>  	format_warning_at_substring
> -	  (fmt_loc, param_loc,
> +	  (fmt_loc, &fmt_label, param_loc, &param_label,
>  	   corrected_substring, OPT_Wformat_,
>  	   "%s %<%s%.*s%> expects a matching %<%s%s%> argument",
>  	   gettext (kind_descriptions[kind]),
> @@ -3627,7 +3693,7 @@ format_type_warning (const substring_loc
> &whole_fmt_loc,
>      {
>        if (arg_type)
>  	format_warning_at_substring
> -	  (fmt_loc, param_loc,
> +	  (fmt_loc, &fmt_label, param_loc, &param_label,
>  	   corrected_substring, OPT_Wformat_,
>  	   "%s %<%s%.*s%> expects argument of type %<%T%s%>, "
>  	   "but argument %d has type %qT",
> @@ -3637,7 +3703,7 @@ format_type_warning (const substring_loc
> &whole_fmt_loc,
>  	   wanted_type, p, arg_num, arg_type);
>        else
>  	format_warning_at_substring
> -	  (fmt_loc, param_loc,
> +	  (fmt_loc, &fmt_label, param_loc, &param_label,
>  	   corrected_substring, OPT_Wformat_,
>  	   "%s %<%s%.*s%> expects a matching %<%T%s%> argument",
>  	   gettext (kind_descriptions[kind]),
> @@ -4217,6 +4283,66 @@ test_get_format_for_type_scanf ()
>
>  #undef ASSERT_FORMAT_FOR_TYPE_STREQ
>
> +/* Exercise the type-printing label code, to give some coverage
> +   under "make selftest-valgrind" (in particular, to ensure that
> +   the label-printing machinery doesn't leak).  */
> +
> +static void
> +test_type_mismatch_range_labels ()
> +{
> +  /* Create a tempfile and write some text to it.
> +     ....................0000000001 11111111 12 22222222
> +     ....................1234567890 12345678 90 12345678.  */
> +  const char *content = "  printf (\"msg: %i\\n\", msg);\n";
> +  temp_source_file tmp (SELFTEST_LOCATION, ".c", content);
> +  line_table_test ltt;
> +
> +  linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1);
> +
> +  location_t c17 = linemap_position_for_column (line_table, 17);
> +  ASSERT_EQ (LOCATION_COLUMN (c17), 17);
> +  location_t c18 = linemap_position_for_column (line_table, 18);
> +  location_t c24 = linemap_position_for_column (line_table, 24);
> +  location_t c26 = linemap_position_for_column (line_table, 26);
> +
> +  /* Don't attempt to run the tests if column data might be unavailable.
> */
> +  if (c26 > LINE_MAP_MAX_LOCATION_WITH_COLS)
> +    return;
> +
> +  location_t fmt = make_location (c18, c17, c18);
> +  ASSERT_EQ (LOCATION_COLUMN (fmt), 18);
> +
> +  location_t param = make_location (c24, c24, c26);
> +  ASSERT_EQ (LOCATION_COLUMN (param), 24);
> +
> +  range_label_for_format_type_mismatch fmt_label (char_type_node,
> +						  integer_type_node, 1);
> +  range_label_for_type_mismatch param_label (integer_type_node,
> +					     char_type_node);
> +  gcc_rich_location richloc (fmt, &fmt_label);
> +  richloc.add_range (param, false, &param_label);
> +
> +  test_diagnostic_context dc;
> +  diagnostic_show_locus (&dc, &richloc, DK_ERROR);
> +  if (c_dialect_cxx ())
> +    /* "char*", without a space.  */
> +    ASSERT_STREQ ("\n"
> +		  "   printf (\"msg: %i\\n\", msg);\n"
> +		  "                 ~^     ~~~\n"
> +		  "                  |     |\n"
> +		  "                  char* int\n",
> +		  pp_formatted_text (dc.printer));
> +  else
> +    /* "char *", with a space.  */
> +    ASSERT_STREQ ("\n"
> +		  "   printf (\"msg: %i\\n\", msg);\n"
> +		  "                 ~^     ~~~\n"
> +		  "                  |     |\n"
> +		  "                  |     int\n"
> +		  "                  char *\n",
> +		  pp_formatted_text (dc.printer));
> +}
> +
>  /* Run all of the selftests within this file.  */
>
>  void
> @@ -4225,6 +4351,7 @@ c_format_c_tests ()
>    test_get_modifier_for_format_len ();
>    test_get_format_for_type_printf ();
>    test_get_format_for_type_scanf ();
> +  test_type_mismatch_range_labels ();
>  }
>
>  } // namespace selftest
> diff --git a/gcc/c/c-objc-common.c b/gcc/c/c-objc-common.c
> index ddbd60c..238af19 100644
> --- a/gcc/c/c-objc-common.c
> +++ b/gcc/c/c-objc-common.c
> @@ -27,6 +27,7 @@ along with GCC; see the file COPYING3.  If not see
>  #include "gimple-pretty-print.h"
>  #include "langhooks.h"
>  #include "c-objc-common.h"
> +#include "gcc-rich-location.h"
>
>  static bool c_tree_printer (pretty_printer *, text_info *, const char *,
>  			    int, bool, bool, bool, bool *, const char **);
> @@ -61,6 +62,60 @@ c_objc_common_init (void)
>    return c_common_init ();
>  }
>
> +/* Print T to CPP.  */
> +
> +static void
> +print_type (c_pretty_printer *cpp, tree t, bool *quoted)
> +{
> +  gcc_assert (TYPE_P (t));
> +  struct obstack *ob = pp_buffer (cpp)->obstack;
> +  char *p = (char *) obstack_base (ob);
> +  /* Remember the end of the initial dump.  */
> +  int len = obstack_object_size (ob);
> +
> +  tree name = TYPE_NAME (t);
> +  if (name && TREE_CODE (name) == TYPE_DECL && DECL_NAME (name))
> +    pp_identifier (cpp, lang_hooks.decl_printable_name (name, 2));
> +  else
> +    cpp->type_id (t);
> +
> +  /* If we're printing a type that involves typedefs, also print the
> +     stripped version.  But sometimes the stripped version looks
> +     exactly the same, so we don't want it after all.  To avoid
> +     printing it in that case, we play ugly obstack games.  */
> +  if (TYPE_CANONICAL (t) && t != TYPE_CANONICAL (t))
> +    {
> +      c_pretty_printer cpp2;
> +      /* Print the stripped version into a temporary printer.  */
> +      cpp2.type_id (TYPE_CANONICAL (t));
> +      struct obstack *ob2 = cpp2.buffer->obstack;
> +      /* Get the stripped version from the temporary printer.  */
> +      const char *aka = (char *) obstack_base (ob2);
> +      int aka_len = obstack_object_size (ob2);
> +      int type1_len = obstack_object_size (ob) - len;
> +
> +      /* If they are identical, bail out.  */
> +      if (aka_len == type1_len && memcmp (p + len, aka, aka_len) == 0)
> +	return;
> +
> +      /* They're not, print the stripped version now.  */
> +      if (*quoted)
> +	pp_end_quote (cpp, pp_show_color (cpp));
> +      pp_c_whitespace (cpp);
> +      pp_left_brace (cpp);
> +      pp_c_ws_string (cpp, _("aka"));
> +      pp_c_whitespace (cpp);
> +      if (*quoted)
> +	pp_begin_quote (cpp, pp_show_color (cpp));
> +      cpp->type_id (TYPE_CANONICAL (t));
> +      if (*quoted)
> +	pp_end_quote (cpp, pp_show_color (cpp));
> +      pp_right_brace (cpp);
> +      /* No further closing quotes are needed.  */
> +      *quoted = false;
> +    }
> +}
> +
>  /* Called during diagnostic message formatting process to print a
>     source-level entity onto BUFFER.  The meaning of the format specifiers
>     is as follows:
> @@ -82,7 +137,6 @@ c_tree_printer (pretty_printer *pp, text_info *text,
> const char *spec,
>  		bool *quoted, const char **)
>  {
>    tree t = NULL_TREE;
> -  tree name;
>    // FIXME: the next cast should be a dynamic_cast, when it is permitted.
>    c_pretty_printer *cpp = (c_pretty_printer *) pp;
>    pp->padding = pp_none;
> @@ -133,56 +187,8 @@ c_tree_printer (pretty_printer *pp, text_info *text,
> const char *spec,
>        break;
>
>      case 'T':
> -      {
> -	gcc_assert (TYPE_P (t));
> -	struct obstack *ob = pp_buffer (cpp)->obstack;
> -	char *p = (char *) obstack_base (ob);
> -	/* Remember the end of the initial dump.  */
> -	int len = obstack_object_size (ob);
> -
> -	name = TYPE_NAME (t);
> -	if (name && TREE_CODE (name) == TYPE_DECL && DECL_NAME (name))
> -	  pp_identifier (cpp, lang_hooks.decl_printable_name (name, 2));
> -	else
> -	  cpp->type_id (t);
> -
> -	/* If we're printing a type that involves typedefs, also print the
> -	   stripped version.  But sometimes the stripped version looks
> -	   exactly the same, so we don't want it after all.  To avoid
> -	   printing it in that case, we play ugly obstack games.  */
> -	if (TYPE_CANONICAL (t) && t != TYPE_CANONICAL (t))
> -	  {
> -	    c_pretty_printer cpp2;
> -	    /* Print the stripped version into a temporary printer.  */
> -	    cpp2.type_id (TYPE_CANONICAL (t));
> -	    struct obstack *ob2 = cpp2.buffer->obstack;
> -	    /* Get the stripped version from the temporary printer.  */
> -	    const char *aka = (char *) obstack_base (ob2);
> -	    int aka_len = obstack_object_size (ob2);
> -	    int type1_len = obstack_object_size (ob) - len;
> -
> -	    /* If they are identical, bail out.  */
> -	    if (aka_len == type1_len && memcmp (p + len, aka, aka_len) == 0)
> -	      return true;
> -
> -	    /* They're not, print the stripped version now.  */
> -	    if (*quoted)
> -	      pp_end_quote (pp, pp_show_color (pp));
> -	    pp_c_whitespace (cpp);
> -	    pp_left_brace (cpp);
> -	    pp_c_ws_string (cpp, _("aka"));
> -	    pp_c_whitespace (cpp);
> -	    if (*quoted)
> -	      pp_begin_quote (pp, pp_show_color (pp));
> -	    cpp->type_id (TYPE_CANONICAL (t));
> -	    if (*quoted)
> -	      pp_end_quote (pp, pp_show_color (pp));
> -	    pp_right_brace (cpp);
> -	    /* No further closing quotes are needed.  */
> -	    *quoted = false;
> -	  }
> -	return true;
> -      }
> +      print_type (cpp, t, quoted);
> +      return true;
>
>      case 'E':
>        if (TREE_CODE (t) == IDENTIFIER_NODE)
> @@ -207,6 +213,22 @@ c_tree_printer (pretty_printer *pp, text_info *text,
> const char *spec,
>    return true;
>  }
>
> +/* C-specific implementation of range_label::get_text () vfunc for
> +   range_label_for_type_mismatch.  */
> +
> +label_text
> +range_label_for_type_mismatch::get_text () const
> +{
> +  if (m_labelled_type == NULL_TREE)
> +    return label_text (NULL, false);
> +
> +  c_pretty_printer cpp;
> +  bool quoted = false;
> +  print_type (&cpp, m_labelled_type, &quoted);
> +  return label_text (xstrdup (pp_formatted_text (&cpp)), true);
> +}
> +
> +
>  /* In C and ObjC, all decls have "C" linkage.  */
>  bool
>  has_c_linkage (const_tree decl ATTRIBUTE_UNUSED)
> diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
> index 2e9338e..726ea83 100644
> --- a/gcc/c/c-typeck.c
> +++ b/gcc/c/c-typeck.c
> @@ -6924,13 +6924,15 @@ convert_for_assignment (location_t location,
> location_t expr_loc, tree type,
>  		switch (errtype)
>  		  {
>  		  case ic_argpass:
> -		    if (pedwarn (expr_loc, OPT_Wpointer_sign,
> -				 "pointer targets in passing argument %d of "
> -				 "%qE differ in signedness", parmnum, rname))
> -		      inform ((fundecl && !DECL_IS_BUILTIN (fundecl))
> -			      ? DECL_SOURCE_LOCATION (fundecl) : expr_loc,
> -			      "expected %qT but argument is of type %qT",
> -			      type, rhstype);
> +		    {
> +		      range_label_for_type_mismatch rhs_label (rhstype, type);
> +		      gcc_rich_location richloc (expr_loc, &rhs_label);
> +		      if (pedwarn (&richloc, OPT_Wpointer_sign,
> +				   "pointer targets in passing argument %d of "
> +				   "%qE differ in signedness", parmnum, rname))
> +			inform_for_arg (fundecl, expr_loc, parmnum, type,
> +					rhstype);
> +		    }
>  		    break;
>  		  case ic_assign:
>  		    pedwarn (location, OPT_Wpointer_sign,
> @@ -6981,10 +6983,14 @@ convert_for_assignment (location_t location,
> location_t expr_loc, tree type,
>  	  switch (errtype)
>  	    {
>  	    case ic_argpass:
> -	      if (pedwarn (expr_loc, OPT_Wincompatible_pointer_types,
> -			   "passing argument %d of %qE from incompatible "
> -			   "pointer type", parmnum, rname))
> -		inform_for_arg (fundecl, expr_loc, parmnum, type, rhstype);
> +	      {
> +		range_label_for_type_mismatch rhs_label (rhstype, type);
> +		gcc_rich_location richloc (expr_loc, &rhs_label);
> +		if (pedwarn (&richloc, OPT_Wincompatible_pointer_types,
> +			     "passing argument %d of %qE from incompatible "
> +			     "pointer type", parmnum, rname))
> +		  inform_for_arg (fundecl, expr_loc, parmnum, type, rhstype);
> +	      }
>  	      break;
>  	    case ic_assign:
>  	      pedwarn (location, OPT_Wincompatible_pointer_types,
> @@ -7024,10 +7030,14 @@ convert_for_assignment (location_t location,
> location_t expr_loc, tree type,
>  	switch (errtype)
>  	  {
>  	  case ic_argpass:
> -	    if (pedwarn (expr_loc, OPT_Wint_conversion,
> -			 "passing argument %d of %qE makes pointer from "
> -			 "integer without a cast", parmnum, rname))
> -	      inform_for_arg (fundecl, expr_loc, parmnum, type, rhstype);
> +	    {
> +	      range_label_for_type_mismatch rhs_label (rhstype, type);
> +	      gcc_rich_location richloc (expr_loc, &rhs_label);
> +	      if (pedwarn (&richloc, OPT_Wint_conversion,
> +			   "passing argument %d of %qE makes pointer from "
> +			   "integer without a cast", parmnum, rname))
> +		inform_for_arg (fundecl, expr_loc, parmnum, type, rhstype);
> +	    }
>  	    break;
>  	  case ic_assign:
>  	    pedwarn (location, OPT_Wint_conversion,
> @@ -7055,10 +7065,14 @@ convert_for_assignment (location_t location,
> location_t expr_loc, tree type,
>        switch (errtype)
>  	{
>  	case ic_argpass:
> -	  if (pedwarn (expr_loc, OPT_Wint_conversion,
> -		       "passing argument %d of %qE makes integer from "
> -		       "pointer without a cast", parmnum, rname))
> -	    inform_for_arg (fundecl, expr_loc, parmnum, type, rhstype);
> +	  {
> +	    range_label_for_type_mismatch rhs_label (rhstype, type);
> +	    gcc_rich_location richloc (expr_loc, &rhs_label);
> +	    if (pedwarn (&richloc, OPT_Wint_conversion,
> +			 "passing argument %d of %qE makes integer from "
> +			 "pointer without a cast", parmnum, rname))
> +	      inform_for_arg (fundecl, expr_loc, parmnum, type, rhstype);
> +	  }
>  	  break;
>  	case ic_assign:
>  	  pedwarn (location, OPT_Wint_conversion,
> @@ -7094,9 +7108,13 @@ convert_for_assignment (location_t location,
> location_t expr_loc, tree type,
>    switch (errtype)
>      {
>      case ic_argpass:
> -      error_at (expr_loc, "incompatible type for argument %d of %qE",
> parmnum,
> -		rname);
> -      inform_for_arg (fundecl, expr_loc, parmnum, type, rhstype);
> +      {
> +	range_label_for_type_mismatch rhs_label (rhstype, type);
> +	gcc_rich_location richloc (expr_loc, &rhs_label);
> +	error_at (&richloc, "incompatible type for argument %d of %qE", parmnum,
> +		  rname);
> +	inform_for_arg (fundecl, expr_loc, parmnum, type, rhstype);
> +      }
>        break;
>      case ic_assign:
>        error_at (location, "incompatible types when assigning to type %qT
> from "
> @@ -10992,6 +11010,38 @@ build_vec_cmp (tree_code code, tree type,
>    return build3 (VEC_COND_EXPR, type, cmp, minus_one_vec, zero_vec);
>  }
>
> +/* Subclass of range_label for labelling the type of EXPR when reporting
> +   a type mismatch between EXPR and OTHER_EXPR.
> +   Either or both of EXPR and OTHER_EXPR could be NULL.  */
> +
> +class maybe_range_label_for_tree_type_mismatch : public range_label
> +{
> + public:
> +  maybe_range_label_for_tree_type_mismatch (tree expr, tree other_expr)
> +  : m_expr (expr), m_other_expr (other_expr)
> +  {
> +  }
> +
> +  label_text get_text () const FINAL OVERRIDE
> +  {
> +    if (m_expr == NULL_TREE
> +	|| !EXPR_P (m_expr))
> +      return label_text (NULL, false);
> +    tree expr_type = TREE_TYPE (m_expr);
> +
> +    tree other_type = NULL_TREE;
> +    if (m_other_expr && EXPR_P (m_other_expr))
> +      other_type = TREE_TYPE (m_other_expr);
> +
> +   range_label_for_type_mismatch inner (expr_type, other_type);
> +   return inner.get_text ();
> +  }
> +
> + private:
> +  tree m_expr;
> +  tree m_other_expr;
> +};
> +
>  /* Build a binary-operation expression without default conversions.
>     CODE is the kind of expression to build.
>     LOCATION is the operator's location.
> @@ -11864,8 +11914,11 @@ build_binary_op (location_t location, enum
> tree_code code,
>  	  || !vector_types_compatible_elements_p (type0, type1)))
>      {
>        gcc_rich_location richloc (location);
> -      richloc.maybe_add_expr (orig_op0);
> -      richloc.maybe_add_expr (orig_op1);
> +      maybe_range_label_for_tree_type_mismatch
> +	label_for_op0 (orig_op0, orig_op1),
> +	label_for_op1 (orig_op1, orig_op0);
> +      richloc.maybe_add_expr (orig_op0, &label_for_op0);
> +      richloc.maybe_add_expr (orig_op1, &label_for_op1);
>        binary_op_error (&richloc, code, type0, type1);
>        return error_mark_node;
>      }
> @@ -12106,8 +12159,11 @@ build_binary_op (location_t location, enum
> tree_code code,
>    if (!result_type)
>      {
>        gcc_rich_location richloc (location);
> -      richloc.maybe_add_expr (orig_op0);
> -      richloc.maybe_add_expr (orig_op1);
> +      maybe_range_label_for_tree_type_mismatch
> +	label_for_op0 (orig_op0, orig_op1),
> +	label_for_op1 (orig_op1, orig_op0);
> +      richloc.maybe_add_expr (orig_op0, &label_for_op0);
> +      richloc.maybe_add_expr (orig_op1, &label_for_op1);
>        binary_op_error (&richloc, code, TREE_TYPE (op0), TREE_TYPE (op1));
>        return error_mark_node;
>      }
> diff --git a/gcc/common.opt b/gcc/common.opt
> index b2f2215..507291f 100644
> --- a/gcc/common.opt
> +++ b/gcc/common.opt
> @@ -1233,6 +1233,10 @@ fdiagnostics-show-caret
>  Common Var(flag_diagnostics_show_caret) Init(1)
>  Show the source line with a caret indicating the column.
>
> +fdiagnostics-show-labels
> +Common Var(flag_diagnostics_show_labels) Init(1)
> +Show labels annotating ranges of source code when showing source
> +
>  fdiagnostics-show-line-numbers
>  Common Var(flag_diagnostics_show_line_numbers) Init(1)
>  Show line numbers in the left margin when showing source
> diff --git a/gcc/cp/call.c b/gcc/cp/call.c
> index 62654a9..16bb6bf 100644
> --- a/gcc/cp/call.c
> +++ b/gcc/cp/call.c
> @@ -41,6 +41,7 @@ along with GCC; see the file COPYING3.  If not see
>  #include "internal-fn.h"
>  #include "stringpool.h"
>  #include "attribs.h"
> +#include "gcc-rich-location.h"
>
>  /* The various kinds of conversion.  */
>
> @@ -6748,8 +6749,13 @@ convert_like_real (conversion *convs, tree expr, tree
> fn, int argnum,
>  	    break;
>  	}
>        if (!complained)
> -	complained = permerror (loc, "invalid conversion from %qH to %qI",
> -				TREE_TYPE (expr), totype);
> +	{
> +	  range_label_for_type_mismatch label (TREE_TYPE (expr), totype);
> +	  gcc_rich_location richloc (loc, &label);
> +	  complained = permerror (&richloc,
> +				  "invalid conversion from %qH to %qI",
> +				  TREE_TYPE (expr), totype);
> +	}
>        if (complained && fn)
>  	inform (get_fndecl_argument_location (fn, argnum),
>  		"  initializing argument %P of %qD", argnum, fn);
> @@ -10755,8 +10761,12 @@ perform_implicit_conversion_flags (tree type, tree
> expr,
>  	  else if (invalid_nonstatic_memfn_p (loc, expr, complain))
>  	    /* We gave an error.  */;
>  	  else
> -	    error_at (loc, "could not convert %qE from %qH to %qI", expr,
> -		      TREE_TYPE (expr), type);
> +	    {
> +	      range_label_for_type_mismatch label (TREE_TYPE (expr), type);
> +	      gcc_rich_location rich_loc (loc, &label);
> +	      error_at (&rich_loc, "could not convert %qE from %qH to %qI",
> +			expr, TREE_TYPE (expr), type);
> +	    }
>  	}
>        expr = error_mark_node;
>      }
> diff --git a/gcc/cp/error.c b/gcc/cp/error.c
> index c49f4d7..355a5e8 100644
> --- a/gcc/cp/error.c
> +++ b/gcc/cp/error.c
> @@ -31,6 +31,7 @@ along with GCC; see the file COPYING3.  If not see
>  #include "c-family/c-objc.h"
>  #include "ubsan.h"
>  #include "internal-fn.h"
> +#include "gcc-rich-location.h"
>
>  #define pp_separate_with_comma(PP) pp_cxx_separate_with (PP, ',')
>  #define pp_separate_with_semicolon(PP) pp_cxx_separate_with (PP, ';')
> @@ -4279,3 +4280,30 @@ qualified_name_lookup_error (tree scope, tree name,
>        suggest_alternatives_for (location, name, true);
>      }
>  }
> +
> +/* C++-specific implementation of range_label::get_text () vfunc for
> +   range_label_for_type_mismatch.
> +
> +   Compare with print_template_differences above.  */
> +
> +label_text
> +range_label_for_type_mismatch::get_text () const
> +{
> +  if (m_labelled_type == NULL_TREE)
> +    return label_text (NULL, false);
> +
> +  const bool verbose = false;
> +  const bool show_color = false;
> +
> +  const char *result;
> +  if (m_other_type
> +      && comparable_template_types_p (m_labelled_type, m_other_type))
> +    result = type_to_string_with_compare (m_labelled_type, m_other_type,
> +					  verbose, show_color);
> +  else
> +    result = type_to_string (m_labelled_type, verbose, true, NULL,
> show_color);
> +
> +  /* Both of the above return GC-allocated buffers, so the caller mustn't
> +     free them.  */
> +  return label_text (const_cast <char *> (result), false);
> +}
> diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
> index 1335da5..64b3d58 100644
> --- a/gcc/cp/typeck.c
> +++ b/gcc/cp/typeck.c
> @@ -8805,7 +8805,16 @@ convert_for_assignment (tree type, tree rhs,
>  		}
>  	      else if (fndecl)
>  		{
> -		  error_at (cp_expr_loc_or_loc (rhs, input_location),
> +		  location_t loc = cp_expr_location (rhs);
> +		  range_label_for_type_mismatch rhs_label (rhstype, type);
> +		  range_label *label = &rhs_label;
> +		  if (loc == UNKNOWN_LOCATION)
> +		    {
> +		      loc = input_location;
> +		      label = NULL;
> +		    }
> +		  gcc_rich_location richloc (loc, label);
> +		  error_at (&richloc,
>  			    "cannot convert %qH to %qI",
>  			    rhstype, type);
>  		  inform (get_fndecl_argument_location (fndecl, parmnum),
> diff --git a/gcc/diagnostic-show-locus.c b/gcc/diagnostic-show-locus.c
> index 238c689..c9edaab 100644
> --- a/gcc/diagnostic-show-locus.c
> +++ b/gcc/diagnostic-show-locus.c
> @@ -127,7 +127,8 @@ class layout_range
>    layout_range (const expanded_location *start_exploc,
>  		const expanded_location *finish_exploc,
>  		bool show_caret_p,
> -		const expanded_location *caret_exploc);
> +		const expanded_location *caret_exploc,
> +		const range_label *label);
>
>    bool contains_point (linenum_type row, int column) const;
>    bool intersects_line_p (linenum_type row) const;
> @@ -136,6 +137,7 @@ class layout_range
>    layout_point m_finish;
>    bool m_show_caret_p;
>    layout_point m_caret;
> +  const range_label *m_label;
>  };
>
>  /* A struct for use by layout::print_source_line for telling
> @@ -253,6 +255,7 @@ class layout
>    bool should_print_annotation_line_p (linenum_type row) const;
>    void start_annotation_line () const;
>    void print_annotation_line (linenum_type row, const line_bounds
> lbounds);
> +  void print_any_labels (linenum_type row);
>    void print_trailing_fixits (linenum_type row);
>
>    bool annotation_line_showed_range_p (linenum_type line, int
> start_column,
> @@ -287,6 +290,7 @@ class layout
>    expanded_location m_exploc;
>    colorizer m_colorizer;
>    bool m_colorize_source_p;
> +  bool m_show_labels_p;
>    bool m_show_line_numbers_p;
>    auto_vec <layout_range> m_layout_ranges;
>    auto_vec <const fixit_hint *> m_fixit_hints;
> @@ -408,11 +412,13 @@ colorizer::get_color_by_name (const char *name)
>  layout_range::layout_range (const expanded_location *start_exploc,
>  			    const expanded_location *finish_exploc,
>  			    bool show_caret_p,
> -			    const expanded_location *caret_exploc)
> +			    const expanded_location *caret_exploc,
> +			    const range_label *label)
>  : m_start (*start_exploc),
>    m_finish (*finish_exploc),
>    m_show_caret_p (show_caret_p),
> -  m_caret (*caret_exploc)
> +  m_caret (*caret_exploc),
> +  m_label (label)
>  {
>  }
>
> @@ -539,7 +545,7 @@ make_range (int start_line, int start_col, int end_line,
> int end_col)
>    const expanded_location finish_exploc
>      = {"test.c", end_line, end_col, NULL, false};
>    return layout_range (&start_exploc, &finish_exploc, false,
> -		       &start_exploc);
> +		       &start_exploc, NULL);
>  }
>
>  /* Selftests for layout_range::contains_point and
> @@ -879,6 +885,7 @@ layout::layout (diagnostic_context * context,
>    m_exploc (richloc->get_expanded_location (0)),
>    m_colorizer (context, diagnostic_kind),
>    m_colorize_source_p (context->colorize_source_p),
> +  m_show_labels_p (context->show_labels_p),
>    m_show_line_numbers_p (context->show_line_numbers_p),
>    m_layout_ranges (richloc->get_num_locations ()),
>    m_fixit_hints (richloc->get_num_fixit_hints ()),
> @@ -989,7 +996,8 @@ layout::maybe_add_location_range (const location_range
> *loc_range,
>
>    /* Everything is now known to be in the correct source file,
>       but it may require further sanitization.  */
> -  layout_range ri (&start, &finish, loc_range->m_show_caret_p, &caret);
> +  layout_range ri (&start, &finish, loc_range->m_show_caret_p, &caret,
> +		   loc_range->m_label);
>
>    /* If we have a range that finishes before it starts (perhaps
>       from something built via macro expansion), printing the
> @@ -1379,6 +1387,180 @@ layout::print_annotation_line (linenum_type row,
> const line_bounds lbounds)
>    print_newline ();
>  }
>
> +/* Implementation detail of layout::print_any_labels.
> +
> +   A label within the given row of source.  */
> +
> +struct line_label
> +{
> +  line_label (int state_idx, int column, label_text text)
> +  : m_state_idx (state_idx), m_column (column),
> +    m_text (text), m_length (strlen (text.m_buffer)),
> +    m_label_line (0)
> +  {}
> +
> +  /* Sorting is primarily by column, then by state index.  */
> +  static int comparator (const void *p1, const void *p2)
> +  {
> +    const line_label *ll1 = (const line_label *)p1;
> +    const line_label *ll2 = (const line_label *)p2;
> +    int column_cmp = compare (ll1->m_column, ll2->m_column);
> +    if (column_cmp)
> +      return column_cmp;
> +    return compare (ll1->m_state_idx, ll2->m_state_idx);
> +  }
> +
> +  int m_state_idx;
> +  int m_column;
> +  label_text m_text;
> +  size_t m_length;
> +  int m_label_line;
> +};
> +
> +/* Print any labels in this row.  */
> +void
> +layout::print_any_labels (linenum_type row)
> +{
> +  int i;
> +  auto_vec<line_label> labels;
> +
> +  /* Gather the labels that are to be printed into "labels".  */
> +  {
> +    layout_range *range;
> +    FOR_EACH_VEC_ELT (m_layout_ranges, i, range)
> +      {
> +	/* Most ranges don't have labels, so reject this first.  */
> +	if (range->m_label == NULL)
> +	  continue;
> +
> +	/* The range's caret must be on this line.  */
> +	if (range->m_caret.m_line != row)
> +	  continue;
> +
> +	/* Reject labels that aren't fully visible due to clipping
> +	   by m_x_offset.  */
> +	if (range->m_caret.m_column <= m_x_offset)
> +	  continue;
> +
> +	label_text text;
> +	text = range->m_label->get_text ();
> +
> +	/* Allow for labels that return NULL from their get_text
> +	   implementation (so e.g. such labels can control their own
> +	   visibility).  */
> +	if (text.m_buffer == NULL)
> +	  continue;
> +
> +	labels.safe_push (line_label (i, range->m_caret.m_column, text));
> +      }
> +  }
> +
> +  /* Bail out if there are no labels on this row.  */
> +  if (labels.length () == 0)
> +    return;
> +
> +  /* Sort them.  */
> +  labels.qsort(line_label::comparator);
> +
> +  /* Figure out how many "label lines" we need, and which
> +     one each label is printed in.
> +
> +     For example, if the labels aren't too densely packed,
> +     we can fit them on the same line, giving two "label lines":
> +
> +       foo + bar
> +       ~~~   ~~~
> +       |     |        : label line 0
> +       l0    l1       : label line 1
> +
> +     If they would touch each other or overlap, then we need
> +     additional "label lines":
> +
> +       foo + bar
> +       ~~~   ~~~
> +       |     |             : label line 0
> +       |     label 1       : label line 1
> +       label 0             : label line 2
> +
> +     Place the final label on label line 1, and work backwards, adding
> +     label lines as needed.
> +
> +     If multiple labels are at the same place, put them on separate
> +     label lines:
> +
> +       foo + bar
> +           ^               : label line 0
> +           |               : label line 1
> +           label 1         : label line 2
> +           label 0         : label line 3.  */
> +
> +  int max_label_line = 1;
> +  {
> +    int next_column = INT_MAX;
> +    line_label *label;
> +    FOR_EACH_VEC_ELT_REVERSE (labels, i, label)
> +      {
> +	/* Would this label "touch" or overlap the next label?  */
> +	if (label->m_column + label->m_length >= (size_t)next_column)
> +	  max_label_line++;
> +
> +	label->m_label_line = max_label_line;
> +	next_column = label->m_column;
> +      }
> +  }
> +
> +  /* Print the "label lines".  For each label within the line, print
> +     either a vertical bar ('|') for the labels that are lower down, or
> the
> +     labels themselves once we've reached their line.  */
> +  {
> +    /* Keep track of in which column we last printed a vertical bar.
> +       This allows us to suppress duplicate vertical bars for the case
> +       where multiple labels are on one column.  */
> +    int last_vbar = 0;
> +    for (int label_line = 0; label_line <= max_label_line; label_line++)
> +      {
> +	start_annotation_line ();
> +	pp_space (m_pp);
> +	int column = 1 + m_x_offset;
> +	line_label *label;
> +	FOR_EACH_VEC_ELT (labels, i, label)
> +	  {
> +	    if (label_line > label->m_label_line)
> +	      /* We've printed all the labels for this label line.  */
> +	      break;
> +
> +	    if (label_line == label->m_label_line)
> +	      {
> +		gcc_assert (column <= label->m_column);
> +		move_to_column (&column, label->m_column, true);
> +		m_colorizer.set_range (label->m_state_idx);
> +		pp_string (m_pp, label->m_text.m_buffer);
> +		m_colorizer.set_normal_text ();
> +		column += label->m_length;
> +	      }
> +	    else if (label->m_column != last_vbar)
> +	      {
> +		gcc_assert (column <= label->m_column);
> +		move_to_column (&column, label->m_column, true);
> +		m_colorizer.set_range (label->m_state_idx);
> +		pp_character (m_pp, '|');
> +		m_colorizer.set_normal_text ();
> +		last_vbar = column;
> +		column++;
> +	      }
> +	  }
> +	print_newline ();
> +      }
> +    }
> +
> +  /* Clean up.  */
> +  {
> +    line_label *label;
> +    FOR_EACH_VEC_ELT (labels, i, label)
> +      label->m_text.maybe_free ();
> +  }
> +}
> +
>  /* If there are any fixit hints inserting new lines before source line
> ROW,
>     print them.
>
> @@ -2023,6 +2205,8 @@ layout::print_line (linenum_type row)
>    print_source_line (row, line.get_buffer (), line.length (), &lbounds);
>    if (should_print_annotation_line_p (row))
>      print_annotation_line (row, lbounds);
> +  if (m_show_labels_p)
> +    print_any_labels (row);
>    print_trailing_fixits (row);
>  }
>
> @@ -2429,6 +2613,157 @@ test_one_liner_many_fixits_2 ()
>  		pp_formatted_text (dc.printer));
>  }
>
> +/* Test of labeling the ranges within a rich_location.  */
> +
> +static void
> +test_one_liner_labels ()
> +{
> +  location_t foo
> +    = make_location (linemap_position_for_column (line_table, 1),
> +		     linemap_position_for_column (line_table, 1),
> +		     linemap_position_for_column (line_table, 3));
> +  location_t bar
> +    = make_location (linemap_position_for_column (line_table, 7),
> +		     linemap_position_for_column (line_table, 7),
> +		     linemap_position_for_column (line_table, 9));
> +  location_t field
> +    = make_location (linemap_position_for_column (line_table, 11),
> +		     linemap_position_for_column (line_table, 11),
> +		     linemap_position_for_column (line_table, 15));
> +
> +  /* Example where all the labels fit on one line.  */
> +  {
> +    text_range_label label0 ("0");
> +    text_range_label label1 ("1");
> +    text_range_label label2 ("2");
> +    gcc_rich_location richloc (foo, &label0);
> +    richloc.add_range (bar, false, &label1);
> +    richloc.add_range (field, false, &label2);
> +
> +    {
> +      test_diagnostic_context dc;
> +      diagnostic_show_locus (&dc, &richloc, DK_ERROR);
> +      ASSERT_STREQ ("\n"
> +		    " foo = bar.field;\n"
> +		    " ^~~   ~~~ ~~~~~\n"
> +		    " |     |   |\n"
> +		    " 0     1   2\n",
> +		    pp_formatted_text (dc.printer));
> +    }
> +
> +    /* Verify that we can disable label-printing.  */
> +    {
> +      test_diagnostic_context dc;
> +      dc.show_labels_p = false;
> +      diagnostic_show_locus (&dc, &richloc, DK_ERROR);
> +      ASSERT_STREQ ("\n"
> +		    " foo = bar.field;\n"
> +		    " ^~~   ~~~ ~~~~~\n",
> +		    pp_formatted_text (dc.printer));
> +    }
> +  }
> +
> +  /* Example where the labels need extra lines.  */
> +  {
> +    text_range_label label0 ("label 0");
> +    text_range_label label1 ("label 1");
> +    text_range_label label2 ("label 2");
> +    gcc_rich_location richloc (foo, &label0);
> +    richloc.add_range (bar, false, &label1);
> +    richloc.add_range (field, false, &label2);
> +
> +    test_diagnostic_context dc;
> +    diagnostic_show_locus (&dc, &richloc, DK_ERROR);
> +    ASSERT_STREQ ("\n"
> +		  " foo = bar.field;\n"
> +		  " ^~~   ~~~ ~~~~~\n"
> +		  " |     |   |\n"
> +		  " |     |   label 2\n"
> +		  " |     label 1\n"
> +		  " label 0\n",
> +		  pp_formatted_text (dc.printer));
> +  }
> +
> +  /* Example of boundary conditions: label 0 and 1 have just enough
> clearance,
> +     but label 1 just touches label 2.  */
> +  {
> +    text_range_label label0 ("aaaaa");
> +    text_range_label label1 ("bbbb");
> +    text_range_label label2 ("c");
> +    gcc_rich_location richloc (foo, &label0);
> +    richloc.add_range (bar, false, &label1);
> +    richloc.add_range (field, false, &label2);
> +
> +    test_diagnostic_context dc;
> +    diagnostic_show_locus (&dc, &richloc, DK_ERROR);
> +    ASSERT_STREQ ("\n"
> +		  " foo = bar.field;\n"
> +		  " ^~~   ~~~ ~~~~~\n"
> +		  " |     |   |\n"
> +		  " |     |   c\n"
> +		  " aaaaa bbbb\n",
> +		  pp_formatted_text (dc.printer));
> +  }
> +
> +  /* Example of out-of-order ranges (thus requiring a sort).  */
> +  {
> +    text_range_label label0 ("0");
> +    text_range_label label1 ("1");
> +    text_range_label label2 ("2");
> +    gcc_rich_location richloc (field, &label0);
> +    richloc.add_range (bar, false, &label1);
> +    richloc.add_range (foo, false, &label2);
> +
> +    test_diagnostic_context dc;
> +    diagnostic_show_locus (&dc, &richloc, DK_ERROR);
> +    ASSERT_STREQ ("\n"
> +		  " foo = bar.field;\n"
> +		  " ~~~   ~~~ ^~~~~\n"
> +		  " |     |   |\n"
> +		  " 2     1   0\n",
> +		  pp_formatted_text (dc.printer));
> +  }
> +
> +  /* Ensure we don't ICE if multiple ranges with labels are on
> +     the same point.  */
> +  {
> +    text_range_label label0 ("label 0");
> +    text_range_label label1 ("label 1");
> +    text_range_label label2 ("label 2");
> +    gcc_rich_location richloc (bar, &label0);
> +    richloc.add_range (bar, false, &label1);
> +    richloc.add_range (bar, false, &label2);
> +
> +    test_diagnostic_context dc;
> +    diagnostic_show_locus (&dc, &richloc, DK_ERROR);
> +    ASSERT_STREQ ("\n"
> +		  " foo = bar.field;\n"
> +		  "       ^~~\n"
> +		  "       |\n"
> +		  "       label 2\n"
> +		  "       label 1\n"
> +		  "       label 0\n",
> +		  pp_formatted_text (dc.printer));
> +  }
> +
> +  /* Verify that a NULL result from range_label::get_text is
> +     handled gracefully.  */
> +  {
> +    text_range_label label (NULL);
> +    gcc_rich_location richloc (bar, &label);
> +
> +    test_diagnostic_context dc;
> +    diagnostic_show_locus (&dc, &richloc, DK_ERROR);
> +    ASSERT_STREQ ("\n"
> +		  " foo = bar.field;\n"
> +		  "       ^~~\n",
> +		  pp_formatted_text (dc.printer));
> +   }
> +
> +  /* TODO: example of formatted printing (needs to be in
> +     gcc-rich-location.c due to Makefile.in issues).  */
> +}
> +
>  /* Run the various one-liner tests.  */
>
>  static void
> @@ -2465,6 +2800,7 @@ test_diagnostic_show_locus_one_liner (const
> line_table_case &case_)
>    test_one_liner_fixit_validation_adhoc_locations ();
>    test_one_liner_many_fixits_1 ();
>    test_one_liner_many_fixits_2 ();
> +  test_one_liner_labels ();
>  }
>
>  /* Verify that gcc_rich_location::add_location_if_nearby works.  */
> diff --git a/gcc/diagnostic.c b/gcc/diagnostic.c
> index e9d93d5..59477ce 100644
> --- a/gcc/diagnostic.c
> +++ b/gcc/diagnostic.c
> @@ -175,6 +175,7 @@ diagnostic_initialize (diagnostic_context *context, int
> n_opts)
>    context->lock = 0;
>    context->inhibit_notes_p = false;
>    context->colorize_source_p = false;
> +  context->show_labels_p = false;
>    context->show_line_numbers_p = false;
>    context->show_ruler_p = false;
>    context->parseable_fixits_p = false;
> diff --git a/gcc/diagnostic.h b/gcc/diagnostic.h
> index 744aec1..fe3130b 100644
> --- a/gcc/diagnostic.h
> +++ b/gcc/diagnostic.h
> @@ -204,6 +204,9 @@ struct diagnostic_context
>       a token, which would look strange).  */
>    bool colorize_source_p;
>
> +  /* When printing source code, should labelled ranges be printed?  */
> +  bool show_labels_p;
> +
>    /* When printing source code, should there be a left-hand margin
>       showing line numbers?  */
>    bool show_line_numbers_p;
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index d7fd0e1..586af17 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -267,7 +267,7 @@ Objective-C and Objective-C++ Dialects}.
>  -fdiagnostics-show-location=@r{[}once@r{|}every-line@r{]}  @gol
>  -fdiagnostics-color=@r{[}auto@r{|}never@r{|}always@r{]}  @gol
>  -fno-diagnostics-show-option  -fno-diagnostics-show-caret @gol
> --fno-diagnostics-show-line-numbers @gol
> +-fno-diagnostics-show-labels -fno-diagnostics-show-line-numbers @gol
>  -fdiagnostics-parseable-fixits  -fdiagnostics-generate-patch @gol
>  -fdiagnostics-show-template-tree -fno-elide-type @gol
>  -fno-show-column}
> @@ -3711,6 +3711,23 @@ the @option{-fmessage-length=n} option is given.
> When the output is done
>  to the terminal, the width is limited to the width given by the
>  @env{COLUMNS} environment variable or, if not set, to the terminal width.
>
> +@item -fno-diagnostics-show-labels
> +@opindex fno-diagnostics-show-labels
> +@opindex fdiagnostics-show-labels
> +By default, when printing source code (via
> @option{-fdiagnostics-show-caret}),
> +diagnostics can label ranges of source code with pertinent information,
> such
> +as the types of expressions:
> +
> +@smallexample
> +    printf ("foo %s bar", long_i + long_j);
> +                 ~^       ~~~~~~~~~~~~~~~
> +                  |              |
> +                  char *         long int
> +@end smallexample
> +
> +This option suppresses the printing of these labels (in the example above,
> +the vertical bars and the ``char *'' and ``long int'' text).
> +
>  @item -fno-diagnostics-show-line-numbers
>  @opindex fno-diagnostics-show-line-numbers
>  @opindex fdiagnostics-show-line-numbers
> diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c
> index 9ed4730..5a74131 100644
> --- a/gcc/dwarf2out.c
> +++ b/gcc/dwarf2out.c
> @@ -24247,6 +24247,7 @@ gen_producer_string (void)
>        case OPT_fdiagnostics_show_location_:
>        case OPT_fdiagnostics_show_option:
>        case OPT_fdiagnostics_show_caret:
> +      case OPT_fdiagnostics_show_labels:
>        case OPT_fdiagnostics_show_line_numbers:
>        case OPT_fdiagnostics_color_:
>        case OPT_fverbose_asm:
> diff --git a/gcc/gcc-rich-location.c b/gcc/gcc-rich-location.c
> index 0a0adf9..2576c73 100644
> --- a/gcc/gcc-rich-location.c
> +++ b/gcc/gcc-rich-location.c
> @@ -38,24 +38,26 @@ along with GCC; see the file COPYING3.  If not see
>  #include "cpplib.h"
>  #include "diagnostic.h"
>
> -/* Add a range to the rich_location, covering expression EXPR. */
> +/* Add a range to the rich_location, covering expression EXPR,
> +   using LABEL if non-NULL. */
>
>  void
> -gcc_rich_location::add_expr (tree expr)
> +gcc_rich_location::add_expr (tree expr, range_label *label)
>  {
>    gcc_assert (expr);
>
>    if (CAN_HAVE_RANGE_P (expr))
> -    add_range (EXPR_LOCATION (expr), false);
> +    add_range (EXPR_LOCATION (expr), false, label);
>  }
>
> -/* If T is an expression, add a range for it to the rich_location.  */
> +/* If T is an expression, add a range for it to the rich_location,
> +   using LABEL if non-NULL. */
>
>  void
> -gcc_rich_location::maybe_add_expr (tree t)
> +gcc_rich_location::maybe_add_expr (tree t, range_label *label)
>  {
>    if (EXPR_P (t))
> -    add_expr (t);
> +    add_expr (t, label);
>  }
>
>  /* Add a fixit hint suggesting replacing the range at MISSPELLED_TOKEN_LOC
> diff --git a/gcc/gcc-rich-location.h b/gcc/gcc-rich-location.h
> index 9c705c8..dc11ee8 100644
> --- a/gcc/gcc-rich-location.h
> +++ b/gcc/gcc-rich-location.h
> @@ -28,15 +28,17 @@ class gcc_rich_location : public rich_location
>    /* Constructors.  */
>
>    /* Constructing from a location.  */
> -  gcc_rich_location (source_location loc) :
> -    rich_location (line_table, loc) {}
> +  gcc_rich_location (source_location loc, const range_label *label = NULL)
> +  : rich_location (line_table, loc, label)
> +  {
> +  }
>
>    /* Methods for adding ranges via gcc entities.  */
>    void
> -  add_expr (tree expr);
> +  add_expr (tree expr, range_label *label);
>
>    void
> -  maybe_add_expr (tree t);
> +  maybe_add_expr (tree t, range_label *label);
>
>    void add_fixit_misspelled_id (location_t misspelled_token_loc,
>  				tree hint_id);
> @@ -99,4 +101,65 @@ class gcc_rich_location : public rich_location
>  				   location_t indent);
>  };
>
> +/* Concrete subclass of libcpp's range_label.
> +   Simple implementation using a string literal.  */
> +
> +class text_range_label : public range_label
> +{
> + public:
> +  text_range_label (const char *text) : m_text (text) {}
> +
> +  label_text get_text () const FINAL OVERRIDE
> +  {
> +    return label_text (const_cast <char *> (m_text), false);
> +  }
> +
> + private:
> +  const char *m_text;
> +};
> +
> +/* Concrete subclass of libcpp's range_label for use in
> +   diagnostics involving mismatched types.
> +
> +   Each frontend that uses this should supply its own implementation.
> +
> +   Generate a label describing LABELLED_TYPE.  The frontend may use
> +   OTHER_TYPE where appropriate for highlighting the differences between
> +   the two types (analogous to C++'s use of %H and %I with
> +   template types).
> +
> +   Either or both of LABELLED_TYPE and OTHER_TYPE may be NULL_TREE.
> +   If LABELLED_TYPE is NULL_TREE, then there is no label.
> +
> +   For example, this rich_location could use two instances of
> +   range_label_for_type_mismatch:
> +
> +      printf ("arg0: %i  arg1: %s arg2: %i",
> +                               ^~
> +                               |
> +                               const char *
> +              100, 101, 102);
> +                   ~~~
> +                   |
> +                   int
> +
> +   (a) the label for "%s" with LABELLED_TYPE for "const char*" and
> +   (b) the label for "101" with LABELLED TYPE for "int"
> +   where each one uses the other's type as OTHER_TYPE.  */
> +
> +class range_label_for_type_mismatch : public range_label
> +{
> + public:
> +  range_label_for_type_mismatch (tree labelled_type, tree other_type)
> +  : m_labelled_type (labelled_type), m_other_type (other_type)
> +  {
> +  }
> +
> +  label_text get_text () const OVERRIDE;
> +
> + protected:
> +  tree m_labelled_type;
> +  tree m_other_type;
> +};
> +
>  #endif /* GCC_RICH_LOCATION_H */
> diff --git a/gcc/gimple-ssa-sprintf.c b/gcc/gimple-ssa-sprintf.c
> index c652c55..5213e17 100644
> --- a/gcc/gimple-ssa-sprintf.c
> +++ b/gcc/gimple-ssa-sprintf.c
> @@ -601,8 +601,8 @@ fmtwarn (const substring_loc &fmt_loc, location_t
> param_loc,
>  {
>    va_list ap;
>    va_start (ap, gmsgid);
> -  bool warned = format_warning_va (fmt_loc, param_loc,
> corrected_substring,
> -				   opt, gmsgid, &ap);
> +  bool warned = format_warning_va (fmt_loc, NULL, param_loc, NULL,
> +				   corrected_substring, opt, gmsgid, &ap);
>    va_end (ap);
>
>    return warned;
> @@ -616,7 +616,8 @@ fmtwarn_n (const substring_loc &fmt_loc, location_t
> param_loc,
>  {
>    va_list ap;
>    va_start (ap, plural_gmsgid);
> -  bool warned = format_warning_n_va (fmt_loc, param_loc,
> corrected_substring,
> +  bool warned = format_warning_n_va (fmt_loc, NULL, param_loc, NULL,
> +				     corrected_substring,
>  				     opt, n, singular_gmsgid, plural_gmsgid,
>  				     &ap);
>    va_end (ap);
> diff --git a/gcc/lto-wrapper.c b/gcc/lto-wrapper.c
> index 39d9f08..d446786 100644
> --- a/gcc/lto-wrapper.c
> +++ b/gcc/lto-wrapper.c
> @@ -255,6 +255,7 @@ merge_and_complain (struct cl_decoded_option
> **decoded_options,
>
>  	  /* Fallthru.  */
>  	case OPT_fdiagnostics_show_caret:
> +	case OPT_fdiagnostics_show_labels:
>  	case OPT_fdiagnostics_show_line_numbers:
>  	case OPT_fdiagnostics_show_option:
>  	case OPT_fdiagnostics_show_location_:
> @@ -537,6 +538,7 @@ append_compiler_options (obstack *argv_obstack, struct
> cl_decoded_option *opts,
>        switch (option->opt_index)
>  	{
>  	case OPT_fdiagnostics_show_caret:
> +	case OPT_fdiagnostics_show_labels:
>  	case OPT_fdiagnostics_show_line_numbers:
>  	case OPT_fdiagnostics_show_option:
>  	case OPT_fdiagnostics_show_location_:
> @@ -584,6 +586,7 @@ append_diag_options (obstack *argv_obstack, struct
> cl_decoded_option *opts,
>  	{
>  	case OPT_fdiagnostics_color_:
>  	case OPT_fdiagnostics_show_caret:
> +	case OPT_fdiagnostics_show_labels:
>  	case OPT_fdiagnostics_show_line_numbers:
>  	case OPT_fdiagnostics_show_option:
>  	case OPT_fdiagnostics_show_location_:
> diff --git a/gcc/opts.c b/gcc/opts.c
> index 4153263..a5c9ed9 100644
> --- a/gcc/opts.c
> +++ b/gcc/opts.c
> @@ -2175,6 +2175,10 @@ common_handle_option (struct gcc_options *opts,
>        dc->show_caret = value;
>        break;
>
> +    case OPT_fdiagnostics_show_labels:
> +      dc->show_labels_p = value;
> +      break;
> +
>      case OPT_fdiagnostics_show_line_numbers:
>        dc->show_line_numbers_p = value;
>        break;
> diff --git a/gcc/selftest-diagnostic.c b/gcc/selftest-diagnostic.c
> index 837488b..f3c255e 100644
> --- a/gcc/selftest-diagnostic.c
> +++ b/gcc/selftest-diagnostic.c
> @@ -37,6 +37,7 @@ test_diagnostic_context::test_diagnostic_context ()
>  {
>    diagnostic_initialize (this, 0);
>    show_caret = true;
> +  show_labels_p = true;
>    show_column = true;
>    start_span = start_span_cb;
>  }
> diff --git a/gcc/substring-locations.c b/gcc/substring-locations.c
> index 2d7f0c1..82f2f45 100644
> --- a/gcc/substring-locations.c
> +++ b/gcc/substring-locations.c
> @@ -26,6 +26,7 @@ along with GCC; see the file COPYING3.  If not see
>  #include "tree.h"
>  #include "langhooks.h"
>  #include "substring-locations.h"
> +#include "gcc-rich-location.h"
>
>  /* Emit a warning governed by option OPT, using SINGULAR_GMSGID as the
>     format string (or if PLURAL_GMSGID is different from SINGULAR_GMSGID,
> @@ -89,6 +90,27 @@ along with GCC; see the file COPYING3.  If not see
>       printf(fmt, msg);
>              ^~~  ~~~
>
> +   If non-NULL, then FMT_LABEL will be used to label the location within
> the
> +   string for cases 1 and 2; if non-NULL, then PARAM_LABEL will be used to
> label
> +   the parameter.  For example with case 1:
> +
> +    test.c:90:16: warning: '%s' here but arg 2 has 'long' type [-Wformat=]
> +     printf ("foo %s bar", long_i + long_j);
> +                  ~^       ~~~~~~~~~~~~~~~
> +                                  |
> +                                  int
> +
> +   and with case 2:
> +
> +     test.c:90:10: warning: problem with '%i' here [-Wformat=]
> +     printf("hello " INT_FMT " world", msg);
> +            ^~~~~~~~~~~~~~~~~~~~~~~~~
> +     test.c:19: note: format string is defined here
> +     #define INT_FMT "%i"
> +                      ~^
> +                       |
> +                       int
> +
>     If CORRECTED_SUBSTRING is non-NULL, use it for cases 1 and 2 to provide
>     a fix-it hint, suggesting that it should replace the text within the
>     substring range.  For example:
> @@ -102,7 +124,9 @@ along with GCC; see the file COPYING3.  If not see
>
>  bool
>  format_warning_n_va (const substring_loc &fmt_loc,
> +		     const range_label *fmt_label,
>  		     location_t param_loc,
> +		     const range_label *param_label,
>  		     const char *corrected_substring,
>  		     int opt, unsigned HOST_WIDE_INT n,
>  		     const char *singular_gmsgid,
> @@ -138,10 +162,15 @@ format_warning_n_va (const substring_loc &fmt_loc,
>  	}
>      }
>
> -  rich_location richloc (line_table, primary_loc);
> +  /* Only use fmt_label in the initial warning for case 1.  */
> +  const range_label *primary_label = NULL;
> +  if (substring_within_range)
> +    primary_label = fmt_label;
> +
> +  gcc_rich_location richloc (primary_loc, primary_label);
>
>    if (param_loc != UNKNOWN_LOCATION)
> -    richloc.add_range (param_loc, false);
> +    richloc.add_range (param_loc, false, param_label);
>
>    if (!err && corrected_substring && substring_within_range)
>      richloc.add_fixit_replace (fmt_substring_range, corrected_substring);
> @@ -173,7 +202,9 @@ format_warning_n_va (const substring_loc &fmt_loc,
>      /* Case 2.  */
>      if (warned)
>        {
> -	rich_location substring_richloc (line_table, fmt_substring_loc);
> +	/* Use fmt_label in the note for case 2.  */
> +	rich_location substring_richloc (line_table, fmt_substring_loc,
> +					 fmt_label);
>  	if (corrected_substring)
>  	  substring_richloc.add_fixit_replace (fmt_substring_range,
>  					       corrected_substring);
> @@ -188,11 +219,14 @@ format_warning_n_va (const substring_loc &fmt_loc,
>
>  bool
>  format_warning_va (const substring_loc &fmt_loc,
> +		   const range_label *fmt_label,
>  		   location_t param_loc,
> +		   const range_label *param_label,
>  		   const char *corrected_substring,
>  		   int opt, const char *gmsgid, va_list *ap)
>  {
> -  return format_warning_n_va (fmt_loc, param_loc, corrected_substring,
> opt,
> +  return format_warning_n_va (fmt_loc, fmt_label, param_loc, param_label,
> +			      corrected_substring, opt,
>  			      0, gmsgid, gmsgid, ap);
>  }
>
> @@ -200,14 +234,16 @@ format_warning_va (const substring_loc &fmt_loc,
>
>  bool
>  format_warning_at_substring (const substring_loc &fmt_loc,
> +			     const range_label *fmt_label,
>  			     location_t param_loc,
> +			     const range_label *param_label,
>  			     const char *corrected_substring,
>  			     int opt, const char *gmsgid, ...)
>  {
>    va_list ap;
>    va_start (ap, gmsgid);
> -  bool warned = format_warning_va (fmt_loc, param_loc,
> corrected_substring,
> -				   opt, gmsgid, &ap);
> +  bool warned = format_warning_va (fmt_loc, fmt_label, param_loc,
> param_label,
> +				   corrected_substring, opt, gmsgid, &ap);
>    va_end (ap);
>
>    return warned;
> @@ -217,7 +253,9 @@ format_warning_at_substring (const substring_loc
> &fmt_loc,
>
>  bool
>  format_warning_at_substring_n (const substring_loc &fmt_loc,
> +			       const range_label *fmt_label,
>  			       location_t param_loc,
> +			       const range_label *param_label,
>  			       const char *corrected_substring,
>  			       int opt, unsigned HOST_WIDE_INT n,
>  			       const char *singular_gmsgid,
> @@ -225,7 +263,8 @@ format_warning_at_substring_n (const substring_loc
> &fmt_loc,
>  {
>    va_list ap;
>    va_start (ap, plural_gmsgid);
> -  bool warned = format_warning_n_va (fmt_loc, param_loc,
> corrected_substring,
> +  bool warned = format_warning_n_va (fmt_loc, fmt_label, param_loc,
> param_label,
> +				     corrected_substring,
>  				     opt, n, singular_gmsgid, plural_gmsgid,
>  				     &ap);
>    va_end (ap);
> diff --git a/gcc/substring-locations.h b/gcc/substring-locations.h
> index fca6fd3..919fdf0 100644
> --- a/gcc/substring-locations.h
> +++ b/gcc/substring-locations.h
> @@ -77,32 +77,40 @@ class substring_loc
>  /* Functions for emitting a warning about a format string.  */
>
>  extern bool format_warning_va (const substring_loc &fmt_loc,
> +			       const range_label *fmt_label,
>  			       location_t param_loc,
> +			       const range_label *param_label,
>  			       const char *corrected_substring,
>  			       int opt, const char *gmsgid, va_list *ap)
> -  ATTRIBUTE_GCC_DIAG (5, 0);
> +  ATTRIBUTE_GCC_DIAG (7, 0);
>
>  extern bool format_warning_n_va (const substring_loc &fmt_loc,
> +				 const range_label *fmt_label,
>  				 location_t param_loc,
> +				 const range_label *param_label,
>  				 const char *corrected_substring,
>  				 int opt, unsigned HOST_WIDE_INT n,
>  				 const char *singular_gmsgid,
>  				 const char *plural_gmsgid, va_list *ap)
> -  ATTRIBUTE_GCC_DIAG (6, 0) ATTRIBUTE_GCC_DIAG (7, 0);
> +  ATTRIBUTE_GCC_DIAG (8, 0) ATTRIBUTE_GCC_DIAG (9, 0);
>
>  extern bool format_warning_at_substring (const substring_loc &fmt_loc,
> +					 const range_label *fmt_label,
>  					 location_t param_loc,
> +					 const range_label *param_label,
>  					 const char *corrected_substring,
>  					 int opt, const char *gmsgid, ...)
> -  ATTRIBUTE_GCC_DIAG (5, 6);
> +  ATTRIBUTE_GCC_DIAG (7, 8);
>
>  extern bool format_warning_at_substring_n (const substring_loc &fmt_loc,
> +					   const range_label *fmt_label,
>  					   location_t param_loc,
> +					   const range_label *param_label,
>  					   const char *corrected_substring,
>  					   int opt, unsigned HOST_WIDE_INT n,
>  					   const char *singular_gmsgid,
>  					   const char *plural_gmsgid, ...)
> -  ATTRIBUTE_GCC_DIAG (6, 8) ATTRIBUTE_GCC_DIAG (7, 8);
> +  ATTRIBUTE_GCC_DIAG (8, 10) ATTRIBUTE_GCC_DIAG (9, 10);
>
>  /* Implementation detail, for use when implementing
>     LANG_HOOKS_GET_SUBSTRING_LOCATION.  */
> diff --git a/gcc/testsuite/g++.dg/diagnostic/aka3.C
> b/gcc/testsuite/g++.dg/diagnostic/aka3.C
> new file mode 100644
> index 0000000..1eb4fb2
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/diagnostic/aka3.C
> @@ -0,0 +1,25 @@
> +/* Verify the "aka" descriptions for typedefs are correctly
> +   quoted and shown within labels.  */
> +
> +/* { dg-options "-fdiagnostics-show-caret" } */
> +
> +typedef struct s1 t1;
> +typedef struct s2 {int i;} t2;
> +
> +int foo(t1 *);
> +
> +void test_1 () {
> +  t2 pos;
> +
> +  foo (&pos); // { dg-error "cannot convert 't2\\*' {aka 's2\\*'} to
> 't1\\*' {aka 's1\\*'}" }
> +  /* { dg-begin-multiline-output "" }
> +   foo (&pos);
> +        ^~~~
> +        |
> +        t2* {aka s2*}
> +     { dg-end-multiline-output "" } */
> +  /* { dg-begin-multiline-output "" }
> + int foo(t1 *);
> +         ^~~~
> +     { dg-end-multiline-output "" } */
> +}
> diff --git a/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch-2.C
> b/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch-2.C
> index c3b6f00..8cf2dab 100644
> --- a/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch-2.C
> +++ b/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch-2.C
> @@ -12,6 +12,8 @@ int test_1 (int first, const char *second, float third)
>    /* { dg-begin-multiline-output "" }
>     return callee_1 (first, second, third);
>                             ^~~~~~
> +                           |
> +                           const char*
>       { dg-end-multiline-output "" } */
>    // { dg-message "initializing argument 2 of 'int callee_1\\(int, const
> char\\*\\*, float\\)'" "" { target *-*-* } callee_1 }
>    /* { dg-begin-multiline-output "" }
> @@ -30,6 +32,8 @@ int test_2 (int first, const char *second, float third)
>    /* { dg-begin-multiline-output "" }
>     return callee_2 (first, second, third);
>                             ^~~~~~
> +                           |
> +                           const char*
>       { dg-end-multiline-output "" } */
>    // { dg-message "initializing argument 2 of 'int callee_2\\(int, const
> char\\*\\*, float\\)'" "" { target *-*-* } callee_2 }
>    /* { dg-begin-multiline-output "" }
> @@ -51,6 +55,8 @@ int test_3 (int first, const char *second, float third)
>    /* { dg-begin-multiline-output "" }
>     return callee_3 (first, second, third);
>                             ^~~~~~
> +                           |
> +                           const char*
>       { dg-end-multiline-output "" } */
>    // { dg-message "initializing argument 2 of 'int callee_3\\(int, const
> char\\*\\*, float\\)'" "" { target *-*-* } callee_3 }
>    /* { dg-begin-multiline-output "" }
> diff --git a/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch.C
> b/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch.C
> index 5fcde0b..50bbd4a 100644
> --- a/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch.C
> +++ b/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch.C
> @@ -12,6 +12,8 @@ int test_1 (int first, int second, float third)
>    /* { dg-begin-multiline-output "" }
>     return callee_1 (first, second, third);
>                             ^~~~~~
> +                           |
> +                           int
>       { dg-end-multiline-output "" } */
>    // { dg-message "initializing argument 2 of 'int callee_1\\(int, const
> char\\*, float\\)'" "" { target *-*-* } callee_1 }
>    /* { dg-begin-multiline-output "" }
> @@ -30,6 +32,8 @@ int test_2 (int first, int second, float third)
>    /* { dg-begin-multiline-output "" }
>     return callee_2 (first, second, third);
>                             ^~~~~~
> +                           |
> +                           int
>       { dg-end-multiline-output "" } */
>    // { dg-message "initializing argument 2 of 'int callee_2\\(int, const
> char\\*, float\\)'" "" { target *-*-* } callee_2 }
>    /* { dg-begin-multiline-output "" }
> @@ -51,6 +55,8 @@ int test_3 (int first, int second, float third)
>    /* { dg-begin-multiline-output "" }
>     return callee_3 (first, second, third);
>                             ^~~~~~
> +                           |
> +                           int
>       { dg-end-multiline-output "" } */
>    // { dg-message "initializing argument 2 of 'int callee_3\\(int, const
> char\\*, float\\)'" "" { target *-*-* } callee_3 }
>    /* { dg-begin-multiline-output "" }
> @@ -69,6 +75,8 @@ int test_4 (int first, int second, float third)
>    /* { dg-begin-multiline-output "" }
>     return s4::member_1 (first, second, third);
>                                 ^~~~~~
> +                               |
> +                               int
>       { dg-end-multiline-output "" } */
>    /* { dg-begin-multiline-output "" }
>   struct s4 { static int member_1 (int one, const char *two, float three);
> };
> @@ -87,6 +95,8 @@ int test_5 (int first, int second, float third)
>    /* { dg-begin-multiline-output "" }
>     return inst.member_1 (first, second, third);
>                                  ^~~~~~
> +                                |
> +                                int
>       { dg-end-multiline-output "" } */
>    /* { dg-begin-multiline-output "" }
>   struct s5 { int member_1 (int one, const char *two, float three); };
> @@ -104,6 +114,8 @@ int test_6 (int first, int second, float third, s6
> *ptr)
>    /* { dg-begin-multiline-output "" }
>     return ptr->member_1 (first, second, third);
>                                  ^~~~~~
> +                                |
> +                                int
>       { dg-end-multiline-output "" } */
>    /* { dg-begin-multiline-output "" }
>   struct s6 { int member_1 (int one, const char *two, float three); };
> @@ -144,6 +156,8 @@ int test_8 (int first, int second, float third)
>    /* { dg-begin-multiline-output "" }
>     return s8 <const char *>::member_1 (first, second, third);
>                                                ^~~~~~
> +                                              |
> +                                              int
>       { dg-end-multiline-output "" } */
>    /* { dg-begin-multiline-output "" }
>   struct s8 { static int member_1 (int one, T two, float three); };
> @@ -163,6 +177,8 @@ int test_9 (int first, int second, float third)
>    /* { dg-begin-multiline-output "" }
>     return inst.member_1 (first, second, third);
>                                  ^~~~~~
> +                                |
> +                                int
>       { dg-end-multiline-output "" } */
>    /* { dg-begin-multiline-output "" }
>   struct s9 { int member_1 (int one, T two, float three); };
> @@ -180,6 +196,8 @@ int test_10 (int first, int second, float third)
>    /* { dg-begin-multiline-output "" }
>     return callee_10 (first, second, third);
>                              ^~~~~~
> +                            |
> +                            int
>       { dg-end-multiline-output "" } */
>    // { dg-message "initializing argument 2 of 'int callee_10\\(int, int
> \\(\\*\\)\\(int, int\\), float\\)'" "" { target *-*-* } callee_10 }
>    /* { dg-begin-multiline-output "" }
> @@ -198,6 +216,8 @@ int test_11 (int first, int second, float third)
>    /* { dg-begin-multiline-output "" }
>     return callee_11 (first, second, third);
>                              ^~~~~~
> +                            |
> +                            int
>       { dg-end-multiline-output "" } */
>    // { dg-message "initializing argument 2 of 'int callee_11\\(int, int
> \\(\\*\\)\\(int, int\\), float\\)'" "" { target *-*-* } callee_11 }
>    /* { dg-begin-multiline-output "" }
> diff --git a/gcc/testsuite/g++.dg/plugin/plugin.exp
> b/gcc/testsuite/g++.dg/plugin/plugin.exp
> index 451c4a9..d9f54ab 100644
> --- a/gcc/testsuite/g++.dg/plugin/plugin.exp
> +++ b/gcc/testsuite/g++.dg/plugin/plugin.exp
> @@ -69,6 +69,7 @@ set plugin_test_list [list \
>  	  diagnostic-test-inlining-1.C } \
>      { show_template_tree_color_plugin.c \
>      	  show-template-tree-color.C \
> +    	  show-template-tree-color-labels.C \
>      	  show-template-tree-color-no-elide-type.C } \
>      { comment_plugin.c comments-1.C } \
>  ]
> diff --git a/gcc/testsuite/g++.dg/plugin/show-template-tree-color-labels.C
> b/gcc/testsuite/g++.dg/plugin/show-template-tree-color-labels.C
> new file mode 100644
> index 0000000..462e1bd
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/plugin/show-template-tree-color-labels.C
> @@ -0,0 +1,38 @@
> +/* Verify colorization of the labels in diagnostic-show-locus.c
> +   for template comparisons.
> +   Doing so requires a plugin; see the comments in the plugin for the
> +   rationale.  */
> +
> +// { dg-options "-fdiagnostics-color=always -fdiagnostics-show-caret" }
> +
> +template<typename> struct vector {};
> +template<typename, typename> struct map {};
> +
> +void fn_1(vector<int>);
> +void fn_2(map<int, int>);
> +
> +void test_1 (vector<double> vec)
> +{
> +  fn_1 (vec);
> +  /* { dg-begin-multiline-output "" }
> +could not convert ' [01m [Kvec [m [K' from ' [01m [Kvector< [01;32m
> [Kdouble [m [K> [m [K' to ' [01m [Kvector< [01;32m [Kint [m [K> [m [K'
> +   fn_1 ( [01;31m [Kvec [m [K);
> +          [01;31m [K^~~ [m [K
> +          [01;31m [K| [m [K
> +          [01;31m [Kvector<double> [m [K
> +     { dg-end-multiline-output "" } */
> +  // TODO: we don't yet highlight the mismatching part with color
> +}
> +
> +void test_2 (const map<int, double> &m)
> +{
> +  fn_2 (m);
> +  /* { dg-begin-multiline-output "" }
> +could not convert ' [01m [Km [m [K' from ' [01m [Kmap<[...], [01;32m
> [Kdouble [m [K> [m [K' to ' [01m [Kmap<[...], [01;32m [Kint [m [K> [m [K'
> +   fn_2 ( [01;31m [Km [m [K);
> +          [01;31m [K^ [m [K
> +          [01;31m [K| [m [K
> +          [01;31m [Kmap<[...],double> [m [K
> +     { dg-end-multiline-output "" } */
> +  // TODO: we don't yet highlight the mismatching part with color
> +}
> diff --git a/gcc/testsuite/gcc.dg/bad-binary-ops.c
> b/gcc/testsuite/gcc.dg/bad-binary-ops.c
> index e1da4d6..46c158e 100644
> --- a/gcc/testsuite/gcc.dg/bad-binary-ops.c
> +++ b/gcc/testsuite/gcc.dg/bad-binary-ops.c
> @@ -13,6 +13,8 @@ void test_1 ()
>  { dg-begin-multiline-output "" }
>     myvec[1]/ptr;
>     ~~~~~~~~^
> +        |
> +        __m128
>  { dg-end-multiline-output "" } */
>
>
> @@ -31,8 +33,12 @@ int test_2 (void)
>  /* { dg-begin-multiline-output "" }
>     return (some_function ()
>             ~~~~~~~~~~~~~~~~
> +           |
> +           struct s
>      + some_other_function ());
>      ^ ~~~~~~~~~~~~~~~~~~~~~~
> +      |
> +      struct t
>     { dg-end-multiline-output "" } */
>  }
>
> @@ -46,3 +52,23 @@ int test_3 (struct s param_s, struct t param_t)
>     { dg-end-multiline-output "" } */
>  /* TODO: ideally we'd underline both params here.  */
>  }
> +
> +typedef struct s S;
> +typedef struct t T;
> +
> +extern S callee_4a (void);
> +extern T callee_4b (void);
> +
> +int test_4 (void)
> +{
> +  return callee_4a () + callee_4b (); /* { dg-error "invalid operands to
> binary \+" } */
> +
> +/* { dg-begin-multiline-output "" }
> +   return callee_4a () + callee_4b ();
> +          ~~~~~~~~~~~~ ^ ~~~~~~~~~~~~
> +          |              |
> +          |              T {aka struct t}
> +          S {aka struct s}
> +   { dg-end-multiline-output "" } */
> +}
> +
> diff --git a/gcc/testsuite/gcc.dg/cpp/pr66415-1.c
> b/gcc/testsuite/gcc.dg/cpp/pr66415-1.c
> index 515252c..cc4e417 100644
> --- a/gcc/testsuite/gcc.dg/cpp/pr66415-1.c
> +++ b/gcc/testsuite/gcc.dg/cpp/pr66415-1.c
> @@ -11,6 +11,8 @@ fn1 (void)
>  /* { dg-begin-multiline-output "" }
>     __builtin_printf
> ("xxxxxxxxxxxxxxxxx%dxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
>                                                                        ~^
> +                                                                       |
> +                                                                       int
>     { dg-end-multiline-output "" } */
>
>  }
> diff --git a/gcc/testsuite/gcc.dg/format/diagnostic-ranges.c
> b/gcc/testsuite/gcc.dg/format/diagnostic-ranges.c
> index e56e159..84535f0 100644
> --- a/gcc/testsuite/gcc.dg/format/diagnostic-ranges.c
> +++ b/gcc/testsuite/gcc.dg/format/diagnostic-ranges.c
> @@ -11,6 +11,8 @@ void test_mismatching_types (const char *msg)
>  /* { dg-begin-multiline-output "" }
>     printf("hello %i", msg);
>                   ~^   ~~~
> +                  |   |
> +                  int const char *
>                   %s
>     { dg-end-multiline-output "" } */
>
> @@ -19,6 +21,9 @@ void test_mismatching_types (const char *msg)
>  /* { dg-begin-multiline-output "" }
>     printf("hello %s", 42);
>                   ~^   ~~
> +                  |   |
> +                  |   int
> +                  char *
>                   %d
>     { dg-end-multiline-output "" } */
>
> @@ -26,6 +31,8 @@ void test_mismatching_types (const char *msg)
>  /* { dg-begin-multiline-output "" }
>     printf("hello %i", (long)0);
>                   ~^   ~~~~~~~
> +                  |   |
> +                  int long int
>                   %li
>     { dg-end-multiline-output "" } */
>  }
> @@ -37,9 +44,13 @@ void test_multiple_arguments (void)
>  /* { dg-begin-multiline-output "" }
>     printf ("arg0: %i  arg1: %s arg 2: %i",
>                              ~^
> +                             |
> +                             char *
>                              %d
>             100, 101, 102);
>                  ~~~
> +                |
> +                int
>     { dg-end-multiline-output "" } */
>  }
>
> @@ -50,9 +61,13 @@ void test_multiple_arguments_2 (int i, int j)
>  /* { dg-begin-multiline-output "" }
>     printf ("arg0: %i  arg1: %s arg 2: %i",
>                              ~^
> +                             |
> +                             char *
>                              %d
>             100, i + j, 102);
>                  ~~~~~
> +                  |
> +                  int
>     { dg-end-multiline-output "" } */
>  }
>
> @@ -72,6 +87,8 @@ void multiline_format_string (void) {
>              ~~
>             "d"
>             ~^
> +            |
> +            int
>     { dg-end-multiline-output "" } */
>  }
>
> @@ -84,6 +101,8 @@ void test_hex (const char *msg)
>  /* { dg-begin-multiline-output "" }
>     printf("hello \x25\x69", msg);
>                   ~~~~^~~~   ~~~
> +                     |      |
> +                     int    const char *
>                   \x25s
>     { dg-end-multiline-output "" } */
>  }
> @@ -97,6 +116,8 @@ void test_oct (const char *msg)
>  /* { dg-begin-multiline-output "" }
>     printf("hello \045\151", msg);
>                   ~~~~^~~~   ~~~
> +                     |      |
> +                     int    const char *
>                   \045s
>     { dg-end-multiline-output "" } */
>  }
> @@ -112,11 +133,15 @@ void test_multiple (const char *msg)
>            ^~~~~~~~
>            msg);
>            ~~~
> +          |
> +          const char *
>    { dg-end-multiline-output "" } */
>
>  /* { dg-begin-multiline-output "" }
>     printf("prefix"  "\x25"  "\151"  "suffix",
>                       ~~~~~~~~^~~~
> +                             |
> +                             int
>                       \x25"  "s
>    { dg-end-multiline-output "" } */
>  }
> @@ -127,6 +152,8 @@ void test_u8 (const char *msg)
>  /* { dg-begin-multiline-output "" }
>     printf(u8"hello %i", msg);
>                     ~^   ~~~
> +                    |   |
> +                    int const char *
>                     %s
>     { dg-end-multiline-output "" } */
>  }
> @@ -137,6 +164,8 @@ void test_param (long long_i, long long_j)
>  /* { dg-begin-multiline-output "" }
>     printf ("foo %s bar", long_i + long_j);
>                  ~^       ~~~~~~~~~~~~~~~
> +                 |              |
> +                 char *         long int
>                  %ld
>     { dg-end-multiline-output "" } */
>  }
> @@ -147,6 +176,8 @@ void test_field_width_specifier (long l, int i1, int
> i2)
>  /* { dg-begin-multiline-output "" }
>     printf (" %*.*d ", l, i1, i2);
>               ~^~~~    ~
> +              |       |
> +              int     long int
>     { dg-end-multiline-output "" } */
>  }
>
> @@ -158,12 +189,16 @@ void test_field_width_specifier_2 (char *d, long foo,
> long bar)
>    /* { dg-begin-multiline-output "" }
>     __builtin_sprintf (d, " %*ld ", foo, foo);
>                             ~^~~    ~~~
> +                            |      |
> +                            int    long int
>     { dg-end-multiline-output "" } */
>
>    __builtin_sprintf (d, " %*ld ", foo + bar, foo); /* { dg-warning "28:
> field width specifier '\\*' expects argument of type 'int', but argument 3
> has type 'long int'" } */
>    /* { dg-begin-multiline-output "" }
>     __builtin_sprintf (d, " %*ld ", foo + bar, foo);
>                             ~^~~    ~~~~~~~~~
> +                            |          |
> +                            int        long int
>     { dg-end-multiline-output "" } */
>  }
>
> @@ -173,12 +208,16 @@ void test_field_precision_specifier (char *d, long
> foo, long bar)
>    /* { dg-begin-multiline-output "" }
>     __builtin_sprintf (d, " %.*ld ", foo, foo);
>                             ~~^~~    ~~~
> +                             |      |
> +                             int    long int
>     { dg-end-multiline-output "" } */
>
>    __builtin_sprintf (d, " %.*ld ", foo + bar, foo); /* { dg-warning "29:
> field precision specifier '\\.\\*' expects argument of type 'int', but
> argument 3 has type 'long int'" } */
>    /* { dg-begin-multiline-output "" }
>     __builtin_sprintf (d, " %.*ld ", foo + bar, foo);
>                             ~~^~~    ~~~~~~~~~
> +                             |          |
> +                             int        long int
>     { dg-end-multiline-output "" } */
>  }
>
> @@ -241,10 +280,14 @@ void test_macro (const char *msg)
>  /* { dg-begin-multiline-output "" }
>     printf("hello " INT_FMT " world", msg);
>            ^~~~~~~~                   ~~~
> +                                     |
> +                                     const char *
>     { dg-end-multiline-output "" } */
>  /* { dg-begin-multiline-output "" }
>   #define INT_FMT "%i"
>                    ~^
> +                   |
> +                   int
>                    %s
>     { dg-end-multiline-output "" } */
>  #undef INT_FMT
> @@ -257,10 +300,14 @@ void test_macro_2 (const char *msg)
>  /* { dg-begin-multiline-output "" }
>     printf("hello %" PRIu32 " world", msg);
>            ^~~~~~~~~                  ~~~
> +                                     |
> +                                     const char *
>     { dg-end-multiline-output "" } */
>  /* { dg-begin-multiline-output "" }
>   #define PRIu32 "u"
>                   ^
> +                 |
> +                 unsigned int
>     { dg-end-multiline-output "" } */
>  #undef PRIu32
>  }
> @@ -295,6 +342,8 @@ void test_macro_4 (const char *msg)
>  /* { dg-begin-multiline-output "" }
>   #define FMT_STRING "hello %i world"
>                             ~^
> +                            |
> +                            int
>                             %s
>     { dg-end-multiline-output "" } */
>  #undef FMT_STRING
> @@ -307,10 +356,14 @@ void test_non_contiguous_strings (void)
>    /* { dg-begin-multiline-output "" }
>     __builtin_printf(" %" "d ", 0.5);
>                      ^~~~       ~~~
> +                               |
> +                               double
>     { dg-end-multiline-output "" } */
>    /* { dg-begin-multiline-output "" }
>     __builtin_printf(" %" "d ", 0.5);
>                        ~~~~^
> +                          |
> +                          int
>                        %" "f
>     { dg-end-multiline-output "" } */
>  }
> @@ -324,5 +377,7 @@ void test_const_arrays (void)
>    /* { dg-begin-multiline-output "" }
>     __builtin_printf(a, 0.5);
>                      ^  ~~~
> +                       |
> +                       double
>     { dg-end-multiline-output "" } */
>  }
> diff --git a/gcc/testsuite/gcc.dg/format/pr72858.c
> b/gcc/testsuite/gcc.dg/format/pr72858.c
> index b8c5829..7726094 100644
> --- a/gcc/testsuite/gcc.dg/format/pr72858.c
> +++ b/gcc/testsuite/gcc.dg/format/pr72858.c
> @@ -28,12 +28,18 @@ test_x (char *d,
>  /* { dg-begin-multiline-output "" }
>     sprintf (d, " %-8x ", lexpr);
>                   ~~~^    ~~~~~
> +                    |    |
> +                    |    long int
> +                    unsigned int
>                   %-8lx
>     { dg-end-multiline-output "" } */
>    sprintf (d, " %-8x ", ulexpr); /* { dg-warning "20: format '%x' expects
> argument of type 'unsigned int', but argument 3 has type 'long unsigned
> int'" } */
>  /* { dg-begin-multiline-output "" }
>     sprintf (d, " %-8x ", ulexpr);
>                   ~~~^    ~~~~~~
> +                    |    |
> +                    |    long unsigned int
> +                    unsigned int
>                   %-8lx
>     { dg-end-multiline-output "" } */
>
> @@ -41,12 +47,18 @@ test_x (char *d,
>  /* { dg-begin-multiline-output "" }
>     sprintf (d, " %-8x ", llexpr);
>                   ~~~^    ~~~~~~
> +                    |    |
> +                    |    long long int
> +                    unsigned int
>                   %-8llx
>     { dg-end-multiline-output "" } */
>    sprintf (d, " %-8x ", ullexpr); /* { dg-warning "20: format '%x' expects
> argument of type 'unsigned int', but argument 3 has type 'long long unsigned
> int'" } */
>  /* { dg-begin-multiline-output "" }
>     sprintf (d, " %-8x ", ullexpr);
>                   ~~~^    ~~~~~~~
> +                    |    |
> +                    |    long long unsigned int
> +                    unsigned int
>                   %-8llx
>     { dg-end-multiline-output "" } */
>
> @@ -56,18 +68,27 @@ test_x (char *d,
>  /* { dg-begin-multiline-output "" }
>     sprintf (d, " %-8x ", fexpr);
>                   ~~~^    ~~~~~
> +                    |    |
> +                    |    double
> +                    unsigned int
>                   %-8f
>     { dg-end-multiline-output "" } */
>    sprintf (d, " %-8x ", dexpr); /* { dg-warning "20: format '%x' expects
> argument of type 'unsigned int', but argument 3 has type 'double'" } */
>  /* { dg-begin-multiline-output "" }
>     sprintf (d, " %-8x ", dexpr);
>                   ~~~^    ~~~~~
> +                    |    |
> +                    |    double
> +                    unsigned int
>                   %-8f
>     { dg-end-multiline-output "" } */
>    sprintf (d, " %-8x ", ldexpr); /* { dg-warning "20: format '%x' expects
> argument of type 'unsigned int', but argument 3 has type 'long double'" }
> */
>  /* { dg-begin-multiline-output "" }
>     sprintf (d, " %-8x ", ldexpr);
>                   ~~~^    ~~~~~~
> +                    |    |
> +                    |    long double
> +                    unsigned int
>                   %-8Lf
>     { dg-end-multiline-output "" } */
>
> @@ -76,6 +97,9 @@ test_x (char *d,
>  /* { dg-begin-multiline-output "" }
>     sprintf (d, " %-8x ", ptr);
>                   ~~~^    ~~~
> +                    |    |
> +                    |    void *
> +                    unsigned int
>                   %-8p
>     { dg-end-multiline-output "" } */
>
> @@ -86,6 +110,9 @@ test_x (char *d,
>  /* { dg-begin-multiline-output "" }
>     sprintf (d, " %-8x ", s);
>                   ~~~^    ~
> +                    |    |
> +                    |    struct s
> +                    unsigned int
>     { dg-end-multiline-output "" } */
>  }
>
> @@ -105,12 +132,18 @@ test_lx (char *d,
>  /* { dg-begin-multiline-output "" }
>     sprintf (d, " %-8lx ", iexpr);
>                   ~~~~^    ~~~~~
> +                     |    |
> +                     |    int
> +                     long unsigned int
>                   %-8x
>     { dg-end-multiline-output "" } */
>    sprintf (d, " %-8lx ", uiexpr); /* { dg-warning "21: format '%lx' expects
> argument of type 'long unsigned int', but argument 3 has type 'unsigned
> int'" } */
>  /* { dg-begin-multiline-output "" }
>     sprintf (d, " %-8lx ", uiexpr);
>                   ~~~~^    ~~~~~~
> +                     |    |
> +                     |    unsigned int
> +                     long unsigned int
>                   %-8x
>     { dg-end-multiline-output "" } */
>
> @@ -121,12 +154,18 @@ test_lx (char *d,
>  /* { dg-begin-multiline-output "" }
>     sprintf (d, " %-8lx ", llexpr);
>                   ~~~~^    ~~~~~~
> +                     |    |
> +                     |    long long int
> +                     long unsigned int
>                   %-8llx
>     { dg-end-multiline-output "" } */
>    sprintf (d, " %-8lx ", ullexpr); /* { dg-warning "21: format '%lx'
> expects argument of type 'long unsigned int', but argument 3 has type 'long
> long unsigned int'" } */
>  /* { dg-begin-multiline-output "" }
>     sprintf (d, " %-8lx ", ullexpr);
>                   ~~~~^    ~~~~~~~
> +                     |    |
> +                     |    long long unsigned int
> +                     long unsigned int
>                   %-8llx
>     { dg-end-multiline-output "" } */
>
> @@ -136,18 +175,27 @@ test_lx (char *d,
>  /* { dg-begin-multiline-output "" }
>     sprintf (d, " %-8lx ", fexpr);
>                   ~~~~^    ~~~~~
> +                     |    |
> +                     |    double
> +                     long unsigned int
>                   %-8f
>     { dg-end-multiline-output "" } */
>    sprintf (d, " %-8lx ", dexpr); /* { dg-warning "21: format '%lx' expects
> argument of type 'long unsigned int', but argument 3 has type 'double'" }
> */
>  /* { dg-begin-multiline-output "" }
>     sprintf (d, " %-8lx ", dexpr);
>                   ~~~~^    ~~~~~
> +                     |    |
> +                     |    double
> +                     long unsigned int
>                   %-8f
>     { dg-end-multiline-output "" } */
>    sprintf (d, " %-8lx ", ldexpr); /* { dg-warning "21: format '%lx' expects
> argument of type 'long unsigned int', but argument 3 has type 'long double'"
> } */
>  /* { dg-begin-multiline-output "" }
>     sprintf (d, " %-8lx ", ldexpr);
>                   ~~~~^    ~~~~~~
> +                     |    |
> +                     |    long double
> +                     long unsigned int
>                   %-8Lf
>     { dg-end-multiline-output "" } */
>  }
> @@ -170,12 +218,18 @@ test_o (char *d,
>  /* { dg-begin-multiline-output "" }
>     sprintf (d, " %-8o ", lexpr);
>                   ~~~^    ~~~~~
> +                    |    |
> +                    |    long int
> +                    unsigned int
>                   %-8lo
>     { dg-end-multiline-output "" } */
>    sprintf (d, " %-8o ", ulexpr); /* { dg-warning "20: format '%o' expects
> argument of type 'unsigned int', but argument 3 has type 'long unsigned
> int'" } */
>  /* { dg-begin-multiline-output "" }
>     sprintf (d, " %-8o ", ulexpr);
>                   ~~~^    ~~~~~~
> +                    |    |
> +                    |    long unsigned int
> +                    unsigned int
>                   %-8lo
>     { dg-end-multiline-output "" } */
>
> @@ -183,12 +237,18 @@ test_o (char *d,
>  /* { dg-begin-multiline-output "" }
>     sprintf (d, " %-8o ", llexpr);
>                   ~~~^    ~~~~~~
> +                    |    |
> +                    |    long long int
> +                    unsigned int
>                   %-8llo
>     { dg-end-multiline-output "" } */
>    sprintf (d, " %-8o ", ullexpr); /* { dg-warning "20: format '%o' expects
> argument of type 'unsigned int', but argument 3 has type 'long long unsigned
> int'" } */
>  /* { dg-begin-multiline-output "" }
>     sprintf (d, " %-8o ", ullexpr);
>                   ~~~^    ~~~~~~~
> +                    |    |
> +                    |    long long unsigned int
> +                    unsigned int
>                   %-8llo
>     { dg-end-multiline-output "" } */
>  }
> @@ -208,12 +268,18 @@ test_lo (char *d,
>  /* { dg-begin-multiline-output "" }
>     sprintf (d, " %-8lo ", iexpr);
>                   ~~~~^    ~~~~~
> +                     |    |
> +                     |    int
> +                     long unsigned int
>                   %-8o
>     { dg-end-multiline-output "" } */
>    sprintf (d, " %-8lo ", uiexpr); /* { dg-warning "21: format '%lo' expects
> argument of type 'long unsigned int', but argument 3 has type 'unsigned
> int'" } */
>  /* { dg-begin-multiline-output "" }
>     sprintf (d, " %-8lo ", uiexpr);
>                   ~~~~^    ~~~~~~
> +                     |    |
> +                     |    unsigned int
> +                     long unsigned int
>                   %-8o
>     { dg-end-multiline-output "" } */
>
> @@ -224,12 +290,18 @@ test_lo (char *d,
>  /* { dg-begin-multiline-output "" }
>     sprintf (d, " %-8lo ", llexpr);
>                   ~~~~^    ~~~~~~
> +                     |    |
> +                     |    long long int
> +                     long unsigned int
>                   %-8llo
>     { dg-end-multiline-output "" } */
>    sprintf (d, " %-8lo ", ullexpr); /* { dg-warning "21: format '%lo'
> expects argument of type 'long unsigned int', but argument 3 has type 'long
> long unsigned int'" } */
>  /* { dg-begin-multiline-output "" }
>     sprintf (d, " %-8lo ", ullexpr);
>                   ~~~~^    ~~~~~~~
> +                     |    |
> +                     |    long long unsigned int
> +                     long unsigned int
>                   %-8llo
>     { dg-end-multiline-output "" } */
>  }
> @@ -246,6 +318,9 @@ test_e (char *d, int iexpr, float fexpr, double dexpr,
> long double ldexpr)
>  /* { dg-begin-multiline-output "" }
>     sprintf (d, " %-8e ", iexpr);
>                   ~~~^    ~~~~~
> +                    |    |
> +                    |    int
> +                    double
>                   %-8d
>     { dg-end-multiline-output "" } */
>
> @@ -257,6 +332,9 @@ test_e (char *d, int iexpr, float fexpr, double dexpr,
> long double ldexpr)
>  /* { dg-begin-multiline-output "" }
>     sprintf (d, " %-8e ", ldexpr);
>                   ~~~^    ~~~~~~
> +                    |    |
> +                    |    long double
> +                    double
>                   %-8Le
>     { dg-end-multiline-output "" } */
>  }
> @@ -273,6 +351,9 @@ test_Le (char *d, int iexpr, float fexpr, double dexpr,
> long double ldexpr)
>  /* { dg-begin-multiline-output "" }
>     sprintf (d, " %-8Le ", iexpr);
>                   ~~~~^    ~~~~~
> +                     |    |
> +                     |    int
> +                     long double
>                   %-8d
>     { dg-end-multiline-output "" } */
>
> @@ -282,6 +363,9 @@ test_Le (char *d, int iexpr, float fexpr, double dexpr,
> long double ldexpr)
>  /* { dg-begin-multiline-output "" }
>     sprintf (d, " %-8Le ", fexpr);
>                   ~~~~^    ~~~~~
> +                     |    |
> +                     |    double
> +                     long double
>                   %-8e
>     { dg-end-multiline-output "" } */
>
> @@ -289,6 +373,9 @@ test_Le (char *d, int iexpr, float fexpr, double dexpr,
> long double ldexpr)
>  /* { dg-begin-multiline-output "" }
>     sprintf (d, " %-8Le ", dexpr);
>                   ~~~~^    ~~~~~
> +                     |    |
> +                     |    double
> +                     long double
>                   %-8e
>     { dg-end-multiline-output "" } */
>
> @@ -307,6 +394,9 @@ test_E (char *d, int iexpr, float fexpr, double dexpr,
> long double ldexpr)
>  /* { dg-begin-multiline-output "" }
>     sprintf (d, " %-8E ", iexpr);
>                   ~~~^    ~~~~~
> +                    |    |
> +                    |    int
> +                    double
>                   %-8d
>     { dg-end-multiline-output "" } */
>
> @@ -318,6 +408,9 @@ test_E (char *d, int iexpr, float fexpr, double dexpr,
> long double ldexpr)
>  /* { dg-begin-multiline-output "" }
>     sprintf (d, " %-8E ", ldexpr);
>                   ~~~^    ~~~~~~
> +                    |    |
> +                    |    long double
> +                    double
>                   %-8LE
>     { dg-end-multiline-output "" } */
>  }
> @@ -334,6 +427,9 @@ test_LE (char *d, int iexpr, float fexpr, double dexpr,
> long double ldexpr)
>  /* { dg-begin-multiline-output "" }
>     sprintf (d, " %-8LE ", iexpr);
>                   ~~~~^    ~~~~~
> +                     |    |
> +                     |    int
> +                     long double
>                   %-8d
>     { dg-end-multiline-output "" } */
>
> @@ -341,6 +437,9 @@ test_LE (char *d, int iexpr, float fexpr, double dexpr,
> long double ldexpr)
>  /* { dg-begin-multiline-output "" }
>     sprintf (d, " %-8LE ", fexpr);
>                   ~~~~^    ~~~~~
> +                     |    |
> +                     |    double
> +                     long double
>                   %-8E
>     { dg-end-multiline-output "" } */
>
> @@ -348,6 +447,9 @@ test_LE (char *d, int iexpr, float fexpr, double dexpr,
> long double ldexpr)
>  /* { dg-begin-multiline-output "" }
>     sprintf (d, " %-8LE ", dexpr);
>                   ~~~~^    ~~~~~
> +                     |    |
> +                     |    double
> +                     long double
>                   %-8E
>     { dg-end-multiline-output "" } */
>
> @@ -367,18 +469,24 @@ test_everything (char *d, long lexpr)
>    /* { dg-begin-multiline-output "" }
>     sprintf (d, "before %-+*.*lld after", lexpr, lexpr, lexpr);
>                         ~~~^~~~~~         ~~~~~
> +                          |              |
> +                          int            long int
>     { dg-end-multiline-output "" } */
>
>    /* { dg-warning "28: field precision specifier '\\.\\*' expects argument
> of type 'int', but argument 4 has type 'long int'" "" { target *-*-* }
> test_everything_sprintf } */
>    /* { dg-begin-multiline-output "" }
>     sprintf (d, "before %-+*.*lld after", lexpr, lexpr, lexpr);
>                         ~~~~~^~~~                ~~~~~
> +                            |                   |
> +                            int                 long int
>     { dg-end-multiline-output "" } */
>
>    /* { dg-warning "31: format '%lld' expects argument of type 'long long
> int', but argument 5 has type 'long int'" "" { target *-*-* }
> test_everything_sprintf } */
>    /* { dg-begin-multiline-output "" }
>     sprintf (d, "before %-+*.*lld after", lexpr, lexpr, lexpr);
>                         ~~~~~~~~^                       ~~~~~
> +                               |                       |
> +                               long long int           long int
>                         %-+*.*ld
>     { dg-end-multiline-output "" } */
>  }
> diff --git a/gcc/testsuite/gcc.dg/format/pr78498.c
> b/gcc/testsuite/gcc.dg/format/pr78498.c
> index 4b53a68..b911b04 100644
> --- a/gcc/testsuite/gcc.dg/format/pr78498.c
> +++ b/gcc/testsuite/gcc.dg/format/pr78498.c
> @@ -7,6 +7,8 @@ void f (void)
>  /* { dg-begin-multiline-output "" }
>     __builtin_printf ("%i", "");
>                        ~^   ~~
> +                       |   |
> +                       int char *
>                        %s
>     { dg-end-multiline-output "" } */
>  }
> diff --git a/gcc/testsuite/gcc.dg/param-type-mismatch.c
> b/gcc/testsuite/gcc.dg/param-type-mismatch.c
> index 9498a74..9e654a9 100644
> --- a/gcc/testsuite/gcc.dg/param-type-mismatch.c
> +++ b/gcc/testsuite/gcc.dg/param-type-mismatch.c
> @@ -1,4 +1,4 @@
> -/* { dg-options "-fdiagnostics-show-caret" }  */
> +/* { dg-options "-fdiagnostics-show-caret -Wpointer-sign" }  */
>
>  /* A collection of calls where argument 2 is of the wrong type.  */
>
> @@ -12,6 +12,8 @@ int test_1 (int first, int second, float third)
>    /* { dg-begin-multiline-output "" }
>     return callee_1 (first, second, third);
>                             ^~~~~~
> +                           |
> +                           int
>       { dg-end-multiline-output "" } */
>    /* { dg-message "expected 'const char \\*' but argument is of type 'int'"
> "" { target *-*-* } callee_1 } */
>    /* { dg-begin-multiline-output "" }
> @@ -30,6 +32,8 @@ int test_2 (int first, int second, float third)
>    /* { dg-begin-multiline-output "" }
>     return callee_2 (first, second, third);
>                             ^~~~~~
> +                           |
> +                           int
>       { dg-end-multiline-output "" } */
>    /* { dg-message "expected 'const char \\*' but argument is of type 'int'"
> "" { target *-*-* } callee_2 } */
>    /* { dg-begin-multiline-output "" }
> @@ -51,6 +55,8 @@ int test_3 (int first, int second, float third)
>    /* { dg-begin-multiline-output "" }
>     return callee_3 (first, second, third);
>                             ^~~~~~
> +                           |
> +                           int
>       { dg-end-multiline-output "" } */
>    /* { dg-message "expected 'const char \\*' but argument is of type 'int'"
> "" { target *-*-* } callee_3 } */
>    /* { dg-begin-multiline-output "" }
> @@ -69,6 +75,8 @@ int test_4 (int first, const char *second, float third)
>    /* { dg-begin-multiline-output "" }
>     return callee_4 (first, second, third);
>                             ^~~~~~
> +                           |
> +                           const char *
>       { dg-end-multiline-output "" } */
>    /* { dg-message "expected 'float' but argument is of type 'const char
> \\*'" "" { target *-*-* } callee_4 } */
>    /* { dg-begin-multiline-output "" }
> @@ -87,6 +95,8 @@ int test_5 (int first, const char *second, float third)
>    /* { dg-begin-multiline-output "" }
>     return callee_5 (first, second, third);
>                             ^~~~~~
> +                           |
> +                           const char *
>       { dg-end-multiline-output "" } */
>    /* { dg-message "expected 'float' but argument is of type 'const char
> \\*'" "" { target *-*-* } callee_5 } */
>    /* { dg-begin-multiline-output "" }
> @@ -105,6 +115,8 @@ int test_6 (int first, int second, float third)
>    /* { dg-begin-multiline-output "" }
>     return callee_6 (first, second, third);
>                             ^~~~~~
> +                           |
> +                           int
>       { dg-end-multiline-output "" } */
>    /* { dg-message " expected 'int \\(\\*\\)\\(int,  int\\)' but argument is
> of type 'int'" "" { target *-*-* } callee_6 } */
>    /* { dg-begin-multiline-output "" }
> @@ -123,6 +135,8 @@ int test_7 (int first, int second, float third)
>    /* { dg-begin-multiline-output "" }
>     return callee_7 (first, second, third);
>                             ^~~~~~
> +                           |
> +                           int
>       { dg-end-multiline-output "" } */
>    /* { dg-message " expected 'int \\(\\*\\)\\(int,  int\\)' but argument is
> of type 'int'" "" { target *-*-* } callee_7 } */
>    /* { dg-begin-multiline-output "" }
> @@ -130,3 +144,43 @@ int test_7 (int first, int second, float third)
>                                 ^~~~~~~~~~~~~~~~~
>       { dg-end-multiline-output "" } */
>  }
> +
> +/* -Wincompatible-pointer-types for a parameter.  */
> +
> +extern int callee_8 (int one, float *two, float (three)); /* { dg-line
> callee_8 } */
> +
> +int test_8 (int first, int *second, float third)
> +{
> +  return callee_8 (first, second, third); /* { dg-warning "passing argument
> 2 of 'callee_8' from incompatible pointer type" } */
> +  /* { dg-begin-multiline-output "" }
> +   return callee_8 (first, second, third);
> +                           ^~~~~~
> +                           |
> +                           int *
> +     { dg-end-multiline-output "" } */
> +  /* { dg-message "expected 'float \\*' but argument is of type 'int \\*'"
> "" { target *-*-* } callee_8 } */
> +  /* { dg-begin-multiline-output "" }
> + extern int callee_8 (int one, float *two, float (three));
> +                               ~~~~~~~^~~
> +     { dg-end-multiline-output "" } */
> +}
> +
> +/* -Wpointer-sign for a parameter.  */
> +
> +extern int callee_9 (int one, int *two, float (three)); /* { dg-line
> callee_9 } */
> +
> +int test_9 (int first, unsigned int *second, float third)
> +{
> +  return callee_9 (first, second, third); /* { dg-warning "pointer targets
> in passing argument 2 of 'callee_9' differ in signedness" } */
> +  /* { dg-begin-multiline-output "" }
> +   return callee_9 (first, second, third);
> +                           ^~~~~~
> +                           |
> +                           unsigned int *
> +     { dg-end-multiline-output "" } */
> +  /* { dg-message "expected 'int \\*' but argument is of type 'unsigned int
> \\*'" "" { target *-*-* } callee_9 } */
> +  /* { dg-begin-multiline-output "" }
> + extern int callee_9 (int one, int *two, float (three));
> +                               ~~~~~^~~
> +     { dg-end-multiline-output "" } */
> +}
> diff --git
> a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-bw-line-numbers.c
> b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-bw-line-numbers.c
> index 66a2faa..89213eb 100644
> ---
> a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-bw-line-numbers.c
> +++
> b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-bw-line-numbers.c
> @@ -31,6 +31,8 @@ void test_multiline (void)
>     |        ~~~~~~~~~~~~~~~~~
>  27 |        + second_function ());
>     |        ^ ~~~~~~~~~~~~~~~~~~
> +   |        |
> +   |        label
>     { dg-end-multiline-output "" } */
>  #endif
>  }
> @@ -43,8 +45,10 @@ void test_very_wide_line (void)
>     | 0         0         0         0         0         0         1
>
>     | 4         5         6         7         8         9         0
>
>     |
> 0123456789012345678901234567890123456789012345678901234567890123456789
> -41 |                                          float f = foo * bar;
> +43 |                                          float f = foo * bar;
>     |                                                    ~~~~^~~~~
> +   |                                                        |
> +   |                                                        label
>     |                                                    bar * foo
>     { dg-end-multiline-output "" } */
>  #endif
> @@ -58,7 +62,7 @@ void test_fixit_insert (void)
>  #if 0
>     int a[2][2] = { 0, 1 , 2, 3 }; /* { dg-warning "insertion hints" } */
>  /* { dg-begin-multiline-output "" }
> -59 |    int a[2][2] = { 0, 1 , 2, 3 };
> +63 |    int a[2][2] = { 0, 1 , 2, 3 };
>     |                    ^~~~
>     |                    {   }
>     { dg-end-multiline-output "" } */
> @@ -72,7 +76,7 @@ void test_fixit_remove (void)
>  #if 0
>    int a;; /* { dg-warning "example of a removal hint" } */
>  /* { dg-begin-multiline-output "" }
> -73 |   int a;;
> +77 |   int a;;
>     |         ^
>     |         -
>     { dg-end-multiline-output "" } */
> @@ -86,7 +90,7 @@ void test_fixit_replace (void)
>  #if 0
>    gtk_widget_showall (dlg); /* { dg-warning "example of a replacement hint"
> } */
>  /* { dg-begin-multiline-output "" }
> -87 |   gtk_widget_showall (dlg);
> +91 |   gtk_widget_showall (dlg);
>     |   ^~~~~~~~~~~~~~~~~~
>     |   gtk_widget_show_all
>     { dg-end-multiline-output "" } */
> @@ -108,7 +112,7 @@ void test_fixit_insert_newline (void)
>      }
>  /* { dg-begin-multiline-output "" }
>      |+      break;
> -106 |     case 'b':
> +110 |     case 'b':
>      |     ^~~~~~~~
>     { dg-end-multiline-output "" } */
>  #endif
> diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-bw.c
> b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-bw.c
> index 513c0af..bdfa420 100644
> --- a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-bw.c
> +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-bw.c
> @@ -44,6 +44,8 @@ void test_multiline (void)
>          ~~~~~~~~~~~~~~~~~
>          + second_function ());
>          ^ ~~~~~~~~~~~~~~~~~~
> +        |
> +        label
>     { dg-end-multiline-output "" } */
>  #endif
>  }
> @@ -66,6 +68,8 @@ void test_many_lines (void)
>  /* { dg-begin-multiline-output "" }
>     x = (first_function_with_a_very_long_name (lorem, ipsum, dolor, sit,
> amet,
>
> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> +        |
> +        label 1
>                                               consectetur, adipiscing,
> elit,
>
> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>                                               sed, eiusmod, tempor,
> @@ -76,6 +80,9 @@ void test_many_lines (void)
>                                               ~~~~~~~~~~~~~~~~~~~~~~
>          + second_function_with_a_very_long_name (lorem, ipsum, dolor, sit,
>          ^
> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> +        | |
> +        | label 2
> +        label 0
>                                                   amet, consectetur,
>                                                   ~~~~~~~~~~~~~~~~~~
>                                                   adipiscing, elit, sed,
> @@ -115,13 +122,32 @@ void test_caret_within_proper_range (void)
>  void test_very_wide_line (void)
>  {
>  #if 0
> -
>     float f = foo * bar; /* { dg-warning "95: test" } */
> +                             float x
>             = foo * bar; /* { dg-warning "95: test" } */
>  /* { dg-begin-multiline-output "" }
>       0         0         0         0         0         0         1
>       4         5         6         7         8         9         0
>   6789012345678901234567890123456789012345678901234567890123456789012345
> -                                              float f = foo * bar;
> + x                                                    = foo * bar;
> + ~                                                      ~~~~^~~~~
> + |                                                          |
> + label 1                                                    label 0
> +                                                        bar * foo
> +   { dg-end-multiline-output "" } */
> +#endif
> +}
> +
> +void test_very_wide_line_2 (void)
> +{
> +#if 0
> +                            float x
>             = foo * bar; /* { dg-warning "95: test" } */
> +/* { dg-begin-multiline-output "" }
> +     0         0         0         0         0         0         1
> +     4         5         6         7         8         9         0
> + 6789012345678901234567890123456789012345678901234567890123456789012345
> +                                                      = foo * bar;
>                                                          ~~~~^~~~~
> +                                                            |
> +                                                            label 0
>                                                          bar * foo
>     { dg-end-multiline-output "" } */
>  #endif
> @@ -226,27 +252,69 @@ void test_many_nested_locations (void)
>     ^
>       Lorem ipsum dolor sit amet, consectetur adipiscing elit,
>       ^~~~~ ^~~~~ ^~~~~ ^~~ ^~~~  ^~~~~~~~~~~ ^~~~~~~~~~ ^~~~
> +     |     |     |     |   |     |           |          |
> +     |     |     |     |   label label       label      label
> +     label label label label
>       LOREM IPSUM DOLOR SIT AMET  CONSECTETUR ADIPISCING ELIT
>       sed do eiusmod tempor incididunt ut labore et dolore magna
>       ^~~ ^~ ^~~~~~~ ^~~~~~ ^~~~~~~~~~ ^~ ^~~~~~ ^~ ^~~~~~ ^~~~~
> +     |   |  |       |      |          |  |      |  |      |
> +     |   |  |       |      |          |  |      |  label  label
> +     |   |  |       |      |          |  label  label
> +     |   |  label   label  label      label
> +     |   label
> +     label
>       SED DO EIUSMOD TEMPOR INCIDIDUNT UT LABORE ET DOLORE MAGNA
>       aliqua. Ut enim ad minim veniam, quis nostrud exercitation
>       ^~~~~~  ^~ ^~~~ ^~ ^~~~~ ^~~~~~  ^~~~ ^~~~~~~ ^~~~~~~~~~~~
> +     |       |  |    |  |     |       |    |       |
> +     |       |  |    |  |     |       |    label   label
> +     |       |  |    |  label label   label
> +     |       |  |    label
> +     |       |  label
> +     label   label
>       ALIQUA  UT ENIM AD MINIM VENIAM  QUIS NOSTRUD EXERCITATION
>       ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis
>       ^~~~~~~ ^~~~~~~ ^~~~ ^~ ^~~~~~~ ^~ ^~ ^~~~~~~ ^~~~~~~~~  ^~~~
> +     |       |       |    |  |       |  |  |       |          |
> +     |       |       |    |  |       |  |  label   label      label
> +     |       |       |    |  |       |  label
> +     |       |       |    |  label   label
> +     |       |       |    label
> +     label   label   label
>       ULLAMCO LABORIS NISI UT ALIQUIP EX EA COMMODO CONSEQUAT  DUIS
>       aute irure dolor in reprehenderit in voluptate velit esse cillum
>       ^~~~ ^~~~~ ^~~~~ ^~ ^~~~~~~~~~~~~ ^~ ^~~~~~~~~ ^~~~~ ^~~~ ^~~~~~
> +     |    |     |     |  |             |  |         |     |    |
> +     |    |     |     |  |             |  |         |     |    label
> +     |    |     |     |  |             |  label     label label
> +     |    |     |     |  label         label
> +     |    label label label
> +     label
>       AUTE IRURE DOLOR IN REPREHENDERIT IN VOLUPTATE VELIT ESSE CILLUM
>       dolore eu fugiat nulla pariatur. Excepteur sint occaecat
>       ^~~~~~ ^~ ^~~~~~ ^~~~~ ^~~~~~~~  ^~~~~~~~~ ^~~~ ^~~~~~~~
> +     |      |  |      |     |         |         |    |
> +     |      |  |      |     |         |         |    label
> +     |      |  label  label label     label     label
> +     label  label
>       DOLORE EU FUGIAT NULLA PARIATUR  EXCEPTEUR SINT OCCAECAT
>       cupidatat non proident, sunt in culpa qui officia deserunt
>       ^~~~~~~~~ ^~~ ^~~~~~~~  ^~~~ ^~ ^~~~~ ^~~ ^~~~~~~ ^~~~~~~~
> +     |         |   |         |    |  |     |   |       |
> +     |         |   |         |    |  |     |   label   label
> +     |         |   |         |    |  label label
> +     |         |   |         |    label
> +     |         |   label     label
> +     label     label
>       CUPIDATAT NON PROIDENT  SUNT IN CULPA QUI OFFICIA DESERUNT
>       mollit anim id est laborum.
>       ^~~~~~ ^~~~ ^~ ^~~ ^~~~~~~
> +     |      |    |  |   |
> +     |      |    |  |   label
> +     |      |    |  label
> +     |      |    label
> +     label  label
>       MOLLIT ANIM ID EST LABORUM
>     { dg-end-multiline-output "" } */
>  }
> diff --git
> a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-color-line-numbers.c
> b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-color-line-numbers.c
> index a80b6de..0453c52 100644
> ---
> a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-color-line-numbers.c
> +++
> b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-color-line-numbers.c
> @@ -19,6 +19,8 @@ void test_multiline (void)
>     |         [32m [K~~~~~~~~~~~~~~~~~ [m [K
>  15 |         [01;35m [K+ [m [K  [34m [Ksecond_function () [m [K);
>     |         [01;35m [K^ [m [K  [34m [K~~~~~~~~~~~~~~~~~~ [m [K
> +   |         [01;35m [K| [m [K
> +   |         [01;35m [Klabel [m [K
>     { dg-end-multiline-output "" } */
>  #endif
>  }
> diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-color.c
> b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-color.c
> index 4cc406d..094bc65 100644
> --- a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-color.c
> +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-color.c
> @@ -44,6 +44,8 @@ void test_multiline (void)
>           [32m [K~~~~~~~~~~~~~~~~~ [m [K
>           [01;35m [K+ [m [K  [34m [Ksecond_function () [m [K);
>           [01;35m [K^ [m [K  [34m [K~~~~~~~~~~~~~~~~~~ [m [K
> +         [01;35m [K| [m [K
> +         [01;35m [Klabel [m [K
>     { dg-end-multiline-output "" } */
>  #endif
>  }
> @@ -66,6 +68,8 @@ void test_many_lines (void)
>  /* { dg-begin-multiline-output "" }
>     x = ( [32m [Kfirst_function_with_a_very_long_name (lorem, ipsum, dolor,
> sit, amet, [m [K
>           [32m
> [K~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [m
> [K
> +         [32m [K| [m [K
> +         [32m [Klabel 1 [m [K
>    [32m [K                                            consectetur,
> adipiscing, elit, [m [K
>                                                [32m
> [K~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [m [K
>    [32m [K                                            sed, eiusmod, tempor,
> [m [K
> @@ -76,6 +80,9 @@ void test_many_lines (void)
>                                                [32m [K~~~~~~~~~~~~~~~~~~~~~~
> [m [K
>           [01;35m [K+ [m [K  [34m [Ksecond_function_with_a_very_long_name
> (lorem, ipsum, dolor, sit,
>           [01;35m [K^ [m [K  [34m
> [K~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> [m [K
> +         [01;35m [K| [m [K  [34m [K| [m [K
> +         [01;35m [K| [m [K  [34m [Klabel 2 [m [K
> +         [01;35m [Klabel 0 [m [K
>    [34m [K                                                amet, consectetur,
> [m [K
>                                                    [34m [K~~~~~~~~~~~~~~~~~~
> [m [K
>    [34m [K                                                adipiscing, elit,
> sed, [m [K
> @@ -115,13 +122,15 @@ void test_caret_within_proper_range (void)
>  void test_very_wide_line (void)
>  {
>  #if 0
> -
>     float f = foo * bar; /* { dg-warning "95: test" } */
> +                             float x
>             = foo * bar; /* { dg-warning "95: test" } */
>  /* { dg-begin-multiline-output "" }
>       0         0         0         0         0         0         1
>       4         5         6         7         8         9         0
>   6789012345678901234567890123456789012345678901234567890123456789012345
> -                                              float f =  [01;35m [Kfoo *
> bar [m [K;
> -                                                         [01;35m
> [K~~~~^~~~~ [m [K
> +  [32m [Kx [m [K                                                    =
> [01;35m [Kfoo * bar [m [K;
> +  [32m [K~ [m [K
> [01;35m [K~~~~^~~~~ [m [K
> +  [32m [K| [m [K
> [01;35m [K| [m [K
> +  [32m [Klabel 1 [m [K
> [01;35m [Klabel 0 [m [K
>                                                           [32m [Kbar * foo
> [m [K
>     { dg-end-multiline-output "" } */
>  #endif
> diff --git
> a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-no-labels.c
> b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-no-labels.c
> new file mode 100644
> index 0000000..4c06368
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-no-labels.c
> @@ -0,0 +1,27 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O -fdiagnostics-show-caret -fno-diagnostics-show-labels"
> } */
> +
> +/* Verify that -fno-diagnostics-show-labels works.  */
> +
> +/* This is a collection of unittests for diagnostic_show_locus;
> +   see the overview in diagnostic_plugin_test_show_locus.c.
> +
> +   In particular, note the discussion of why we need a very long line
> here:
> +01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
> +   and that we can't use macros in this file.  */
> +
> +void test_multiline (void)
> +{
> +#if 0
> +  x = (first_function ()
> +       + second_function ()); /* { dg-warning "test" } */
> +
> +  /* This shouldn't have a label.  */
> +  /* { dg-begin-multiline-output "" }
> +   x = (first_function ()
> +        ~~~~~~~~~~~~~~~~~
> +        + second_function ());
> +        ^ ~~~~~~~~~~~~~~~~~~
> +     { dg-end-multiline-output "" } */
> +#endif
> +}
> diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_show_trees.c
> b/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_show_trees.c
> index 0bdd877..71e6740 100644
> --- a/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_show_trees.c
> +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_show_trees.c
> @@ -41,7 +41,7 @@ show_tree (tree node)
>      return;
>
>    gcc_rich_location richloc (EXPR_LOCATION (node));
> -  richloc.add_expr (node);
> +  richloc.add_expr (node, NULL);
>
>    if (richloc.get_num_locations () < 2)
>      {
> 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 1d340aa..3d78538 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
> @@ -145,9 +145,10 @@ custom_diagnostic_finalizer (diagnostic_context
> *context,
>
>  static void
>  add_range (rich_location *richloc, location_t start, location_t finish,
> -	   bool show_caret_p)
> +	   bool show_caret_p, const range_label *label = NULL)
>  {
> -  richloc->add_range (make_location (start, start, finish), show_caret_p);
> +  richloc->add_range (make_location (start, start, finish), show_caret_p,
> +		      label);
>  }
>
>  /* Exercise the diagnostic machinery to emit various warnings,
> @@ -192,7 +193,8 @@ test_show_locus (function *fun)
>    if (0 == strcmp (fnname, "test_multiline"))
>      {
>        const int line = fnstart_line + 2;
> -      rich_location richloc (line_table, get_loc (line + 1, 7));
> +      text_range_label label ("label");
> +      rich_location richloc (line_table, get_loc (line + 1, 7), &label);
>        add_range (&richloc, get_loc (line, 7), get_loc (line, 23), false);
>        add_range (&richloc, get_loc (line + 1, 9), get_loc (line + 1, 26),
>  		 false);
> @@ -202,10 +204,14 @@ test_show_locus (function *fun)
>    if (0 == strcmp (fnname, "test_many_lines"))
>      {
>        const int line = fnstart_line + 2;
> -      rich_location richloc (line_table, get_loc (line + 5, 7));
> -      add_range (&richloc, get_loc (line, 7), get_loc (line + 4, 65),
> false);
> +      text_range_label label0 ("label 0");
> +      text_range_label label1 ("label 1");
> +      text_range_label label2 ("label 2");
> +      rich_location richloc (line_table, get_loc (line + 5, 7), &label0);
> +      add_range (&richloc, get_loc (line, 7), get_loc (line + 4, 65),
> false,
> +		 &label1);
>        add_range (&richloc, get_loc (line + 5, 9), get_loc (line + 10, 61),
> -		 false);
> +		 false, &label2);
>        warning_at (&richloc, 0, "test");
>      }
>
> @@ -231,16 +237,40 @@ test_show_locus (function *fun)
>      }
>
>    /* Example of a very wide line, where the information of interest
> -     is beyond the width of the terminal (hardcoded above).  */
> +     is beyond the width of the terminal (hardcoded above), with
> +     a secondary location that exactly fits on the left-margin.  */
>    if (0 == strcmp (fnname, "test_very_wide_line"))
>      {
>        const int line = fnstart_line + 2;
>        global_dc->show_ruler_p = true;
> +      text_range_label label0 ("label 0");
> +      text_range_label label1 ("label 1");
> +      rich_location richloc (line_table,
> +			     make_location (get_loc (line, 94),
> +					    get_loc (line, 90),
> +					    get_loc (line, 98)),
> +			     &label0);
> +      richloc.add_range (get_loc (line, 35), false, &label1);
> +      richloc.add_fixit_replace ("bar * foo");
> +      warning_at (&richloc, 0, "test");
> +      global_dc->show_ruler_p = false;
> +    }
> +
> +  /* Likewise, but with a secondary location that's immediately before
> +     the left margin; the location and label should be gracefully dropped.
> */
> +  if (0 == strcmp (fnname, "test_very_wide_line_2"))
> +    {
> +      const int line = fnstart_line + 2;
> +      global_dc->show_ruler_p = true;
> +      text_range_label label0 ("label 0");
> +      text_range_label label1 ("label 1");
>        rich_location richloc (line_table,
>  			     make_location (get_loc (line, 94),
>  					    get_loc (line, 90),
> -					    get_loc (line, 98)));
> +					    get_loc (line, 98)),
> +			     &label0);
>        richloc.add_fixit_replace ("bar * foo");
> +      richloc.add_range (get_loc (line, 34), false, &label1);
>        warning_at (&richloc, 0, "test");
>        global_dc->show_ruler_p = false;
>      }
> @@ -391,13 +421,14 @@ test_show_locus (function *fun)
>
>    /* Example of many locations and many fixits.
>       Underline (separately) every word in a comment, and convert them
> -     to upper case.  */
> +     to upper case.  Give all of the ranges labels (sharing one label).
> */
>    if (0 == strcmp (fnname, "test_many_nested_locations"))
>      {
>        const char *file = LOCATION_FILE (fnstart);
>        const int start_line = fnstart_line + 2;
>        const int finish_line = start_line + 7;
>        location_t loc = get_loc (start_line - 1, 2);
> +      text_range_label label ("label");
>        rich_location richloc (line_table, loc);
>        for (int line = start_line; line <= finish_line; line++)
>  	{
> @@ -418,7 +449,7 @@ test_show_locus (function *fun)
>  		      location_t word
>  			= make_location (start_of_word, start_of_word,
>  					 end_of_word);
> -		      richloc.add_range (word, true);
> +		      richloc.add_range (word, true, &label);
>
>  		      /* Add a fixit, converting to upper case.  */
>  		      char_span word_span = content.subspan (start_idx, idx -
> start_idx);
> diff --git a/gcc/testsuite/gcc.dg/plugin/plugin.exp
> b/gcc/testsuite/gcc.dg/plugin/plugin.exp
> index b2f8507..86ab1dd 100644
> --- a/gcc/testsuite/gcc.dg/plugin/plugin.exp
> +++ b/gcc/testsuite/gcc.dg/plugin/plugin.exp
> @@ -72,6 +72,7 @@ set plugin_test_list [list \
>      { diagnostic_plugin_test_show_locus.c \
>  	  diagnostic-test-show-locus-bw.c \
>  	  diagnostic-test-show-locus-color.c \
> +	  diagnostic-test-show-locus-no-labels.c \
>  	  diagnostic-test-show-locus-bw-line-numbers.c \
>  	  diagnostic-test-show-locus-color-line-numbers.c \
>  	  diagnostic-test-show-locus-parseable-fixits.c \
> diff --git a/gcc/testsuite/gcc.dg/pr69554-1.c
> b/gcc/testsuite/gcc.dg/pr69554-1.c
> index 07ad0db..b979b55 100644
> --- a/gcc/testsuite/gcc.dg/pr69554-1.c
> +++ b/gcc/testsuite/gcc.dg/pr69554-1.c
> @@ -12,6 +12,9 @@ int test_1 (const char *p, const char *q)
>  /* { dg-begin-multiline-output "" }
>     return (p + 1) + (q + 1);
>            ~~~~~~~ ^ ~~~~~~~
> +             |         |
> +             |         const char *
> +             const char *
>     { dg-end-multiline-output "" } */
>  }
>
> @@ -26,10 +29,14 @@ int test_2 (const char *p, const char *q)
>  /* { dg-begin-multiline-output "" }
>     return (p + 1)
>            ~~~~~~~
> +             |
> +             const char *
>              +
>              ^
>               (q + 1);
>               ~~~~~~~
> +                |
> +                const char *
>     { dg-end-multiline-output "" } */
>  }
>
> @@ -43,16 +50,20 @@ int test_3 (const char *p, const char *q)
>
>             +  /* { dg-error "invalid operands" } */
>               (q + 1);
> -/* { dg-locus "12" "" { target *-*-* } "44" } */
> +/* { dg-locus "12" "" { target *-*-* } "51" } */
>  /* { dg-begin-multiline-output "" }
>     return (p + 1)
>            ~~~~~~~
> +             |
> +             const char *
>     { dg-end-multiline-output "" } */
>  /* { dg-begin-multiline-output "" }
>              +
>              ^
>                (q + 1);
>                ~~~~~~~
> +                 |
> +                 const char *
>     { dg-end-multiline-output "" } */
>  }
>
> @@ -68,12 +79,16 @@ int test_4 (const char *p, const char *q)
>  /* { dg-begin-multiline-output "" }
>     return (p + 1)
>            ~~~~~~~
> +             |
> +             const char *
>              +
>              ^
>     { dg-end-multiline-output "" } */
>  /* { dg-begin-multiline-output "" }
>                (q + 1);
>                ~~~~~~~
> +                 |
> +                 const char *
>     { dg-end-multiline-output "" } */
>  }
>
> @@ -88,10 +103,12 @@ int test_5 (const char *p, const char *q)
>             +  /* { dg-error "invalid operands" } */
>
>               (q + 1); /* { dg-locus "14" } */
> -/* { dg-locus "12" "" { target *-*-* } "88" } */
> +/* { dg-locus "12" "" { target *-*-* } "103" } */
>  /* { dg-begin-multiline-output "" }
>     return (p + 1)
>            ~~~~~~~
> +             |
> +             const char *
>     { dg-end-multiline-output "" } */
>  /* { dg-begin-multiline-output "" }
>              +
> @@ -100,6 +117,8 @@ int test_5 (const char *p, const char *q)
>  /* { dg-begin-multiline-output "" }
>                (q + 1);
>                ~~~~~~~
> +                 |
> +                 const char *
>     { dg-end-multiline-output "" } */
>  }
>
> @@ -136,10 +155,12 @@ int test_6 (const char *p, const char *q)
>  	     fringilla sapien elit vitae nisl. Fusce mattis commodo risus
>  	     nec convallis. */
>               (q + 1); /* { dg-locus "14" } */
> -/* { dg-locus "12" "" { target *-*-* } "125" } */
> +/* { dg-locus "12" "" { target *-*-* } "144" } */
>  /* { dg-begin-multiline-output "" }
>     return (p + 1)
>            ~~~~~~~
> +             |
> +             const char *
>     { dg-end-multiline-output "" } */
>  /* { dg-begin-multiline-output "" }
>              +
> @@ -148,5 +169,7 @@ int test_6 (const char *p, const char *q)
>  /* { dg-begin-multiline-output "" }
>                (q + 1);
>                ~~~~~~~
> +                 |
> +                 const char *
>     { dg-end-multiline-output "" } */
>  }
> diff --git a/gcc/testsuite/gcc.dg/pr69627.c
> b/gcc/testsuite/gcc.dg/pr69627.c
> index b7f56cd..bc48bb1 100644
> --- a/gcc/testsuite/gcc.dg/pr69627.c
> +++ b/gcc/testsuite/gcc.dg/pr69627.c
> @@ -11,6 +11,8 @@ foo ()
>  /* { dg-begin-multiline-output "" }
>     t[1] / s;
>     ~~~~ ^
> +    |
> +    float
>     { dg-end-multiline-output "" } */
>  }
>
> @@ -23,5 +25,7 @@ bar ()
>  /* { dg-begin-multiline-output "" }
>     t[1] / s[0];
>     ~~~~ ^ ~~~~
> +    |      |
> +    float  const int *
>     { dg-end-multiline-output "" } */
>  }
> diff --git a/gcc/testsuite/lib/multiline.exp
> b/gcc/testsuite/lib/multiline.exp
> index 84c59e1..5f8b62f 100644
> --- a/gcc/testsuite/lib/multiline.exp
> +++ b/gcc/testsuite/lib/multiline.exp
> @@ -202,26 +202,6 @@ proc _build_multiline_regex { multiline index } {
>  	if {[string match "*^" $line] || [string match "*~" $line]} {
>  	    # Assume a line containing a caret/range.  This must be
>  	    # an exact match.
> -	} elseif {[string match "*\\|" $line]} {
> -	    # Assume a source line with a right-margin.  Support
> -	    # arbitrary text in place of any whitespace before the
> -	    # right-margin, to deal with comments containing containing
> -	    # DejaGnu directives.
> -
> -	    # Remove final "\|":
> -	    set rexp [string range $rexp 0 [expr [string length $rexp] - 3]]
> -
> -	    # Trim off trailing whitespace:
> -	    set old_length [string length $rexp]
> -	    set rexp [string trimright $rexp]
> -	    set new_length [string length $rexp]
> -
> -	    # Replace the trimmed whitespace with "." chars to match anything:
> -	    set ws [string repeat "." [expr $old_length - $new_length]]
> -	    set rexp "${rexp}${ws}"
> -
> -	    # Add back the trailing '\|':
> -	    set rexp "${rexp}\\|"
>  	} else {
>  	    # Assume that we have a quoted source line.
>  	    if {![string equal "" $line] }  {
> diff --git a/gcc/toplev.c b/gcc/toplev.c
> index aa943a8..2789d71 100644
> --- a/gcc/toplev.c
> +++ b/gcc/toplev.c
> @@ -1112,6 +1112,8 @@ general_init (const char *argv0, bool init_signals)
>
>    global_dc->show_caret
>      = global_options_init.x_flag_diagnostics_show_caret;
> +  global_dc->show_labels_p
> +    = global_options_init.x_flag_diagnostics_show_labels;
>    global_dc->show_line_numbers_p
>      = global_options_init.x_flag_diagnostics_show_line_numbers;
>    global_dc->show_option_requested
> diff --git a/libcpp/include/line-map.h b/libcpp/include/line-map.h
> index 1061d20..4f0ff87 100644
> --- a/libcpp/include/line-map.h
> +++ b/libcpp/include/line-map.h
> @@ -1281,8 +1281,11 @@ typedef struct
>    bool sysp;
>  } expanded_location;
>
> +class range_label;
> +
>  /* A location within a rich_location: a caret&range, with
> -   the caret potentially flagged for display.  */
> +   the caret potentially flagged for display, and an optional
> +   label.  */
>
>  struct location_range
>  {
> @@ -1298,6 +1301,9 @@ struct location_range
>
>       where "1" and "2" are notionally carets.  */
>    bool m_show_caret_p;
> +
> +  /* If non-NULL, the label for this range.  */
> +  const range_label *m_label;
>  };
>
>  /* A partially-embedded vec for use within rich_location for storing
> @@ -1439,6 +1445,8 @@ class fixit_hint;
>     Additional ranges may be added to help the user identify other
>     pertinent clauses in a diagnostic.
>
> +   Ranges can (optionally) be given labels via class range_label.
> +
>     rich_location instances are intended to be allocated on the stack
>     when generating diagnostics, and to be short-lived.
>
> @@ -1484,18 +1492,22 @@ class fixit_hint;
>     equal to their caret point.  The frontend overrides the diagnostic
>     context's default caret character for these ranges.
>
> -   Example E
> -   *********
> +   Example E (range labels)
> +   ************************
>        printf ("arg0: %i  arg1: %s arg2: %i",
>                                 ^~
> +                               |
> +                               const char *
>                100, 101, 102);
>                     ~~~
> +                   |
> +                   int
>     This rich location has two ranges:
>     - range 0 is at the "%s" with start = caret = "%" and finish at
> -     the "s".
> +     the "s".  It has a range_label ("const char *").
>     - range 1 has start/finish covering the "101" and is not flagged for
> -     caret printing; it is perhaps at the start of "101".
> -
> +     caret printing.  The caret is at the start of "101", where its
> +     range_label is printed ("int").
>
>     Fix-it hints
>     ------------
> @@ -1587,7 +1599,8 @@ class rich_location
>    /* Constructors.  */
>
>    /* Constructing from a location.  */
> -  rich_location (line_maps *set, source_location loc);
> +  rich_location (line_maps *set, source_location loc,
> +		 const range_label *label = NULL);
>
>    /* Destructor.  */
>    ~rich_location ();
> @@ -1597,7 +1610,8 @@ class rich_location
>    source_location get_loc (unsigned int idx) const;
>
>    void
> -  add_range (source_location loc,  bool show_caret_p);
> +  add_range (source_location loc,  bool show_caret_p,
> +	     const range_label *label = NULL);
>
>    void
>    set_range (unsigned int idx, source_location loc, bool show_caret_p);
> @@ -1721,6 +1735,54 @@ protected:
>    bool m_fixits_cannot_be_auto_applied;
>  };
>
> +/* A struct for the result of range_label::get_text: a NUL-terminated
> buffer
> +   of localized text, and a flag to determine if the caller should "free"
> the
> +   buffer.  */
> +
> +struct label_text
> +{
> +  label_text ()
> +  : m_buffer (NULL), m_caller_owned (false)
> +  {}
> +
> +  label_text (char *buffer, bool caller_owned)
> +  : m_buffer (buffer), m_caller_owned (caller_owned)
> +  {}
> +
> +  void maybe_free ()
> +  {
> +    if (m_caller_owned)
> +      free (m_buffer);
> +  }
> +
> +  char *m_buffer;
> +  bool m_caller_owned;
> +};
> +
> +/* Abstract base class for labelling a range within a rich_location
> +   (e.g. for labelling expressions with their type).
> +
> +   Generating the text could require non-trivial work, so this work
> +   is delayed (via the "get_text" virtual function) until the diagnostic
> +   printing code "knows" it needs it, thus avoiding doing it e.g. for
> +   warnings that are filtered by command-line flags.  This virtual
> +   function also isolates libcpp and the diagnostics subsystem from
> +   the front-end and middle-end-specific code for generating the text
> +   for the labels.
> +
> +   Like the rich_location instances they annotate, range_label instances
> +   are intended to be allocated on the stack when generating diagnostics,
> +   and to be short-lived.  */
> +
> +class range_label
> +{
> + public:
> +  virtual ~range_label () {}
> +
> +  /* Get localized text for the label.  */
> +  virtual label_text get_text () const = 0;
> +};
> +
>  /* A fix-it hint: a suggested insertion, replacement, or deletion of text.
>     We handle these three types of edit with one class, by representing
>     them as replacement of a half-open range:
> diff --git a/libcpp/line-map.c b/libcpp/line-map.c
> index 555cd12..f0e6318 100644
> --- a/libcpp/line-map.c
> +++ b/libcpp/line-map.c
> @@ -1988,7 +1988,8 @@ line_table_dump (FILE *stream, struct line_maps *set,
> unsigned int num_ordinary,
>
>  /* Construct a rich_location with location LOC as its initial range.  */
>
> -rich_location::rich_location (line_maps *set, source_location loc) :
> +rich_location::rich_location (line_maps *set, source_location loc,
> +			      const range_label *label) :
>    m_line_table (set),
>    m_ranges (),
>    m_column_override (0),
> @@ -1997,7 +1998,7 @@ rich_location::rich_location (line_maps *set,
> source_location loc) :
>    m_seen_impossible_fixit (false),
>    m_fixits_cannot_be_auto_applied (false)
>  {
> -  add_range (loc, true);
> +  add_range (loc, true, label);
>  }
>
>  /* The destructor for class rich_location.  */
> @@ -2073,11 +2074,13 @@ rich_location::override_column (int column)
>  /* Add the given range.  */
>
>  void
> -rich_location::add_range (source_location loc, bool show_caret_p)
> +rich_location::add_range (source_location loc, bool show_caret_p,
> +			  const range_label *label)
>  {
>    location_range range;
>    range.m_loc = loc;
>    range.m_show_caret_p = show_caret_p;
> +  range.m_label = label;
>    m_ranges.push (range);
>  }
>
> --
> 1.8.5.3
>
>



More information about the Gcc-patches mailing list