From 854f9ace03d65a512e224f43e809669c76e46010 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Mon, 31 Aug 2015 21:32:20 -0400
Subject: [PATCH 07/63] FIXME: Reimplement diagnostic_show_locus, introducing
 rich_location classes (compiles, no ChangeLog)

Rework diagnostic core to use rich_location *.
and colorize the [-Wfoo] suffix of warnings/errors, based on effective severity.
and add the show-locus unittesting plugin (including the plugin.exp change)
and add linemap_client_expand_location_to_spelling_point

Note to self:
  make -j64 && make check-gcc RUNTESTFLAGS="-v -v -v plugin.exp=diagnostic*"

DONE:
  * get tests to pass (needs 1st part of gcc-rich-location)
  * rename the testcases to ascii/utf-8
  * Fortran's numbered carets
  * testcases for color
  * cleanup choices of color

TODO
  * disable/kill "range_or_loc" (in line-map.h)
  * ensure that the new show-locus implementation can do everything
    that the old one could, including:
    * column offsetting for very wide source lines
  * clean up FIXMEs and preprocessor cruft
  * blurb
  * ChangeLog
---
 gcc/Makefile.in                                    |   3 +-
 gcc/box-drawing.c                                  |  99 +++
 gcc/box-drawing.h                                  |  43 +
 gcc/c-family/c-common.c                            |  15 +-
 gcc/c-family/c-common.h                            |   4 +-
 gcc/c/c-decl.c                                     |   3 +-
 gcc/c/c-errors.c                                   |  12 +-
 gcc/c/c-objc-common.c                              |   2 +-
 gcc/cp/error.c                                     |   5 +-
 gcc/diagnostic-color.c                             |   5 +-
 gcc/diagnostic-core.h                              |   8 +
 gcc/diagnostic-show-locus.c                        | 865 ++++++++++++++++++++-
 gcc/diagnostic.c                                   | 217 +++++-
 gcc/diagnostic.h                                   |  44 +-
 gcc/fortran/cpp.c                                  |  13 +-
 gcc/fortran/error.c                                |  40 +-
 gcc/gcc-rich-location.c                            |  86 ++
 gcc/gcc-rich-location.h                            |  54 ++
 gcc/genmatch.c                                     |  27 +-
 gcc/input.c                                        |   7 +
 gcc/intl.c                                         |  10 +
 gcc/pretty-print.c                                 |  21 +
 gcc/pretty-print.h                                 |  25 +-
 gcc/rtl-error.c                                    |   3 +-
 .../plugin/diagnostic-test-show-locus-ascii-bw.c   | 114 +++
 .../diagnostic-test-show-locus-ascii-color.c       |  35 +
 .../plugin/diagnostic-test-show-locus-utf-8-bw.c   |  58 ++
 .../diagnostic-test-show-locus-utf-8-color.c       |  62 ++
 .../plugin/diagnostic_plugin_test_show_locus.c     | 353 +++++++++
 gcc/testsuite/gcc.dg/plugin/plugin.exp             |   5 +
 gcc/testsuite/lib/gcc-dg.exp                       |   1 +
 gcc/tree-diagnostic.c                              |   2 +-
 gcc/tree-pretty-print.c                            |   2 +-
 libcpp/errors.c                                    |   8 +-
 libcpp/include/cpplib.h                            |   4 +-
 libcpp/include/line-map.h                          | 236 ++++++
 libcpp/line-map.c                                  | 196 +++++
 37 files changed, 2552 insertions(+), 135 deletions(-)
 create mode 100644 gcc/box-drawing.c
 create mode 100644 gcc/box-drawing.h
 create mode 100644 gcc/gcc-rich-location.c
 create mode 100644 gcc/gcc-rich-location.h
 create mode 100644 gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-ascii-bw.c
 create mode 100644 gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-ascii-color.c
 create mode 100644 gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-utf-8-bw.c
 create mode 100644 gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-utf-8-color.c
 create mode 100644 gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_show_locus.c

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index f0b10fe..5cfa06b 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1255,6 +1255,7 @@ OBJS = \
 	fold-const.o \
 	function.o \
 	fwprop.o \
+	gcc-rich-location.o \
 	gcse.o \
 	gcse-common.o \
 	ggc-common.o \
@@ -1516,7 +1517,7 @@ OBJS = \
 # Objects in libcommon.a, potentially used by all host binaries and with
 # no target dependencies.
 OBJS-libcommon = diagnostic.o diagnostic-color.o diagnostic-show-locus.o \
-	pretty-print.o intl.o \
+	box-drawing.o pretty-print.o intl.o \
 	vec.o input.o version.o hash-table.o ggc-none.o
 
 # Objects in libcommon-target.a, used by drivers and by the core
diff --git a/gcc/box-drawing.c b/gcc/box-drawing.c
new file mode 100644
index 0000000..af0619e
--- /dev/null
+++ b/gcc/box-drawing.c
@@ -0,0 +1,99 @@
+/* Box-drawing, either using Unicode box-drawing chars, or as pure ASCII
+   Copyright (C) 2015 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "box-drawing.h"
+
+/* Global singleton instance.  */
+box_drawing g_line_art;
+
+/* Strings for drawing the caret and ranges within diagnostics.
+   There are either UTF-8-encoded Unicode box-drawing characters,
+   or pure ASCII.  */
+
+/* Initializer for box_drawing.
+   This isn't a constructor since the singleton instance is
+   statically-allocated.  */
+void
+box_drawing::init (bool have_utf8)
+{
+  if (have_utf8)
+    {
+      /* For the caret, use:
+	   U+25B2 BLACK UP-POINTING TRIANGLE
+	 which in UTF-8 is: 0xE2 0x96 0xB2.  */
+      default_caret = "\xE2\x96\xB2";
+
+      /* Underlining text ranges.  */
+
+      /* The start of an underline: the south-western corner of a box:
+	   U+2514 BOX DRAWINGS LIGHT UP AND RIGHT
+	 in UTF-8: 0xE2 0x94 0x94.  */
+      underline_start = "\xE2\x94\x94";
+
+      /* Within an underline:
+	   U+2500 BOX DRAWINGS LIGHT HORIZONTAL
+	 which in UTF-8 is: 0xE2 0x94 0x80.  */
+      underline_hbar = "\xE2\x94\x80";
+
+      /* The end of an underline: the south-eastern corner of a box:
+	   U+2518 BOX DRAWINGS LIGHT UP AND LEFT
+	 UTF-8: 0xE2 0x94 0x98.  */
+      underline_end = "\xE2\x94\x98";
+
+      /* Vertical margins (when showing captions for ranges).  */
+
+      /* The top of a rmargin: the north-eastern corner of a box:
+	   U+2510 BOX DRAWINGS LIGHT DOWN AND LEFT
+	 UTF-8: 0xE2 0x94 0x90.  */
+      rmargin_start = "\xE2\x94\x90";
+
+      /* For vertical lines, use:
+	   U+2502 BOX DRAWINGS LIGHT VERTICAL
+	   which in UTF-8 is: 0xE2 0x94 0x82.  */
+      rmargin_vbar = "\xE2\x94\x82";
+
+      /* For the row within the rmargin containing the caption:
+	   U+251C BOX DRAWINGS LIGHT VERTICAL AND RIGHT
+	 UTF-8: 0xE2 0x94 0x9C.  */
+      rmargin_caption_row = "\xE2\x94\x9C";
+
+      /* Unless its on the very last line, in which case:
+	   U+2534 BOX DRAWINGS LIGHT UP AND HORIZONTAL
+	 UTF-8: 0xE2 0x94 0xB4.  */
+      rmargin_caption_row_at_end = "\xE2\x94\xB4";
+
+      /* The end of a rmargin: the south-eastern corner of a box:
+	   U+2518 BOX DRAWINGS LIGHT UP AND LEFT
+	 UTF-8: 0xE2 0x94 0x98.  */
+      rmargin_end = "\xE2\x94\x98";
+    }
+  else
+    {
+      default_caret = "^";
+      underline_start = underline_hbar = underline_end = "~";
+
+      rmargin_start = "|";
+      rmargin_vbar = "|";
+      rmargin_caption_row = rmargin_caption_row_at_end = "+";
+      rmargin_end = "|";
+    }
+}
diff --git a/gcc/box-drawing.h b/gcc/box-drawing.h
new file mode 100644
index 0000000..7fef108
--- /dev/null
+++ b/gcc/box-drawing.h
@@ -0,0 +1,43 @@
+/* Box-drawing, either using Unicode box-drawing chars, or as pure ASCII
+   Copyright (C) 2015 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+/* Strings for drawing the caret and ranges within diagnostics.
+   There are either UTF-8-encoded Unicode box-drawing characters,
+   or pure ASCII.  */
+
+class box_drawing
+{
+ public:
+  void init (bool have_utf8);
+
+  const char *default_caret;
+  const char *underline_start;
+  const char *underline_hbar;
+  const char *underline_end;
+
+  /* Right-hand-side margins.  */
+  const char *rmargin_start;
+  const char *rmargin_vbar;
+  const char *rmargin_caption_row;
+  const char *rmargin_caption_row_at_end;
+  const char *rmargin_end;
+};
+
+/* Singleton instance.  */
+extern box_drawing g_line_art;
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index a8e5353..4c70a73 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -10449,15 +10449,14 @@ c_option_controlling_cpp_error (int reason)
 /* Callback from cpp_error for PFILE to print diagnostics from the
    preprocessor.  The diagnostic is of type LEVEL, with REASON set
    to the reason code if LEVEL is represents a warning, at location
-   LOCATION unless this is after lexing and the compiler's location
-   should be used instead, with column number possibly overridden by
-   COLUMN_OVERRIDE if not zero; MSG is the translated message and AP
+   RICHLOC unless this is after lexing and the compiler's location
+   should be used instead; MSG is the translated message and AP
    the arguments.  Returns true if a diagnostic was emitted, false
    otherwise.  */
 
 bool
 c_cpp_error (cpp_reader *pfile ATTRIBUTE_UNUSED, int level, int reason,
-	     location_t location, unsigned int column_override,
+	     rich_location *richloc,
 	     const char *msg, va_list *ap)
 {
   diagnostic_info diagnostic;
@@ -10498,11 +10497,11 @@ c_cpp_error (cpp_reader *pfile ATTRIBUTE_UNUSED, int level, int reason,
       gcc_unreachable ();
     }
   if (done_lexing)
-    location = input_location;
+    richloc->set_range (0,
+			source_range::from_location (input_location),
+			true);
   diagnostic_set_info_translated (&diagnostic, msg, ap,
-				  location, dlevel);
-  if (column_override)
-    diagnostic_override_column (&diagnostic, column_override);
+				  richloc, dlevel);
   diagnostic_override_option_index (&diagnostic,
                                     c_option_controlling_cpp_error (reason));
   ret = report_diagnostic (&diagnostic);
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index ff74e53..9f37f92 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -974,9 +974,9 @@ extern void init_c_lex (void);
 
 extern void c_cpp_builtins (cpp_reader *);
 extern void c_cpp_builtins_optimize_pragma (cpp_reader *, tree, tree);
