diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index 569f000..7179ef0 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -1140,24 +1140,24 @@ get_cpp_ttype_from_string_type (tree string_type)
 
 GTY(()) string_concat_db *g_string_concat_db;
 
-/* Attempt to determine the source range of the substring.
-   If successful, return NULL and write the source range to *OUT_RANGE.
+/* Attempt to determine the source location of the substring.
+   If successful, return NULL and write the source location to *OUT_LOC.
    Otherwise return an error message.  Error messages are intended
    for GCC developers (to help debugging) rather than for end-users.  */
 
 const char *
-substring_loc::get_range (source_range *out_range) const
+substring_loc::get_location (location_t *out_loc) const
 {
-  gcc_assert (out_range);
+  gcc_assert (out_loc);
 
   enum cpp_ttype tok_type = get_cpp_ttype_from_string_type (m_string_type);
   if (tok_type == CPP_OTHER)
     return "unrecognized string type";
 
-  return get_source_range_for_substring (parse_in, g_string_concat_db,
-					 m_fmt_string_loc, tok_type,
-					 m_start_idx, m_end_idx,
-					 out_range);
+  return get_source_location_for_substring (parse_in, g_string_concat_db,
+					    m_fmt_string_loc, tok_type,
+					    m_caret_idx, m_start_idx, m_end_idx,
+					    out_loc);
 }
 
 
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 61f9ced..4673123 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -1124,17 +1124,20 @@ class substring_loc
 {
  public:
   substring_loc (location_t fmt_string_loc, tree string_type,
-		 int start_idx, int end_idx)
+		 int caret_idx, int start_idx, int end_idx)
   : m_fmt_string_loc (fmt_string_loc), m_string_type (string_type),
-    m_start_idx (start_idx), m_end_idx (end_idx) {}
+    m_caret_idx (caret_idx), m_start_idx (start_idx), m_end_idx (end_idx) {}
 
-  const char *get_range (source_range *out_range) const;
+  void set_caret_index (int caret_idx) { m_caret_idx = caret_idx; }
+
+  const char *get_location (location_t *out_loc) const;
 
   location_t get_fmt_string_loc () const { return m_fmt_string_loc; }
 
  private:
   location_t m_fmt_string_loc;
   tree m_string_type;
+  int m_caret_idx;
   int m_start_idx;
   int m_end_idx;
 };
