From c92d1fa5623e7b7303ba6832b8ff7b46d1cb4508 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Fri, 10 Aug 2018 18:34:51 -0400
Subject: [PATCH 03/18] {NEEDS CHANGELOG, B&R, BLURB}: diagnostics: add labeled
 source ranges

---
 gcc/c-family/c-format.c                            | 179 +++++++++--
 gcc/c/c-objc-common.c                              | 124 +++++---
 gcc/c/c-typeck.c                                   | 154 ++++++---
 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/format/diagnostic-ranges.c    |  55 ++++
 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/lib/multiline.exp                    |  20 --
 gcc/toplev.c                                       |   2 +
 libcpp/include/line-map.h                          |  78 ++++-
 libcpp/line-map.c                                  |   9 +-
 40 files changed, 1364 insertions(+), 213 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..3f7e31a 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,
@@ -7021,32 +7027,38 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
 	 or one that results from arithmetic, even including
 	 a cast to integer type.  */
       if (!null_pointer_constant)
-	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);
-	    break;
-	  case ic_assign:
-	    pedwarn (location, OPT_Wint_conversion,
-		     "assignment to %qT from %qT makes pointer from integer "
-		     "without a cast", type, rhstype);
-	    break;
-	  case ic_init:
-	    pedwarn_init (location, OPT_Wint_conversion,
-			  "initialization of %qT from %qT makes pointer from "
-			  "integer without a cast", type, rhstype);
-	    break;
-	  case ic_return:
-	    pedwarn (location, OPT_Wint_conversion, "returning %qT from a "
-		     "function with return type %qT makes pointer from "
-		     "integer without a cast", rhstype, type);
-	    break;
-	  default:
-	    gcc_unreachable ();
-	  }
+	{
+	  switch (errtype)
+	    {
+	    case ic_argpass:
+	      {
+		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,
+		       "assignment to %qT from %qT makes pointer from integer "
+		       "without a cast", type, rhstype);
+	      break;
+	    case ic_init:
+	      pedwarn_init (location, OPT_Wint_conversion,
+			    "initialization of %qT from %qT makes pointer from "
+			    "integer without a cast", type, rhstype);
+	      break;
+	    case ic_return:
+	      pedwarn (location, OPT_Wint_conversion, "returning %qT from a "
+		       "function with return type %qT makes pointer from "
+		       "integer without a cast", rhstype, type);
+	      break;
+	    default:
+	      gcc_unreachable ();
+	    }
+	}
 
       return convert (type, rhs);
     }
@@ -7055,10 +7067,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 +7110,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 +11012,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 +11916,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 +12161,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 b35a7e9..a08555f 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 209c1fd..0ee70aa 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.  */
 
@@ -6736,8 +6737,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);
@@ -10742,8 +10748,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..41b8925 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 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 f43d3d9..36af1e8 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 0e9a9c3..760f6e3 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/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/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/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 a4baa49..2be2f6c 100644
--- a/libcpp/include/line-map.h
+++ b/libcpp/include/line-map.h
@@ -1320,8 +1320,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
 {
@@ -1337,6 +1340,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
@@ -1478,6 +1484,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.
 
@@ -1523,18 +1531,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
    ------------
@@ -1626,7 +1638,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 ();
@@ -1636,7 +1649,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);
@@ -1760,6 +1774,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 a5dc485..ab43a73 100644
--- a/libcpp/line-map.c
+++ b/libcpp/line-map.c
@@ -1977,7 +1977,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),
@@ -1986,7 +1987,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.  */
@@ -2062,11 +2063,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