-extern bool c_cpp_error (cpp_reader *, int, int, location_t, unsigned int,
+extern bool c_cpp_error (cpp_reader *, int, int, rich_location *,
 			 const char *, va_list *)
-     ATTRIBUTE_GCC_DIAG(6,0);
+     ATTRIBUTE_GCC_DIAG(5,0);
 extern int c_common_has_attribute (cpp_reader *);
 
 extern bool parse_optimize_options (tree, bool);
diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c
index 0d7aa3f..b86d6c7 100644
--- a/gcc/c/c-decl.c
+++ b/gcc/c/c-decl.c
@@ -5273,9 +5273,10 @@ warn_defaults_to (location_t location, int opt, const char *gmsgid, ...)
 {
   diagnostic_info diagnostic;
   va_list ap;
+  rich_location richloc (location);
 
   va_start (ap, gmsgid);
-  diagnostic_set_info (&diagnostic, gmsgid, &ap, location,
+  diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc,
                        flag_isoc99 ? DK_PEDWARN : DK_WARNING);
   diagnostic.option_index = opt;
   report_diagnostic (&diagnostic);
diff --git a/gcc/c/c-errors.c b/gcc/c/c-errors.c
index e5fbf05..0f8b933 100644
--- a/gcc/c/c-errors.c
+++ b/gcc/c/c-errors.c
@@ -42,13 +42,14 @@ pedwarn_c99 (location_t location, int opt, const char *gmsgid, ...)
   diagnostic_info diagnostic;
   va_list ap;
   bool warned = false;
+  rich_location richloc (location);
 
   va_start (ap, gmsgid);
   /* If desired, issue the C99/C11 compat warning, which is more specific
      than -pedantic.  */
   if (warn_c99_c11_compat > 0)
     {
-      diagnostic_set_info (&diagnostic, gmsgid, &ap, location,
+      diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc,
 			   (pedantic && !flag_isoc11)
 			   ? DK_PEDWARN : DK_WARNING);
       diagnostic.option_index = OPT_Wc99_c11_compat;
@@ -60,7 +61,7 @@ pedwarn_c99 (location_t location, int opt, const char *gmsgid, ...)
   /* For -pedantic outside C11, issue a pedwarn.  */
   else if (pedantic && !flag_isoc11)
     {
-      diagnostic_set_info (&diagnostic, gmsgid, &ap, location, DK_PEDWARN);
+      diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_PEDWARN);
       diagnostic.option_index = opt;
       warned = report_diagnostic (&diagnostic);
     }
@@ -80,6 +81,7 @@ pedwarn_c90 (location_t location, int opt, const char *gmsgid, ...)
 {
   diagnostic_info diagnostic;
   va_list ap;
+  rich_location richloc (location);
 
   va_start (ap, gmsgid);
   /* Warnings such as -Wvla are the most specific ones.  */
@@ -90,7 +92,7 @@ pedwarn_c90 (location_t location, int opt, const char *gmsgid, ...)
         goto out;
       else if (opt_var > 0)
 	{
-	  diagnostic_set_info (&diagnostic, gmsgid, &ap, location,
+	  diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc,
 			       (pedantic && !flag_isoc99)
 			       ? DK_PEDWARN : DK_WARNING);
 	  diagnostic.option_index = opt;
@@ -102,7 +104,7 @@ pedwarn_c90 (location_t location, int opt, const char *gmsgid, ...)
      specific than -pedantic.  */
   if (warn_c90_c99_compat > 0)
     {
-      diagnostic_set_info (&diagnostic, gmsgid, &ap, location,
+      diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc,
 			   (pedantic && !flag_isoc99)
 			   ? DK_PEDWARN : DK_WARNING);
       diagnostic.option_index = OPT_Wc90_c99_compat;
@@ -114,7 +116,7 @@ pedwarn_c90 (location_t location, int opt, const char *gmsgid, ...)
   /* For -pedantic outside C99, issue a pedwarn.  */
   else if (pedantic && !flag_isoc99)
     {
-      diagnostic_set_info (&diagnostic, gmsgid, &ap, location, DK_PEDWARN);
+      diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_PEDWARN);
       diagnostic.option_index = opt;
       report_diagnostic (&diagnostic);
     }
diff --git a/gcc/c/c-objc-common.c b/gcc/c/c-objc-common.c
index 47fd7de..1e601f9 100644
--- a/gcc/c/c-objc-common.c
+++ b/gcc/c/c-objc-common.c
@@ -101,7 +101,7 @@ c_tree_printer (pretty_printer *pp, text_info *text, const char *spec,
     {
       t = va_arg (*text->args_ptr, tree);
       if (set_locus)
-	text->set_location (0, DECL_SOURCE_LOCATION (t));
+	text->set_location (0, DECL_SOURCE_LOCATION (t), true);
     }
 
   switch (*spec)
diff --git a/gcc/cp/error.c b/gcc/cp/error.c
index b811df2..8607e44 100644
--- a/gcc/cp/error.c
+++ b/gcc/cp/error.c
@@ -3502,7 +3502,7 @@ cp_printer (pretty_printer *pp, text_info *text, const char *spec,
 
   pp_string (pp, result);
   if (set_locus && t != NULL)
-    text->set_location (0, location_of (t));
+    text->set_location (0, location_of (t), true);
   return true;
 #undef next_tree
 #undef next_tcode
@@ -3616,9 +3616,10 @@ pedwarn_cxx98 (location_t location, int opt, const char *gmsgid, ...)
   diagnostic_info diagnostic;
   va_list ap;
   bool ret;
+  rich_location richloc (location);
 
   va_start (ap, gmsgid);
-  diagnostic_set_info (&diagnostic, gmsgid, &ap, location,
+  diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc,
 		       (cxx_dialect == cxx98) ? DK_PEDWARN : DK_WARNING);
   diagnostic.option_index = opt;
   ret = report_diagnostic (&diagnostic);
diff --git a/gcc/diagnostic-color.c b/gcc/diagnostic-color.c
index 3fe49b2..d848dfc 100644
--- a/gcc/diagnostic-color.c
+++ b/gcc/diagnostic-color.c
@@ -164,7 +164,8 @@ static struct color_cap color_dict[] =
   { "warning", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_MAGENTA),
 	       7, false },
   { "note", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_CYAN), 4, false },
-  { "caret", SGR_SEQ (COLOR_BOLD COLOR_SEPARATOR COLOR_FG_GREEN), 5, false },
+  { "range1", SGR_SEQ (COLOR_FG_GREEN), 6, false },
+  { "range2", SGR_SEQ (COLOR_FG_BLUE), 6, false },
   { "locus", SGR_SEQ (COLOR_BOLD), 5, false },
   { "quote", SGR_SEQ (COLOR_BOLD), 5, false },
   { NULL, NULL, 0, false }
@@ -195,7 +196,7 @@ colorize_stop (bool show_color)
 }
 
 /* Parse GCC_COLORS.  The default would look like:
-   GCC_COLORS='error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01'
+   GCC_COLORS='error=01;31:warning=01;35:note=01;36:range1=32:range2=34;locus=01:quote=01'
    No character escaping is needed or supported.  */
 static bool
 parse_gcc_colors (void)
diff --git a/gcc/diagnostic-core.h b/gcc/diagnostic-core.h
index 66d2e42..a8a7c37 100644
--- a/gcc/diagnostic-core.h
+++ b/gcc/diagnostic-core.h
@@ -63,18 +63,26 @@ extern bool warning_n (location_t, int, int, const char *, const char *, ...)
     ATTRIBUTE_GCC_DIAG(4,6) ATTRIBUTE_GCC_DIAG(5,6);
 extern bool warning_at (location_t, int, const char *, ...)
     ATTRIBUTE_GCC_DIAG(3,4);
+extern bool warning_at_rich_loc (rich_location *, int, const char *, ...)
+    ATTRIBUTE_GCC_DIAG(3,4);
 extern void error (const char *, ...) ATTRIBUTE_GCC_DIAG(1,2);
 extern void error_n (location_t, int, const char *, const char *, ...)
     ATTRIBUTE_GCC_DIAG(3,5) ATTRIBUTE_GCC_DIAG(4,5);
 extern void error_at (location_t, const char *, ...) ATTRIBUTE_GCC_DIAG(2,3);
+extern void error_at_rich_loc (rich_location *, const char *, ...)
+  ATTRIBUTE_GCC_DIAG(2,3);
 extern void fatal_error (location_t, const char *, ...) ATTRIBUTE_GCC_DIAG(2,3)
      ATTRIBUTE_NORETURN;
 /* Pass one of the OPT_W* from options.h as the second parameter.  */
 extern bool pedwarn (location_t, int, const char *, ...)
      ATTRIBUTE_GCC_DIAG(3,4);
 extern bool permerror (location_t, const char *, ...) ATTRIBUTE_GCC_DIAG(2,3);
+extern bool permerror_at_rich_loc (rich_location *, const char *,
+				   ...) ATTRIBUTE_GCC_DIAG(2,3);
 extern void sorry (const char *, ...) ATTRIBUTE_GCC_DIAG(1,2);
 extern void inform (location_t, const char *, ...) ATTRIBUTE_GCC_DIAG(2,3);
+extern void inform_at_rich_loc (rich_location *, const char *,
+				...) ATTRIBUTE_GCC_DIAG(2,3);
 extern void inform_n (location_t, int, const char *, const char *, ...)
     ATTRIBUTE_GCC_DIAG(3,5) ATTRIBUTE_GCC_DIAG(4,5);
 extern void verbatim (const char *, ...) ATTRIBUTE_GCC_DIAG(1,2);
diff --git a/gcc/diagnostic-show-locus.c b/gcc/diagnostic-show-locus.c
index 147a2b8..60013e8 100644
--- a/gcc/diagnostic-show-locus.c
+++ b/gcc/diagnostic-show-locus.c
@@ -27,6 +27,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "backtrace.h"
 #include "diagnostic.h"
 #include "diagnostic-color.h"
+#include "box-drawing.h"
 
 #ifdef HAVE_TERMIOS_H
 # include <termios.h>
@@ -60,6 +61,805 @@ adjust_line (const char *line, int line_width,
   return line;
 }
 
+/* Is (column, row) within the given range?
+
+   Ranges are closed (both limits are within the range).
+
+   Example A: a single-line range:
+     start:  (col=22, line=2)
+     finish: (col=38, line=2)
+
+  |00000011111111112222222222333333333344444444444
+  |34567890123456789012345678901234567890123456789
+--+-----------------------------------------------
+01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+02|bbbbbbbbbbbbbbbbbbbSwwwwwwwwwwwwwwwFaaaaaaaaaaa
+03|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+
+   Example B: a multiline range with
+     start:  (col=14, line=3)
+     finish: (col=08, line=5)
+
+  |00000011111111112222222222333333333344444444444
+  |34567890123456789012345678901234567890123456789
+--+-----------------------------------------------
+01|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+02|bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+03|bbbbbbbbbbbSwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
+04|wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
+05|wwwwwFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+06|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+--+-----------------------------------------------
+
+   Legend:
+   - 'b' indicates a point *before* the range
+   - 'S' indicates the start of the range
+   - 'w' indicates a point within the range
+   - 'F' indicates the finish of the range (which is
+	 within it).
+   - 'a' indicates a subsequent point *after* the range.  */
+
+bool
+location_range::contains_point (int row, int column) const
+{
+  gcc_assert (m_start.line <= m_finish.line);
+  /* ...but the equivalent isn't true for the columns;
+     consider example B in the comment above.  */
+
+  if (row < m_start.line)
+    /* Points before the first line of the range are
+       outside it (corresponding to line 01 in example A
+       and lines 01 and 02 in example B above).  */
+    return false;
+
+  if (row == m_start.line)
+    /* On same line as start of range (corresponding
+       to line 02 in example A and line 03 in example B).  */
+    {
+      if (column < m_start.column)
+	/* Points on the starting line of the range, but
+	   before the column in which it begins.  */
+	return false;
+
+      if (row < m_finish.line)
+	/* This is a multiline range; the point
+	   is within it (corresponds to line 03 in example B
+	   from column 14 onwards) */
+	return true;
+      else
+	{
+	  /* This is a single-line range.  */
+	  gcc_assert (row == m_finish.line);
+	  return column <= m_finish.column;
+	}
+    }
+
+  /* The point is in a line beyond that containing the
+     start of the range: lines 03 onwards in example A,
+     and lines 04 onwards in example B.  */
+  gcc_assert (row > m_start.line);
+
+  if (row > m_finish.line)
+    /* The point is beyond the final line of the range
+       (lines 03 onwards in example A, and lines 06 onwards
+       in example B).  */
+    return false;
+
+  if (row < m_finish.line)
+    {
+      /* The point is in a line that's fully within a multiline
+	 range (e.g. line 04 in example B).  */
+      gcc_assert (m_start.line < m_finish.line);
+      return true;
+    }
+
+  gcc_assert (row ==  m_finish.line);
+
+  return column <= m_finish.column;
+}
+
+/* Return true if (ROW/COLUMN) is within a range of RICHLOC.
+   If it returns true, OUT_RANGE_IDX and OUT_DRAW_CARET_P are
+   written to, with the range index, and whether we should draw
+   the caret at (ROW/COLUMN) (as opposed to an underline).  */
+
+static bool
+get_state_at_point (/* Inputs.  */
+		    int row, int column, rich_location *richloc,
+		    int first_non_ws, int last_non_ws,
+		    /* Outputs.  */
+		    int *out_range_idx,
+		    bool *out_draw_caret_p)
+{
+  /* Within a multiline range, don't display any underline or caret
+     in any leading or trailing whitespace on a line.  */
+  if (column < first_non_ws || column > last_non_ws)
+    return false;
+
+  for (rich_location::range_iter iter = richloc->iter_ranges ();
+       !iter.at_end ();
+       iter.next())
+    {
+      const location_range *range = *iter;
+
+      if (0)
+	fprintf (stderr,
+		 "range ( (%i, %i), (%i, %i))->contains_point (%i, %i): %s\n",
+		 range->m_start.line,
+		 range->m_start.column,
+		 range->m_finish.line,
+		 range->m_finish.column,
+		 row,
+		 column,
+		 range->contains_point (row, column) ? "true" : "false");
+
+      if (range->contains_point (row, column))
+	{
+	  *out_range_idx = iter.index();
+
+	  /* Are we at the range's caret?  is it visible? */
+	  *out_draw_caret_p = false;
+	  if (row == range->m_start.line
+	      && column == range->m_start.column)
+	    *out_draw_caret_p = range->m_show_caret_p;
+
+	  /* We are within a range.  */
+	  return true;
+	}
+    }
+
+  return false;
+}
+
+/* Get the column beyond the rightmost one that could contain a caret or
+   range marker, given that we stop rendering at trailing whitespace.  */
+
+static int
+get_x_bound_for_row (int row, int caret_column,
+		     rich_location *richloc,
+		     int last_non_ws)
+{
+  int result = caret_column + 1;
+
+  for (rich_location::range_iter iter = richloc->iter_ranges ();
+       !iter.at_end ();
+       iter.next())
+    {
+      const location_range *range = *iter;
+      if (row >= range->m_start.line)
+	{
+	  if (range->m_finish.line == row)
+	    {
+	      /* On the final line within a range; ensure that
+		 we render up to the end of the range.  */
+	      if (result <= range->m_finish.column)
+		result = range->m_finish.column + 1;
+	    }
+	  else if (row < range->m_finish.line)
+	    {
+	      /* Within a multiline range; ensure that we render up to the
+		 last non-whitespace column.  */
+	      if (result <= last_non_ws)
+		result = last_non_ws + 1;
+	    }
+	}
+    }
+
+  return result;
+}
+
+/* Classes for rendering source code and diagnostics, within an
+   anonymous namespace.
+   The work is done by "class layout", which embeds and uses
+   "class colorizer" and "class per_range_info" to get things done.  */
+
+namespace {
+
+/* A class to inject colorization codes when printing the diagnostic locus,
+   tracking state as it goes.  */
+
+class colorizer
+{
+ public:
+  colorizer (diagnostic_context *context,
+	     const diagnostic_info *diagnostic);
+  ~colorizer ();
+
+  void set_range (int range_idx) { set_state (range_idx); }
+  void set_normal_text () { set_state (STATE_NORMAL_TEXT); }
+
+ private:
+  void set_state (int state);
+  void begin_state (int state);
+  void finish_state (int state);
+
+ private:
+  static const int STATE_NORMAL_TEXT = -1;
+
+  diagnostic_context *m_context;
+  const diagnostic_info *m_diagnostic;
+  int m_current_state;
+  const char *m_caret_cs;
+  const char *m_caret_ce;
+  const char *m_range1_cs;
+  const char *m_range2_cs;
+  const char *m_range_ce;
+};
+
+/* A class for use by "class layout" below for capturing information on
+   how to display a specific range within a rich_location.  */
+
+class per_range_info
+{
+ public:
+  per_range_info (int range_idx);
+
+  int get_range_index () const { return m_range_idx; }
+
+  void add_uniquely_captioned_row (int row);
+
+  int get_first_unique_row () const;
+  int get_last_unique_row () const;
+
+  bool contains_line (int line) const;
+
+  void determine_location_for_caption (const char *filename);
+
+  int get_caption_row () const { return m_caption_row; }
+  int get_caption_column () const { return m_caption_column; }
+
+ private:
+  int m_range_idx;
+  auto_vec<int> m_unique_rows;
+  int m_caption_row;
+  int m_caption_column;
+};
+
+/* A class to control the overall layout of a diagnostic.
+
+   The layout is determined within the constructor.
+   It is then printed by repeatedly calling the "print_line" method.
+   Each such call can print two lines: one for the source line itself,
+   and potentially an "annotation" line, containing carets/underlines.
+   Both such lines could be printed with a right-hand margin, containing
+   additional information.
+
+   For now, we just support the case of a single margin at once,
+   showing any captioned ranges that exclusively occupy their lines.
+
+   We also assume we have disjoint ranges.  */
+
+class layout
+{
+ public:
+  layout (diagnostic_context *context,
+	  const diagnostic_info *diagnostic);
+
+  void print_line (int row);
+
+ private:
+  enum print_line_kind {
+    PRINT_LINE_KIND_SOURCE,
+    PRINT_LINE_KIND_ANNOTATION};
+
+ private:
+  per_range_info *
+  get_any_range (int line);
+
+  void
+  print_any_margin (int line, int column, enum print_line_kind kind);
+
+ private:
+  diagnostic_context *m_context;
+  pretty_printer *m_pp;
+  diagnostic_t m_diagnostic_kind;
+  rich_location *m_richloc;
+  expanded_location m_exploc;
+  colorizer m_colorizer;
+  auto_vec <per_range_info> m_per_range_vec;
+};
+
+/* Implementation of "class colorizer".  */
+
+/* The constructor for "colorizer".  Lookup and store color codes for the
+   different kinds of things we might need to print.  */
+
+colorizer::colorizer (diagnostic_context *context,
+		      const diagnostic_info *diagnostic) :
+  m_context (context),
+  m_diagnostic (diagnostic),
+  m_current_state (STATE_NORMAL_TEXT)
+{
+  m_caret_ce = colorize_stop (pp_show_color (context->printer));
+  m_range1_cs = colorize_start (pp_show_color (context->printer), "range1");
+  m_range2_cs = colorize_start (pp_show_color (context->printer), "range2");
+  m_range_ce = colorize_stop (pp_show_color (context->printer));
+}
+
+/* The destructor for "colorize".  If colorization is on, print a code to
+   turn it off.  */
+
+colorizer::~colorizer ()
+{
+  finish_state (m_current_state);
+}
+
+/* Update state, printing color codes if necessary if there's a state
+   change.  */
+
+void
+colorizer::set_state (int new_state)
+{
+  if (m_current_state != new_state)
+    {
+      finish_state (m_current_state);
+      m_current_state = new_state;
+      begin_state (new_state);
+    }
+}
+
+/* Turn on any colorization for STATE.  */
+
+void
+colorizer::begin_state (int state)
+{
+  switch (state)
+    {
+    case STATE_NORMAL_TEXT:
+      break;
+
+    case 0:
+      /* Make range 0 be the same color as the "kind" text
+	 (error vs warning vs note).  */
+      pp_string
+	(m_context->printer,
+	 colorize_start (pp_show_color (m_context->printer),
+			 diagnostic_get_color_for_kind (m_diagnostic->kind)));
+      break;
+
+    case 1:
+      pp_string (m_context->printer, m_range1_cs);
+      break;
+
+    case 2:
+      pp_string (m_context->printer, m_range2_cs);
+      break;
+
+    default:
+      /* We don't expect more than 3 ranges per diagnostic.  */
+      gcc_unreachable ();
+      break;
+    }
+}
+
+/* Turn off any colorization for STATE.  */
+
+void
+colorizer::finish_state (int state)
+{
+  switch (state)
+    {
+    case STATE_NORMAL_TEXT:
+      break;
+
+    case 0:
+      pp_string (m_context->printer, m_caret_ce);
+      break;
+
+    default:
+      /* Within a range.  */
+      gcc_assert (state > 0);
+      pp_string (m_context->printer, m_range_ce);
+      break;
+    }
+}
+
+/* Implementation of class per_range_info.  */
+
+/* The constructor for class per_range_info.  */
+
+per_range_info::per_range_info (int range_idx)
+: m_range_idx (range_idx),
+  m_unique_rows ()
+{
+}
+
+/* FIXME.  */
+
+void
+per_range_info::add_uniquely_captioned_row (int row)
+{
+  m_unique_rows.safe_push (row);
+}
+
+/* FIXME.  */
+
+int
+per_range_info::get_first_unique_row () const
+{
+  gcc_assert (m_unique_rows.length () > 0);
+  return m_unique_rows[0];
+}
+
+/* FIXME.  */
+
+int
+per_range_info::get_last_unique_row () const
+{
+  gcc_assert (m_unique_rows.length () > 0);
+
+  return m_unique_rows[m_unique_rows.length () - 1];
+}
+
+/* FIXME.  */
+
+bool
+per_range_info::contains_line (int line) const
+{
+  if (0 == m_unique_rows.length ())
+    return false;
+
+  return (line >= get_first_unique_row ()
+	  && line <= get_last_unique_row ());
+}
+
+/* FIXME.  */
+
+static int
+get_line_width_without_trailing_whitespace (const char *line, int line_width)
+{
+  int result = line_width;
+  while (result > 0)
+    {
+      char ch = line[result - 1];
+      if (ch == ' ' || ch == '\t')
+	result--;
+      else
+	break;
+    }
+  gcc_assert (result >= 0);
+  gcc_assert (result <= line_width);
+  gcc_assert (result == 0 ||
+	      (line[result - 1] != ' '
+	       && line[result -1] != '\t'));
+  return result;
+}
+
+/* FIXME.  */
+
+void
+per_range_info::determine_location_for_caption (const char *filename)
+{
+  if (0 == m_unique_rows.length ())
+    return;
+
+  /* Determine the widest line, using it as the column in which to render
+     the caption (m_caption_column).  */
+  int result = 0;
+  for (int row = get_first_unique_row ();
+       row <= get_last_unique_row ();
+       row++)
+    {
+      int line_width;
+      const char *line = location_get_source_line (filename, row, &line_width);
+      line_width = get_line_width_without_trailing_whitespace (line,
+							       line_width);
+      if (result < line_width)
+	result = line_width + 1;
+    }
+  m_caption_column = result;
+
+  /* Determine which row to write the caption text in: the middle row
+     within the range, rounding down mathematically (and thus up
+     visually).  */
+  m_caption_row = (get_first_unique_row () + get_last_unique_row ()) / 2;
+}
+
+/* Implementation of class layout.  */
+
+/* Constructor for class layout.
+   FIXME.  */
+
+layout::layout (diagnostic_context * context,
+		const diagnostic_info *diagnostic)
+: m_context (context),
+  m_pp (context->printer),
+  m_diagnostic_kind (diagnostic->kind),
+  m_richloc (diagnostic->richloc),
+  m_exploc (m_richloc->lazily_expand_location ()),
+  m_colorizer (context, diagnostic),
+  m_per_range_vec ()
+{
+  for (rich_location::range_iter iter = m_richloc->iter_ranges ();
+       !iter.at_end ();
+       iter.next())
+    {
+      per_range_info ri (iter.index ());
+      m_per_range_vec.safe_push (ri);
+    }
+
+  /* For each row, determine if it has a unique captioned range.  */
+  int last_line = m_richloc->get_last_line ();
+  for (int row = m_richloc->get_first_line ();
+       row <= last_line;
+       row++)
+    {
+      int num_captions_in_row = 0;
+      const location_range *first_caption = NULL;
+      int first_caption_idx = 0; /* silence "maybe-uninitialized" warning
+				    (PR 67196).  */
+      for (rich_location::range_iter iter = m_richloc->iter_ranges ();
+	   !iter.at_end ();
+	   iter.next())
+	{
+	  const location_range *range = *iter;
+	  if (range->m_caption)
+	    {
+	      if (row >= range->m_start.line
+		  && row <= range->m_finish.line)
+		{
+		  num_captions_in_row++;
+		  if (!first_caption)
+		    {
+		      first_caption = range;
+		      first_caption_idx = iter.index ();
+		    }
+		}
+	    }
+	}
+      if (first_caption && num_captions_in_row == 1)
+	m_per_range_vec[first_caption_idx].add_uniquely_captioned_row (row);
+    }
+
+  /* At this stage, each per_range_info now has a list of lines for which its
+     location_range uniquely captions that source line.  */
+
+  /* Next, calculate the best position in which to draw each caption.  */
+  per_range_info *ri;
+  int i;
+  FOR_EACH_VEC_ELT (m_per_range_vec, i, ri)
+    ri->determine_location_for_caption (m_richloc->get_range (i)->m_start.file);
+}
+
+/* Print text describing a line of source code.
+   This typically prints two lines:
+
+   (1) the source code itself, colorized at any ranges, and
+   (2) an annotation line containing any carets/underlines
+   describing the ranges.
+
+   Both lines (1) and (2) may contain a right-most margin containing a
+   vertical bar and a caption, describing a range.  */
+
+void
+layout::print_line (int row)
+{
+  int line_width;
+  const char *line = location_get_source_line (m_exploc.file, row,
+					       &line_width);
+  if (!line)
+    return;
+
+  m_colorizer.set_normal_text ();
+
+  /* Step 1: print the source code line.  */
+
+  /* We will stop printing at any trailing whitespace.  */
+  line_width
+    = get_line_width_without_trailing_whitespace (line,
+						  line_width);
+  pp_space (m_pp);
+  int first_non_ws = INT_MAX;
+  int last_non_ws = 0;
+  int column;
+  for (column = 1; column <= line_width; column++)
+    {
+      bool in_range_p;
+      int range_idx;
+      bool draw_caret_p;
+      in_range_p = get_state_at_point (row, column,
+				       m_richloc,
+				       0, INT_MAX,
+				       &range_idx, &draw_caret_p);
+      if (in_range_p)
+	m_colorizer.set_range (range_idx);
+      else
+	m_colorizer.set_normal_text ();
+      char c = *line == '\t' ? ' ' : *line;
+      if (c == '\0')
+	c = ' ';
+      if (c != ' ')
+	{
+	  last_non_ws = column;
+	  if (first_non_ws == INT_MAX)
+	    first_non_ws = column;
+	}
+      pp_character (m_pp, c);
+      line++;
+    }
+  print_any_margin (row, column, PRINT_LINE_KIND_SOURCE);
+
+  pp_newline (m_pp);
+
+  /* Step 2: print a line consisting of the caret/underlines for the
+     given source line.  */
+  int x_bound = get_x_bound_for_row (row, m_exploc.column,
+				     m_richloc,
+				     last_non_ws);
+
+  pp_space (m_pp);
+  for (int column = 1; column < x_bound; column++)
+    {
+      bool in_range_p;
+      int range_idx;
+      bool draw_caret_p;
+      in_range_p = get_state_at_point (row, column,
+				       m_richloc,
+				       first_non_ws, last_non_ws,
+				       &range_idx, &draw_caret_p);
+      if (in_range_p)
+	{
+	  /* Within a range.  Draw either the caret or an underline.  */
+	  m_colorizer.set_range (range_idx);
+	  if (draw_caret_p)
+	    /* Draw the caret.  */
+	    pp_string (m_pp, m_context->caret_chars[range_idx]);
+	  else
+	    {
+	      /* Within a range, but not drawing the caret.  Draw an underline
+		 character. */
+	      const location_range *range
+		= m_richloc->get_range (range_idx);
+	      if (range->m_start.line != range->m_finish.line)
+		{
+		  /* Underline multiline ranges using southwest/southeast
+		     corners for the very first/last positions, and hbar
+		     everywhere else.  */
+		  if (row == range->m_start.line
+		      && column == range->m_start.column)
+		    /* Start of multiline range: use a southwest corner for
+		       the underline.  */
+		    pp_string (m_pp, g_line_art.underline_start);
+		  else if (row == range->m_finish.line
+			   && column == range->m_finish.column)
+		    /* End of multiline range: use a southeast corner for
+		       the underline.  */
+		    pp_string (m_pp, g_line_art.underline_end);
+		  else
+		    pp_string (m_pp, g_line_art.underline_hbar);
+		}
+	      else
+		/* Render single-line ranges with the hbar character
+		   throughout.  */
+		pp_string (m_pp, g_line_art.underline_hbar);
+	    }
+	}
+      else
+	{
+	  /* Not in a range.  */
+	  m_colorizer.set_normal_text ();
+	  pp_character (m_pp, ' ');
+	}
+    }
+  print_any_margin (row, x_bound, PRINT_LINE_KIND_ANNOTATION);
+  pp_newline (m_pp);
+}
+
+/* Given a line of source code, get the per_range_info for the first range
+   within it, or NULL if there are no ranges.  */
+
+per_range_info *
+layout::get_any_range (int line)
+{
+  int i;
+  per_range_info *ri;
+  FOR_EACH_VEC_ELT (m_per_range_vec, i, ri)
+    if (ri->contains_line (line))
+      return ri;
+  return NULL;
+}
+
+/* Helper function for layout::print_line, to print the right-most margin
+   area (for both kinds of line, source and caret/underline).  */
+
+void
+layout::print_any_margin (int line, int column, enum print_line_kind kind)
+{
+  /* Locate the range layout for this line, if any.  */
+  per_range_info *ri = get_any_range (line);
+
+  /* If we're not in a range, there's no margin.  */
+  if (!ri)
+    return;
+
+  /* Fill with whitespace to get to the appropriate y-coordinate for
+     the margin.  */
+  while (column++ < ri->get_caption_column ())
+    pp_space (m_pp);
+
+  /* Colorize based on the range.  */
+  int range_idx = ri->get_range_index ();
+  m_colorizer.set_range (range_idx);
+
+  /* Draw a vertical line, with corners pointing to the left,
+     (falling back to '|' if unicode box-drawing is not available),
+     printing any caption on the *annotation* line for the
+     appropriate source line*/
+
+  if (line == ri->get_caption_row ()
+      && kind == PRINT_LINE_KIND_ANNOTATION)
+    {
+      /* We are on the annotation line of the
+	 appropriate source line to print the caption.  */
+
+      /* Draw the line before the caption.  */
+      pp_string (m_pp,
+		 line == ri->get_last_unique_row ()
+		 ? g_line_art.rmargin_caption_row_at_end
+		 : g_line_art.rmargin_caption_row);
+
+      location_range *range
+	= m_richloc->get_range (ri->get_range_index ());
+      gcc_assert (range);
+
+     /* Display any caption.  */
+      if (range->m_caption)
+	pp_string (m_pp, range->m_caption);
+    }
+  else if (line == ri->get_first_unique_row ()
+	   && kind == PRINT_LINE_KIND_SOURCE)
+    /* Start of multiline range.  */
+    pp_string (m_pp, g_line_art.rmargin_start);
+  else if (line == ri->get_last_unique_row ()
+	   && kind == PRINT_LINE_KIND_ANNOTATION)
+    /* End of multiline range.  */
+    pp_string (m_pp, g_line_art.rmargin_end);
+  else
+    /* A normal row: a vertical line.  */
+    pp_string (m_pp, g_line_art.rmargin_vbar);
+}
+
+} /* End of anonymous namespace.  */
+
+/* For debugging layout issues in diagnostic_show_locus and friends,
+   render a ruler giving column numbers (after the 1-column indent).  */
+
+static void
+show_ruler (diagnostic_context *context, int max_width)
+{
+  /* Hundreds.  */
+  if (max_width > 99)
+    {
+      pp_space (context->printer);
+      for (int column = 1; column < max_width; column++)
+	if (0 == column % 10)
+	  pp_character (context->printer, '0' + (column / 100) % 10);
+	else
+	  pp_space (context->printer);
+      pp_newline (context->printer);
+    }
+
+  /* Tens.  */
+  pp_space (context->printer);
+  for (int column = 1; column < max_width; column++)
+    if (0 == column % 10)
+      pp_character (context->printer, '0' + (column / 10) % 10);
+    else
+      pp_space (context->printer);
+  pp_newline (context->printer);
+
+  /* Units.  */
+  pp_space (context->printer);
+  for (int column = 1; column < max_width; column++)
+    pp_character (context->printer, '0' + (column % 10));
+  pp_newline (context->printer);
+}
+
+/* FIXME.  */
+extern void
+diagnostic_print_ranges (diagnostic_context * context,
+			 const diagnostic_info *diagnostic);
+
 /* Print the physical source line corresponding to the location of
    this diagnostic, and a caret indicating the precise column.  This
    function only prints two caret characters if the two locations
@@ -75,16 +875,25 @@ diagnostic_show_locus (diagnostic_context * context,
     return;
 
   context->last_location = diagnostic_location (diagnostic, 0);
-  expanded_location s0 = diagnostic_expand_location (diagnostic, 0);
-  expanded_location s1 = { };
-  /* Zero-initialized. This is checked later by diagnostic_print_caret_line.  */
 
-  if (diagnostic_location (diagnostic, 1) > BUILTINS_LOCATION)
-    s1 = diagnostic_expand_location (diagnostic, 1);
+  if (0)
+    {
+      /* The GCC 5 routine. */
+      expanded_location s0 = diagnostic_expand_location (diagnostic, 0);
+      expanded_location s1 = { };
+      /* Zero-initialized. This is checked later by
+	 diagnostic_print_caret_line.  */
 
-  diagnostic_print_caret_line (context, s0, s1,
-			       context->caret_chars[0],
-			       context->caret_chars[1]);
+      if (diagnostic_num_locations (diagnostic) >= 2)
+	s1 = diagnostic->message.m_richloc->get_range (1)->m_start;
+
+      diagnostic_print_caret_line (context, s0, s1,
+				   context->caret_chars[0],
+				   context->caret_chars[1]);
+    }
+  else
+    /* The GCC 6 routine.  */
+    diagnostic_print_ranges (context, diagnostic);
 }
 
 /* Print (part) of the source line given by xloc1 with caret1 pointing
@@ -96,7 +905,7 @@ void
 diagnostic_print_caret_line (diagnostic_context * context,
 			     expanded_location xloc1,
 			     expanded_location xloc2,
-			     char caret1, char caret2)
+			     const char *caret1, const char *caret2)
 {
   if (!diagnostic_same_line (context, xloc1, xloc2))
     /* This will mean ignore xloc2.  */
@@ -126,6 +935,8 @@ diagnostic_print_caret_line (diagnostic_context * context,
   pp_newline (context->printer);
   const char *saved_prefix = pp_get_prefix (context->printer);
   pp_set_prefix (context->printer, NULL);
+  /* FIXME: need to reimplement the adjust_line margin-adjustment logic
+     for the case of multiline, with ranges as well as carets.  */
   pp_space (context->printer);
   while (max_width > 0 && line_width > 0)
     {
@@ -145,22 +956,50 @@ diagnostic_print_caret_line (diagnostic_context * context,
   caret_ce = colorize_stop (pp_show_color (context->printer));
   int cmin = xloc2.column
     ? MIN (xloc1.column, xloc2.column) : xloc1.column;
-  int caret_min = cmin == xloc1.column ? caret1 : caret2;
-  int caret_max = cmin == xloc1.column ? caret2 : caret1;
+  const char *caret_min = cmin == xloc1.column ? caret1 : caret2;
+  const char *caret_max = cmin == xloc1.column ? caret2 : caret1;
 
   /* cmin is >= 1, but we indent with an extra space at the start like
      we did above.  */
   int i;
   for (i = 0; i < cmin; i++)
     pp_space (context->printer);
-  pp_printf (context->printer, "%s%c%s", caret_cs, caret_min, caret_ce);
+  pp_printf (context->printer, "%s%s%s", caret_cs, caret_min, caret_ce);
 
   if (xloc2.column)
     {
       for (i++; i < cmax; i++)
 	pp_space (context->printer);
-      pp_printf (context->printer, "%s%c%s", caret_cs, caret_max, caret_ce);
+      pp_printf (context->printer, "%s%s%s", caret_cs, caret_max, caret_ce);
     }
   pp_set_prefix (context->printer, saved_prefix);
   pp_needs_newline (context->printer) = true;
 }
+
+/* FIXME.  */
+
+void
+diagnostic_print_ranges (diagnostic_context * context,
+			 const diagnostic_info *diagnostic)
+{
+  int max_width = context->caret_max_width;
+
+  pp_newline (context->printer);
+
+  const char *saved_prefix = pp_get_prefix (context->printer);
+  pp_set_prefix (context->printer, NULL);
+
+  if (0)
+    show_ruler (context, max_width);
+
+  {
+    layout layout (context, diagnostic);
+    int last_line = diagnostic->richloc->get_last_line ();
+    for (int row = diagnostic->richloc->get_first_line ();
+	 row <= last_line;
+	 row++)
+      layout.print_line (row);
+  }
+
+  pp_set_prefix (context->printer, saved_prefix);
+}
diff --git a/gcc/diagnostic.c b/gcc/diagnostic.c
index 1eb85fc..c8f11f5 100644
--- a/gcc/diagnostic.c
+++ b/gcc/diagnostic.c
@@ -25,12 +25,29 @@ along with GCC; see the file COPYING3.  If not see
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
+#include "tm.h"
 #include "version.h"
 #include "demangle.h"
 #include "intl.h"
 #include "backtrace.h"
 #include "diagnostic.h"
 #include "diagnostic-color.h"
+#include "vec.h"
+
+#include "hash-set.h"
+#include "machmode.h"
+#include "vec.h"
+#include "double-int.h"
+#include "input.h"
+#include "alias.h"
+#include "symtab.h"
+#include "wide-int.h"
+#include "inchash.h"
+
+#include "tree-core.h"
+#include "tree.h"
+
+#include "box-drawing.h"
 
 #ifdef HAVE_TERMIOS_H
 # include <termios.h>
@@ -145,8 +162,9 @@ diagnostic_initialize (diagnostic_context *context, int n_opts)
     context->classify_diagnostic[i] = DK_UNSPECIFIED;
   context->show_caret = false;
   diagnostic_set_caret_max_width (context, pp_line_cutoff (context->printer));
-  for (i = 0; i < MAX_LOCATIONS_PER_MESSAGE; i++)
-    context->caret_chars[i] = '^';
+  gcc_assert (g_line_art.default_caret);
+  for (i = 0; i < rich_location::MAX_RANGES; i++)
+    context->caret_chars[i] = g_line_art.default_caret;
   context->show_option_requested = false;
   context->abort_on_error = false;
   context->show_column = false;
@@ -235,16 +253,15 @@ diagnostic_finish (diagnostic_context *context)
    translated.  */
 void
 diagnostic_set_info_translated (diagnostic_info *diagnostic, const char *msg,
-				va_list *args, location_t location,
+				va_list *args, rich_location *richloc,
 				diagnostic_t kind)
 {
+  gcc_assert (richloc);
   diagnostic->message.err_no = errno;
   diagnostic->message.args_ptr = args;
   diagnostic->message.format_spec = msg;
-  diagnostic->message.set_location (0, location);
-  for (int i = 1; i < MAX_LOCATIONS_PER_MESSAGE; i++)
-    diagnostic->message.set_location (i, UNKNOWN_LOCATION);
-  diagnostic->override_column = 0;
+  diagnostic->message.m_richloc = richloc;
+  diagnostic->richloc = richloc;
   diagnostic->kind = kind;
   diagnostic->option_index = 0;
 }
@@ -253,10 +270,27 @@ diagnostic_set_info_translated (diagnostic_info *diagnostic, const char *msg,
    translated.  */
 void
 diagnostic_set_info (diagnostic_info *diagnostic, const char *gmsgid,
-		     va_list *args, location_t location,
+		     va_list *args, rich_location *richloc,
 		     diagnostic_t kind)
 {
-  diagnostic_set_info_translated (diagnostic, _(gmsgid), args, location, kind);
+  gcc_assert (richloc);
+  diagnostic_set_info_translated (diagnostic, _(gmsgid), args, richloc, kind);
+}
+
+static const char *const diagnostic_kind_color[] = {
+#define DEFINE_DIAGNOSTIC_KIND(K, T, C) (C),
+#include "diagnostic.def"
+#undef DEFINE_DIAGNOSTIC_KIND
+  NULL
+};
+
+/* Get a color name for diagnostics of type KIND
+   Result could be NULL.  */
+
+const char *
+diagnostic_get_color_for_kind (diagnostic_t kind)
+{
+  return diagnostic_kind_color[kind];
 }
 
 /* Return a malloc'd string describing a location.  The caller is
@@ -271,12 +305,6 @@ diagnostic_build_prefix (diagnostic_context *context,
 #undef DEFINE_DIAGNOSTIC_KIND
     "must-not-happen"
   };
-  static const char *const diagnostic_kind_color[] = {
-#define DEFINE_DIAGNOSTIC_KIND(K, T, C) (C),
-#include "diagnostic.def"
-#undef DEFINE_DIAGNOSTIC_KIND
-    NULL
-  };
   gcc_assert (diagnostic->kind < DK_LAST_DIAGNOSTIC_KIND);
 
   const char *text = _(diagnostic_kind_text[diagnostic->kind]);
@@ -774,14 +802,19 @@ diagnostic_report_diagnostic (diagnostic_context *context,
 
       if (option_text)
 	{
+	  const char *cs
+	    = colorize_start (pp_show_color (context->printer),
+			      diagnostic_kind_color[diagnostic->kind]);
+	  const char *ce = colorize_stop (pp_show_color (context->printer));
 	  diagnostic->message.format_spec
 	    = ACONCAT ((diagnostic->message.format_spec,
 			" ", 
-			"[", option_text, "]",
+			"[", cs, option_text, ce, "]",
 			NULL));
 	  free (option_text);
 	}
     }
+  //diagnostic->message.locus = diagnostic->richloc->get_loc_addr ();
   diagnostic->message.x_data = &diagnostic->x_data;
   diagnostic->x_data = NULL;
   pp_format (context->printer, &diagnostic->message);
@@ -857,9 +890,40 @@ diagnostic_append_note (diagnostic_context *context,
   diagnostic_info diagnostic;
   va_list ap;
   const char *saved_prefix;
+  rich_location richloc (location);
+
+  va_start (ap, gmsgid);
+  diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_NOTE);
+  if (context->inhibit_notes_p)
+    {
+      va_end (ap);
+      return;
+    }
+  saved_prefix = pp_get_prefix (context->printer);
+  pp_set_prefix (context->printer,
+                 diagnostic_build_prefix (context, &diagnostic));
+  pp_newline (context->printer);
+  pp_format (context->printer, &diagnostic.message);
+  pp_output_formatted_text (context->printer);
+  pp_destroy_prefix (context->printer);
+  pp_set_prefix (context->printer, saved_prefix);
+  diagnostic_show_locus (context, &diagnostic);
+  va_end (ap);
+}
+
+/* Same as diagnostic_append_note, but at RICHLOC. */
+
+void
+diagnostic_append_note_at_rich_loc (diagnostic_context *context,
+				    rich_location *richloc,
+				    const char * gmsgid, ...)
+{
+  diagnostic_info diagnostic;
+  va_list ap;
+  const char *saved_prefix;
 
   va_start (ap, gmsgid);
-  diagnostic_set_info (&diagnostic, gmsgid, &ap, location, DK_NOTE);
+  diagnostic_set_info (&diagnostic, gmsgid, &ap, richloc, DK_NOTE);
   if (context->inhibit_notes_p)
     {
       va_end (ap);
@@ -884,16 +948,17 @@ emit_diagnostic (diagnostic_t kind, location_t location, int opt,
   diagnostic_info diagnostic;
   va_list ap;
   bool ret;
+  rich_location richloc (location);
 
   va_start (ap, gmsgid);
   if (kind == DK_PERMERROR)
     {
-      diagnostic_set_info (&diagnostic, gmsgid, &ap, location,
+      diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc,
 			   permissive_error_kind (global_dc));
       diagnostic.option_index = permissive_error_option (global_dc);
     }
   else {
-      diagnostic_set_info (&diagnostic, gmsgid, &ap, location, kind);
+      diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, kind);
       if (kind == DK_WARNING || kind == DK_PEDWARN)
 	diagnostic.option_index = opt;
   }
@@ -910,9 +975,23 @@ inform (location_t location, const char *gmsgid, ...)
 {
   diagnostic_info diagnostic;
   va_list ap;
+  rich_location richloc (location);
+
+  va_start (ap, gmsgid);
+  diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_NOTE);
+  report_diagnostic (&diagnostic);
+  va_end (ap);
+}
+
+/* Same as "inform", but at RICHLOC.  */
+void
+inform_at_rich_loc (rich_location *richloc, const char *gmsgid, ...)
+{
+  diagnostic_info diagnostic;
+  va_list ap;
 
   va_start (ap, gmsgid);
-  diagnostic_set_info (&diagnostic, gmsgid, &ap, location, DK_NOTE);
+  diagnostic_set_info (&diagnostic, gmsgid, &ap, richloc, DK_NOTE);
   report_diagnostic (&diagnostic);
   va_end (ap);
 }
@@ -925,11 +1004,12 @@ inform_n (location_t location, int n, const char *singular_gmsgid,
 {
   diagnostic_info diagnostic;
   va_list ap;
+  rich_location richloc (location);
 
   va_start (ap, plural_gmsgid);
   diagnostic_set_info_translated (&diagnostic,
                                   ngettext (singular_gmsgid, plural_gmsgid, n),
-                                  &ap, location, DK_NOTE);
+                                  &ap, &richloc, DK_NOTE);
   report_diagnostic (&diagnostic);
   va_end (ap);
 }
@@ -943,9 +1023,10 @@ warning (int opt, const char *gmsgid, ...)
   diagnostic_info diagnostic;
   va_list ap;
   bool ret;
+  rich_location richloc (input_location);
 
   va_start (ap, gmsgid);
-  diagnostic_set_info (&diagnostic, gmsgid, &ap, input_location, DK_WARNING);
+  diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_WARNING);
   diagnostic.option_index = opt;
 
   ret = report_diagnostic (&diagnostic);
@@ -963,9 +1044,27 @@ warning_at (location_t location, int opt, const char *gmsgid, ...)
   diagnostic_info diagnostic;
   va_list ap;
   bool ret;
+  rich_location richloc (location);
+
+  va_start (ap, gmsgid);
+  diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_WARNING);
+  diagnostic.option_index = opt;
+  ret = report_diagnostic (&diagnostic);
+  va_end (ap);
+  return ret;
+}
+
+/* Same as warning at, but using RICHLOC.  */
+
+bool
+warning_at_rich_loc (rich_location *richloc, int opt, const char *gmsgid, ...)
+{
+  diagnostic_info diagnostic;
+  va_list ap;
+  bool ret;
 
   va_start (ap, gmsgid);
-  diagnostic_set_info (&diagnostic, gmsgid, &ap, location, DK_WARNING);
+  diagnostic_set_info (&diagnostic, gmsgid, &ap, richloc, DK_WARNING);
   diagnostic.option_index = opt;
   ret = report_diagnostic (&diagnostic);
   va_end (ap);
@@ -983,11 +1082,13 @@ warning_n (location_t location, int opt, int n, const char *singular_gmsgid,
   diagnostic_info diagnostic;
   va_list ap;
   bool ret;
+  rich_location richloc (location);
 
   va_start (ap, plural_gmsgid);
   diagnostic_set_info_translated (&diagnostic,
                                   ngettext (singular_gmsgid, plural_gmsgid, n),
-                                  &ap, location, DK_WARNING);
+                                  &ap, &richloc, DK_WARNING
+);
   diagnostic.option_index = opt;
   ret = report_diagnostic (&diagnostic);
   va_end (ap);
@@ -1013,9 +1114,10 @@ pedwarn (location_t location, int opt, const char *gmsgid, ...)
   diagnostic_info diagnostic;
   va_list ap;
   bool ret;
+  rich_location richloc (location);
 
   va_start (ap, gmsgid);
-  diagnostic_set_info (&diagnostic, gmsgid, &ap, location,  DK_PEDWARN);
+  diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc,  DK_PEDWARN);
   diagnostic.option_index = opt;
   ret = report_diagnostic (&diagnostic);
   va_end (ap);
@@ -1035,9 +1137,28 @@ permerror (location_t location, const char *gmsgid, ...)
   diagnostic_info diagnostic;
   va_list ap;
   bool ret;
+  rich_location richloc (location);
+
+  va_start (ap, gmsgid);
+  diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc,
+                       permissive_error_kind (global_dc));
+  diagnostic.option_index = permissive_error_option (global_dc);
+  ret = report_diagnostic (&diagnostic);
+  va_end (ap);
+  return ret;
+}
+
+/* Same as "permerror", but at RICHLOC.  */
+
+bool
+permerror_at_rich_loc (rich_location *richloc, const char *gmsgid, ...)
+{
+  diagnostic_info diagnostic;
+  va_list ap;
+  bool ret;
 
   va_start (ap, gmsgid);
-  diagnostic_set_info (&diagnostic, gmsgid, &ap, location,
+  diagnostic_set_info (&diagnostic, gmsgid, &ap, richloc,
                        permissive_error_kind (global_dc));
   diagnostic.option_index = permissive_error_option (global_dc);
   ret = report_diagnostic (&diagnostic);
@@ -1052,9 +1173,10 @@ error (const char *gmsgid, ...)
 {
   diagnostic_info diagnostic;
   va_list ap;
+  rich_location richloc (input_location);
 
   va_start (ap, gmsgid);
-  diagnostic_set_info (&diagnostic, gmsgid, &ap, input_location, DK_ERROR);
+  diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_ERROR);
   report_diagnostic (&diagnostic);
   va_end (ap);
 }
@@ -1067,11 +1189,12 @@ error_n (location_t location, int n, const char *singular_gmsgid,
 {
   diagnostic_info diagnostic;
   va_list ap;
+  rich_location richloc (location);
 
   va_start (ap, plural_gmsgid);
   diagnostic_set_info_translated (&diagnostic,
                                   ngettext (singular_gmsgid, plural_gmsgid, n),
-                                  &ap, location, DK_ERROR);
+                                  &ap, &richloc, DK_ERROR);
   report_diagnostic (&diagnostic);
   va_end (ap);
 }
@@ -1082,9 +1205,25 @@ error_at (location_t loc, const char *gmsgid, ...)
 {
   diagnostic_info diagnostic;
   va_list ap;
+  rich_location richloc (loc);
+
+  va_start (ap, gmsgid);
+  diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_ERROR);
+  report_diagnostic (&diagnostic);
+  va_end (ap);
+}
+
+/* Same as above, but use RICH_LOC.  */
+
+void
+error_at_rich_loc (rich_location *rich_loc, const char *gmsgid, ...)
+{
+  diagnostic_info diagnostic;
+  va_list ap;
 
   va_start (ap, gmsgid);
-  diagnostic_set_info (&diagnostic, gmsgid, &ap, loc, DK_ERROR);
+  diagnostic_set_info (&diagnostic, gmsgid, &ap, rich_loc,
+		       DK_ERROR);
   report_diagnostic (&diagnostic);
   va_end (ap);
 }
@@ -1097,9 +1236,10 @@ sorry (const char *gmsgid, ...)
 {
   diagnostic_info diagnostic;
   va_list ap;
+  rich_location richloc (input_location);
 
   va_start (ap, gmsgid);
-  diagnostic_set_info (&diagnostic, gmsgid, &ap, input_location, DK_SORRY);
+  diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_SORRY);
   report_diagnostic (&diagnostic);
   va_end (ap);
 }
