This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH 04/22] Reimplement diagnostic_show_locus, introducing rich_location classes


This patch rewrites diagnostic_show_locus so that it can display
underlined source ranges in addition to the main caret.

It does this by introducing a new "rich_location" class, containing
a location and (potentially) some source ranges.  These are to be
allocated on the stack when generating diagnostics.

The patch reworks various diagnostics machinery to use a
rich_location * rather than a source_location.  The "override_column"
machinery is largely eliminated.

The patch unit-tests the new diagnostic printer using a plugin, which
injects calls to print various diagnostics on some dummy source code,
and verifies the expected output, for the 4 combinations of
ASCII vs UTF-8 output and black&white vs colored output; screenshots
can be seen here:
 https://dmalcolm.fedorapeople.org/gcc/2015-09-09/ascii-bw.html
 https://dmalcolm.fedorapeople.org/gcc/2015-09-09/ascii-color.html
 https://dmalcolm.fedorapeople.org/gcc/2015-09-09/utf-8-bw.html
 https://dmalcolm.fedorapeople.org/gcc/2015-09-09/utf-8-color.html

Diagnostics already have a "severity color": errors default to bold red,
warnings to bold magenta, notes to bold cyan.  I chose to use this
severity color when coloring range 0, and after some experiments it
became natural to also use the color for the caret, since this is
notionally part of range 0.  Hence the "caret" color name goes away
from diagnostic-color.c, and we gain two new color names: "range1" and
"range2", for additional ranges.  Based on the discussion in that file
I chose green and blue (both non-bold) for these ranges.

The patch also tweaks diagnostic_report_diagnostic, so that the final
option_text for warnings is colored, using the effective severity, e.g.:
  [-Wformat]
   ^^^^^^^^ colored with the "warning" color
or:
  [-Werror=format]
   ^^^^^^^^^^^^^^ colored with the "error" color

This patch bootstraps and passes regression testing, but isn't ready
as-is:

  - The new implementation of diagnostic_show_locus doesn't yet have
    any logic for offsetting columns when displaying very wide source
    lines on a narrow terminal.  This is a regression compared to the
    existing implementation (AFAIK our test suite doesn't have
    coverage for it yet).