diff --git a/gcc/c-family/c-format.c b/gcc/c-family/c-format.c
index 951ffd0..ad434f8 100644
--- a/gcc/c-family/c-format.c
+++ b/gcc/c-family/c-format.c
@@ -146,26 +146,23 @@ format_warning_va (const substring_loc &fmt_loc, source_range *param_range,
 {
   bool substring_within_range = false;
   location_t primary_loc;
-  location_t substring_loc = UNKNOWN_LOCATION;
+  location_t fmt_substring_loc = UNKNOWN_LOCATION;
   source_range fmt_loc_range
     = get_range_from_loc (line_table, fmt_loc.get_fmt_string_loc ());
-  source_range fmt_substring_range;
-  const char *err = fmt_loc.get_range (&fmt_substring_range);
+  const char *err = fmt_loc.get_location (&fmt_substring_loc);
+  source_range fmt_substring_range
+    = get_range_from_loc (line_table, fmt_substring_loc);
   if (err)
     /* Case 3: unable to get substring location.  */
     primary_loc = fmt_loc.get_fmt_string_loc ();
   else
     {
-      substring_loc = make_location (fmt_substring_range.m_finish,
-				     fmt_substring_range.m_start,
-				     fmt_substring_range.m_finish);
-
       if (fmt_substring_range.m_start >= fmt_loc_range.m_start
 	  && fmt_substring_range.m_finish <= fmt_loc_range.m_finish)
 	/* Case 1.  */
 	{
 	  substring_within_range = true;
-	  primary_loc = substring_loc;
+	  primary_loc = fmt_substring_loc;
 	}
       else
 	/* Case 2.  */
@@ -193,11 +190,11 @@ format_warning_va (const substring_loc &fmt_loc, source_range *param_range,
   diagnostic.option_index = opt;
   bool warned = report_diagnostic (&diagnostic);
 
-  if (!err && substring_loc && !substring_within_range)
+  if (!err && fmt_substring_loc && !substring_within_range)
     /* Case 2.  */
     if (warned)
       {
-	rich_location substring_richloc (line_table, substring_loc);
+	rich_location substring_richloc (line_table, fmt_substring_loc);
 	if (corrected_substring)
 	  substring_richloc.add_fixit_replace (fmt_substring_range,
 					       corrected_substring);
@@ -247,7 +244,8 @@ format_warning_at_char (location_t fmt_string_loc, tree format_string_cst,
       be emitted.  Fix it.  */
   char_idx -= 1;
 
-  substring_loc fmt_loc (fmt_string_loc, string_type, char_idx, char_idx);
+  substring_loc fmt_loc (fmt_string_loc, string_type, char_idx, char_idx,
+			 char_idx);
   bool warned = format_warning_va (fmt_loc, NULL, NULL, opt, gmsgid, &ap);
   va_end (ap);
 
@@ -2809,7 +2807,9 @@ check_argument_type (const format_char_info *fci,
     {
       ptrdiff_t offset_to_format_start = (start_of_this_format - 1) - orig_format_chars;
       ptrdiff_t offset_to_format_end = (format_chars - 1) - orig_format_chars;
+      /* By default, use the end of the range for the caret location.  */
       substring_loc fmt_loc (fmt_param_loc, TREE_TYPE (format_string_cst),
+			     offset_to_format_end,
 			     offset_to_format_start, offset_to_format_end);
       check_format_types (fmt_loc, first_wanted_type, fki);
     }
@@ -3262,8 +3262,10 @@ get_format_for_type (const format_kind_info *fki, tree arg_type)
   return NULL;
 }
 
-/* Give a warning at FMT_LOC about a format argument of different type
-   from that expected.  If non-NULL, PARAM_RANGE is the source range of the
+/* Give a warning about a format argument of different type from that expected.
+   The range of the diagnostic is taken from WHOLE_FMT_LOC; the caret location
+   is based on the location of the char at TYPE->offset_loc.
+   If non-NULL, PARAM_RANGE is the source range of the
    relevant argument.  WANTED_TYPE is the type the argument should have,
    possibly stripped of pointer dereferences.  The description (such as "field
    precision"), the placement in the format string, a possibly more
@@ -3271,7 +3273,7 @@ get_format_for_type (const format_kind_info *fki, tree arg_type)
    are taken from TYPE.  ARG_TYPE is the type of the actual argument,
    or NULL if it is missing.  */
 static void
-format_type_warning (const substring_loc &fmt_loc,
+format_type_warning (const substring_loc &whole_fmt_loc,
 		     source_range *param_range,
 		     format_wanted_type *type,
 		     tree wanted_type, tree arg_type,
@@ -3316,6 +3318,12 @@ format_type_warning (const substring_loc &fmt_loc,
       p[pointer_count + 1] = 0;
     }
 
+  /* WHOLE_FMT_LOC has the caret at the end of the range.
+     Set the caret to be at the offset from TYPE.  Subtract one
+     from the offset for the same reason as in format_warning_at_char.  */
+  substring_loc fmt_loc (whole_fmt_loc);
+  fmt_loc.set_caret_index (type->offset_loc - 1);
+
   /* Attempt to provide hints for argument types, but not for field widths
      and precisions.  */
   char *format_for_type = NULL;
diff --git a/gcc/input.c b/gcc/input.c
index 790de93..653e54d 100644
--- a/gcc/input.c
+++ b/gcc/input.c
@@ -1402,10 +1402,17 @@ get_substring_ranges_for_loc (cpp_reader *pfile,
   return NULL;
 }
 
-/* Attempt to populate *OUT_RANGE with source location information on the
-   range of given characters within the string literal found at STRLOC.
-   START_IDX and END_IDX refer to offsets within the execution character
-   set.
+/* Attempt to populate *OUT_LOC with source location information on the
+   given characters within the string literal found at STRLOC.
+   CARET_IDX, START_IDX, and END_IDX refer to offsets within the execution
+   character set.
+
+   For example, given CARET_IDX = 4, START_IDX = 3, END_IDX  = 7
+   and string literal "012345\n789"
+   *OUT_LOC is written to with:
+     "012345\n789"
+         ~^~~~~
+
    If CONCATS is non-NULL, then any string literals that the token at
    STRLOC was concatenated with are also considered.
 
@@ -1416,16 +1423,17 @@ get_substring_ranges_for_loc (cpp_reader *pfile,
    than for end-users.  */
 
 const char *
-get_source_range_for_substring (cpp_reader *pfile,
-				string_concat_db *concats,
-				location_t strloc,
-				enum cpp_ttype type,
-				int start_idx, int end_idx,
-				source_range *out_range)
-{
+get_source_location_for_substring (cpp_reader *pfile,
+				   string_concat_db *concats,
+				   location_t strloc,
+				   enum cpp_ttype type,
+				   int caret_idx, int start_idx, int end_idx,
+				   source_location *out_loc)
+{
+  gcc_checking_assert (caret_idx >= 0);
   gcc_checking_assert (start_idx >= 0);
   gcc_checking_assert (end_idx >= 0);
-  gcc_assert (out_range);
+  gcc_assert (out_loc);
 
   cpp_substring_ranges ranges;
   const char *err
@@ -1433,17 +1441,56 @@ get_source_range_for_substring (cpp_reader *pfile,
   if (err)
     return err;
 
+  if (caret_idx >= ranges.get_num_ranges ())
+    return "caret_idx out of range";
   if (start_idx >= ranges.get_num_ranges ())
     return "start_idx out of range";
   if (end_idx >= ranges.get_num_ranges ())
     return "end_idx out of range";
 
-  out_range->m_start = ranges.get_range (start_idx).m_start;
-  out_range->m_finish = ranges.get_range (end_idx).m_finish;
+  *out_loc = make_location (ranges.get_range (caret_idx).m_start,
+			    ranges.get_range (start_idx).m_start,
+			    ranges.get_range (end_idx).m_finish);
+  return NULL;
+}
+
+/* Attempt to populate *OUT_RANGE with source location information on the
+   given character within the string literal found at STRLOC.
+   CHAR_IDX refers to an offset within the execution character set.
+   If CONCATS is non-NULL, then any string literals that the token at
+   STRLOC was concatenated with are also considered.
+
+   This is implemented by re-parsing the relevant source line(s).
+
+   Return NULL if successful, or an error message if any errors occurred.
+   Error messages are intended for GCC developers (to help debugging) rather
+   than for end-users.  */
+
+static const char *
+get_source_range_for_char (cpp_reader *pfile,
+			   string_concat_db *concats,
+			   location_t strloc,
+			   enum cpp_ttype type,
+			   int char_idx,
+			   source_range *out_range)
+{
+  gcc_checking_assert (char_idx >= 0);
+  gcc_assert (out_range);
+
+  cpp_substring_ranges ranges;
+  const char *err
+    = get_substring_ranges_for_loc (pfile, concats, strloc, type, ranges);
+  if (err)
+    return err;
+
+  if (char_idx >= ranges.get_num_ranges ())
+    return "char_idx out of range";
+
+  *out_range = ranges.get_range (char_idx);
   return NULL;
 }
 
-/* As get_source_range_for_substring, but write to *OUT the number
+/* As get_source_range_for_char, but write to *OUT the number
    of ranges that are available.  */
 
 const char *
@@ -1981,8 +2028,8 @@ assert_char_at_range (const location &loc,
 
   source_range actual_range;
   const char *err
-    = get_source_range_for_substring (pfile, concats, strloc, type,
-				      idx, idx, &actual_range);
+    = get_source_range_for_char (pfile, concats, strloc, type, idx,
+				 &actual_range);
   if (should_have_column_data_p (strloc))
     ASSERT_EQ_AT (loc, NULL, err);
   else
@@ -2831,9 +2878,8 @@ test_lexer_string_locations_concatenation_2 (const line_table_case &case_)
 	 this case.  */
       source_range actual_range;
       const char *err
-	= get_source_range_for_substring (test.m_parser, &test.m_concats,
-					  initial_loc, type, 0, 0,
-					  &actual_range);
+	= get_source_range_for_char (test.m_parser, &test.m_concats,
+				     initial_loc, type, 0, &actual_range);
       ASSERT_STREQ ("range starts after LINE_MAP_MAX_LOCATION_WITH_COLS", err);
       return;
     }
diff --git a/gcc/substring-locations.h b/gcc/substring-locations.h
index 274ebbe..f839c74 100644
--- a/gcc/substring-locations.h
+++ b/gcc/substring-locations.h
@@ -20,11 +20,12 @@ along with GCC; see the file COPYING3.  If not see
 #ifndef GCC_SUBSTRING_LOCATIONS_H
 #define GCC_SUBSTRING_LOCATIONS_H
 
-extern const char *get_source_range_for_substring (cpp_reader *pfile,
-						   string_concat_db *concats,
-						   location_t strloc,
-						   enum cpp_ttype type,
-						   int start_idx, int end_idx,
-						   source_range *out_range);
+extern const char *get_source_location_for_substring (cpp_reader *pfile,
+						      string_concat_db *concats,
+						      location_t strloc,
+						      enum cpp_ttype type,
+						      int caret_idx,
+						      int start_idx, int end_idx,
+						      location_t *out_loc);
 
 #endif /* ! GCC_SUBSTRING_LOCATIONS_H */
diff --git a/gcc/testsuite/gcc.dg/format/asm_fprintf-1.c b/gcc/testsuite/gcc.dg/format/asm_fprintf-1.c
index 50ca572..2eabbf9 100644
--- a/gcc/testsuite/gcc.dg/format/asm_fprintf-1.c
+++ b/gcc/testsuite/gcc.dg/format/asm_fprintf-1.c
@@ -66,9 +66,9 @@ foo (int i, int i1, int i2, unsigned int u, double d, char *s, void *p,
   asm_fprintf ("%d", i, i); /* { dg-warning "16:arguments" "wrong number of args" } */
   /* Miscellaneous bogus constructions.  */
   asm_fprintf (""); /* { dg-warning "16:zero-length" "warning for empty format" } */
-  asm_fprintf ("\0"); /* { dg-warning "18:embedded" "warning for embedded NUL" } */
-  asm_fprintf ("%d\0", i); /* { dg-warning "20:embedded" "warning for embedded NUL" } */
-  asm_fprintf ("%d\0%d", i, i); /* { dg-warning "20:embedded|too many" "warning for embedded NUL" } */
+  asm_fprintf ("\0"); /* { dg-warning "17:embedded" "warning for embedded NUL" } */
+  asm_fprintf ("%d\0", i); /* { dg-warning "19:embedded" "warning for embedded NUL" } */
+  asm_fprintf ("%d\0%d", i, i); /* { dg-warning "19:embedded|too many" "warning for embedded NUL" } */
   asm_fprintf (NULL); /* { dg-warning "null" "null format string warning" } */
   asm_fprintf ("%"); /* { dg-warning "17:trailing" "trailing % warning" } */
   asm_fprintf ("%++d", i); /* { dg-warning "19:repeated" "repeated flag warning" } */
diff --git a/gcc/testsuite/gcc.dg/format/c90-printf-1.c b/gcc/testsuite/gcc.dg/format/c90-printf-1.c
index 338b971..5329dad 100644
--- a/gcc/testsuite/gcc.dg/format/c90-printf-1.c
+++ b/gcc/testsuite/gcc.dg/format/c90-printf-1.c
@@ -58,11 +58,11 @@ foo (int i, int i1, int i2, unsigned int u, double d, char *s, void *p,
   printf ("%-%"); /* { dg-warning "13:type" "missing type" } */
   /* { dg-warning "14:trailing" "bogus %%" { target *-*-* } 58 } */
   printf ("%-%\n"); /* { dg-warning "13:format" "bogus %%" } */
-  /* { dg-warning "16:format" "bogus %%" { target *-*-* } 60 } */
+  /* { dg-warning "15:format" "bogus %%" { target *-*-* } 60 } */
   printf ("%5%\n"); /* { dg-warning "13:format" "bogus %%" } */
-  /* { dg-warning "16:format" "bogus %%" { target *-*-* } 62 } */
+  /* { dg-warning "15:format" "bogus %%" { target *-*-* } 62 } */
   printf ("%h%\n"); /* { dg-warning "13:format" "bogus %%" } */
-  /* { dg-warning "16:format" "bogus %%" { target *-*-* } 64 } */
+  /* { dg-warning "15:format" "bogus %%" { target *-*-* } 64 } */
   /* Valid and invalid %h, %l, %L constructions.  */
   printf ("%hd", i);
   printf ("%hi", i);
@@ -184,8 +184,8 @@ foo (int i, int i1, int i2, unsigned int u, double d, char *s, void *p,
   printf ("%-08G", d); /* { dg-warning "11:flags|ignored" "0 flag ignored with - flag" } */
   /* Various tests of bad argument types.  */
   printf ("%d", l); /* { dg-warning "13:format" "bad argument types" } */
-  printf ("%*.*d", l, i2, i); /* { dg-warning "16:field" "bad * argument types" } */
-  printf ("%*.*d", i1, l, i); /* { dg-warning "16:field" "bad * argument types" } */
+  printf ("%*.*d", l, i2, i); /* { dg-warning "13:field" "bad * argument types" } */
+  printf ("%*.*d", i1, l, i); /* { dg-warning "15:field" "bad * argument types" } */
   printf ("%ld", i); /* { dg-warning "14:format" "bad argument types" } */
   printf ("%s", n); /* { dg-warning "13:format" "bad argument types" } */
   printf ("%p", i); /* { dg-warning "13:format" "bad argument types" } */
@@ -231,8 +231,8 @@ foo (int i, int i1, int i2, unsigned int u, double d, char *s, void *p,
   printf ("%d", i, i); /* { dg-warning "11:arguments" "wrong number of args" } */
   /* Miscellaneous bogus constructions.  */
   printf (""); /* { dg-warning "11:zero-length" "warning for empty format" } */
-  printf ("\0"); /* { dg-warning "13:embedded" "warning for embedded NUL" } */
-  printf ("%d\0", i); /* { dg-warning "15:embedded" "warning for embedded NUL" } */
+  printf ("\0"); /* { dg-warning "12:embedded" "warning for embedded NUL" } */
+  printf ("%d\0", i); /* { dg-warning "14:embedded" "warning for embedded NUL" } */
   printf ("%d\0%d", i, i); /* { dg-warning "embedded|too many" "warning for embedded NUL" } */
   printf (NULL); /* { dg-warning "3:null" "null format string warning" } */
   printf ("%"); /* { dg-warning "12:trailing" "trailing % warning" } */
diff --git a/gcc/testsuite/gcc.dg/format/diagnostic-ranges.c b/gcc/testsuite/gcc.dg/format/diagnostic-ranges.c
index ff51833..63075c8 100644
--- a/gcc/testsuite/gcc.dg/format/diagnostic-ranges.c
+++ b/gcc/testsuite/gcc.dg/format/diagnostic-ranges.c
@@ -87,7 +87,7 @@ void test_hex (const char *msg)
 /* TODO: ideally would also underline "msg".  */
 /* { dg-begin-multiline-output "" }
    printf("hello \x25\x69", msg);
-                 ~~~~~~~^
+                 ~~~~^~~~
                  %s
    { dg-end-multiline-output "" } */
 }
@@ -101,7 +101,7 @@ void test_oct (const char *msg)
 /* TODO: ideally would also underline "msg".  */
 /* { dg-begin-multiline-output "" }
    printf("hello \045\151", msg);
-                 ~~~~~~~^
+                 ~~~~^~~~
                  %s
    { dg-end-multiline-output "" } */
 }
@@ -120,7 +120,7 @@ void test_multiple (const char *msg)
 /* TODO: ideally would also underline "msg".  */
 /* { dg-begin-multiline-output "" }
    printf("prefix"  "\x25"  "\151"  "suffix",
-                     ~~~~~~~~~~~^
+                     ~~~~~~~~^~~~
                      %s
   { dg-end-multiline-output "" } */
 }
@@ -148,10 +148,44 @@ void test_param (long long_i, long long_j)
 
 void test_field_width_specifier (long l, int i1, int i2)
 {
-  printf (" %*.*d ", l, i1, i2); /* { dg-warning "17: field width specifier '\\*' expects argument of type 'int', but argument 2 has type 'long int'" } */
+  printf (" %*.*d ", l, i1, i2); /* { dg-warning "14: field width specifier '\\*' expects argument of type 'int', but argument 2 has type 'long int'" } */
 /* { dg-begin-multiline-output "" }
    printf (" %*.*d ", l, i1, i2);
-             ~~~~^
+             ~^~~~
+   { dg-end-multiline-output "" } */
+}
+
+/* PR c/72857.  */
+
+void test_field_width_specifier_2 (char *d, long foo, long bar)
+{
+  __builtin_sprintf (d, " %*ld ", foo, foo); /* { dg-warning "28: field width specifier '\\*' expects argument of type 'int', but argument 3 has type 'long int'" } */
+  /* TODO: ideally we'd underline the first "foo" here".  */
+  /* { dg-begin-multiline-output "" }
+   __builtin_sprintf (d, " %*ld ", foo, foo);
+                           ~^~~
+   { dg-end-multiline-output "" } */
+
+  __builtin_sprintf (d, " %*ld ", foo + bar, foo); /* { dg-warning "28: field width specifier '\\*' expects argument of type 'int', but argument 3 has type 'long int'" } */
+  /* { dg-begin-multiline-output "" }
+   __builtin_sprintf (d, " %*ld ", foo + bar, foo);
+                           ~^~~    ~~~~~~~~~
+   { dg-end-multiline-output "" } */
+}
+
+void test_field_precision_specifier (char *d, long foo, long bar)
+{
+  __builtin_sprintf (d, " %.*ld ", foo, foo); /* { dg-warning "29: field precision specifier '\\.\\*' expects argument of type 'int', but argument 3 has type 'long int'" } */
+  /* TODO: ideally we'd underline the first "foo" here".  */
+  /* { dg-begin-multiline-output "" }
+   __builtin_sprintf (d, " %.*ld ", foo, foo);
+                           ~~^~~
+   { dg-end-multiline-output "" } */
+
+  __builtin_sprintf (d, " %.*ld ", foo + bar, foo); /* { dg-warning "29: field precision specifier '\\.\\*' expects argument of type 'int', but argument 3 has type 'long int'" } */
+  /* { dg-begin-multiline-output "" }
+   __builtin_sprintf (d, " %.*ld ", foo + bar, foo);
+                           ~~^~~    ~~~~~~~~~
    { dg-end-multiline-output "" } */
 }
 
@@ -200,10 +234,10 @@ void test_conversion_lacks_type (void)
 
 void test_embedded_nul (void)
 {
-  printf (" \0 "); /* { dg-warning "14:embedded" "warning for embedded NUL" } */
+  printf (" \0 "); /* { dg-warning "13:embedded" "warning for embedded NUL" } */
 /* { dg-begin-multiline-output "" }
    printf (" \0 ");
-             ~^
+             ^~
    { dg-end-multiline-output "" } */
 }
 
@@ -225,7 +259,7 @@ void test_macro (const char *msg)
 void test_non_contiguous_strings (void)
 {
   __builtin_printf(" %" "d ", 0.5); /* { dg-warning "20: format .%d. expects argument of type .int., but argument 2 has type .double." } */
-                                    /* { dg-message "26: format string is defined here" "" { target *-*-* } 227 } */
+                                    /* { dg-message "26: format string is defined here" "" { target *-*-* } 261 } */
   /* { dg-begin-multiline-output "" }
    __builtin_printf(" %" "d ", 0.5);
                     ^~~~
diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-string-literals-1.c b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-string-literals-1.c
index 82689b4..d5be021 100644
--- a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-string-literals-1.c
+++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-string-literals-1.c
@@ -13,14 +13,14 @@
    LITERAL is a const void * to allow testing the various kinds of wide
    string literal, rather than just const char *.  */
 
-extern void __emit_string_literal_range (const void *literal,
+extern void __emit_string_literal_range (const void *literal, int caret_idx,
 					 int start_idx, int end_idx);
 
 void
 test_simple_string_literal (void)
 {
   __emit_string_literal_range ("0123456789", /* { dg-warning "range" } */
-			       6, 7);
+			       6, 6, 7);
 /* { dg-begin-multiline-output "" }
    __emit_string_literal_range ("0123456789",
                                        ^~
@@ -31,10 +31,10 @@ void
 test_concatenated_string_literal (void)
 {
   __emit_string_literal_range ("01234" "56789", /* { dg-warning "range" } */
-			       3, 6);
+			       4, 3, 6);
 /* { dg-begin-multiline-output "" }
    __emit_string_literal_range ("01234" "56789",
-                                    ^~~~~~~
+                                    ~^~~~~~
    { dg-end-multiline-output "" } */
 }
 
@@ -43,14 +43,14 @@ test_multiline_string_literal (void)
 {
   __emit_string_literal_range ("01234" /* { dg-warning "range" } */
                                "56789",
-                               3, 6);
+                               4, 3, 6);
 /* { dg-begin-multiline-output "" }
    __emit_string_literal_range ("01234"
-                                    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                                    ~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                                 "56789",
-                                ~~~  
+                                ~~~   
    { dg-end-multiline-output "" } */
-  /* FIXME: why does the above need two trailing spaces?  */
+  /* FIXME: why does the above need three trailing spaces?  */
 }
 
 /* Tests of various unicode encodings.
@@ -79,10 +79,10 @@ test_hex (void)
      and with a space in place of digit 6, to terminate the escaped
      hex code.  */
   __emit_string_literal_range ("01234\x35 789", /* { dg-warning "range" } */
-			       3, 7);
+			       4, 3, 7);
 /* { dg-begin-multiline-output "" }
    __emit_string_literal_range ("01234\x35 789"
-                                    ^~~~~~~~
+                                    ~^~~~~~~
    { dg-end-multiline-output "" } */
 }
 
@@ -93,10 +93,10 @@ test_oct (void)
      and with a space in place of digit 6, to terminate the escaped
      octal code.  */
   __emit_string_literal_range ("01234\065 789", /* { dg-warning "range" } */
-			       3, 7);
+			       4, 3, 7);
 /* { dg-begin-multiline-output "" }
    __emit_string_literal_range ("01234\065 789"
-                                    ^~~~~~~~
+                                    ~^~~~~~~
    { dg-end-multiline-output "" } */
 }
 
@@ -106,10 +106,10 @@ test_multiple (void)
   /* Digits 0-9, expressing digit 5 in ASCII as hex "\x35"
      digit 6 in ASCII as octal "\066", concatenating multiple strings.  */
   __emit_string_literal_range ("01234"  "\x35"  "\066"  "789", /* { dg-warning "range" } */
-			       3, 8);
+			       5, 3, 8);
 /* { dg-begin-multiline-output "" }
    __emit_string_literal_range ("01234"  "\x35"  "\066"  "789",
-                                    ^~~~~~~~~~~~~~~~~~~~~~~~
+                                    ~~~~~~^~~~~~~~~~~~~~~~~~
    { dg-end-multiline-output "" } */
 }
 
@@ -123,10 +123,10 @@ test_ucn4 (void)
      Hence to underline digits 4-7 we need to underling using bytes 4-11 in
      the UTF-8 encoding.  */
   __emit_string_literal_range ("01234\u2174\u2175789", /* { dg-warning "range" } */
-			       4, 11);
+			       5, 4, 11);
 /* { dg-begin-multiline-output "" }
    __emit_string_literal_range ("01234\u2174\u2175789",
-                                     ^~~~~~~~~~~~~~
+                                     ~^~~~~~~~~~~~~
    { dg-end-multiline-output "" } */
 }
 
@@ -138,10 +138,10 @@ test_ucn8 (void)
      has the same UTF-8 encoding, and so we again need to underline bytes
      4-11 in the UTF-8 encoding in order to underline digits 4-7.  */
   __emit_string_literal_range ("01234\U00002174\U00002175789", /* { dg-warning "range" } */
-			       4, 11);
+			       5, 4, 11);
 /* { dg-begin-multiline-output "" }
    __emit_string_literal_range ("01234\U00002174\U00002175789",
-                                     ^~~~~~~~~~~~~~~~~~~~~~
+                                     ~^~~~~~~~~~~~~~~~~~~~~
    { dg-end-multiline-output "" } */
 }
 