@@ -1120,9 +1260,10 @@ fatal_error (location_t loc, const char *gmsgid, ...)
 {
   diagnostic_info diagnostic;
   va_list ap;
+  rich_location richloc (loc);
 
   va_start (ap, gmsgid);
-  diagnostic_set_info (&diagnostic, gmsgid, &ap, loc, DK_FATAL);
+  diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_FATAL);
   report_diagnostic (&diagnostic);
   va_end (ap);
 
@@ -1138,9 +1279,10 @@ internal_error (const char *gmsgid, ...)
 {
   diagnostic_info diagnostic;
   va_list ap;
+  rich_location richloc (input_location);
 
   va_start (ap, gmsgid);
-  diagnostic_set_info (&diagnostic, gmsgid, &ap, input_location, DK_ICE);
+  diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_ICE);
   report_diagnostic (&diagnostic);
   va_end (ap);
 
@@ -1155,9 +1297,10 @@ internal_error_no_backtrace (const char *gmsgid, ...)
 {
   diagnostic_info diagnostic;
   va_list ap;
+  rich_location richloc (input_location);
 
   va_start (ap, gmsgid);
-  diagnostic_set_info (&diagnostic, gmsgid, &ap, input_location, DK_ICE_NOBT);
+  diagnostic_set_info (&diagnostic, gmsgid, &ap, &richloc, DK_ICE_NOBT);
   report_diagnostic (&diagnostic);
   va_end (ap);
 
@@ -1221,3 +1364,11 @@ real_abort (void)
 {
   abort ();
 }
+
+void
+source_range::debug (const char *msg) const
+{
+  rich_location richloc (m_start);
+  richloc.add_range (m_start, m_finish);
+  inform_at_rich_loc (&richloc, "%s", msg);
+}
diff --git a/gcc/diagnostic.h b/gcc/diagnostic.h
index 1b9b7d4..5763a18 100644
--- a/gcc/diagnostic.h
+++ b/gcc/diagnostic.h
@@ -32,7 +32,10 @@ struct diagnostic_info
   /* Text to be formatted. It also contains the location(s) for this
      diagnostic.  */
   text_info message;
-  unsigned int override_column;
+
+  /* FIXME.  */
+  rich_location *richloc;
+
   /* Auxiliary data for client.  */
   void *x_data;
   /* The kind of diagnostic it is about.  */
@@ -107,7 +110,7 @@ struct diagnostic_context
   int caret_max_width;
 
   /* Characters used for caret diagnostics.  */
-  char caret_chars[MAX_LOCATIONS_PER_MESSAGE];
+  const char *caret_chars[rich_location::MAX_RANGES];
 
   /* True if we should print the command line option which controls
      each diagnostic, if known.  */
@@ -256,10 +259,6 @@ extern diagnostic_context *global_dc;
 
 #define report_diagnostic(D) diagnostic_report_diagnostic (global_dc, D)
 
-/* Override the column number to be used for reporting a
-   diagnostic.  */
-#define diagnostic_override_column(DI, COL) (DI)->override_column = (COL)
-
 /* Override the option index to be used for reporting a
    diagnostic.  */
 #define diagnostic_override_option_index(DI, OPTIDX) \
@@ -283,13 +282,17 @@ extern bool diagnostic_report_diagnostic (diagnostic_context *,
 					  diagnostic_info *);
 #ifdef ATTRIBUTE_GCC_DIAG
 extern void diagnostic_set_info (diagnostic_info *, const char *, va_list *,
-				 location_t, diagnostic_t) ATTRIBUTE_GCC_DIAG(2,0);
+				 rich_location *, diagnostic_t) ATTRIBUTE_GCC_DIAG(2,0);
 extern void diagnostic_set_info_translated (diagnostic_info *, const char *,
-					    va_list *, location_t,
+					    va_list *, rich_location *,
 					    diagnostic_t)
      ATTRIBUTE_GCC_DIAG(2,0);
 extern void diagnostic_append_note (diagnostic_context *, location_t,
                                     const char *, ...) ATTRIBUTE_GCC_DIAG(3,4);