Other questions and notes:

  - The Fortran frontend has its own logic for printing multiple
    locations, repeatedly calling in to diagnostic_print_caret_line.
    I hope the new printing logic is suitable for use by Fortran, but I
    wanted to keep the job of
      "introducing range-capable printing logic"
    separate from that of
      "updating Fortran diagnostics to use it",
    since I'm not very familiar with Fortran, and what is desirable
    there.  Hence to faithfully preserve the existing behavior, I
    introduced a flag into the diagnostic_context:
      "frontend_calls_diagnostic_print_caret_line_p"
    which is set by the Fortran frontend, and makes
    diagnostic_show_locus use the existing printing logic.  Hopefully
    that's acceptable, say, as a migration path.

  - I tried losing the "_at_richloc" suffix to "warning_at_richloc" etc,
    and just having an overload (e.g. of "warning_at"), but we then run
    into lots of calls to e.g.
       warning_at (0,
    where the "0" means "UNKNOWN_LOCATION", and this would become a
    compilation errors, due to ambiguity of the overload (0 location_t
    vs a NULL rich_location *).
    The call sites of the above form could be changed to explicitly use
    UNKNOWN_LOCATION instead of 0, if desired.  I think I prefer this
    approach, though it touches more code.

  - There is a nasty hack in the test plugin due to a linker issue (due
    to it being the only user of a new C++ method, which makes it into
    libbackend.a but not cc1; see the notes in that file); help with
    resolving it cleanly would be appreciated.

Thoughts?

libcpp/ChangeLog:
	* errors.c (cpp_diagnostic): Update for change in signature
	of "error" callback.
	(cpp_diagnostic_with_line): Likewise, calling override_column
	on the rich_location.
	* include/cpplib.h (struct cpp_callbacks): Within "error"
	callback, convert param from source_location to rich_location *,
	and drop column_override param.
	* include/line-map.h (struct source_range): New struct.
	(struct location_range): New struct.
	(enum buffer_ownership): New enum.
	(class rich_location): New class.
	(linemap_client_expand_location_to_spelling_point): New declaration.
	* line-map.c (rich_location::rich_location): New ctors.
	(rich_location::~rich_location): New dtor.
	(rich_location::get_first_line): New method.
	(rich_location::get_last_line): New method.
	(rich_location::lazily_expand_location): New method.
	(rich_location::override_column): New method.
	(rich_location::add_range): New methods.
	(rich_location::set_range): New method.

gcc/ChangeLog:
	* Makefile.in (OBJS): Add gcc-rich-location.o.
	(OBJS-libcommon): Add box-drawing.o.
	* box-drawing.c: New file.
	* box-drawing.h: New file.
	* diagnostic-color.c (color_dict): Eliminate "caret"; add "range1"
	and "range2".
	(parse_gcc_colors): Update comment to describe default GCC_COLORS.
	* diagnostic-core.h (warning_at_rich_loc): New declaration.
	(error_at_rich_loc): New declaration.
	(permerror_at_rich_loc): New declaration.
	(inform_at_rich_loc): New declaration.
	* diagnostic-show-locus.c: Include box-drawing.h.
	(location_range::contains_point): New method.
	(get_state_at_point): New function.
	(get_x_bound_for_row): New function.
	(class colorizer): New class.
	(class per_range_info): New class.
	(class layout): New class.
	(colorizer::colorizer): New ctor.
	(colorizer::~colorizer): New dtor.
	(colorizer::set_state): New method.
	(colorizer::begin_state): New method.
	(colorizer::finish_state): New method.
	(per_range_info::per_range_info): New method.
	(per_range_info::add_uniquely_captioned_row): New method.
	(per_range_info::get_first_unique_row): New method.
	(per_range_info::get_last_unique_row): New method.
	(per_range_info::contains_line): New method.
	(get_line_width_without_trailing_whitespace): New method.
	(per_range_info::determine_location_for_caption): New method.
	(layout::layout): New ctor.
	(layout::print_line): New method.
	(layout::get_any_range): New method.
	(layout::print_any_margin): New method.
	(show_ruler): New function.
	(diagnostic_print_ranges): New function.
	(diagnostic_show_locus): Call new function diagnostic_print_ranges,
	falling back to diagnostic_print_caret_line if the frontend has
	set frontend_calls_diagnostic_print_caret_line_p on the
	diagnostic_context.
	(diagnostic_print_caret_line): Convert params caret1 and caret2
	from char to const char *.
	(diagnostic_print_ranges): New function.
	* diagnostic.c: Include "box-drawing.h".
	(diagnostic_initialize): Initialize caret_chars from g_line_art.
	(diagnostic_set_info_translated): Convert param from location_t
	to rich_location *.  Eliminate calls to set_location on the
	message in favor of storing the rich_location ptr there.
	(diagnostic_set_info): Convert param from location_t to
	rich_location *.
	(diagnostic_build): Break out array into...
	(diagnostic_kind_color): New variable.
	(diagnostic_get_color_for_kind): New function.
	(diagnostic_report_diagnostic): Colorize the option_text
	using the color for the severity.
	(diagnostic_append_note): Update for change in signature of
	diagnostic_set_info.
	(diagnostic_append_note_at_rich_loc): New function.
	(emit_diagnostic): Update for change in signature of
	diagnostic_set_info.
	(inform): Likewise.
	(inform_at_rich_loc): New function.
	(inform_n): Update for change in signature of diagnostic_set_info.
	(warning): Likewise.
	(warning_at): Likewise.
	(warning_at_rich_loc): New function.
	(warning_n): Update for change in signature of diagnostic_set_info.
	(pedwarn): Likewise.
	(permerror): Likewise.
	(permerror_at_rich_loc): New function.
	(error): Update for change in signature of diagnostic_set_info.
	(error_n): Likewise.
	(error_at): Likewise.
	(error_at_rich_loc): New function.
	(sorry): Update for change in signature of diagnostic_set_info.
	(fatal_error): Likewise.
	(internal_error): Likewise.
	(internal_error_no_backtrace): Likewise.
	(source_range::debug): New function.
	* diagnostic.h (struct diagnostic_info): Eliminate field
	"override_column".  Add field "richloc".
	(struct diagnostic_context): Convert "caret_chars" elements from
	char to const char *.  Add field
	"frontend_calls_diagnostic_print_caret_line_p".
	(diagnostic_override_column): Eliminate this macro.
	(diagnostic_set_info): Convert param from location_t to
	rich_location *.
	(diagnostic_set_info_translated): Likewise.
	(diagnostic_append_note_at_rich_loc): New function.
	(diagnostic_num_locations): New function.
	(diagnostic_expand_location): Get the location from the
	rich_location.
	(diagnostic_print_caret_line): Convert params "caret1" and
	"caret2" from char to const char *.
	(diagnostic_get_color_for_kind): New declaration.
	* gcc-rich-location.c: New file.
	* gcc-rich-location.h: New file.
	* genmatch.c (linemap_client_expand_location_to_spelling_point): New.
	(error_cb): Update for change in signature of "error" callback.
	(fatal_at): Likewise.
	(warning_at): Likewise.
	* input.c (linemap_client_expand_location_to_spelling_point): New.
	* intl.c: Include "box-drawing.h".
	(gcc_init_libintl): Initialize g_line_art.
	* pretty-print.c (text_info::set_range): New method.
	(text_info::get_location): New method.
	* pretty-print.h (MAX_LOCATIONS_PER_MESSAGE): Eliminate this macro.
	(struct text_info): Eliminate "locations" array in favor of
	"m_richloc", a rich_location *.
	(textinfo::set_location): Add a "caret_p" param, and reimplement
	in terms of a call to set_range.
	(textinfo::get_location): Eliminate inline implementation in favor of
	an out-of-line reimplementation.
	(textinfo::set_range): New method.
	* rtl-error.c (diagnostic_for_asm): Update for change in signature
	of diagnostic_set_info.
	* tree-diagnostic.c (default_tree_printer): Update for new
	"caret_p" param for textinfo::set_location.
	* tree-pretty-print.c (percent_K_format): Likewise.

gcc/c-family/ChangeLog:
	* c-common.c (c_cpp_error): Convert parameter from location_t to
	rich_location *.  Eliminate the "column_override" parameter and
	the call to diagnostic_override_column.
	Update the "done_lexing" clause to set range 0
	on the rich_location, rather than overwriting a location_t.
	* c-common.h (c_cpp_error): Convert parameter from location_t to
	rich_location *.  Eliminate the "column_override" parameter.

gcc/c/ChangeLog:
	* c-decl.c (warn_defaults_to): Update for change in signature
	of diagnostic_set_info.
	* c-errors.c (pedwarn_c99): Likewise.
	(pedwarn_c90): Likewise.
	* c-objc-common.c (c_tree_printer): Update for new "caret_p" param
	for textinfo::set_location.

gcc/cp/ChangeLog:
	* error.c (cp_printer): Update for new "caret_p" param for
	textinfo::set_location.
	(pedwarn_cxx98): Update for change in signature of
	diagnostic_set_info.

gcc/fortran/ChangeLog:
	* cpp.c (cb_cpp_error): Convert parameter from location_t to
	rich_location *.  Eliminate the "column_override" parameter.
	* error.c: Include "box-drawing.h".
	(gfc_warning): Update for change in signature of
	diagnostic_set_info.
	(gfc_format_decoder): Update handling of %C/%L for changes
	to struct text_info.
	(gfc_diagnostic_starter): Use richloc when determining whether to
	print one locus or two.
	(gfc_warning_now_at): Update for change in signature of
	diagnostic_set_info.
	(gfc_warning_now): Likewise.
	(gfc_error_now): Likewise.
	(gfc_fatal_error): Likewise.
	(gfc_error): Likewise.
	(gfc_internal_error): Likewise.
	(gfc_diagnostics_init): Update initialization of caret_chars from char
	to const char *.  Set frontend_calls_diagnostic_print_caret_line_p.
	(gfc_diagnostics_finish): Update resetting of caret_chars from char
	to const char *.

gcc/testsuite/ChangeLog:
	* gcc.dg/plugin/diagnostic-test-show-locus-ascii-bw.c: New file.
	* gcc.dg/plugin/diagnostic-test-show-locus-ascii-color.c: New file.
	* gcc.dg/plugin/diagnostic-test-show-locus-utf-8-bw.c: New file.
	* gcc.dg/plugin/diagnostic-test-show-locus-utf-8-color.c: New file.
	* gcc.dg/plugin/diagnostic_plugin_test_show_locus.c: New file.
	* gcc.dg/plugin/plugin.exp (plugin_test_list): Add the above.
	* lib/gcc-dg.exp: Load multiline.exp.
---
 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                        | 877 ++++++++++++++++++++-
 gcc/diagnostic.c                                   | 200 ++++-
 gcc/diagnostic.h                                   |  48 +-
 gcc/fortran/cpp.c                                  |  13 +-
 gcc/fortran/error.c                                |  43 +-
 gcc/gcc-rich-location.c                            |  93 +++
 gcc/gcc-rich-location.h                            |  54 ++
 gcc/genmatch.c                                     |  27 +-
 gcc/input.c                                        |   7 +
 gcc/intl.c                                         |   9 +
 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     | 361 +++++++++
 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                                    |   7 +-
 libcpp/include/cpplib.h                            |   4 +-
 libcpp/include/line-map.h                          | 236 ++++++
 libcpp/line-map.c                                  | 208 +++++
 37 files changed, 2569 insertions(+), 145 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 f183b22..c472696 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 \
@@ -1513,7 +1514,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 9758b9e..c02ea39 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -10451,15 +10451,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;
@@ -10500,11 +10499,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 74d1bc1..bb17fcc 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -981,9 +981,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 b83c584..e6b6ba5 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 faf8744..19ca8c3 100644
--- a/gcc/cp/error.c
+++ b/gcc/cp/error.c
@@ -3554,7 +3554,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
@@ -3668,9 +3668,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..9216c4c 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>
@@ -36,6 +37,10 @@ along with GCC; see the file COPYING3.  If not see
 # include <sys/ioctl.h>
 #endif
 
+static void
+diagnostic_print_ranges (diagnostic_context * context,
+			 const diagnostic_info *diagnostic);
+
 /* If LINE is longer than MAX_WIDTH, and COLUMN is not smaller than
    MAX_WIDTH by some margin, then adjust the start of the line such
    that the COLUMN is smaller than MAX_WIDTH minus the margin.  The
@@ -60,11 +65,808 @@ adjust_line (const char *line, int line_width,
   return line;
 }
 
-/* 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
-   given by DIAGNOSTIC are on the same line according to
-   diagnostic_same_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 ()
+{
+}
+
+/* This range is the only range with a caption on row ROW; record it as
+   such.  */
+
+void
+per_range_info::add_uniquely_captioned_row (int row)
+{
+  m_unique_rows.safe_push (row);
+}
+
+/* Locate the first row for which this range is the only one with a caption.  */
+
+int
+per_range_info::get_first_unique_row () const
+{
+  gcc_assert (m_unique_rows.length () > 0);
+  return m_unique_rows[0];
+}
+
+/* Locate the last row for which this range is the only one with a caption.  */
+
+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];
+}
+
+/* Is LINE uniquely captioned by this range?  */
+
+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 ());
+}
+
+/* Given a source line LINE of length LINE_WIDTH, determin the width
+   without any trailing whitespace.  */
+
+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;
+}
+
+/* Determine which row to write this range's caption text in (if any).  */
+
+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. Populate m_per_range_vec, determining
+   for each range where to draw its caption (if any).  */
+
+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);
+}
+
+/* Print the physical source code corresponding to the location of
+   this diagnostic, with additional annotations.
+   If CONTEXT has set frontend_calls_diagnostic_print_caret_line_p,
+   the code is printed using diagnostic_print_caret_line; otherwise
+   it is printed using diagnostic_print_ranges.  */
+
 void
 diagnostic_show_locus (diagnostic_context * context,
 		       const diagnostic_info *diagnostic)
