diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 7228b79f223a..753f2f36618e 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1038,6 +1038,7 @@ SYSTEM_H = system.h hwint.h $(srcdir)/../include/libiberty.h \
 	$(HASHTAB_H)
 PREDICT_H = predict.h predict.def
 CPPLIB_H = $(srcdir)/../libcpp/include/line-map.h \
+	$(srcdir)/../libcpp/include/rich-location.h \
 	$(srcdir)/../libcpp/include/cpplib.h
 CODYLIB_H = $(srcdir)/../libcody/cody.hh
 INPUT_H = $(srcdir)/../libcpp/include/line-map.h input.h
diff --git a/gcc/analyzer/analyzer.h b/gcc/analyzer/analyzer.h
index f08572bb633e..cf32d4b85b15 100644
--- a/gcc/analyzer/analyzer.h
+++ b/gcc/analyzer/analyzer.h
@@ -21,6 +21,7 @@ along with GCC; see the file COPYING3.  If not see
 #ifndef GCC_ANALYZER_ANALYZER_H
 #define GCC_ANALYZER_ANALYZER_H
 
+#include "rich-location.h"
 #include "function.h"
 #include "json.h"
 #include "tristate.h"
diff --git a/gcc/c-family/c-lex.cc b/gcc/c-family/c-lex.cc
index 06c2453c89a6..86ec679aebfe 100644
--- a/gcc/c-family/c-lex.cc
+++ b/gcc/c-family/c-lex.cc
@@ -31,6 +31,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "file-prefix-map.h" /* remap_macro_filename()  */
 #include "langhooks.h"
 #include "attribs.h"
+#include "rich-location.h"
 
 /* We may keep statistics about how long which files took to compile.  */
 static int header_time, body_time;
diff --git a/gcc/coretypes.h b/gcc/coretypes.h
index 9848cde2b97b..fe5b868fb4f3 100644
--- a/gcc/coretypes.h
+++ b/gcc/coretypes.h
@@ -156,6 +156,7 @@ struct cl_optimization;
 struct cl_option;
 struct cl_decoded_option;
 struct cl_option_handlers;
+class rich_location;
 class diagnostic_context;
 class pretty_printer;
 class diagnostic_event_id_t;
diff --git a/gcc/cp/mapper-client.cc b/gcc/cp/mapper-client.cc
index 927271952468..f1a0c4cc009a 100644
--- a/gcc/cp/mapper-client.cc
+++ b/gcc/cp/mapper-client.cc
@@ -31,6 +31,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "system.h"
 
 #include "line-map.h"
+#include "rich-location.h"
 #include "diagnostic-core.h"
 #include "mapper-client.h"
 #include "intl.h"
diff --git a/gcc/diagnostic.h b/gcc/diagnostic.h
index dbf972d25875..cbd25541f502 100644
--- a/gcc/diagnostic.h
+++ b/gcc/diagnostic.h
@@ -21,6 +21,7 @@ along with GCC; see the file COPYING3.  If not see
 #ifndef GCC_DIAGNOSTIC_H
 #define GCC_DIAGNOSTIC_H
 
+#include "rich-location.h"
 #include "pretty-print.h"
 #include "diagnostic-core.h"
 
diff --git a/gcc/edit-context.h b/gcc/edit-context.h
index 3ae9ba103ca7..71735c8b9c6f 100644
--- a/gcc/edit-context.h
+++ b/gcc/edit-context.h
@@ -22,6 +22,7 @@ along with GCC; see the file COPYING3.  If not see
 
 #include "typed-splay-tree.h"
 
+class fixit_hint;
 class edit_context;
 class edited_file;
 
diff --git a/gcc/gcc-rich-location.h b/gcc/gcc-rich-location.h
index ffba4b8abcfd..e42daa44b70c 100644
--- a/gcc/gcc-rich-location.h
+++ b/gcc/gcc-rich-location.h
@@ -20,6 +20,8 @@ along with GCC; see the file COPYING3.  If not see
 #ifndef GCC_RICH_LOCATION_H
 #define GCC_RICH_LOCATION_H
 
+#include "rich-location.h"
+
 /* A gcc_rich_location is libcpp's rich_location with additional
    helper methods for working with gcc's types.  The class is not
    copyable or assignable because rich_location isn't. */
diff --git a/gcc/genmatch.cc b/gcc/genmatch.cc
index 3488764ec640..98268ee19c9b 100644
--- a/gcc/genmatch.cc
+++ b/gcc/genmatch.cc
@@ -25,6 +25,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "system.h"
 #include "coretypes.h"
 #include <cpplib.h>
+#include "rich-location.h"
 #include "errors.h"
 #include "hash-table.h"
 #include "hash-set.h"
diff --git a/gcc/pretty-print.h b/gcc/pretty-print.h
index 9ba2c0a406e1..138c582c8a79 100644
--- a/gcc/pretty-print.h
+++ b/gcc/pretty-print.h
@@ -22,6 +22,7 @@ along with GCC; see the file COPYING3.  If not see
 #define GCC_PRETTY_PRINT_H
 
 #include "obstack.h"
+#include "rich-location.h"
 #include "diagnostic-url.h"
 
 /* Maximum number of format string arguments.  */
diff --git a/gcc/rust/rust-location.h b/gcc/rust/rust-location.h
index 9cb29b20919f..873b40f58c0c 100644
--- a/gcc/rust/rust-location.h
+++ b/gcc/rust/rust-location.h
@@ -21,6 +21,7 @@
 #ifndef RUST_LOCATION_H
 #define RUST_LOCATION_H
 
+#include "rich-location.h"
 #include "rust-system.h"
 
 // A location in an input source file.
diff --git a/libcpp/Makefile.in b/libcpp/Makefile.in
index faf6834f1ee7..1e063a5ac97f 100644
--- a/libcpp/Makefile.in
+++ b/libcpp/Makefile.in
@@ -270,7 +270,9 @@ po/$(PACKAGE).pot: $(libcpp_a_SOURCES)
 ETAGS = @ETAGS@
 
 TAGS_SOURCES = $(libcpp_a_SOURCES) internal.h system.h ucnid.h \
-    include/cpplib.h include/line-map.h include/mkdeps.h include/symtab.h
+    include/cpplib.h include/line-map.h include/mkdeps.h include/symtab.h \
+    include/rich-location.h
+
 
 TAGS: $(TAGS_SOURCES)
 	cd $(srcdir) && $(ETAGS) $(TAGS_SOURCES)