+extern void diagnostic_append_note_at_rich_loc (diagnostic_context *,
+						rich_location *,
+						const char *, ...)
+  ATTRIBUTE_GCC_DIAG(3,4);
 #endif
 extern char *diagnostic_build_prefix (diagnostic_context *, const diagnostic_info *);
 void default_diagnostic_starter (diagnostic_context *, diagnostic_info *);
@@ -310,18 +313,31 @@ diagnostic_location (const diagnostic_info * diagnostic, int which = 0)
   return diagnostic->message.get_location (which);
 }
 
+/* FIXME.  */
+
+static inline unsigned int
+diagnostic_num_locations (const diagnostic_info * diagnostic)
+{
+  return diagnostic->message.m_richloc->get_num_locations ();
+}
+
 /* Expand the location of this diagnostic. Use this function for
    consistency.  Parameter WHICH specifies which location. By default,
    expand the first one.  */
-
 static inline expanded_location
 diagnostic_expand_location (const diagnostic_info * diagnostic, int which = 0)
 {
   expanded_location s
+#if 0
     = expand_location_to_spelling_point (diagnostic_location (diagnostic,
 							      which));
-  if (which == 0 && diagnostic->override_column)
-    s.column = diagnostic->override_column;
+#endif
+
+#if 0
+    = expand_location_to_spelling_point (diagnostic->richloc->get_loc ());
+#endif
+    = diagnostic->richloc->get_range (which)->m_start;
+
   return s;
 }
 