@@ -75,16 +877,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 (context->frontend_calls_diagnostic_print_caret_line_p)
+    {
+      /* 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.  */
+
+      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]);
+      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 +907,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.  */
@@ -145,22 +956,52 @@ 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;
 }
+
+/* Print all source lines covered by the locations and any ranges
+   within DIAGNOSTIC, displaying one or more carets and zero or more
+   underlines as appropriate, potentially with captions.  */
+
+static 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 f40e469..060f071 100644
--- a/gcc/diagnostic.c
+++ b/gcc/diagnostic.c
@@ -31,6 +31,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>
@@ -145,8 +146,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 +237,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 +254,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 +289,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]);
@@ -775,10 +787,14 @@ 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);
 	}
@@ -858,9 +874,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, location, DK_NOTE);
+  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, richloc, DK_NOTE);
   if (context->inhibit_notes_p)
     {
       va_end (ap);
@@ -885,16 +932,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;
   }
@@ -911,9 +959,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, location, DK_NOTE);
+  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, richloc, DK_NOTE);
   report_diagnostic (&diagnostic);
   va_end (ap);
 }
@@ -926,11 +988,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);
 }
@@ -944,9 +1007,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);
@@ -964,9 +1028,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, location, DK_WARNING);
+  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, richloc, DK_WARNING);
   diagnostic.option_index = opt;
   ret = report_diagnostic (&diagnostic);
   va_end (ap);