@@ -150,10 +150,10 @@ test_u8 (void)
 {
   /* Digits 0-9.  */
   __emit_string_literal_range (u8"0123456789", /* { dg-warning "range" } */
-			       4, 7);
+			       6, 4, 7);
 /* { dg-begin-multiline-output "" }
    __emit_string_literal_range (u8"0123456789",
-                                       ^~~~
+                                       ~~^~
    { dg-end-multiline-output "" } */
 }
 
@@ -161,8 +161,8 @@ void
 test_u (void)
 {
   /* Digits 0-9.  */
-  __emit_string_literal_range (u"0123456789", /* { dg-error "unable to read substring range: execution character set != source character set" } */
-			       4, 7);
+  __emit_string_literal_range (u"0123456789", /* { dg-error "unable to read substring location: execution character set != source character set" } */
+			       6, 4, 7);
 /* { dg-begin-multiline-output "" }
    __emit_string_literal_range (u"0123456789",
                                 ^~~~~~~~~~~~~
@@ -173,8 +173,8 @@ void
 test_U (void)
 {
   /* Digits 0-9.  */
-  __emit_string_literal_range (U"0123456789", /* { dg-error "unable to read substring range: execution character set != source character set" } */
-			       4, 7);
+  __emit_string_literal_range (U"0123456789", /* { dg-error "unable to read substring location: execution character set != source character set" } */
+			       6, 4, 7);
 /* { dg-begin-multiline-output "" }
    __emit_string_literal_range (U"0123456789",
                                 ^~~~~~~~~~~~~
@@ -185,8 +185,8 @@ void
 test_L (void)
 {
   /* Digits 0-9.  */
-  __emit_string_literal_range (L"0123456789", /* { dg-error "unable to read substring range: execution character set != source character set" } */
-			       4, 7);
+  __emit_string_literal_range (L"0123456789", /* { dg-error "unable to read substring location: execution character set != source character set" } */
+			       6, 4, 7);
 /* { dg-begin-multiline-output "" }
    __emit_string_literal_range (L"0123456789",
                                 ^~~~~~~~~~~~~
@@ -199,10 +199,10 @@ test_macro (void)
 #define START "01234"  /* { dg-warning "range" } */
   __emit_string_literal_range (START
                                "56789",
-                               3, 6);
+                               4, 3, 6);
 /* { dg-begin-multiline-output "" }
  #define START "01234"
-                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                   ~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    __emit_string_literal_range (START
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                                 "56789",
diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-string-literals-2.c b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-string-literals-2.c
index 7851c02..25cb3f0 100644
--- a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-string-literals-2.c
+++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-string-literals-2.c
@@ -3,7 +3,7 @@
 /* See the notes in diagnostic-test-string-literals-1.c.
    This test case has caret-printing disabled.  */
 