diff --git a/libcpp/include/cpplib.h b/libcpp/include/cpplib.h
index 54814f335d73..f857ffa44796 100644
--- a/libcpp/include/cpplib.h
+++ b/libcpp/include/cpplib.h
@@ -38,6 +38,8 @@ typedef struct cpp_dir cpp_dir;
 
 struct _cpp_file;
 
+class rich_location;
+
 /* The first three groups, apart from '=', can appear in preprocessor
    expressions (+= and -= are used to indicate unary + and - resp.).
    This allows a lookup table to be implemented in _cpp_parse_expr.
diff --git a/libcpp/include/line-map.h b/libcpp/include/line-map.h
index 615cb420adf7..72db310bfb75 100644
--- a/libcpp/include/line-map.h
+++ b/libcpp/include/line-map.h
@@ -1275,677 +1275,6 @@ typedef struct
   bool sysp;
 } expanded_location;
 
-class range_label;
-
-/* A hint to diagnostic_show_locus on how to print a source range within a
-   rich_location.
-
-   Typically this is SHOW_RANGE_WITH_CARET for the 0th range, and
-   SHOW_RANGE_WITHOUT_CARET for subsequent ranges,
-   but the Fortran frontend uses SHOW_RANGE_WITH_CARET repeatedly for
-   printing 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.  */
-
-enum range_display_kind
-{
-  /* Show the pertinent source line(s), the caret, and underline(s).  */
-  SHOW_RANGE_WITH_CARET,
-
-  /* Show the pertinent source line(s) and underline(s), but don't
-     show the caret (just an underline).  */
-  SHOW_RANGE_WITHOUT_CARET,
-
-  /* Just show the source lines; don't show the range itself.
-     This is for use when displaying some line-insertion fix-it hints (for
-     showing the user context on the change, for when it doesn't make sense
-     to highlight the first column on the next line).  */
-  SHOW_LINES_WITHOUT_RANGE
-};
-
-/* A location within a rich_location: a caret&range, with
-   the caret potentially flagged for display, and an optional
-   label.  */
-
-struct location_range
-{
-  location_t m_loc;
-
-  enum range_display_kind m_range_display_kind;
-
-  /* If non-NULL, the label for this range.  */
-  const range_label *m_label;
-};
-
-/* A partially-embedded vec for use within rich_location for storing
-   ranges and fix-it hints.
-
-   Elements [0..NUM_EMBEDDED) are allocated within m_embed, after
-   that they are within the dynamically-allocated m_extra.
-
-   This allows for static allocation in the common case, whilst
-   supporting the rarer case of an arbitrary number of elements.
-
-   Dynamic allocation is not performed unless it's needed.  */
-
-template <typename T, int NUM_EMBEDDED>
-class semi_embedded_vec
-{
- public:
-  semi_embedded_vec ();
-  ~semi_embedded_vec ();
-
-  unsigned int count () const { return m_num; }
-  T& operator[] (int idx);
-  const T& operator[] (int idx) const;
-
-  void push (const T&);
-  void truncate (int len);
-
- private:
-  int m_num;
-  T m_embedded[NUM_EMBEDDED];
-  int m_alloc;
-  T *m_extra;
-};
-
-/* Constructor for semi_embedded_vec.  In particular, no dynamic allocation
-   is done.  */
-
-template <typename T, int NUM_EMBEDDED>
-semi_embedded_vec<T, NUM_EMBEDDED>::semi_embedded_vec ()
-: m_num (0), m_alloc (0), m_extra (NULL)
-{
-}
-
-/* semi_embedded_vec's dtor.  Release any dynamically-allocated memory.  */
-
-template <typename T, int NUM_EMBEDDED>
-semi_embedded_vec<T, NUM_EMBEDDED>::~semi_embedded_vec ()
-{
-  XDELETEVEC (m_extra);
-}
-
-/* Look up element IDX, mutably.  */
-
-template <typename T, int NUM_EMBEDDED>
-T&
-semi_embedded_vec<T, NUM_EMBEDDED>::operator[] (int idx)
-{
-  linemap_assert (idx < m_num);
-  if (idx < NUM_EMBEDDED)
-    return m_embedded[idx];
-  else
-    {
-      linemap_assert (m_extra != NULL);
-      return m_extra[idx - NUM_EMBEDDED];
-    }
-}
-
-/* Look up element IDX (const).  */
-
-template <typename T, int NUM_EMBEDDED>
-const T&
-semi_embedded_vec<T, NUM_EMBEDDED>::operator[] (int idx) const
-{
-  linemap_assert (idx < m_num);
-  if (idx < NUM_EMBEDDED)
-    return m_embedded[idx];
-  else
-    {
-      linemap_assert (m_extra != NULL);
-      return m_extra[idx - NUM_EMBEDDED];
-    }
-}
-
-/* Append VALUE to the end of the semi_embedded_vec.  */
-
-template <typename T, int NUM_EMBEDDED>
-void
-semi_embedded_vec<T, NUM_EMBEDDED>::push (const T& value)
-{
-  int idx = m_num++;
-  if (idx < NUM_EMBEDDED)
-    m_embedded[idx] = value;
-  else
-    {
-      /* Offset "idx" to be an index within m_extra.  */
-      idx -= NUM_EMBEDDED;
-      if (NULL == m_extra)
-	{
-	  linemap_assert (m_alloc == 0);
-	  m_alloc = 16;
-	  m_extra = XNEWVEC (T, m_alloc);
-	}
-      else if (idx >= m_alloc)
-	{
-	  linemap_assert (m_alloc > 0);
-	  m_alloc *= 2;
-	  m_extra = XRESIZEVEC (T, m_extra, m_alloc);
-	}
-      linemap_assert (m_extra);
-      linemap_assert (idx < m_alloc);
-      m_extra[idx] = value;
-    }
-}
-
-/* Truncate to length LEN.  No deallocation is performed.  */
-
-template <typename T, int NUM_EMBEDDED>
-void
-semi_embedded_vec<T, NUM_EMBEDDED>::truncate (int len)
-{
-  linemap_assert (len <= m_num);
-  m_num = len;
-}
-
-class fixit_hint;
-class diagnostic_path;
-
-/* A "rich" source code location, for use when printing diagnostics.
-   A rich_location has one or more carets&ranges, where the carets
-   are optional.  These are referred to as "ranges" from here.
-   Typically the zeroth range has a caret; other ranges sometimes
-   have carets.
-
-   The "primary" location of a rich_location is the caret of range 0,
-   used for determining the line/column when printing diagnostic
-   text, such as:
-
-      some-file.c:3:1: error: ...etc...
-
-   Additional ranges may be added to help the user identify other
-   pertinent clauses in a diagnostic.
-
-   Ranges can (optionally) be given labels via class range_label.
-
-   rich_location instances are intended to be allocated on the stack
-   when generating diagnostics, and to be short-lived.
-
-   Examples of rich locations
-   --------------------------
-
-   Example A
-   *********
-      int i = "foo";
-              ^
-   This "rich" location is simply a single range (range 0), with
-   caret = start = finish at the given point.
-
-   Example B
-   *********
-      a = (foo && bar)
-          ~~~~~^~~~~~~
-   This rich location has a single range (range 0), with the caret
-   at the first "&", and the start/finish at the parentheses.
-   Compare with example C below.
-
-   Example C
-   *********
-      a = (foo && bar)
-           ~~~ ^~ ~~~
-   This rich location has three ranges:
-   - Range 0 has its caret and start location at the first "&" and
-     end at the second "&.
-   - Range 1 has its start and finish at the "f" and "o" of "foo";
-     the caret is not flagged for display, but is perhaps at the "f"
-     of "foo".
-   - Similarly, range 2 has its start and finish at the "b" and "r" of
-     "bar"; the caret is not flagged for display, but is perhaps at the
-     "b" of "bar".
-   Compare with example B above.
-
-   Example D (Fortran frontend)
-   ****************************
-       x = x + y
-           1   2
-   This rich location has range 0 at "1", and range 1 at "2".
-   Both are flagged for caret display.  Both ranges have start/finish
-   equal to their caret point.  The frontend overrides the diagnostic
-   context's default caret character for these ranges.
-
-   Example E (range labels)
-   ************************
-      printf ("arg0: %i  arg1: %s arg2: %i",
-                               ^~
-                               |
-                               const char *
-              100, 101, 102);
-                   ~~~
-                   |
-                   int
-   This rich location has two ranges:
-   - range 0 is at the "%s" with start = caret = "%" and finish at
-     the "s".  It has a range_label ("const char *").
-   - range 1 has start/finish covering the "101" and is not flagged for
-     caret printing.  The caret is at the start of "101", where its
-     range_label is printed ("int").
-
-   Fix-it hints
-   ------------
-
-   Rich locations can also contain "fix-it hints", giving suggestions
-   for the user on how to edit their code to fix a problem.  These
-   can be expressed as insertions, replacements, and removals of text.
-   The edits by default are relative to the zeroth range within the
-   rich_location, but optionally they can be expressed relative to
-   other locations (using various overloaded methods of the form
-   rich_location::add_fixit_*).
-
-   For example:
-
-   Example F: fix-it hint: insert_before
-   *************************************
-      ptr = arr[0];
-	    ^~~~~~
-	    &
-   This rich location has a single range (range 0) covering "arr[0]",
-   with the caret at the start.  The rich location has a single
-   insertion fix-it hint, inserted before range 0, added via
-     richloc.add_fixit_insert_before ("&");
-
-   Example G: multiple fix-it hints: insert_before and insert_after
-   ****************************************************************
-      #define FN(ARG0, ARG1, ARG2) fn(ARG0, ARG1, ARG2)
-				      ^~~~  ^~~~  ^~~~
-				      (   ) (   ) (   )
-   This rich location has three ranges, covering "arg0", "arg1",
-   and "arg2", all with caret-printing enabled.
-   The rich location has 6 insertion fix-it hints: each arg
-   has a pair of insertion fix-it hints, suggesting wrapping
-   them with parentheses: one a '(' inserted before,
-   the other a ')' inserted after, added via
-     richloc.add_fixit_insert_before (LOC, "(");
-   and
-     richloc.add_fixit_insert_after (LOC, ")");
-
-   Example H: fix-it hint: removal
-   *******************************
-     struct s {int i};;
-		      ^
-		      -
-   This rich location has a single range at the stray trailing
-   semicolon, along with a single removal fix-it hint, covering
-   the same range, added via:
-     richloc.add_fixit_remove ();
-
-   Example I: fix-it hint: replace
-   *******************************
-      c = s.colour;
-	    ^~~~~~
-	    color
-   This rich location has a single range (range 0) covering "colour",
-   and a single "replace" fix-it hint, covering the same range,
-   added via
-     richloc.add_fixit_replace ("color");
-
-   Example J: fix-it hint: line insertion
-   **************************************
-
-     3 | #include <stddef.h>
-     + |+#include <stdio.h>
-     4 | int the_next_line;
-
-   This rich location has a single range at line 4 column 1, marked
-   with SHOW_LINES_WITHOUT_RANGE (to avoid printing a meaningless caret
-   on the "i" of int).  It has a insertion fix-it hint of the string
-   "#include <stdio.h>\n".
-
-   Adding a fix-it hint can fail: for example, attempts to insert content
-   at the transition between two line maps may fail due to there being no
-   location_t value to express the new location.
-
-   Attempts to add a fix-it hint within a macro expansion will fail.
-
-   There is only limited support for newline characters in fix-it hints:
-   only hints with newlines which insert an entire new line are permitted,
-   inserting at the start of a line, and finishing with a newline
-   (with no interior newline characters).  Other attempts to add
-   fix-it hints containing newline characters will fail.
-   Similarly, attempts to delete or replace a range *affecting* multiple
-   lines will fail.
-
-   The rich_location API handles these failures gracefully, so that
-   diagnostics can attempt to add fix-it hints without each needing
-   extensive checking.
-
-   Fix-it hints within a rich_location are "atomic": if any hints can't
-   be applied, none of them will be (tracked by the m_seen_impossible_fixit
-   flag), and no fix-its hints will be displayed for that rich_location.
-   This implies that diagnostic messages need to be worded in such a way
-   that they make sense whether or not the fix-it hints are displayed,
-   or that richloc.seen_impossible_fixit_p () should be checked before
-   issuing the diagnostics.  */
-
-class rich_location
-{
- public:
-  /* Constructors.  */
-
-  /* Constructing from a location.  */
-  rich_location (line_maps *set, location_t loc,
-		 const range_label *label = NULL);
-
-  /* Destructor.  */
-  ~rich_location ();
-
-  /* The class manages the memory pointed to by the elements of
-     the M_FIXIT_HINTS vector and is not meant to be copied or
-     assigned.  */
-  rich_location (const rich_location &) = delete;
-  void operator= (const rich_location &) = delete;
-
-  /* Accessors.  */
-  location_t get_loc () const { return get_loc (0); }
-  location_t get_loc (unsigned int idx) const;
-
-  void
-  add_range (location_t loc,
-	     enum range_display_kind range_display_kind
-	       = SHOW_RANGE_WITHOUT_CARET,
-	     const range_label *label = NULL);
-
-  void
-  set_range (unsigned int idx, location_t loc,
-	     enum range_display_kind range_display_kind);
-
-  unsigned int get_num_locations () const { return m_ranges.count (); }
-
-  const location_range *get_range (unsigned int idx) const;
-  location_range *get_range (unsigned int idx);
-
-  expanded_location get_expanded_location (unsigned int idx) const;
-
-  void
-  override_column (int column);
-
-  /* Fix-it hints.  */
-
-  /* Methods for adding insertion fix-it hints.  */
-
-  /* Suggest inserting NEW_CONTENT immediately before the primary
-     range's start.  */
-  void
-  add_fixit_insert_before (const char *new_content);
-
-  /* Suggest inserting NEW_CONTENT immediately before the start of WHERE.  */
-  void
-  add_fixit_insert_before (location_t where,
-			   const char *new_content);
-
-  /* Suggest inserting NEW_CONTENT immediately after the end of the primary
-     range.  */
-  void
-  add_fixit_insert_after (const char *new_content);
-
-  /* Suggest inserting NEW_CONTENT immediately after the end of WHERE.  */
-  void
-  add_fixit_insert_after (location_t where,
-			  const char *new_content);
-
-  /* Methods for adding removal fix-it hints.  */
-
-  /* Suggest removing the content covered by range 0.  */
-  void
-  add_fixit_remove ();
-
-  /* Suggest removing the content covered between the start and finish
-     of WHERE.  */
-  void
-  add_fixit_remove (location_t where);
-
-  /* Suggest removing the content covered by SRC_RANGE.  */
-  void
-  add_fixit_remove (source_range src_range);
-
-  /* Methods for adding "replace" fix-it hints.  */
-
-  /* Suggest replacing the content covered by range 0 with NEW_CONTENT.  */
-  void
-  add_fixit_replace (const char *new_content);
-
-  /* Suggest replacing the content between the start and finish of
-     WHERE with NEW_CONTENT.  */
-  void
-  add_fixit_replace (location_t where,
-		     const char *new_content);
-
-  /* Suggest replacing the content covered by SRC_RANGE with
-     NEW_CONTENT.  */
-  void
-  add_fixit_replace (source_range src_range,
-		     const char *new_content);
-
-  unsigned int get_num_fixit_hints () const { return m_fixit_hints.count (); }
-  fixit_hint *get_fixit_hint (int idx) const { return m_fixit_hints[idx]; }
-  fixit_hint *get_last_fixit_hint () const;
-  bool seen_impossible_fixit_p () const { return m_seen_impossible_fixit; }
-
-  /* Set this if the fix-it hints are not suitable to be
-     automatically applied.
-
-     For example, if you are suggesting more than one
-     mutually exclusive solution to a problem, then
-     it doesn't make sense to apply all of the solutions;
-     manual intervention is required.
-
-     If set, then the fix-it hints in the rich_location will
-     be printed, but will not be added to generated patches,
-     or affect the modified version of the file.  */
-  void fixits_cannot_be_auto_applied ()
-  {
-    m_fixits_cannot_be_auto_applied = true;
-  }
-
-  bool fixits_can_be_auto_applied_p () const
-  {
-    return !m_fixits_cannot_be_auto_applied;
-  }
-
-  /* An optional path through the code.  */
-  const diagnostic_path *get_path () const { return m_path; }
-  void set_path (const diagnostic_path *path) { m_path = path; }
-
-  /* A flag for hinting that the diagnostic involves character encoding
-     issues, and thus that it will be helpful to the user if we show some
-     representation of how the characters in the pertinent source lines
-     are encoded.
-     The default is false (i.e. do not escape).
-     When set to true, non-ASCII bytes in the pertinent source lines will
-     be escaped in a manner controlled by the user-supplied option
-     -fdiagnostics-escape-format=, so that the user can better understand
-     what's going on with the encoding in their source file.  */
-  bool escape_on_output_p () const { return m_escape_on_output; }
-  void set_escape_on_output (bool flag) { m_escape_on_output = flag; }
-
-  const line_maps *get_line_table () const { return m_line_table; }
-
-private:
-  bool reject_impossible_fixit (location_t where);
-  void stop_supporting_fixits ();
-  void maybe_add_fixit (location_t start,
-			location_t next_loc,
-			const char *new_content);
-
-public:
-  static const int STATICALLY_ALLOCATED_RANGES = 3;
-
-protected:
-  line_maps * const m_line_table;
-  semi_embedded_vec <location_range, STATICALLY_ALLOCATED_RANGES> m_ranges;
-
-  int m_column_override;
-
-  mutable bool m_have_expanded_location;
-  bool m_seen_impossible_fixit;
-  bool m_fixits_cannot_be_auto_applied;
-  bool m_escape_on_output;
-
-  mutable expanded_location m_expanded_location;
-
-  static const int MAX_STATIC_FIXIT_HINTS = 2;
-  semi_embedded_vec <fixit_hint *, MAX_STATIC_FIXIT_HINTS> m_fixit_hints;
-
-  const diagnostic_path *m_path;
-};
-
-/* A struct for the result of range_label::get_text: a NUL-terminated buffer
-   of localized text, and a flag to determine if the caller should "free" the
-   buffer.  */
-
-class label_text
-{
-public:
-  label_text ()
-  : m_buffer (NULL), m_owned (false)
-  {}
-
-  ~label_text ()
-  {
-    if (m_owned)
-      free (m_buffer);
-  }
-
-  /* Move ctor.  */
-  label_text (label_text &&other)
-  : m_buffer (other.m_buffer), m_owned (other.m_owned)
-  {
-    other.release ();
-  }
-
-  /* Move assignment.  */
-  label_text & operator= (label_text &&other)
-  {
-    if (m_owned)
-      free (m_buffer);
-    m_buffer = other.m_buffer;
-    m_owned = other.m_owned;
-    other.release ();
-    return *this;
-  }
-
-  /* Delete the copy ctor and copy-assignment operator.  */
-  label_text (const label_text &) = delete;
-  label_text & operator= (const label_text &) = delete;
-
-  /* Create a label_text instance that borrows BUFFER from a
-     longer-lived owner.  */
-  static label_text borrow (const char *buffer)
-  {
-    return label_text (const_cast <char *> (buffer), false);
-  }
-
-  /* Create a label_text instance that takes ownership of BUFFER.  */
-  static label_text take (char *buffer)
-  {
-    return label_text (buffer, true);
-  }
-
-  void release ()
-  {
-    m_buffer = NULL;
-    m_owned = false;
-  }
-
-  const char *get () const
-  {
-    return m_buffer;
-  }
-
-  bool is_owner () const
-  {
-    return m_owned;
-  }
-
-private:
-  char *m_buffer;
-  bool m_owned;
-
-  label_text (char *buffer, bool owned)
-  : m_buffer (buffer), m_owned (owned)
-  {}
-};
-
-/* Abstract base class for labelling a range within a rich_location
-   (e.g. for labelling expressions with their type).
-
-   Generating the text could require non-trivial work, so this work
-   is delayed (via the "get_text" virtual function) until the diagnostic
-   printing code "knows" it needs it, thus avoiding doing it e.g. for
-   warnings that are filtered by command-line flags.  This virtual
-   function also isolates libcpp and the diagnostics subsystem from
-   the front-end and middle-end-specific code for generating the text
-   for the labels.
-
-   Like the rich_location instances they annotate, range_label instances
-   are intended to be allocated on the stack when generating diagnostics,
-   and to be short-lived.  */
-
-class range_label
-{
- public:
-  virtual ~range_label () {}
-
-  /* Get localized text for the label.
-     The RANGE_IDX is provided, allowing for range_label instances to be
-     shared by multiple ranges if need be (the "flyweight" design pattern).  */
-  virtual label_text get_text (unsigned range_idx) const = 0;
-};
-
-/* A fix-it hint: a suggested insertion, replacement, or deletion of text.
-   We handle these three types of edit with one class, by representing
-   them as replacement of a half-open range:
-       [start, next_loc)
-   Insertions have start == next_loc: "replace" the empty string at the
-   start location with the new string.
-   Deletions are replacement with the empty string.
-
-   There is only limited support for newline characters in fix-it hints
-   as noted above in the comment for class rich_location.
-   A fixit_hint instance can have at most one newline character; if
-   present, the newline character must be the final character of
-   the content (preventing e.g. fix-its that split a pre-existing line).  */
-
-class fixit_hint
-{
- public:
-  fixit_hint (location_t start,
-	      location_t next_loc,
-	      const char *new_content);
-  ~fixit_hint () { free (m_bytes); }
-
-  bool affects_line_p (const line_maps *set,
-		       const char *file,
-		       int line) const;
-  location_t get_start_loc () const { return m_start; }
-  location_t get_next_loc () const { return m_next_loc; }
-  bool maybe_append (location_t start,
-		     location_t next_loc,
-		     const char *new_content);
-
-  const char *get_string () const { return m_bytes; }
-  size_t get_length () const { return m_len; }
-
-  bool insertion_p () const { return m_start == m_next_loc; }
-
-  bool ends_with_newline_p () const;
-
- private:
-  /* We don't use source_range here since, unlike most places,
-     this is a half-open/half-closed range:
-       [start, next_loc)
-     so that we can support insertion via start == next_loc.  */
-  location_t m_start;
-  location_t m_next_loc;
-  char *m_bytes;
-  size_t m_len;
-};
-
-
 /* 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.  */