@@ -984,11 +1066,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);
@@ -1014,9 +1098,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);
@@ -1036,9 +1121,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);
@@ -1053,9 +1157,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);
 }
@@ -1068,11 +1173,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);
 }
@@ -1083,9 +1189,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, loc, DK_ERROR);
+  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, rich_loc,
+		       DK_ERROR);
   report_diagnostic (&diagnostic);
   va_end (ap);
 }
@@ -1098,9 +1220,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);
 }
@@ -1121,9 +1244,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);
 
@@ -1139,9 +1263,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);
 
@@ -1156,9 +1281,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);
 
@@ -1222,3 +1348,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..48512b2 100644
--- a/gcc/diagnostic.h
+++ b/gcc/diagnostic.h
@@ -29,10 +29,12 @@ along with GCC; see the file COPYING3.  If not see
    list in diagnostic.def.  */
 struct diagnostic_info
 {
-  /* Text to be formatted. It also contains the location(s) for this
-     diagnostic.  */
+  /* Text to be formatted.  */
   text_info message;
-  unsigned int override_column;
+
+  /* The location at which the diagnostic is to be reported.  */
+  rich_location *richloc;
+
   /* Auxiliary data for client.  */
   void *x_data;
   /* The kind of diagnostic it is about.  */
@@ -107,7 +109,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.  */
@@ -185,6 +187,11 @@ struct diagnostic_context
   int lock;
 
   bool inhibit_notes_p;
+
+  /* Does the frontend make calls to diagnostic_print_caret_line?
+     If so, we fall back to the old implementation of
+     diagnostic_show_locus.  */
+  bool frontend_calls_diagnostic_print_caret_line_p;
 };
 
 static inline void