-extern void __emit_string_literal_range (const void *literal,
+extern void __emit_string_literal_range (const void *literal, int caret_idx,
 					 int start_idx, int end_idx);
 /* Test of a stringified macro argument, by itself.  */
 
@@ -12,8 +12,8 @@ test_stringified_token_1 (int x)
 {
 #define STRINGIFY(EXPR) #EXPR
 
-  __emit_string_literal_range (STRINGIFY(x > 0), /* { dg-error "unable to read substring range: macro expansion" } */
-                               0, 4);
+  __emit_string_literal_range (STRINGIFY(x > 0), /* { dg-error "unable to read substring location: macro expansion" } */
+                               0, 0, 4);
 
 #undef STRINGIFY
 }
@@ -23,14 +23,14 @@ test_stringified_token_1 (int x)
 void
 test_stringized_token_2 (int x)
 {
-#define EXAMPLE(EXPR, START_IDX, END_IDX)			\
+#define EXAMPLE(EXPR, CARET_IDX, START_IDX, END_IDX)		\
   do {								\
     __emit_string_literal_range ("  before " #EXPR " after \n",	\
-				 START_IDX, END_IDX);		\
+				 CARET_IDX, START_IDX, END_IDX);	\
   } while (0)
 
-  EXAMPLE(x > 0, 1, 6);
-  /* { dg-error "unable to read substring range: cpp_interpret_string_1 failed" "" { target *-*-* } 28 } */
+  EXAMPLE(x > 0, 1, 1, 6);
+  /* { dg-error "unable to read substring location: cpp_interpret_string_1 failed" "" { target *-*-* } 28 } */
 
 #undef EXAMPLE
 }