@@ -346,7 +362,11 @@ void
 diagnostic_print_caret_line (diagnostic_context * context,
 			     expanded_location xloc1,
 			     expanded_location xloc2,
-			     char caret1, char caret2);
+			     const char *caret1, const char *caret2);
+
+
+extern const char *
+diagnostic_get_color_for_kind (diagnostic_t kind);
 
 /* Pure text formatting support functions.  */
 extern char *file_name_as_prefix (diagnostic_context *, const char *);
diff --git a/gcc/fortran/cpp.c b/gcc/fortran/cpp.c
index daffc20..92dc584 100644
--- a/gcc/fortran/cpp.c
+++ b/gcc/fortran/cpp.c
@@ -149,9 +149,9 @@ static void cb_include (cpp_reader *, source_location, const unsigned char *,
 static void cb_ident (cpp_reader *, source_location, const cpp_string *);
 static void cb_used_define (cpp_reader *, source_location, cpp_hashnode *);
 static void cb_used_undef (cpp_reader *, source_location, cpp_hashnode *);
-static bool cb_cpp_error (cpp_reader *, int, int, location_t, unsigned int,
+static bool cb_cpp_error (cpp_reader *, int, int, rich_location *,
 			  const char *, va_list *)
-     ATTRIBUTE_GCC_DIAG(6,0);
+     ATTRIBUTE_GCC_DIAG(5,0);
 void pp_dir_change (cpp_reader *, const char *);
 
 static int dump_macro (cpp_reader *, cpp_hashnode *, void *);
@@ -1026,13 +1026,12 @@ cb_used_define (cpp_reader *pfile, source_location line ATTRIBUTE_UNUSED,
 /* Callback from cpp_error for PFILE to print diagnostics from the
    preprocessor.  The diagnostic is of type LEVEL, with REASON set
    to the reason code if LEVEL is represents a warning, at location
-   LOCATION, with column number possibly overridden by COLUMN_OVERRIDE
-   if not zero; MSG is the translated message and AP the arguments.
+   RICHLOC; MSG is the translated message and AP the arguments.
    Returns true if a diagnostic was emitted, false otherwise.  */
 
 static bool
 cb_cpp_error (cpp_reader *pfile ATTRIBUTE_UNUSED, int level, int reason,
-	      location_t location, unsigned int column_override,
+	      rich_location *richloc,
 	      const char *msg, va_list *ap)
 {
   diagnostic_info diagnostic;
@@ -1067,9 +1066,7 @@ cb_cpp_error (cpp_reader *pfile ATTRIBUTE_UNUSED, int level, int reason,
       gcc_unreachable ();
     }
   diagnostic_set_info_translated (&diagnostic, msg, ap,
-				  location, dlevel);
-  if (column_override)
-    diagnostic_override_column (&diagnostic, column_override);
+				  richloc, dlevel);
   if (reason == CPP_W_WARNING_DIRECTIVE)
     diagnostic_override_option_index (&diagnostic, OPT_Wcpp);
   ret = report_diagnostic (&diagnostic);
diff --git a/gcc/fortran/error.c b/gcc/fortran/error.c
index 7689bbd..e19b1c7 100644
--- a/gcc/fortran/error.c
+++ b/gcc/fortran/error.c
@@ -33,6 +33,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "diagnostic.h"
 #include "diagnostic-color.h"
 #include "tree-diagnostic.h" /* tree_diagnostics_defaults */
+#include "box-drawing.h"
 
 #include <new> /* For placement-new */
 
@@ -770,6 +771,7 @@ gfc_warning (int opt, const char *gmsgid, va_list ap)
   va_copy (argp, ap);
 
   diagnostic_info diagnostic;
+  rich_location rich_loc (UNKNOWN_LOCATION);
   bool fatal_errors = global_dc->fatal_errors;
   pretty_printer *pp = global_dc->printer;
   output_buffer *tmp_buffer = pp->buffer;
@@ -784,7 +786,7 @@ gfc_warning (int opt, const char *gmsgid, va_list ap)
       --werrorcount;
     }
 
-  diagnostic_set_info (&diagnostic, gmsgid, &argp, UNKNOWN_LOCATION,
+  diagnostic_set_info (&diagnostic, gmsgid, &argp, &rich_loc,
 		       DK_WARNING);
   diagnostic.option_index = opt;
   bool ret = report_diagnostic (&diagnostic);
@@ -935,10 +937,12 @@ gfc_format_decoder (pretty_printer *pp,
 	/* If location[0] != UNKNOWN_LOCATION means that we already
 	   processed one of %C/%L.  */
 	int loc_num = text->get_location (0) == UNKNOWN_LOCATION ? 0 : 1;
-	text->set_location (loc_num,
-			    linemap_position_for_loc_and_offset (line_table,
-								 loc->lb->location,
-								 offset));
+	source_range range
+	  = source_range::from_location (
+	      linemap_position_for_loc_and_offset (line_table,
+						   loc->lb->location,
+						   offset));
+	text->set_range (loc_num, range, true);
 	pp_string (pp, result[loc_num]);
 	return true;
       }
@@ -1170,10 +1174,11 @@ gfc_warning_now_at (location_t loc, int opt, const char *gmsgid, ...)
 {
   va_list argp;
   diagnostic_info diagnostic;
+  rich_location rich_loc (loc);
   bool ret;
 
   va_start (argp, gmsgid);
-  diagnostic_set_info (&diagnostic, gmsgid, &argp, loc, DK_WARNING);
+  diagnostic_set_info (&diagnostic, gmsgid, &argp, &rich_loc, DK_WARNING);
   diagnostic.option_index = opt;
   ret = report_diagnostic (&diagnostic);
   va_end (argp);
@@ -1187,10 +1192,11 @@ gfc_warning_now (int opt, const char *gmsgid, ...)
 {
   va_list argp;
   diagnostic_info diagnostic;
+  rich_location rich_loc (UNKNOWN_LOCATION);
   bool ret;
 
   va_start (argp, gmsgid);
-  diagnostic_set_info (&diagnostic, gmsgid, &argp, UNKNOWN_LOCATION,
+  diagnostic_set_info (&diagnostic, gmsgid, &argp, &rich_loc,
 		       DK_WARNING);
   diagnostic.option_index = opt;
   ret = report_diagnostic (&diagnostic);
@@ -1206,11 +1212,12 @@ gfc_error_now (const char *gmsgid, ...)
 {
   va_list argp;
   diagnostic_info diagnostic;
+  rich_location rich_loc (UNKNOWN_LOCATION);
 
   error_buffer.flag = true;
 
   va_start (argp, gmsgid);
-  diagnostic_set_info (&diagnostic, gmsgid, &argp, UNKNOWN_LOCATION, DK_ERROR);
+  diagnostic_set_info (&diagnostic, gmsgid, &argp, &rich_loc, DK_ERROR);
   report_diagnostic (&diagnostic);
   va_end (argp);
 }
@@ -1223,9 +1230,10 @@ gfc_fatal_error (const char *gmsgid, ...)
 {
   va_list argp;
   diagnostic_info diagnostic;
+  rich_location rich_loc (UNKNOWN_LOCATION);
 
   va_start (argp, gmsgid);
-  diagnostic_set_info (&diagnostic, gmsgid, &argp, UNKNOWN_LOCATION, DK_FATAL);
+  diagnostic_set_info (&diagnostic, gmsgid, &argp, &rich_loc, DK_FATAL);
   report_diagnostic (&diagnostic);
   va_end (argp);
 
@@ -1288,6 +1296,7 @@ gfc_error (const char *gmsgid, va_list ap)
     }
 
   diagnostic_info diagnostic;
+  rich_location richloc (UNKNOWN_LOCATION);
   bool fatal_errors = global_dc->fatal_errors;
   pretty_printer *pp = global_dc->printer;
   output_buffer *tmp_buffer = pp->buffer;
@@ -1303,7 +1312,7 @@ gfc_error (const char *gmsgid, va_list ap)
       --errorcount;
     }
 
-  diagnostic_set_info (&diagnostic, gmsgid, &argp, UNKNOWN_LOCATION, DK_ERROR);
+  diagnostic_set_info (&diagnostic, gmsgid, &argp, &richloc, DK_ERROR);
   report_diagnostic (&diagnostic);
 
   if (buffered_p)
@@ -1333,9 +1342,10 @@ gfc_internal_error (const char *gmsgid, ...)
 {
   va_list argp;
   diagnostic_info diagnostic;
+  rich_location rich_loc (UNKNOWN_LOCATION);
 
   va_start (argp, gmsgid);
-  diagnostic_set_info (&diagnostic, gmsgid, &argp, UNKNOWN_LOCATION, DK_ICE);
+  diagnostic_set_info (&diagnostic, gmsgid, &argp, &rich_loc, DK_ICE);
   report_diagnostic (&diagnostic);
   va_end (argp);
 
@@ -1467,8 +1477,8 @@ gfc_diagnostics_init (void)
   diagnostic_starter (global_dc) = gfc_diagnostic_starter;
   diagnostic_finalizer (global_dc) = gfc_diagnostic_finalizer;
   diagnostic_format_decoder (global_dc) = gfc_format_decoder;
-  global_dc->caret_chars[0] = '1';
-  global_dc->caret_chars[1] = '2';
+  global_dc->caret_chars[0] = "1";
+  global_dc->caret_chars[1] = "2";
   pp_warning_buffer = new (XNEW (output_buffer)) output_buffer ();
   pp_warning_buffer->flush_p = false;
   /* pp_error_buffer is statically allocated.  This simplifies memory
@@ -1485,6 +1495,6 @@ gfc_diagnostics_finish (void)
      defaults.  */
   diagnostic_starter (global_dc) = gfc_diagnostic_starter;
   diagnostic_finalizer (global_dc) = gfc_diagnostic_finalizer;
-  global_dc->caret_chars[0] = '^';
-  global_dc->caret_chars[1] = '^';
+  global_dc->caret_chars[0] = g_line_art.default_caret;
+  global_dc->caret_chars[1] = g_line_art.default_caret;
 }
diff --git a/gcc/gcc-rich-location.c b/gcc/gcc-rich-location.c
new file mode 100644
index 0000000..98f0f9d
--- /dev/null
+++ b/gcc/gcc-rich-location.c
@@ -0,0 +1,86 @@
+/* Implementation of gcc_rich_location class
+   Copyright (C) 2014-2015 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "rtl.h"
+#include "hash-set.h"
+#include "machmode.h"
+#include "vec.h"
+#include "double-int.h"
+#include "input.h"
+#include "alias.h"
+#include "symtab.h"
+#include "wide-int.h"
+#include "inchash.h"
+#include "tree-core.h"
+#include "tree.h"
+#include "diagnostic-core.h"
+#include "gcc-rich-location.h"
+#include "print-tree.h"
+#include "pretty-print.h"
+#include "intl.h"
+#include "cpplib.h"
+#include "diagnostic.h"
+
+void
+gcc_rich_location::add_range_with_caption (location_t start, location_t finish,
+					   diagnostic_context *context,
+					   const char *gmsgid, ...)
+{
+  gcc_assert (context);
+  gcc_assert (gmsgid);
+
+  va_list ap;
+  va_start (ap, gmsgid);
+
+  char *caption = expand_caption_va (context, gmsgid, &ap);
+  add_range (start, finish, caption, BUFFER_OWNERSHIP_GIVEN, false);
+
+  va_end (ap);
+}
+
+/* FIXME.  */
+
+char *
+gcc_rich_location::expand_caption_va (diagnostic_context *context,
+				      const char *gmsgid, va_list *args)
+{
+  gcc_assert (context);
+  gcc_assert (gmsgid);
+
+  /* Only bother if show-caret is enabled.  */
+  if (!context->show_caret)
+    return NULL;
+
+  /* Format the text, and return a copy.  */
+  pretty_printer * const pp = context->printer;
+  char *result;
+  text_info text;
+  text.err_no = errno;
+  text.args_ptr = args;
+  text.format_spec = G_(gmsgid); //_(gmsgid);
+  pp_format (pp, &text);
+  pp_output_formatted_text (pp);
+  result = xstrdup (pp_formatted_text (pp));
+  pp_clear_output_area (pp);
+  return result;
+}
diff --git a/gcc/gcc-rich-location.h b/gcc/gcc-rich-location.h
new file mode 100644
index 0000000..795b60f
--- /dev/null
+++ b/gcc/gcc-rich-location.h
@@ -0,0 +1,54 @@
+/* Declarations relating to class gcc_rich_location
+   Copyright (C) 2014-2015 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_RICH_LOCATION_H
+#define GCC_RICH_LOCATION_H
+
+/* A gcc_rich_location is libcpp's rich_location with additional
+   helper methods for working with gcc's types.  */
+class gcc_rich_location : public rich_location
+{
+ public:
+  /* Constructors.  */
+
+  /* Constructing from a location.  */
+  gcc_rich_location (source_location loc) :
+    rich_location (loc) {}
+
+  /* Constructing from a source_range.  */
+  gcc_rich_location (source_range src_range) :
+    rich_location (src_range) {}
+
+
+  /* Methods for adding additional details.  */
+
+  void
+  add_range_with_caption (location_t start, location_t finish,
+			  diagnostic_context *context,
+			  const char *gmsgid, ...)
+    ATTRIBUTE_GCC_DIAG(5,6);
+
+ private:
+  static char *
+  expand_caption_va (diagnostic_context *context,
+		     const char *gmsgid, va_list *args)
+    ATTRIBUTE_GCC_DIAG(2, 0);
+};
+
+#endif /* GCC_RICH_LOCATION_H */
diff --git a/gcc/genmatch.c b/gcc/genmatch.c
index f48e79d..5ce1c1c 100644
--- a/gcc/genmatch.c
+++ b/gcc/genmatch.c
@@ -53,14 +53,23 @@ unsigned verbose;
 
 static struct line_maps *line_table;
 
+expanded_location
+linemap_client_expand_location_to_spelling_point (source_location loc)
+{
+  const struct line_map_ordinary *map;
+  loc = linemap_resolve_location (line_table, loc, LRK_SPELLING_LOCATION, &map);
+  return linemap_expand_location (line_table, map, loc);
+}
+
 static bool
 #if GCC_VERSION >= 4001
-__attribute__((format (printf, 6, 0)))
+__attribute__((format (printf, 5, 0)))
 #endif
-error_cb (cpp_reader *, int errtype, int, source_location location,
-	  unsigned int, const char *msg, va_list *ap)
+error_cb (cpp_reader *, int errtype, int, rich_location *richloc,
+	  const char *msg, va_list *ap)
 {
   const line_map_ordinary *map;
+  source_location location = richloc->get_loc ();
   linemap_resolve_location (line_table, location, LRK_SPELLING_LOCATION, &map);
   expanded_location loc = linemap_expand_location (line_table, map, location);
   fprintf (stderr, "%s:%d:%d %s: ", loc.file, loc.line, loc.column,
@@ -102,9 +111,10 @@ __attribute__((format (printf, 2, 3)))
 #endif
 fatal_at (const cpp_token *tk, const char *msg, ...)
 {
+  rich_location richloc (tk->src_loc);
   va_list ap;
   va_start (ap, msg);
-  error_cb (NULL, CPP_DL_FATAL, 0, tk->src_loc, 0, msg, &ap);
+  error_cb (NULL, CPP_DL_FATAL, 0, &richloc, msg, &ap);
   va_end (ap);
 }
 
@@ -114,9 +124,10 @@ __attribute__((format (printf, 2, 3)))
 #endif
 fatal_at (source_location loc, const char *msg, ...)
 {
+  rich_location richloc (loc);
   va_list ap;
   va_start (ap, msg);
-  error_cb (NULL, CPP_DL_FATAL, 0, loc, 0, msg, &ap);
+  error_cb (NULL, CPP_DL_FATAL, 0, &richloc, msg, &ap);
   va_end (ap);
 }
 
@@ -126,9 +137,10 @@ __attribute__((format (printf, 2, 3)))
 #endif
 warning_at (const cpp_token *tk, const char *msg, ...)
 {
+  rich_location richloc (tk->src_loc);
   va_list ap;
   va_start (ap, msg);
-  error_cb (NULL, CPP_DL_WARNING, 0, tk->src_loc, 0, msg, &ap);
+  error_cb (NULL, CPP_DL_WARNING, 0, &richloc, msg, &ap);
   va_end (ap);
 }
 
@@ -138,9 +150,10 @@ __attribute__((format (printf, 2, 3)))
 #endif
 warning_at (source_location loc, const char *msg, ...)
 {
+  rich_location richloc (loc);
   va_list ap;
   va_start (ap, msg);
-  error_cb (NULL, CPP_DL_WARNING, 0, loc, 0, msg, &ap);
+  error_cb (NULL, CPP_DL_WARNING, 0, &richloc, msg, &ap);
   va_end (ap);
 }
 
diff --git a/gcc/input.c b/gcc/input.c
index e7302a4..bdba20f 100644
--- a/gcc/input.c
+++ b/gcc/input.c
@@ -751,6 +751,13 @@ expand_location_to_spelling_point (source_location loc)
   return expand_location_1 (loc, /*expansion_point_p=*/false);
 }
 
+expanded_location
+linemap_client_expand_location_to_spelling_point (source_location loc)
+{
+  return expand_location_to_spelling_point (loc);
+}
+
+
 /* If LOCATION is in a system header and if it is a virtual location for
    a token coming from the expansion of a macro, unwind it to the
    location of the expansion point of the macro.  Otherwise, just return
diff --git a/gcc/intl.c b/gcc/intl.c
index a902446..23c0539 100644
--- a/gcc/intl.c
+++ b/gcc/intl.c
@@ -21,6 +21,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "system.h"
 #include "coretypes.h"
 #include "intl.h"
+#include "box-drawing.h"
 
 #ifdef HAVE_LANGINFO_CODESET
 #include <langinfo.h>
@@ -86,6 +87,15 @@ gcc_init_libintl (void)
 	}
 #endif
     }
+
+  bool have_utf8 = false;
+#if defined HAVE_LANGINFO_CODESET
+  if (locale_utf8)
+    have_utf8 = true;
+#endif
+
+  //fprintf (stderr, "have_utf8: %s\n", have_utf8 ? "true" : "false");
+  g_line_art.init (have_utf8);
 }
 
 #if defined HAVE_WCHAR_H && defined HAVE_WORKING_MBSTOWCS && defined HAVE_WCSWIDTH
diff --git a/gcc/pretty-print.c b/gcc/pretty-print.c
index fdc7b4d..02cf991 100644
--- a/gcc/pretty-print.c
+++ b/gcc/pretty-print.c
@@ -31,6 +31,27 @@ along with GCC; see the file COPYING3.  If not see
 #include <iconv.h>
 #endif
 
+/* Overwrite the range within this text_info's rich_location.
+   For use e.g. when implementing "+" in client format decoders.  */
+
+void
+text_info::set_range (unsigned int idx, source_range range, bool caret_p)
+{
+  gcc_checking_assert (m_richloc);
+  m_richloc->set_range (idx, range, caret_p);
+}
+
+location_t
+text_info::get_location (unsigned int index_of_location) const
+{
+  gcc_checking_assert (m_richloc);
+
+  if (index_of_location == 0)
+    return m_richloc->get_loc ();
+  else
+    return UNKNOWN_LOCATION;
+}
+
 // Default construct an output buffer.
 
 output_buffer::output_buffer ()
diff --git a/gcc/pretty-print.h b/gcc/pretty-print.h
index 36d4e37..d10272c 100644
--- a/gcc/pretty-print.h
+++ b/gcc/pretty-print.h
@@ -27,11 +27,6 @@ along with GCC; see the file COPYING3.  If not see
 /* Maximum number of format string arguments.  */
 #define PP_NL_ARGMAX   30
 
-/* Maximum number of locations associated to each message.  If
-   location 'i' is UNKNOWN_LOCATION, then location 'i+1' is not
-   valid.  */
-#define MAX_LOCATIONS_PER_MESSAGE 2
-
 /* The type of a text to be formatted according a format specification
    along with a list of things.  */
 struct text_info
@@ -40,21 +35,17 @@ struct text_info
   va_list *args_ptr;
   int err_no;  /* for %m */
   void **x_data;
+  rich_location *m_richloc;
 
-  inline void set_location (unsigned int index_of_location, location_t loc)
+  inline void set_location (unsigned int idx, location_t loc, bool caret_p)
   {
-    gcc_checking_assert (index_of_location < MAX_LOCATIONS_PER_MESSAGE);
-    this->locations[index_of_location] = loc;
+    source_range src_range;
+    src_range.m_start = loc;
+    src_range.m_finish = loc;
+    set_range (idx, src_range, caret_p);
   }
-
-  inline location_t get_location (unsigned int index_of_location) const
-  {
-    gcc_checking_assert (index_of_location < MAX_LOCATIONS_PER_MESSAGE);
-    return this->locations[index_of_location];
-  }
-
-private:
-  location_t locations[MAX_LOCATIONS_PER_MESSAGE];
+  void set_range (unsigned int idx, source_range range, bool caret_p);
+  location_t get_location (unsigned int index_of_location) const;
 };
 
 /* How often diagnostics are prefixed by their locations:
diff --git a/gcc/rtl-error.c b/gcc/rtl-error.c
index 8b9b391..d28be1d 100644
--- a/gcc/rtl-error.c
+++ b/gcc/rtl-error.c
@@ -69,9 +69,10 @@ diagnostic_for_asm (const rtx_insn *insn, const char *msg, va_list *args_ptr,
 		    diagnostic_t kind)
 {
   diagnostic_info diagnostic;
+  rich_location richloc (location_for_asm (insn));
 
   diagnostic_set_info (&diagnostic, msg, args_ptr,
-		       location_for_asm (insn), kind);
+		       &richloc, kind);
   report_diagnostic (&diagnostic);
 }
 
diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-ascii-bw.c b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-ascii-bw.c
new file mode 100644
index 0000000..8ffe2e0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-ascii-bw.c
@@ -0,0 +1,114 @@
+/* { dg-do compile } */
+/* { dg-options "-O -fdiagnostics-show-caret" } */
+
+/* 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 test1 (void)
+{
+#if 0
+  myvar = myvar.x; /* { dg-warning "test 1" } */
+
+/* { dg-begin-multiline-output "" }
+   myvar = myvar.x;
+           ~~~~~^~
+   { dg-end-multiline-output "" } */
+#endif
+}
+
+void test2 (void)
+{
+#if 0
+  myvar = myvar.x; /* { dg-warning "test 2" } */
+
+/* { dg-begin-multiline-output "" }
+   myvar = myvar.x;
+           ~~~~~^~
+   { dg-end-multiline-output "" } */
+#endif
+}
+
+void test3 (void)
+{
+#if 0
+  x = first_function () + second_function ();  /* { dg-warning "test 3" } */
+
+/* { dg-begin-multiline-output "" }
+   x = first_function () + second_function ();
+       ~~~~~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+#endif
+}
+
+
+void test4 (void)
+{
+#if 0
+  x = (first_function ()
+       + second_function ()); /* { dg-warning "test 4" } */
+
+/* { dg-begin-multiline-output "" }
+   x = (first_function ()|
+        ~~~~~~~~~~~~~~~~~+type 'float'
+        + second_function ());                              |
+        ^ ~~~~~~~~~~~~~~~~~~                                +type 'void'
+   { dg-end-multiline-output "" } */
+#endif
+}
+
+void test5 (void)
+{
+#if 0
+  x = (first_function_with_a_very_long_name (lorem, ipsum, dolor, sit, amet,
+                                            consectetur, adipiscing, elit,
+                                            sed, eiusmod, tempor,
+                                            incididunt, ut, labore, et,
+                                            dolore, magna, aliqua)
+       + second_function_with_a_very_long_name (lorem, ipsum, dolor, sit, /* { dg-warning "test 5" } */
+                                                amet, consectetur,
+                                                adipiscing, elit, sed,
+                                                eiusmod, tempor, incididunt,
+                                                ut, labore, et, dolore,
+                                                magna, aliqua));
+
+/* { dg-begin-multiline-output "" }
+   x = (first_function_with_a_very_long_name (lorem, ipsum, dolor, sit, amet,|
+        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|
+                                             consectetur, adipiscing, elit,  |
+                                             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  |
+                                             sed, eiusmod, tempor,           |
+                                             ~~~~~~~~~~~~~~~~~~~~~           +type 'float'
+                                             incididunt, ut, labore, et,     |
+                                             ~~~~~~~~~~~~~~~~~~~~~~~~~~~     |
+                                             dolore, magna, aliqua)          |
+                                             ~~~~~~~~~~~~~~~~~~~~~~          |
+        + second_function_with_a_very_long_name (lorem, ipsum, dolor, sit,                              |
+        ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|
+                                                 amet, consectetur,                                     |
+                                                 ~~~~~~~~~~~~~~~~~~                                     |
+                                                 adipiscing, elit, sed,                                 |
+                                                 ~~~~~~~~~~~~~~~~~~~~~~                                 +type 'void'
+                                                 eiusmod, tempor, incididunt,                           |
+                                                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~                           |
+                                                 ut, labore, et, dolore,                                |
+                                                 ~~~~~~~~~~~~~~~~~~~~~~~                                |
+                                                 magna, aliqua));                                       |
+                                                 ~~~~~~~~~~~~~~                                         |
+   { dg-end-multiline-output "" } */
+#endif
+}
+
+void test6 (void)
+{
+#if 0
+  float f = 98.6f; /* { dg-warning "test 6" } */
+/* { dg-begin-multiline-output "" }
+   float f = 98.6f;
+             ^~~~~
+   { dg-end-multiline-output "" } */
+#endif
+}
diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-ascii-color.c b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-ascii-color.c
new file mode 100644
index 0000000..dba851d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-ascii-color.c
@@ -0,0 +1,35 @@
+/* { dg-do compile } */
+/* { dg-options "-O -fdiagnostics-show-caret -fplugin-arg-diagnostic_plugin_test_show_locus-color" } */
+
+/* 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 test1 (void)
+{
+#if 0
+  myvar = myvar.x; /* { dg-warning "test 1" } */
+
+/* { dg-begin-multiline-output "" }
+   myvar = [32m[Kmyvar[m[K[01;35m[K.[m[K[34m[Kx[m[K;
+           [32m[K~~~~~[m[K[01;35m[K^[m[K[34m[K~
+[m[K
+   { dg-end-multiline-output "" } */
+#endif
+}
+
+void test2 (void)
+{
+#if 0
+  myvar = myvar.x; /* { dg-warning "test 2" } */
+
+/* { dg-begin-multiline-output "" }
+   myvar = [32m[Kmyvar[m[K[01;35m[K.[m[K[34m[Kx[m[K;
+           [32m[K~~~~~[m[K[01;35m[K^[m[K[34m[K~
+[m[K
+   { dg-end-multiline-output "" } */
+#endif
+}
diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-utf-8-bw.c b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-utf-8-bw.c
new file mode 100644
index 0000000..a04851f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-utf-8-bw.c
@@ -0,0 +1,58 @@
+/* { dg-do compile } */
+/* { dg-options "-O -fdiagnostics-show-caret -fplugin-arg-diagnostic_plugin_test_show_locus-force-utf8" } */
+
+/* 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 test1 (void)
+{
+#if 0
+  myvar = myvar.x; /* { dg-warning "test 1" } */
+
+/* { dg-begin-multiline-output "" }
+   myvar = myvar.x;
+           ─────▲─
+   { dg-end-multiline-output "" } */
+#endif
+}
+
+void test2 (void)
+{
+#if 0
+  myvar = myvar.x; /* { dg-warning "test 2" } */
+
+/* { dg-begin-multiline-output "" }
+   myvar = myvar.x;
+           ─────▲─
+   { dg-end-multiline-output "" } */
+#endif
+}
+
+void test3 (void)
+{
+#if 0
+  x = first_function () + second_function ();  /* { dg-warning "test 3" } */
+
+/* { dg-begin-multiline-output "" }
+   x = first_function () + second_function ();
+       ───────────────── ▲ ──────────────────
+   { dg-end-multiline-output "" } */
+#endif
+}
+
+/* FIXME: missing test4 and test5 for now (regex issues).  */
+
+void test6 (void)
+{
+#if 0
+  float f = 98.6f; /* { dg-warning "test 6" } */
+/* { dg-begin-multiline-output "" }
+   float f = 98.6f;
+             ▲────
+   { dg-end-multiline-output "" } */
+#endif
+}
diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-utf-8-color.c b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-utf-8-color.c
new file mode 100644
index 0000000..85b8186
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-show-locus-utf-8-color.c
@@ -0,0 +1,62 @@
+/* { dg-do compile } */
+/* { dg-options "-O -fdiagnostics-show-caret -fplugin-arg-diagnostic_plugin_test_show_locus-force-utf8 -fplugin-arg-diagnostic_plugin_test_show_locus-color" } */
+
+/* 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 test1 (void)
+{
+#if 0
+  myvar = myvar.x; /* { dg-warning "test 1" } */
+
+/* { dg-begin-multiline-output "" }
+   myvar = [32m[Kmyvar[m[K[01;35m[K.[m[K[34m[Kx[m[K;
+           [32m[K─────[m[K[01;35m[K▲[m[K[34m[K─
+[m[K
+   { dg-end-multiline-output "" } */
+#endif
+}
+
+void test2 (void)
+{
+#if 0
+  myvar = myvar.x; /* { dg-warning "test 2" } */
+
+/* { dg-begin-multiline-output "" }
+   myvar = [32m[Kmyvar[m[K[01;35m[K.[m[K[34m[Kx[m[K;
+           [32m[K─────[m[K[01;35m[K▲[m[K[34m[K─
+[m[K
+   { dg-end-multiline-output "" } */
+#endif
+}
+
+void test3 (void)
+{
+#if 0
+  x = first_function () + second_function ();  /* { dg-warning "test 3" } */
+
+/* { dg-begin-multiline-output "" }
+   x = [32m[Kfirst_function ()[m[K [01;35m[K+[m[K [34m[Ksecond_function ()[m[K;
+       [32m[K─────────────────[m[K [01;35m[K▲[m[K [34m[K──────────────────
+[m[K
+   { dg-end-multiline-output "" } */
+#endif
+}
+
+/* FIXME: missing test4 and test5 for now (regex issues).  */
+
+void test6 (void)
+{
+#if 0
+  float f = 98.6f; /* { dg-warning "test 6" } */
+/* { dg-begin-multiline-output "" }
+   float f = [01;35m[K98.6f[m[K;
+             [01;35m[K▲────
+[m[K
+   { dg-end-multiline-output "" } */
+#endif
+}
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
new file mode 100644
index 0000000..867ef6c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_show_locus.c
@@ -0,0 +1,353 @@
+/* { dg-options "-O" } */
+
+/* This plugin exercises the diagnostics-printing code.
+
+   The goal is to unit-test the range-printing code without needing any
+   correct range data within the compiler's IR.  We can't use any real
+   diagnostics for this, so we have to fake it, hence this plugin.
+
+   There are four test files used with this code:
+
+     diagnostic-test-show-locus-ascii-bw.c
+     ..........................-ascii-color.c
+     ..........................-utf-8-bw.c
+     ..........................-utf-8-color.c
+
+   to exercise the different combinations of:
+
+     - ASCII vs UTF-8
+     - uncolored vs colored output.
+
+   by supplying plugin arguments to hack in the desired behavior:
+
+     -fplugin-arg-diagnostic_plugin_test_show_locus-force-utf8
+     -fplugin-arg-diagnostic_plugin_test_show_locus-color
+
+   The test files contain functions, but the body of each
+   function is disabled using the preprocessor.  The plugin detects
+   the functions by name, and inject diagnostics within them, using
+   hard-coded locations relative to the top of each function.
+
+   The plugin uses a function "get_loc" below to map from line/column
+   numbers to source_location, and this relies on input_location being in
+   the same ordinary line_map as the locations in question.  The plugin
+   runs after parsing, so input_location will be at the end of the file.
+
+   This need for all of the test code to be in a single ordinary line map
+   means that each test file needs to have a very long line near the top
+   (potentially to cover the extra byte-count of UTF-8 or colorized data),
+   to ensure that further very long lines don't start a new linemap.
+   This also means that we can't use macros in the test files.  */
+
+#include "gcc-plugin.h"
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "tree.h"
+#include "stringpool.h"
+#include "toplev.h"
+#include "basic-block.h"
+#include "hash-table.h"
+#include "vec.h"
+#include "ggc.h"
+#include "basic-block.h"
+#include "tree-ssa-alias.h"
+#include "internal-fn.h"
+#include "gimple-fold.h"
+#include "tree-eh.h"
+#include "gimple-expr.h"
+#include "is-a.h"
+#include "gimple.h"
+#include "gimple-iterator.h"
+#include "tree.h"
+#include "tree-pass.h"
+#include "intl.h"
+#include "plugin-version.h"
+#include "diagnostic.h"
+#include "context.h"
+#include "gcc-rich-location.h"
+#include "print-tree.h"
+#include "box-drawing.h"
+
+/* FIXME: (dmalcolm)
+   This plugin is currently the only user of
+     gcc_rich_location::add_range_with_caption
+   As such, the symbol is present in libbackend.a, but not in "cc1",
+   and running the plugin fails with a linker error:
+     ./diagnostic_plugin_test_show_locus.so: undefined symbol: _ZN17gcc_rich_location22add_range_with_captionEjjP18diagnostic_contextPKcz
+   which c++filt tells us is:
+     ./diagnostic_plugin_test_show_locus.so: undefined symbol: gcc_rich_location::add_range_with_caption(unsigned int, unsigned int, diagnostic_context*, char const*, ...)
+
+   I've tried various workarounds (adding DEBUG_FUNCTION to the
+   method, taking its address), but can't seem to fix it that way.
+   So as a nasty workaround, the following material is copied&pasted
+   from gcc-rich-location.c: */
+
+void
+gcc_rich_location::add_range_with_caption (location_t start, location_t finish,
+					   diagnostic_context *context,
+					   const char *gmsgid, ...)
+{
+  gcc_assert (context);
+  gcc_assert (gmsgid);
+
+  va_list ap;
+  va_start (ap, gmsgid);
+
+  char *caption = expand_caption_va (context, gmsgid, &ap);
+  add_range (start, finish, caption, BUFFER_OWNERSHIP_GIVEN, false);
+
+  va_end (ap);
+}
+
+char *
+gcc_rich_location::expand_caption_va (diagnostic_context *context,
+				      const char *gmsgid, va_list *args)
+{
+  gcc_assert (context);
+  gcc_assert (gmsgid);
+
+  /* Only bother if show-caret is enabled.  */
+  if (!context->show_caret)
+    return NULL;
+
+  /* Format the text, and return a copy.  */
+  pretty_printer * const pp = context->printer;
+  char *result;
+  text_info text;
+  text.err_no = errno;
+  text.args_ptr = args;
+  text.format_spec = G_(gmsgid); //_(gmsgid);
+  pp_format (pp, &text);
+  pp_output_formatted_text (pp);
+  result = xstrdup (pp_formatted_text (pp));
+  pp_clear_output_area (pp);
+  return result;
+}
+
+/* FIXME: end of material taken from gcc-rich-location.c */
+
+int plugin_is_GPL_compatible;
+
+const pass_data pass_data_test_show_locus =
+{
+  GIMPLE_PASS, /* type */
+  "test_show_locus", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_NONE, /* tv_id */
+  PROP_ssa, /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  0, /* todo_flags_finish */
+};
+
+class pass_test_show_locus : public gimple_opt_pass
+{
+public:
+  pass_test_show_locus(gcc::context *ctxt)
+    : gimple_opt_pass(pass_data_test_show_locus, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  bool gate (function *) { return true; }
+  virtual unsigned int execute (function *);
+
+}; // class pass_test_show_locus
+
+/* FIXME: description here.
+   COL_NUM uses the Emacs convention of 0-based column numbers.  */
+static source_location
+get_loc (unsigned int line_num, unsigned int col_num)
+{
+  /* Use input_location to get the relevant line_map */
+  const struct line_map_ordinary *line_map
+    = (const line_map_ordinary *)(linemap_lookup (line_table,
+						  input_location));
+
+  /* Convert from 0-based column numbers to 1-based column numbers.  */
+  source_location loc
+    = linemap_position_for_line_and_column (line_map,
+					    line_num, col_num + 1);
+
+  return loc;
+}
+
+/* Was "color" passed in as a plugin argument?  */
+static bool force_show_locus_color = false;
+
+/* We want to verify the colorized output of diagnostic_show_locus,
+   but turning on colorization for everything confuses "dg-warning" etc.
+   Hence we special-case it within this plugin by using this modified
+   version of default_diagnostic_finalizer, which, if "color" is
+   passed in as a plugin argument turns on colorization, but just
+   for diagnostic_show_locus.  */
+
+static void
+custom_diagnostic_finalizer (diagnostic_context *context,
+			     diagnostic_info *diagnostic)
+{
+  bool old_show_color = pp_show_color (context->printer);
+  if (force_show_locus_color)
+    pp_show_color (context->printer) = true;
+  diagnostic_show_locus (context, diagnostic);
+  pp_show_color (context->printer) = old_show_color;
+
+  pp_destroy_prefix (context->printer);
+  pp_newline_and_flush (context->printer);
+}
+
+/* Exercise the diagnostic machinery to emit various warnings,
+   for use by diagnostic-test-show-locus-*.c.
+
+   We inject each warning relative to the start of a function,
+   which avoids lots of hardcoded absolute locations.  */
+
+static void
+test_show_locus (function *fun)
+{
+  tree fndecl = fun->decl;
+  tree identifier = DECL_NAME (fndecl);
+  const char *fnname = IDENTIFIER_POINTER (identifier);
+  location_t fnstart = fun->function_start_locus;
+  int fnstart_line = LOCATION_LINE (fnstart);
+
+  diagnostic_finalizer (global_dc) = custom_diagnostic_finalizer;
+
+  /* Test 1.  */
+  if (0 == strcmp (fnname, "test1"))
+    {
+      const int line = fnstart_line + 2;
+      rich_location richloc (get_loc (line, 15));
+      richloc.add_range (get_loc (line, 10), get_loc (line, 14));
+      richloc.add_range (get_loc (line, 16), get_loc (line, 16));
+      warning_at_rich_loc (&richloc, 0, "test 1");
+    }
+
+  /* Test 2: as before, but add captions.  */
+  if (0 == strcmp (fnname, "test2"))
+    {
+      const int line = fnstart_line + 2;
+      gcc_rich_location richloc (get_loc (line, 15));
+      richloc.add_range_with_caption (get_loc (line, 10), get_loc (line, 14),
+				      global_dc,
+				      "hello");
+      richloc.add_range_with_caption (get_loc (line, 16), get_loc (line, 16),
+				      global_dc,
+				      "world");
+      warning_at_rich_loc (&richloc, 0, "test 2");
+    }
+
+  /* Test 3.  */
+  if (0 == strcmp (fnname, "test3"))
+    {
+      const int line = fnstart_line + 2;
+      gcc_rich_location richloc (get_loc (line, 24));
+      richloc.add_range_with_caption (get_loc (line, 6),
+				      get_loc (line, 22),
+				      global_dc,
+				      "type %qT", float_type_node);
+      richloc.add_range_with_caption (get_loc (line, 26),
+				      get_loc (line, 43),
+				      global_dc,
+				      "type %qT", void_type_node);
+      warning_at_rich_loc (&richloc, 0, "test 3");
+    }
+
+  /* Test 4.  */
+  if (0 == strcmp (fnname, "test4"))
+    {
+      const int line = fnstart_line + 2;
+      gcc_rich_location richloc (get_loc (line + 1, 7));
+      richloc.add_range_with_caption (get_loc (line, 7),
+				      get_loc (line, 23),
+				      global_dc,
+				      "type %qT", float_type_node);
+      richloc.add_range_with_caption (get_loc (line + 1, 9),
+				      get_loc (line + 1, 26),
+				      global_dc,
+				      "type %qT", void_type_node);
+      warning_at_rich_loc (&richloc, 0, "test 4");
+    }
+
+  /* Test 5.  Multiline example with captions.  */
+  if (0 == strcmp (fnname, "test5"))
+    {
+      const int line = fnstart_line + 2;
+      gcc_rich_location richloc (get_loc (line + 5, 7));
+      richloc.add_range_with_caption (get_loc (line, 7),
+				      get_loc (line + 4, 65),
+				      global_dc,
+				      "type %qT", float_type_node);
+      richloc.add_range_with_caption (get_loc (line + 5, 9),
+				      get_loc (line + 10, 61),
+				      global_dc,
+				      "type %qT", void_type_node);
+      warning_at_rich_loc (&richloc, 0, "test 5");
+    }
+
+  /* Test 6: example of a single-range location where the range extends
+     beyond the caret.  */
+  if (0 == strcmp (fnname, "test6"))
+    {
+      const int line = fnstart_line + 2;
+      source_range src_range;
+      src_range.m_start = get_loc (line, 12);
+      src_range.m_finish = get_loc (line, 16);
+      gcc_rich_location richloc (src_range);
+      warning_at_rich_loc (&richloc, 0, "test 6");
+    }
+}
+
+unsigned int
+pass_test_show_locus::execute (function *fun)
+{
+  test_show_locus (fun);
+  return 0;
+}
+
+static gimple_opt_pass *
+make_pass_test_show_locus (gcc::context *ctxt)
+{
+  return new pass_test_show_locus (ctxt);
+}
+
+int
+plugin_init (struct plugin_name_args *plugin_info,
+	     struct plugin_gcc_version *version)
+{
+  struct register_pass_info pass_info;
+  const char *plugin_name = plugin_info->base_name;
+  int argc = plugin_info->argc;
+  struct plugin_argument *argv = plugin_info->argv;
+
+  if (!plugin_default_version_check (version, &gcc_version))
+    return 1;
+
+  for (int i = 0; i < argc; i++)
+    {
+      if (0 == strcmp (argv[i].key, "force-utf8"))
+	{
+	  /* Forcibly reinit-box-drawing to use UTF-8, so that
+	     we can exercise this even though LANG=C.  */
+	  g_line_art.init (1);
+
+	  /* We also need to reset the carets within the diagnostic
+	     context to use the new default caret.  */
+	  for (int j = 0; j < rich_location::MAX_RANGES; j++)
+	    global_dc->caret_chars[j] = g_line_art.default_caret;
+	}
+      else if (0 == strcmp (argv[i].key, "color"))
+	force_show_locus_color = true;
+    }
+
+  pass_info.pass = make_pass_test_show_locus (g);
+  pass_info.reference_pass_name = "ssa";
+  pass_info.ref_pass_instance_number = 1;
+  pass_info.pos_op = PASS_POS_INSERT_AFTER;
+  register_callback (plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL,
+		     &pass_info);
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/plugin/plugin.exp b/gcc/testsuite/gcc.dg/plugin/plugin.exp
index 39fab6e..1017044 100644
--- a/gcc/testsuite/gcc.dg/plugin/plugin.exp
+++ b/gcc/testsuite/gcc.dg/plugin/plugin.exp
@@ -63,6 +63,11 @@ set plugin_test_list [list \
     { start_unit_plugin.c start_unit-test-1.c } \
     { finish_unit_plugin.c finish_unit-test-1.c } \
     { wide-int_plugin.c wide-int-test-1.c } \
+    { diagnostic_plugin_test_show_locus.c \
+	  diagnostic-test-show-locus-ascii-bw.c \
+	  diagnostic-test-show-locus-ascii-color.c \
+	  diagnostic-test-show-locus-utf-8-bw.c \
+	  diagnostic-test-show-locus-utf-8-color.c } \
 ]
 
 foreach plugin_test $plugin_test_list {
diff --git a/gcc/testsuite/lib/gcc-dg.exp b/gcc/testsuite/lib/gcc-dg.exp
index 7ce71df..322583b 100644
--- a/gcc/testsuite/lib/gcc-dg.exp
+++ b/gcc/testsuite/lib/gcc-dg.exp
@@ -29,6 +29,7 @@ load_lib libgloss.exp
 load_lib target-libpath.exp
 load_lib torture-options.exp
 load_lib fortran-modules.exp
+load_lib multiline.exp
 
 # We set LC_ALL and LANG to C so that we get the same error messages as expected.
 setenv LC_ALL C
diff --git a/gcc/tree-diagnostic.c b/gcc/tree-diagnostic.c
index 79048c5..13acb22 100644
--- a/gcc/tree-diagnostic.c
+++ b/gcc/tree-diagnostic.c
@@ -289,7 +289,7 @@ default_tree_printer (pretty_printer *pp, text_info *text, const char *spec,
     }
 
   if (set_locus)
-    text->set_location (0, DECL_SOURCE_LOCATION (t));
+    text->set_location (0, DECL_SOURCE_LOCATION (t), true);
 
   if (DECL_P (t))
     {
diff --git a/gcc/tree-pretty-print.c b/gcc/tree-pretty-print.c
index 7cd1fe7..3c34d51 100644
--- a/gcc/tree-pretty-print.c
+++ b/gcc/tree-pretty-print.c
@@ -3602,7 +3602,7 @@ void
 percent_K_format (text_info *text)
 {
   tree t = va_arg (*text->args_ptr, tree), block;
-  text->set_location (0, EXPR_LOCATION (t));
+  text->set_location (0, EXPR_LOCATION (t), true);
   gcc_assert (pp_ti_abstract_origin (text) != NULL);
   block = TREE_BLOCK (t);
   *pp_ti_abstract_origin (text) = NULL;
diff --git a/libcpp/errors.c b/libcpp/errors.c
index a33196e..62326d1 100644
--- a/libcpp/errors.c
+++ b/libcpp/errors.c
@@ -57,7 +57,8 @@ cpp_diagnostic (cpp_reader * pfile, int level, int reason,
 
   if (!pfile->cb.error)
     abort ();
-  ret = pfile->cb.error (pfile, level, reason, src_loc, 0, _(msgid), ap);
+  rich_location richloc (src_loc);
+  ret = pfile->cb.error (pfile, level, reason, &richloc, _(msgid), ap);
 
   return ret;
 }
@@ -132,14 +133,15 @@ cpp_warning_syshdr (cpp_reader * pfile, int reason, const char *msgid, ...)
 ATTRIBUTE_FPTR_PRINTF(6,0)
 static bool
 cpp_diagnostic_with_line (cpp_reader * pfile, int level, int reason,
-		          source_location src_loc, unsigned int column,
+		          source_location src_loc, unsigned int /*column*/,
 		          const char *msgid, va_list *ap)
 {
   bool ret;
   
   if (!pfile->cb.error)
     abort ();
-  ret = pfile->cb.error (pfile, level, reason, src_loc, column, _(msgid), ap);
+  rich_location richloc (src_loc); //, column);
+  ret = pfile->cb.error (pfile, level, reason, &richloc, _(msgid), ap);
 
   return ret;
 }
diff --git a/libcpp/include/cpplib.h b/libcpp/include/cpplib.h
index 5eaea6b..a2bdfa0 100644
--- a/libcpp/include/cpplib.h
+++ b/libcpp/include/cpplib.h
@@ -573,9 +573,9 @@ struct cpp_callbacks
 
   /* Called to emit a diagnostic.  This callback receives the
      translated message.  */
-  bool (*error) (cpp_reader *, int, int, source_location, unsigned int,
+  bool (*error) (cpp_reader *, int, int, rich_location *,
 		 const char *, va_list *)
-       ATTRIBUTE_FPTR_PRINTF(6,0);
+       ATTRIBUTE_FPTR_PRINTF(5,0);
 
   /* Callbacks for when a macro is expanded, or tested (whether
      defined or not at the time) in #ifdef, #ifndef or "defined".  */
diff --git a/libcpp/include/line-map.h b/libcpp/include/line-map.h
index bc747c1..239be35 100644
--- a/libcpp/include/line-map.h
+++ b/libcpp/include/line-map.h
@@ -118,6 +118,52 @@ typedef unsigned int linenum_type;
   libcpp/location-example.txt.  */
 typedef unsigned int source_location;
 
+/* A range of source locations.
+
+   Ranges are half-open:
+   m_start is the first location within the range, whereas
+   m_finish is the first location *after* the range.
+
+   We may need a more compact way to store these, but for now,
+   let's do it the simple way, as a pair.  */
+struct GTY(()) source_range
+{
+  source_location m_start;
+  source_location m_finish;
+
+  void debug (const char *msg) const;
+
+  /* FIXME: can we simply use constructors?  */
+  static source_range from_location (source_location loc)
+  {
+    source_range result;
+    result.m_start = loc;
+    result.m_finish = loc;
+    return result;
+  }
+};
+typedef struct source_range source_range;
+extern const source_range UNKNOWN_SOURCE_RANGE;
+
+/* FIXME.  */
+class range_or_loc : public source_range
+{
+ public:
+  range_or_loc (source_location loc)
+    {
+      m_start = loc;
+      m_finish = loc;
+    }
+
+  range_or_loc (source_range src_range)
+    {
+      m_start = src_range.m_start;
+      m_finish = src_range.m_finish;
+    }
+};
+
+typedef struct rich_location rich_location;
+
 /* Memory allocation function typedef.  Works like xrealloc.  */
 typedef void *(*line_map_realloc) (void *, size_t);
 
@@ -1015,6 +1061,187 @@ typedef struct
   bool sysp;
 } expanded_location;
 
+/* Both gcc and emacs number source *lines* starting at 1, but
+   they have differing conventions for *columns*.
+
+   GCC uses a 1-based convention for source columns,
+   whereas Emacs's M-x column-number-mode uses a 0-based convention.
+
+   For example, an error in the initial, left-hand
+   column of source line 3 is reported by GCC as:
+
+      some-file.c:3:1: error: ...etc...
+
+   On navigating to the location of that error in Emacs
+   (e.g. via "next-error"),
+   the locus is reported in the Mode Line
+   (assuming M-x column-number-mode) as:
+
+     some-file.c   10%   (3, 0)
+
+   i.e. "3:1:" in GCC corresponds to "(3, 0)" in Emacs.  */
+
+/* Ranges are closed
+   m_start is the first location within the range, and
+   m_finish is the last location within the range.  */
+struct location_range
+{
+  expanded_location m_start;
+  expanded_location m_finish;
+
+  /* Should a caret be drawn for this range?  Typically this is
+     true for the 0th range, and false for subsequent ranges,
+     but the Fortran frontend overrides this for rendering things like:
+
+       x = x + y
+           1   2
+       Error: Shapes for operands at (1) and (2) are not conformable
+
+     where "1" and "2" are notionally carets.  */
+  bool m_show_caret_p;
+
+  /* Caption, if any.  If non-NULL, this is dynamically allocated
+     and must be freed.  */
+  char *m_caption;
+
+  bool contains_point (int row, int column) const;
+};
+
+/* A "rich" source code location.  A position, along with
+   one or more ranges.  Each range may optionally have a caption.
+   The zeroth range is associated with the caret.
+   This is subclassed by gcc (class gcc_rich_location) to add additional
+   methods; see gcc/gcc-rich-location.h.  */
+
+/* Should the callee take ownership of char *, or make a copy?  */
+enum buffer_ownership
+{
+  BUFFER_OWNERSHIP_GIVEN,   /* Take ownership.  */
+  BUFFER_OWNERSHIP_BORROWED /* Make a copy.  */
+};
+
+class rich_location
+{
+ public:
+  /* Constructors.  */
+
+  /* Constructing from a location.  */
+  rich_location (source_location loc);
+
+  /* Constructing from a source_range.  */
+  rich_location (source_range src_range);
+
+  /* Destructor.  */
+  virtual ~rich_location ();
+
+  /* Accessors.  */
+  source_location get_loc () const { return m_loc; }
+
+  source_location *get_loc_addr () { return &m_loc; }
+
+  void
+  add_range (source_location start, source_location finish,
+	     bool show_caret_p = false);
+
+  void
+  add_range (source_location start, source_location finish,
+	     char *caption,
+	     enum buffer_ownership ownership,
+	     bool show_caret_p);
+
+  void
+  add_range (source_range src_range,
+	     bool show_caret_p = false);
+
+  void
+  add_range (source_range src_range,
+	     char *caption,
+	     enum buffer_ownership ownership);
+
+  /* The caption, if non-NULL must already be a copy.  */
+  void
+  add_range (location_range *src_range);
+
+  void
+  set_range (unsigned int idx, source_range src_range,
+	     bool show_caret_p);
+
+  int get_first_line ();
+  int get_last_line ();
+
+  unsigned int get_num_locations () const { return m_num_ranges; }
+
+  location_range *get_range (unsigned int idx)
+  {
+    linemap_assert (idx < m_num_ranges);
+    return &m_ranges[idx];
+  }
+
+  expanded_location lazily_expand_location ();
+
+  class range_iter
+  {
+  public:
+    range_iter (rich_location *richloc);
+    bool at_end () const;
+    void next ();
+    location_range *operator * () const;
+    unsigned int index () const;
+
+  private:
+    rich_location *m_richloc;
+    unsigned int m_idx;
+  };
+
+  range_iter iter_ranges () { return range_iter (this); }
+
+public:
+  static const int MAX_RANGES = 3;
+
+protected:
+  friend class range_iter;
+
+  source_location m_loc;
+
+  unsigned int m_num_ranges;
+  location_range m_ranges[MAX_RANGES];
+
+  bool m_have_expanded_location;
+  expanded_location m_expanded_location;
+};
+
+inline
+rich_location::range_iter::range_iter (rich_location *richloc) :
+  m_richloc (richloc),
+  m_idx (0)
+{
+}
+
+inline bool
+rich_location::range_iter::at_end () const
+{
+  return m_idx >= m_richloc->m_num_ranges;
+}
+
+inline void
+rich_location::range_iter::next ()
+{
+  m_idx++;
+}
+
+inline location_range *
+rich_location::range_iter::operator * () const
+{
+  return m_richloc->get_range (m_idx);
+}
+
+inline unsigned int
+rich_location::range_iter::index () const
+{
+  return m_idx;
+}
+
+
 /* This is enum is used by the function linemap_resolve_location
    below.  The meaning of the values is explained in the comment of
    that function.  */
@@ -1158,4 +1385,13 @@ void linemap_dump (FILE *, struct line_maps *, unsigned, bool);
    specifies how many macro maps to dump.  */
 void line_table_dump (FILE *, struct line_maps *, unsigned int, unsigned int);
 
+/* The rich_location class requires a way to expand source_location instances.
+   We would directly use expand_location_to_spelling_point, which is
+   implemented in gcc/input.c, but we also need to use it for rich_location
+   within genmatch.c.
+   Hence we require client code of libcpp to implement the following
+   symbol.  */
+extern expanded_location
+linemap_client_expand_location_to_spelling_point (source_location );
+
 #endif /* !LIBCPP_LINE_MAP_H  */
diff --git a/libcpp/line-map.c b/libcpp/line-map.c
index d58cad2..c16f9d4 100644
--- a/libcpp/line-map.c
+++ b/libcpp/line-map.c
@@ -1746,3 +1746,199 @@ line_table_dump (FILE *stream, struct line_maps *set, unsigned int num_ordinary,
       fprintf (stream, "\n");
     }
 }
+
+/* class rich_location.  */
+
+/* Construct a rich_location with location LOC as its initial range.  */
+
+rich_location::rich_location (source_location loc) :
+  m_loc (loc),
+  m_num_ranges (0),
+  m_have_expanded_location (false)
+{
+  /* Set up the 0th range: */
+  add_range (loc, loc, true);
+}
+
+/* Construct a rich_location with source_range SRC_RANGE as its
+   initial range.  */
+
+rich_location::rich_location (source_range src_range)
+: m_loc (src_range.m_start),
+  m_num_ranges (0),
+  m_have_expanded_location (false)
+{
+  /* Set up the 0th range: */
+  add_range (src_range, true);
+}
+
+/* The destructor for class rich_location.  */
+
+rich_location::~rich_location ()
+{
+  for (unsigned int i = 0; i < m_num_ranges; i++)
+    free (m_ranges[i].m_caption);
+}
+
+/* FIXME.  */
+
+int
+rich_location::get_first_line ()
+{
+  lazily_expand_location ();
+  int result = m_expanded_location.line;
+  for (range_iter iter = iter_ranges (); !iter.at_end (); iter.next())
+    {
+      location_range *range = *iter;
+      if (result > range->m_start.line)
+	result = range->m_start.line;
+    }
+  return result;
+}
+
+/* FIXME.  */
+
+int
+rich_location::get_last_line ()
+{
+  lazily_expand_location ();
+  int result = m_expanded_location.line;
+  for (range_iter iter = iter_ranges (); !iter.at_end (); iter.next())
+    {
+      location_range *range = *iter;
+      if (result < range->m_finish.line)
+	result = range->m_finish.line;
+    }
+  return result;
+}
+
+/* FIXME.  */
+
+expanded_location
+rich_location::lazily_expand_location ()
+{
+  if (!m_have_expanded_location)
+    {
+      m_expanded_location
+	= linemap_client_expand_location_to_spelling_point (m_loc);
+      m_have_expanded_location = true;
+    }
+
+  return m_expanded_location;
+}
+
+/* Add the given range, with no caption.  */
+
+void
+rich_location::add_range (source_location start, source_location finish,
+			  bool show_caret_p)
+{
+  linemap_assert (m_num_ranges < MAX_RANGES);
+
+  add_range (start, finish, NULL, BUFFER_OWNERSHIP_GIVEN, show_caret_p);
+}
+
+/* Add the given range, with a caption, taking a copy if OWNERSHIP
+   is BUFFER_OWNERSHIP_BORROWED, or assuming ownership if OWNERSHIP
+   is BUFFER_OWNERSHIP_GIVEN.  */
+
+void
+rich_location::add_range (source_location start, source_location finish,
+			  char *caption,
+			  enum buffer_ownership ownership,
+			  bool show_caret_p)
+{
+  linemap_assert (m_num_ranges < MAX_RANGES);
+
+  location_range *range = &m_ranges[m_num_ranges++];
+  range->m_start = linemap_client_expand_location_to_spelling_point (start);
+  range->m_finish = linemap_client_expand_location_to_spelling_point (finish);
+  range->m_show_caret_p = show_caret_p;
+  if (ownership == BUFFER_OWNERSHIP_BORROWED)
+    range->m_caption = caption ? xstrdup (caption) : NULL;
+  else
+    range->m_caption = caption;
+}
+
+/* Add the given range, with no caption.  */
+
+void
+rich_location::add_range (source_range src_range, bool show_caret_p)
+{
+  linemap_assert (m_num_ranges < MAX_RANGES);
+
+  add_range (src_range.m_start, src_range.m_finish,
+	     NULL, BUFFER_OWNERSHIP_GIVEN,
+	     show_caret_p);
+}
+
+/* Add the given range, with a caption, taking a copy if OWNERSHIP
+   is BUFFER_OWNERSHIP_BORROWED, or assuming ownership if OWNERSHIP
+   is BUFFER_OWNERSHIP_GIVEN.  */
+
+void
+rich_location::add_range (source_range src_range,
+			  char *caption,
+			  enum buffer_ownership ownership)
+{
+  linemap_assert (m_num_ranges < MAX_RANGES);
+
+  add_range (src_range.m_start, src_range.m_finish, caption, ownership,
+	     false);
+}
+
+void
+rich_location::add_range (location_range *src_range)
+{
+  linemap_assert (m_num_ranges < MAX_RANGES);
+
+  /* The caption, if non-NULL, must already be a copy.  */
+  m_ranges[m_num_ranges++] = *src_range;
+}
+
+/* Add or overwrite the range given by IDX.  It must either
+   overwrite an existing range, or add one *exactly* on the end of
+   the array.
+
+   This is primarily for use by gcc when implementing diagnostic
+   format decoders e.g. the "+" in the C/C++ frontends, for handling
+   format codes like "%q+D" (which writes the source location of a
+   tree back into range 0 of the rich_location).
+
+   If SHOW_CARET_P is true, then the range should be rendered with
+   a caret at its starting location.  This
+   is for use by the Fortran frontend, for implementing the
+   "%C" and "%L" format codes.  */
+
+void
+rich_location::set_range (unsigned int idx, source_range src_range,
+			  bool show_caret_p)
+{
+  linemap_assert (idx < MAX_RANGES);
+
+  /* We can either overwrite an existing range, or add one exactly
+     on the end of the array.  */
+  linemap_assert (idx <= m_num_ranges);
+
+  location_range *locrange = &m_ranges[idx];
+  locrange->m_start
+    = linemap_client_expand_location_to_spelling_point (src_range.m_start);
+  locrange->m_finish
+    = linemap_client_expand_location_to_spelling_point (src_range.m_finish);
+
+  locrange->m_show_caret_p = show_caret_p;
+
+  /* Are we adding a range onto the end?  */
+  if (idx == m_num_ranges)
+    {
+      locrange->m_caption = NULL;
+      m_num_ranges = idx + 1;
+    }
+
+  if (idx == 0)
+    {
+      m_loc = src_range.m_start;
+      /* Mark any cached value here as dirty.  */
+      m_have_expanded_location = false;
+    }
+}
-- 
1.8.5.3