@@ -256,10 +263,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 +286,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,6 +317,14 @@ diagnostic_location (const diagnostic_info * diagnostic, int which = 0)
   return diagnostic->message.get_location (which);
 }
 
+/* Return the number of locations to be printed in DIAGNOSTIC.  */
+
+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.  */
@@ -317,12 +332,7 @@ diagnostic_location (const diagnostic_info * diagnostic, int which = 0)
 static inline expanded_location
 diagnostic_expand_location (const diagnostic_info * diagnostic, int which = 0)
 {
-  expanded_location s
-    = expand_location_to_spelling_point (diagnostic_location (diagnostic,
-							      which));
-  if (which == 0 && diagnostic->override_column)
-    s.column = diagnostic->override_column;
-  return s;
+  return diagnostic->richloc->get_range (which)->m_start;
 }
 
 /* This is somehow the right-side margin of a caret line, that is, we
@@ -346,7 +356,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 3825751..b5b7beb 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 */
 
@@ -773,6 +774,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;
@@ -787,7 +789,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);
@@ -938,10 +940,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;
       }
@@ -1075,7 +1079,7 @@ gfc_diagnostic_starter (diagnostic_context *context,
 
   expanded_location s1 = diagnostic_expand_location (diagnostic);
   expanded_location s2;
-  bool one_locus = diagnostic_location (diagnostic, 1) == UNKNOWN_LOCATION;
+  bool one_locus = diagnostic->richloc->get_num_locations () < 2;
   bool same_locus = false;
 
   if (!one_locus) 
@@ -1173,10 +1177,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);
@@ -1190,10 +1195,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);
@@ -1209,11 +1215,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);
 }
@@ -1226,9 +1233,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);
 
@@ -1291,6 +1299,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;
@@ -1306,7 +1315,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)
@@ -1336,9 +1345,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);
 