@@ -43,8 +43,8 @@ test_stringified_token_3 (int x)
 #define XSTR(s) STR(s)
 #define STR(s) #s
 #define FOO 123456789
-  __emit_string_literal_range (XSTR (FOO), /* { dg-error "unable to read substring range: macro expansion" } */
-                               2, 3);
+  __emit_string_literal_range (XSTR (FOO), /* { dg-error "unable to read substring location: macro expansion" } */
+                               2, 2, 3);
 
 #undef XSTR
 #undef STR
diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_string_literals.c b/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_string_literals.c
index d44612a..dff999c 100644
--- a/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_string_literals.c
+++ b/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_string_literals.c
@@ -95,14 +95,12 @@ check_for_named_call (gimple *stmt,
   return call;
 }
 
-/* Emit a warning covering SRC_RANGE, with the caret at the start of
-   SRC_RANGE.  */
+/* Emit a warning at LOC.  */
 
 static void
-emit_warning (source_range src_range)
+emit_warning (location_t loc)
 {
-  location_t loc
-    = make_location (src_range.m_start, src_range.m_start, src_range.m_finish);
+  source_range src_range = get_range_from_loc (line_table, loc);
   warning_at (loc, 0, "range %i:%i-%i:%i",
 	      LOCATION_LINE (src_range.m_start),
 	      LOCATION_COLUMN (src_range.m_start),
@@ -114,14 +112,14 @@ emit_warning (source_range src_range)
    within string literals, for use by diagnostic-test-string-literals-*.c.
    Emit a warning showing the range of a string literal, for each call to
    a function named "__emit_string_literal_range".
-   The initial argument should be a string literal; arguments 2 and 3
-   should be integer constants, giving the range within the string
+   The initial argument should be a string literal; arguments 2, 3, and 4
+   should be integer constants, giving the caret and range within the string
    to be printed.  */
 
 static void
 test_string_literals (gimple *stmt)
 {
-  gcall *call = check_for_named_call (stmt, "__emit_string_literal_range", 3);
+  gcall *call = check_for_named_call (stmt, "__emit_string_literal_range", 4);
   if (!call)
     return;
 
@@ -141,32 +139,40 @@ test_string_literals (gimple *stmt)
       return;
     }
 
-  tree t_start_idx = gimple_call_arg (call, 1);
-  if (TREE_CODE (t_start_idx) != INTEGER_CST)
+  tree t_caret_idx = gimple_call_arg (call, 1);
+  if (TREE_CODE (t_caret_idx) != INTEGER_CST)
     {
       error_at (call->location, "integer constant required for arg 2");
       return;
     }
+  int caret_idx = TREE_INT_CST_LOW (t_caret_idx);
+
+  tree t_start_idx = gimple_call_arg (call, 2);
+  if (TREE_CODE (t_start_idx) != INTEGER_CST)
+    {
+      error_at (call->location, "integer constant required for arg 3");
+      return;
+    }
   int start_idx = TREE_INT_CST_LOW (t_start_idx);
 
-  tree t_end_idx = gimple_call_arg (call, 2);
+  tree t_end_idx = gimple_call_arg (call, 3);
   if (TREE_CODE (t_end_idx) != INTEGER_CST)
     {
-      error_at (call->location, "integer constant required for arg 3");
+      error_at (call->location, "integer constant required for arg 4");
       return;
     }
   int end_idx = TREE_INT_CST_LOW (t_end_idx);
 
   /* A STRING_CST doesn't have a location, but the ADDR_EXPR does.  */
   location_t strloc = EXPR_LOCATION (t_addr_string);
-  source_range src_range;
+  location_t loc;
   substring_loc substr_loc (strloc, TREE_TYPE (t_string),
-			    start_idx, end_idx);
-  const char *err = substr_loc.get_range (&src_range);
+			    caret_idx, start_idx, end_idx);
+  const char *err = substr_loc.get_location (&loc);
   if (err)
-    error_at (strloc, "unable to read substring range: %s", err);
+    error_at (strloc, "unable to read substring location: %s", err);
   else
-    emit_warning (src_range);
+    emit_warning (loc);
 }
 
 /* Call test_string_literals on every statement within FUN.  */