diff --git a/libcpp/include/rich-location.h b/libcpp/include/rich-location.h
new file mode 100644
index 000000000000..878c71c8bbe8
--- /dev/null
+++ b/libcpp/include/rich-location.h
@@ -0,0 +1,695 @@
+/* Bundles of location information used when printing diagnostics.
+   Copyright (C) 2015-2023 Free Software Foundation, Inc.
+
+This program 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.
+
+This program 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 this program; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.
+
+ In other words, you are welcome to use, share and improve this program.
+ You are forbidden to forbid anyone else to use, share and improve
+ what you give them.   Help stamp out software-hoarding!  */
+
+#ifndef LIBCPP_RICH_LOCATION_H
+#define LIBCPP_RICH_LOCATION_H
+
+class range_label;
+
+/* A hint to diagnostic_show_locus on how to print a source range within a
+   rich_location.
+
+   Typically this is SHOW_RANGE_WITH_CARET for the 0th range, and
+   SHOW_RANGE_WITHOUT_CARET for subsequent ranges,
+   but the Fortran frontend uses SHOW_RANGE_WITH_CARET repeatedly for
+   printing 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.  */
+
+enum range_display_kind
+{
+  /* Show the pertinent source line(s), the caret, and underline(s).  */
+  SHOW_RANGE_WITH_CARET,
+
+  /* Show the pertinent source line(s) and underline(s), but don't
+     show the caret (just an underline).  */
+  SHOW_RANGE_WITHOUT_CARET,
+
+  /* Just show the source lines; don't show the range itself.
+     This is for use when displaying some line-insertion fix-it hints (for
+     showing the user context on the change, for when it doesn't make sense
+     to highlight the first column on the next line).  */
+  SHOW_LINES_WITHOUT_RANGE
+};
+
+/* A location within a rich_location: a caret&range, with
+   the caret potentially flagged for display, and an optional
+   label.  */
+
+struct location_range
+{
+  location_t m_loc;
+
+  enum range_display_kind m_range_display_kind;
+
+  /* If non-NULL, the label for this range.  */
+  const range_label *m_label;
+};
+
+/* A partially-embedded vec for use within rich_location for storing
+   ranges and fix-it hints.
+
+   Elements [0..NUM_EMBEDDED) are allocated within m_embed, after
+   that they are within the dynamically-allocated m_extra.
+
+   This allows for static allocation in the common case, whilst
+   supporting the rarer case of an arbitrary number of elements.
+
+   Dynamic allocation is not performed unless it's needed.  */
+
+template <typename T, int NUM_EMBEDDED>
+class semi_embedded_vec
+{
+ public:
+  semi_embedded_vec ();
+  ~semi_embedded_vec ();
+
+  unsigned int count () const { return m_num; }
+  T& operator[] (int idx);
+  const T& operator[] (int idx) const;
+
+  void push (const T&);
+  void truncate (int len);
+
+ private:
+  int m_num;
+  T m_embedded[NUM_EMBEDDED];
+  int m_alloc;
+  T *m_extra;
+};
+
+/* Constructor for semi_embedded_vec.  In particular, no dynamic allocation
+   is done.  */
+
+template <typename T, int NUM_EMBEDDED>
+semi_embedded_vec<T, NUM_EMBEDDED>::semi_embedded_vec ()
+: m_num (0), m_alloc (0), m_extra (NULL)
+{
+}
+
+/* semi_embedded_vec's dtor.  Release any dynamically-allocated memory.  */
+
+template <typename T, int NUM_EMBEDDED>
+semi_embedded_vec<T, NUM_EMBEDDED>::~semi_embedded_vec ()
+{
+  XDELETEVEC (m_extra);
+}
+
+/* Look up element IDX, mutably.  */
+
+template <typename T, int NUM_EMBEDDED>
+T&
+semi_embedded_vec<T, NUM_EMBEDDED>::operator[] (int idx)
+{
+  linemap_assert (idx < m_num);
+  if (idx < NUM_EMBEDDED)
+    return m_embedded[idx];
+  else
+    {
+      linemap_assert (m_extra != NULL);
+      return m_extra[idx - NUM_EMBEDDED];
+    }
+}
+
+/* Look up element IDX (const).  */
+
+template <typename T, int NUM_EMBEDDED>
+const T&
+semi_embedded_vec<T, NUM_EMBEDDED>::operator[] (int idx) const
+{
+  linemap_assert (idx < m_num);
+  if (idx < NUM_EMBEDDED)
+    return m_embedded[idx];
+  else
+    {
+      linemap_assert (m_extra != NULL);
+      return m_extra[idx - NUM_EMBEDDED];
+    }
+}
+
+/* Append VALUE to the end of the semi_embedded_vec.  */
+
+template <typename T, int NUM_EMBEDDED>
+void
+semi_embedded_vec<T, NUM_EMBEDDED>::push (const T& value)
+{
+  int idx = m_num++;
+  if (idx < NUM_EMBEDDED)
+    m_embedded[idx] = value;
+  else
+    {
+      /* Offset "idx" to be an index within m_extra.  */
+      idx -= NUM_EMBEDDED;
+      if (NULL == m_extra)
+	{
+	  linemap_assert (m_alloc == 0);
+	  m_alloc = 16;
+	  m_extra = XNEWVEC (T, m_alloc);
+	}
+      else if (idx >= m_alloc)
+	{
+	  linemap_assert (m_alloc > 0);
+	  m_alloc *= 2;
+	  m_extra = XRESIZEVEC (T, m_extra, m_alloc);
+	}
+      linemap_assert (m_extra);
+      linemap_assert (idx < m_alloc);
+      m_extra[idx] = value;
+    }
+}
+
+/* Truncate to length LEN.  No deallocation is performed.  */
+
+template <typename T, int NUM_EMBEDDED>
+void
+semi_embedded_vec<T, NUM_EMBEDDED>::truncate (int len)
+{
+  linemap_assert (len <= m_num);
+  m_num = len;
+}
+
+class fixit_hint;
+class diagnostic_path;
+
+/* A "rich" source code location, for use when printing diagnostics.
+   A rich_location has one or more carets&ranges, where the carets
+   are optional.  These are referred to as "ranges" from here.
+   Typically the zeroth range has a caret; other ranges sometimes
+   have carets.
+
+   The "primary" location of a rich_location is the caret of range 0,
+   used for determining the line/column when printing diagnostic
+   text, such as:
+
+      some-file.c:3:1: error: ...etc...
+
+   Additional ranges may be added to help the user identify other
+   pertinent clauses in a diagnostic.
+
+   Ranges can (optionally) be given labels via class range_label.
+
+   rich_location instances are intended to be allocated on the stack
+   when generating diagnostics, and to be short-lived.
+
+   Examples of rich locations
+   --------------------------
+
+   Example A
+   *********
+      int i = "foo";
+              ^
+   This "rich" location is simply a single range (range 0), with
+   caret = start = finish at the given point.
+
+   Example B
+   *********
+      a = (foo && bar)
+          ~~~~~^~~~~~~
+   This rich location has a single range (range 0), with the caret
+   at the first "&", and the start/finish at the parentheses.
+   Compare with example C below.
+
+   Example C
+   *********
+      a = (foo && bar)
+           ~~~ ^~ ~~~
+   This rich location has three ranges:
+   - Range 0 has its caret and start location at the first "&" and
+     end at the second "&.
+   - Range 1 has its start and finish at the "f" and "o" of "foo";
+     the caret is not flagged for display, but is perhaps at the "f"
+     of "foo".
+   - Similarly, range 2 has its start and finish at the "b" and "r" of
+     "bar"; the caret is not flagged for display, but is perhaps at the
+     "b" of "bar".
+   Compare with example B above.
+
+   Example D (Fortran frontend)
+   ****************************
+       x = x + y
+           1   2
+   This rich location has range 0 at "1", and range 1 at "2".
+   Both are flagged for caret display.  Both ranges have start/finish
+   equal to their caret point.  The frontend overrides the diagnostic
+   context's default caret character for these ranges.
+
+   Example E (range labels)
+   ************************
+      printf ("arg0: %i  arg1: %s arg2: %i",
+                               ^~
+                               |
+                               const char *
+              100, 101, 102);
+                   ~~~
+                   |
+                   int
+   This rich location has two ranges:
+   - range 0 is at the "%s" with start = caret = "%" and finish at
+     the "s".  It has a range_label ("const char *").
+   - range 1 has start/finish covering the "101" and is not flagged for
+     caret printing.  The caret is at the start of "101", where its
+     range_label is printed ("int").
+
+   Fix-it hints
+   ------------
+
+   Rich locations can also contain "fix-it hints", giving suggestions
+   for the user on how to edit their code to fix a problem.  These
+   can be expressed as insertions, replacements, and removals of text.
+   The edits by default are relative to the zeroth range within the
+   rich_location, but optionally they can be expressed relative to
+   other locations (using various overloaded methods of the form
+   rich_location::add_fixit_*).
+
+   For example:
+
+   Example F: fix-it hint: insert_before
+   *************************************
+      ptr = arr[0];
+	    ^~~~~~
+	    &
+   This rich location has a single range (range 0) covering "arr[0]",
+   with the caret at the start.  The rich location has a single
+   insertion fix-it hint, inserted before range 0, added via
+     richloc.add_fixit_insert_before ("&");
+
+   Example G: multiple fix-it hints: insert_before and insert_after
+   ****************************************************************
+      #define FN(ARG0, ARG1, ARG2) fn(ARG0, ARG1, ARG2)
+				      ^~~~  ^~~~  ^~~~
+				      (   ) (   ) (   )
+   This rich location has three ranges, covering "arg0", "arg1",
+   and "arg2", all with caret-printing enabled.
+   The rich location has 6 insertion fix-it hints: each arg
+   has a pair of insertion fix-it hints, suggesting wrapping
+   them with parentheses: one a '(' inserted before,
+   the other a ')' inserted after, added via
+     richloc.add_fixit_insert_before (LOC, "(");
+   and
+     richloc.add_fixit_insert_after (LOC, ")");
+
+   Example H: fix-it hint: removal
+   *******************************
+     struct s {int i};;
+		      ^
+		      -
+   This rich location has a single range at the stray trailing
+   semicolon, along with a single removal fix-it hint, covering
+   the same range, added via:
+     richloc.add_fixit_remove ();
+
+   Example I: fix-it hint: replace
+   *******************************
+      c = s.colour;
+	    ^~~~~~
+	    color
+   This rich location has a single range (range 0) covering "colour",
+   and a single "replace" fix-it hint, covering the same range,
+   added via
+     richloc.add_fixit_replace ("color");
+
+   Example J: fix-it hint: line insertion
+   **************************************
+
+     3 | #include <stddef.h>
+     + |+#include <stdio.h>
+     4 | int the_next_line;
+
+   This rich location has a single range at line 4 column 1, marked
+   with SHOW_LINES_WITHOUT_RANGE (to avoid printing a meaningless caret
+   on the "i" of int).  It has a insertion fix-it hint of the string
+   "#include <stdio.h>\n".
+
+   Adding a fix-it hint can fail: for example, attempts to insert content
+   at the transition between two line maps may fail due to there being no
+   location_t value to express the new location.
+
+   Attempts to add a fix-it hint within a macro expansion will fail.
+
+   There is only limited support for newline characters in fix-it hints:
+   only hints with newlines which insert an entire new line are permitted,
+   inserting at the start of a line, and finishing with a newline
+   (with no interior newline characters).  Other attempts to add
+   fix-it hints containing newline characters will fail.
+   Similarly, attempts to delete or replace a range *affecting* multiple
+   lines will fail.
+
+   The rich_location API handles these failures gracefully, so that
+   diagnostics can attempt to add fix-it hints without each needing
+   extensive checking.
+
+   Fix-it hints within a rich_location are "atomic": if any hints can't
+   be applied, none of them will be (tracked by the m_seen_impossible_fixit
+   flag), and no fix-its hints will be displayed for that rich_location.
+   This implies that diagnostic messages need to be worded in such a way
+   that they make sense whether or not the fix-it hints are displayed,
+   or that richloc.seen_impossible_fixit_p () should be checked before
+   issuing the diagnostics.  */
+
+class rich_location
+{
+ public:
+  /* Constructors.  */
+
+  /* Constructing from a location.  */
+  rich_location (line_maps *set, location_t loc,
+		 const range_label *label = NULL);
+
+  /* Destructor.  */
+  ~rich_location ();
+
+  /* The class manages the memory pointed to by the elements of
+     the M_FIXIT_HINTS vector and is not meant to be copied or
+     assigned.  */
+  rich_location (const rich_location &) = delete;
+  void operator= (const rich_location &) = delete;
+
+  /* Accessors.  */
+  location_t get_loc () const { return get_loc (0); }
+  location_t get_loc (unsigned int idx) const;
+
+  void
+  add_range (location_t loc,
+	     enum range_display_kind range_display_kind
+	       = SHOW_RANGE_WITHOUT_CARET,
+	     const range_label *label = NULL);
+
+  void
+  set_range (unsigned int idx, location_t loc,
+	     enum range_display_kind range_display_kind);
+
+  unsigned int get_num_locations () const { return m_ranges.count (); }
+
+  const location_range *get_range (unsigned int idx) const;
+  location_range *get_range (unsigned int idx);
+
+  expanded_location get_expanded_location (unsigned int idx) const;
+
+  void
+  override_column (int column);
+
+  /* Fix-it hints.  */
+
+  /* Methods for adding insertion fix-it hints.  */
+
+  /* Suggest inserting NEW_CONTENT immediately before the primary
+     range's start.  */
+  void
+  add_fixit_insert_before (const char *new_content);
+
+  /* Suggest inserting NEW_CONTENT immediately before the start of WHERE.  */
+  void
+  add_fixit_insert_before (location_t where,
+			   const char *new_content);
+
+  /* Suggest inserting NEW_CONTENT immediately after the end of the primary
+     range.  */
+  void
+  add_fixit_insert_after (const char *new_content);
+
+  /* Suggest inserting NEW_CONTENT immediately after the end of WHERE.  */
+  void
+  add_fixit_insert_after (location_t where,
+			  const char *new_content);
+
+  /* Methods for adding removal fix-it hints.  */
+
+  /* Suggest removing the content covered by range 0.  */
+  void
+  add_fixit_remove ();
+
+  /* Suggest removing the content covered between the start and finish
+     of WHERE.  */
+  void
+  add_fixit_remove (location_t where);
+
+  /* Suggest removing the content covered by SRC_RANGE.  */
+  void
+  add_fixit_remove (source_range src_range);
+
+  /* Methods for adding "replace" fix-it hints.  */
+
+  /* Suggest replacing the content covered by range 0 with NEW_CONTENT.  */
+  void
+  add_fixit_replace (const char *new_content);
+
+  /* Suggest replacing the content between the start and finish of
+     WHERE with NEW_CONTENT.  */
+  void
+  add_fixit_replace (location_t where,
+		     const char *new_content);
+
+  /* Suggest replacing the content covered by SRC_RANGE with
+     NEW_CONTENT.  */
+  void
+  add_fixit_replace (source_range src_range,
+		     const char *new_content);
+
+  unsigned int get_num_fixit_hints () const { return m_fixit_hints.count (); }
+  fixit_hint *get_fixit_hint (int idx) const { return m_fixit_hints[idx]; }
+  fixit_hint *get_last_fixit_hint () const;
+  bool seen_impossible_fixit_p () const { return m_seen_impossible_fixit; }
+
+  /* Set this if the fix-it hints are not suitable to be
+     automatically applied.
+
+     For example, if you are suggesting more than one
+     mutually exclusive solution to a problem, then
+     it doesn't make sense to apply all of the solutions;
+     manual intervention is required.
+
+     If set, then the fix-it hints in the rich_location will
+     be printed, but will not be added to generated patches,
+     or affect the modified version of the file.  */
+  void fixits_cannot_be_auto_applied ()
+  {
+    m_fixits_cannot_be_auto_applied = true;
+  }
+
+  bool fixits_can_be_auto_applied_p () const
+  {
+    return !m_fixits_cannot_be_auto_applied;
+  }
+
+  /* An optional path through the code.  */
+  const diagnostic_path *get_path () const { return m_path; }
+  void set_path (const diagnostic_path *path) { m_path = path; }
+
+  /* A flag for hinting that the diagnostic involves character encoding
+     issues, and thus that it will be helpful to the user if we show some
+     representation of how the characters in the pertinent source lines
+     are encoded.
+     The default is false (i.e. do not escape).
+     When set to true, non-ASCII bytes in the pertinent source lines will
+     be escaped in a manner controlled by the user-supplied option
+     -fdiagnostics-escape-format=, so that the user can better understand
+     what's going on with the encoding in their source file.  */
+  bool escape_on_output_p () const { return m_escape_on_output; }
+  void set_escape_on_output (bool flag) { m_escape_on_output = flag; }
+
+  const line_maps *get_line_table () const { return m_line_table; }
+
+private:
+  bool reject_impossible_fixit (location_t where);
+  void stop_supporting_fixits ();
+  void maybe_add_fixit (location_t start,
+			location_t next_loc,
+			const char *new_content);
+
+public:
+  static const int STATICALLY_ALLOCATED_RANGES = 3;
+
+protected:
+  line_maps * const m_line_table;
+  semi_embedded_vec <location_range, STATICALLY_ALLOCATED_RANGES> m_ranges;
+
+  int m_column_override;
+
+  mutable bool m_have_expanded_location;
+  bool m_seen_impossible_fixit;
+  bool m_fixits_cannot_be_auto_applied;
+  bool m_escape_on_output;
+
+  mutable expanded_location m_expanded_location;
+
+  static const int MAX_STATIC_FIXIT_HINTS = 2;
+  semi_embedded_vec <fixit_hint *, MAX_STATIC_FIXIT_HINTS> m_fixit_hints;
+
+  const diagnostic_path *m_path;
+};
+
+/* A struct for the result of range_label::get_text: a NUL-terminated buffer
+   of localized text, and a flag to determine if the caller should "free" the
+   buffer.  */
+
+class label_text
+{
+public:
+  label_text ()
+  : m_buffer (NULL), m_owned (false)
+  {}
+
+  ~label_text ()
+  {
+    if (m_owned)
+      free (m_buffer);
+  }
+
+  /* Move ctor.  */
+  label_text (label_text &&other)
+  : m_buffer (other.m_buffer), m_owned (other.m_owned)
+  {
+    other.release ();
+  }
+
+  /* Move assignment.  */
+  label_text & operator= (label_text &&other)
+  {
+    if (m_owned)
+      free (m_buffer);
+    m_buffer = other.m_buffer;
+    m_owned = other.m_owned;
+    other.release ();
+    return *this;
+  }
+
+  /* Delete the copy ctor and copy-assignment operator.  */
+  label_text (const label_text &) = delete;
+  label_text & operator= (const label_text &) = delete;
+
+  /* Create a label_text instance that borrows BUFFER from a
+     longer-lived owner.  */
+  static label_text borrow (const char *buffer)
+  {
+    return label_text (const_cast <char *> (buffer), false);
+  }
+
+  /* Create a label_text instance that takes ownership of BUFFER.  */
+  static label_text take (char *buffer)
+  {
+    return label_text (buffer, true);
+  }
+
+  void release ()
+  {
+    m_buffer = NULL;
+    m_owned = false;
+  }
+
+  const char *get () const
+  {
+    return m_buffer;
+  }
+
+  bool is_owner () const
+  {
+    return m_owned;
+  }
+
+private:
+  char *m_buffer;
+  bool m_owned;
+
+  label_text (char *buffer, bool owned)
+  : m_buffer (buffer), m_owned (owned)
+  {}
+};
+
+/* Abstract base class for labelling a range within a rich_location
+   (e.g. for labelling expressions with their type).
+
+   Generating the text could require non-trivial work, so this work
+   is delayed (via the "get_text" virtual function) until the diagnostic
+   printing code "knows" it needs it, thus avoiding doing it e.g. for
+   warnings that are filtered by command-line flags.  This virtual
+   function also isolates libcpp and the diagnostics subsystem from
+   the front-end and middle-end-specific code for generating the text
+   for the labels.
+
+   Like the rich_location instances they annotate, range_label instances
+   are intended to be allocated on the stack when generating diagnostics,
+   and to be short-lived.  */
+
+class range_label
+{
+ public:
+  virtual ~range_label () {}
+
+  /* Get localized text for the label.
+     The RANGE_IDX is provided, allowing for range_label instances to be
+     shared by multiple ranges if need be (the "flyweight" design pattern).  */
+  virtual label_text get_text (unsigned range_idx) const = 0;
+};
+
+/* A fix-it hint: a suggested insertion, replacement, or deletion of text.
+   We handle these three types of edit with one class, by representing
+   them as replacement of a half-open range:
+       [start, next_loc)
+   Insertions have start == next_loc: "replace" the empty string at the
+   start location with the new string.
+   Deletions are replacement with the empty string.
+
+   There is only limited support for newline characters in fix-it hints
+   as noted above in the comment for class rich_location.
+   A fixit_hint instance can have at most one newline character; if
+   present, the newline character must be the final character of
+   the content (preventing e.g. fix-its that split a pre-existing line).  */
+
+class fixit_hint
+{
+ public:
+  fixit_hint (location_t start,
+	      location_t next_loc,
+	      const char *new_content);
+  ~fixit_hint () { free (m_bytes); }
+
+  bool affects_line_p (const line_maps *set,
+		       const char *file,
+		       int line) const;
+  location_t get_start_loc () const { return m_start; }
+  location_t get_next_loc () const { return m_next_loc; }
+  bool maybe_append (location_t start,
+		     location_t next_loc,
+		     const char *new_content);
+
+  const char *get_string () const { return m_bytes; }
+  size_t get_length () const { return m_len; }
+
+  bool insertion_p () const { return m_start == m_next_loc; }
+
+  bool ends_with_newline_p () const;
+
+ private:
+  /* We don't use source_range here since, unlike most places,
+     this is a half-open/half-closed range:
+       [start, next_loc)
+     so that we can support insertion via start == next_loc.  */
+  location_t m_start;
+  location_t m_next_loc;
+  char *m_bytes;
+  size_t m_len;
+};
+
+#endif /* !LIBCPP_RICH_LOCATION_H  */
diff --git a/libcpp/internal.h b/libcpp/internal.h
index 6a10e9de43e7..134fc33d40c2 100644
--- a/libcpp/internal.h
+++ b/libcpp/internal.h
@@ -24,6 +24,7 @@ along with this program; see the file COPYING3.  If not see
 
 #include "symtab.h"
 #include "cpplib.h"
+#include "rich-location.h"
 
 #if HAVE_ICONV
 #include <iconv.h>