@@ -1470,8 +1480,9 @@ 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";
+  global_dc->frontend_calls_diagnostic_print_caret_line_p = true;
   pp_warning_buffer = new (XNEW (output_buffer)) output_buffer ();
   pp_warning_buffer->flush_p = false;
   /* pp_error_buffer is statically allocated.  This simplifies memory
@@ -1488,6 +1499,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..bdb2915
--- /dev/null
+++ b/gcc/gcc-rich-location.c
@@ -0,0 +1,93 @@
+/* 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"
+
+/* Add a range covering [START, FINISH], with a caption given
+   by translating and formatting GMSGID and any variadic args.  */
+
+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);
+}
+
+/* Translate and expand the given GMSGID and ARGS into a caption
+   (or NULL).
+
+   If non-NULL, ownership of the result transfers to the caller,
+   which must free it.  */
+
+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);
+  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 7266637..e7d8b98 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..110e1ea 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,14 @@ gcc_init_libintl (void)
 	}
 #endif
     }
+
+  bool have_utf8 = false;
+#if defined HAVE_LANGINFO_CODESET
+  if (locale_utf8)
+    have_utf8 = true;
+#endif
+
+  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 = 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
+}
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..5fc8395
--- /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
+}
+
+/* TODO: 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..a8f4bc3
--- /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 = 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
+}
+
+/* TODO: 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_plugin_test_show_locus.c b/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_show_locus.c
new file mode 100644
index 0000000..f724ef4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_show_locus.c
@@ -0,0 +1,361 @@
+/* { 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);
+  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
+
+/* Given LINE_NUM and COL_NUM, generate a source_location in the
+   current file, relative to input_location.  This relies on the
+   location being expressible in the same ordinary line_map as
+   input_location (which is typically at the end of the source file
+   when this is called).  Hence the test files we compile with this
+   plugin must have an initial very long line (to avoid long lines
+   starting a new line map), and must not use macros.
+
+   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 7c1ab85..8cc1d87 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 135f142..02009d8 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..c351c11 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;
 }
@@ -139,7 +140,9 @@ cpp_diagnostic_with_line (cpp_reader * pfile, int level, int reason,
   
   if (!pfile->cb.error)
     abort ();
-  ret = pfile->cb.error (pfile, level, reason, src_loc, column, _(msgid), ap);
+  rich_location richloc (src_loc);
+  richloc.override_column (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..53ba68b 100644
--- a/libcpp/include/line-map.h
+++ b/libcpp/include/line-map.h
@@ -118,6 +118,35 @@ 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;
+
+  /* We avoid using constructors, since various structs that
+     don't yet have constructors will embed instances of
+     source_range.  */
+
+  /* Make a source_range from a source_location.  */
+  static source_range from_location (source_location loc)
+  {
+    source_range result;
+    result.m_start = loc;
+    result.m_finish = loc;
+    return result;
+  }
+};
+
 /* Memory allocation function typedef.  Works like xrealloc.  */
 typedef void *(*line_map_realloc) (void *, size_t);
 
@@ -1015,6 +1044,204 @@ 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;
+};
+
+/* 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.  */
+};
+
+/* A "rich" source code location, for use when printing diagnostics.
+   A rich_location has a "primary location", along with zero or more
+   additional ranges.
+
+   rich_location instances are intended to be allocated on the stack
+   when generating diagnostics, and to be short-lived.
+
+   The zeroth range can be thought of as an extension of the primary
+   location within the rich_location; additional ranges may be added
+   to help the user identify other pertinent clauses in a diagnostic.
+
+   Each range may be flagged for having a caret displayed
+   at its start; typically this is the case for the zeroth
+   range.
+
+   Each range may optionally have a caption.
+
+   This class is subclassed by gcc (class gcc_rich_location) to add
+   additional methods; see gcc/gcc-rich-location.h.  */
+
+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); }
+
+  void
+  override_column (int column);
+
+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..79d8eee 100644
--- a/libcpp/line-map.c
+++ b/libcpp/line-map.c
@@ -1746,3 +1746,211 @@ 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);
+}
+
+/* Get the first line of the rich_location, either that of
+   the primary location, or of one of the ranges.  */
+
+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;
+}
+
+/* Get the last line of the rich_location, either that of
+   the primary location, or of one of the ranges.  */
+
+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;
+}
+
+/* Get an expanded_location for this rich_location's primary
+   location.  */
+
+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;
+}
+
+/* Set the column of the primary location.  */
+
+void
+rich_location::override_column (int column)
+{
+  lazily_expand_location ();
+  m_expanded_location.column = column;
+}
+
+/* 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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]