diff --git a/gcc/c-family/c-ppoutput.cc b/gcc/c-family/c-ppoutput.cc
index 6e054358e9e..ec607f67a75 100644
--- a/gcc/c-family/c-ppoutput.cc
+++ b/gcc/c-family/c-ppoutput.cc
@@ -280,7 +280,7 @@ token_streamer::stream (cpp_reader *pfile, const cpp_token *token,
 	  const char *space;
 	  const char *name;
 
-	  line_marker_emitted = maybe_print_line (token->src_loc);
+	  line_marker_emitted = maybe_print_line (loc);
 	  fputs ("#pragma ", print.outf);
 	  c_pp_lookup_pragma (token->val.pragma, &space, &name);
 	  if (space)
diff --git a/gcc/testsuite/c-c++-common/cpp/diagnostic-pragma-1.c b/gcc/testsuite/c-c++-common/cpp/diagnostic-pragma-1.c
index 9867c94a8dd..801c93935b8 100644
--- a/gcc/testsuite/c-c++-common/cpp/diagnostic-pragma-1.c
+++ b/gcc/testsuite/c-c++-common/cpp/diagnostic-pragma-1.c
@@ -1,4 +1,5 @@
 // { dg-do compile }
+// { dg-additional-options "-ftrack-macro-expansion=0" }
 
 #pragma GCC warning "warn-a" // { dg-warning warn-a }
 #pragma GCC error "err-b" // { dg-error err-b }
diff --git a/gcc/testsuite/c-c++-common/cpp/pr57580.c b/gcc/testsuite/c-c++-common/cpp/pr57580.c
index e77462b20de..b0e54d876d6 100644
--- a/gcc/testsuite/c-c++-common/cpp/pr57580.c
+++ b/gcc/testsuite/c-c++-common/cpp/pr57580.c
@@ -1,6 +1,6 @@
 /* PR preprocessor/57580 */
 /* { dg-do compile } */
-/* { dg-options "-save-temps" } */
+/* { dg-options "-save-temps -ftrack-macro-expansion=0" } */
 
 #define MSG 	\
   _Pragma("message(\"message0\")")	\
diff --git a/gcc/testsuite/c-c++-common/gomp/pragma-3.c b/gcc/testsuite/c-c++-common/gomp/pragma-3.c
index 3e1b2111c3d..e0cffb8aeea 100644
--- a/gcc/testsuite/c-c++-common/gomp/pragma-3.c
+++ b/gcc/testsuite/c-c++-common/gomp/pragma-3.c
@@ -8,7 +8,8 @@ void
 f (void)
 {
   const char *str = outer(inner(1,2)); /* { dg-line str_location } */
-  /* { dg-warning "35:'pragma omp error' encountered: Test" "" { target *-*-* } inner_location }
+  /* { dg-warning "1:'pragma omp error' encountered: Test" "" { target *-*-* } 1 }
+     { dg-note "35: in <_Pragma directive>" "" { target *-*-* } inner_location }
      { dg-note "20:in expansion of macro 'inner'" "" { target *-*-* } outer_location }
      { dg-note "21:in expansion of macro 'outer'" "" { target *-*-* } str_location } */
 }
diff --git a/gcc/testsuite/c-c++-common/gomp/pragma-5.c b/gcc/testsuite/c-c++-common/gomp/pragma-5.c
index 173c25e803a..787a334882d 100644
--- a/gcc/testsuite/c-c++-common/gomp/pragma-5.c
+++ b/gcc/testsuite/c-c++-common/gomp/pragma-5.c
@@ -8,7 +8,8 @@ void
 f (void)
 {
   const char *str = outer(inner(1,2)); /* { dg-line str_location } */
-  /* { dg-warning "35:'pragma omp error' encountered: Test" "" { target *-*-* } inner_location }
+  /* { dg-warning "4:'pragma omp error' encountered: Test" "" { target *-*-* } 1 }
+     { dg-note "35:in <_Pragma directive>" "" { target *-*-*} inner_location }
      { dg-note "20:in expansion of macro 'inner'" "" { target *-*-* } outer_location }
      { dg-note "21:in expansion of macro 'outer'" "" { target *-*-* } str_location } */
 }
diff --git a/gcc/testsuite/c-c++-common/pragma-diag-17.c b/gcc/testsuite/c-c++-common/pragma-diag-17.c
new file mode 100644
index 00000000000..b9539c9598b
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/pragma-diag-17.c
@@ -0,0 +1,35 @@
+/* Test virtual location aspects of _Pragmas, when an error is reported after
+   lexing the tokens from the _Pragma string.  */
+/* { dg-additional-options "-Wpragmas -Wunknown-pragmas" } */
+
+_Pragma("GCC diagnostic ignored \"oops1\"") /* { dg-note {1:in <_Pragma directive>} } */
+/* { dg-warning {24:'oops1' is not an option} "" { target *-*-* } 1 } */
+
+#define S2 "GCC diagnostic ignored \"oops2\""
+_Pragma(S2) /* { dg-note {1:in <_Pragma directive>} } */
+/* { dg-warning {24:'oops2' is not an option} "" { target *-*-* } 1 } */
+
+#define PP(x) _Pragma(x) /* { dg-note {15:in <_Pragma directive>} } */
+PP("GCC diagnostic ignored \"oops3\"") /* { dg-note {1:in expansion of macro 'PP'} } */
+/* { dg-warning {24:'oops3' is not an option} "" { target *-*-* } 1 } */
+
+#define X4 _Pragma("GCC diagnostic ignored \"oops4\"") /* { dg-note {12:in <_Pragma directive>} } */
+#define Y4 X4 /* { dg-note {12:in expansion of macro 'X4'} } */
+Y4 /* { dg-note {1:in expansion of macro 'Y4'} } */
+/* { dg-warning {24:'oops4' is not an option} "" { target *-*-* } 1 } */
+
+#define P5 _Pragma /* { dg-note {12:in <_Pragma directive>} } */
+#define S5 "GCC diagnostic ignored \"oops5\""
+#define Y5 P5(S5) /* { dg-note {12:in expansion of macro 'P5'} } */
+Y5 /* { dg-note {1:in expansion of macro 'Y5'} } */
+/* { dg-warning {24:'oops5' is not an option} "" { target *-*-* } 1 } */
+
+#define P6 _Pragma /* { dg-note {12:in <_Pragma directive>} } */
+#define X6 P6("GCC diagnostic ignored \"oops6\"") /* { dg-note {12:in expansion of macro 'P6'} } */
+X6 /* { dg-note {1:in expansion of macro 'X6'} } */
+/* { dg-warning {24:'oops6' is not an option} "" { target *-*-* } 1 } */
+
+_Pragma(__DATE__) /* { dg-warning {-:[-Wunknown-pragmas]} } */
+
+_Pragma("once") /* { dg-note {1:in <_Pragma directive>} } */
+/* { dg-warning {#pragma once in main file} "" { target *-*-*} 1 } */
diff --git a/gcc/testsuite/c-c++-common/pragma-diag-18.c b/gcc/testsuite/c-c++-common/pragma-diag-18.c
new file mode 100644
index 00000000000..5de0fbcb8f1
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/pragma-diag-18.c
@@ -0,0 +1,18 @@
+/* Test virtual location aspects of _Pragmas, when an error is reported during
+   lexing of the _Pragma string itself or of the tokens within it.  */
+/* { dg-additional-options "-Wpragmas" } */
+
+#define X1 "\""
+_Pragma(X1) /* { dg-note {1:in <_Pragma directive>} } */
+/* { dg-warning {1:missing terminating " character} "" { target *-*-* } 1 } */
+
+#define X2a _Pragma("GCC warning \"hello\"") ( /* { dg-note {13:in <_Pragma directive>} } */
+#define X2b "GCC warning \"goodbye\"" )
+_Pragma X2a X2b /* { dg-note {9:in expansion of macro 'X2a'} } */
+/* { dg-note {1:in <_Pragma directive>} "" { target *-*-* } .-1 } */
+/* { dg-warning {13:hello} "" { target *-*-* } 1 } */
+/* { dg-warning {13:goodbye} "" { target *-*-* } 1 } */
+
+_Pragma() /* { dg-error {9:_Pragma takes a parenthesized string literal} } */
+/* { dg-note {1:in <_Pragma directive>} "" { target *-*-* } .-1 } */
+/* { dg-error {at end of input|'_Pragma' does not name a type} "" { target *-*-* } .-2 } */
diff --git a/gcc/testsuite/g++.dg/cpp/pragma-raw-string.C b/gcc/testsuite/g++.dg/cpp/pragma-raw-string.C
new file mode 100644
index 00000000000..5a495aadeec
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp/pragma-raw-string.C
@@ -0,0 +1,16 @@
+/* Test that _Pragma with a raw string works correctly.  */
+/* { dg-do compile { target c++11 } } */
+/* { dg-additional-options "-Wunused-variable -Wpragmas" } */
+
+_Pragma(R"delim(GCC diagnostic push)delim")
+_Pragma(R"(GCC diagnostic ignored "-Wunused-variable")")
+void f1 () { int i; }
+_Pragma(R"(GCC diagnostic pop)")
+void f2 () { int i; } /* { dg-warning {18:-Wunused-variable} } */
+
+/* Make sure lines stay in sync if there is an embedded newline too.  */
+_Pragma(R"xyz(GCC diagnostic ignored R"(two
+line option?)")xyz")
+/* { dg-note {1:in <_Pragma directive>} "" { target *-*-* } .-2 } */
+/* { dg-warning {24:unknown option} "" { target *-*-* } 1 } */
+void f3 () { int i; } /* { dg-warning {18:-Wunused-variable} } */
diff --git a/gcc/testsuite/g++.dg/pch/LC_GEN-maps.C b/gcc/testsuite/g++.dg/pch/LC_GEN-maps.C
new file mode 100644
index 00000000000..c21bce29bd2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pch/LC_GEN-maps.C
@@ -0,0 +1,20 @@
+#include "LC_GEN-maps.H"
+
+/* The LC_GEN map was written to the PCH, but there is not currently a way to
+   observe that fact in normal user code.  Let's try to test it anyway, using
+   -fdump-internal-locations to inspect the line_maps object we received from
+   the PCH.  */
+
+/* { dg-additional-options -fdump-internal-locations } */
+/* { dg-allow-blank-lines-in-output "" } */
+
+/* These regexps themselves will also appear in the output of
+   -fdump-internal-locations, so we need to make sure they contain at least
+   some regexp special characters, even if not strictly necessary, so they
+   match the intended text only, and not themselves.  Also, we make the second
+   one intentionally match the whole output if it maches anything.  We could
+   use dg-excess-errors instead, but that outputs XFAILS which are not really
+   helpful for this test.  */
+
+/* { dg-regexp {reason: . \(LC_GEN\)} } */
+/* { dg-regexp {(.|[\n\r])*data: this string should end up in the "PCH"(.|[\n\r])*} } */
diff --git a/gcc/testsuite/g++.dg/pch/LC_GEN-maps.Hs b/gcc/testsuite/g++.dg/pch/LC_GEN-maps.Hs
new file mode 100644
index 00000000000..76eefa7d1ae
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pch/LC_GEN-maps.Hs
@@ -0,0 +1,5 @@
+/* Evaluating the _Pragma directive here creates an LC_GEN map in the
+   line_maps object that will be stored in the PCH.  The test will make sure
+   that the buffer holding the de-stringified _Pragma string contents makes
+   its way there.  */
+_Pragma("this string should end up in the \"PCH\"")
diff --git a/gcc/testsuite/g++.dg/pch/operator-1.C b/gcc/testsuite/g++.dg/pch/operator-1.C
index 290b5f7ab21..bf1c8b07bdb 100644
--- a/gcc/testsuite/g++.dg/pch/operator-1.C
+++ b/gcc/testsuite/g++.dg/pch/operator-1.C
@@ -1,2 +1,3 @@
+/* { dg-additional-options "-ftrack-macro-expansion=0" } */
 #include "operator-1.H"
 int main(void){ major(0);} /* { dg-warning "Did not Work" } */
diff --git a/gcc/testsuite/gcc.dg/cpp/pr28165.c b/gcc/testsuite/gcc.dg/cpp/pr28165.c
index 71c7c1dba46..3e5e49ffa01 100644
--- a/gcc/testsuite/gcc.dg/cpp/pr28165.c
+++ b/gcc/testsuite/gcc.dg/cpp/pr28165.c
@@ -2,5 +2,6 @@
 /* PR preprocessor/28165 */
 
 /* { dg-do preprocess } */
+/* { dg-additional-options "-ftrack-macro-expansion=0" } */
 #pragma GCC system_header   /* { dg-warning "system_header" "ignored" } */
 _Pragma ("GCC system_header")   /* { dg-warning "system_header" "ignored" } */
diff --git a/gcc/testsuite/gcc.dg/cpp/pr35322.c b/gcc/testsuite/gcc.dg/cpp/pr35322.c
index 1af9605eac6..5bd5f69b73d 100644
--- a/gcc/testsuite/gcc.dg/cpp/pr35322.c
+++ b/gcc/testsuite/gcc.dg/cpp/pr35322.c
@@ -1,4 +1,5 @@
 /* Test case for PR 35322 -- _Pragma ICE.  */
 
 /* { dg-do preprocess } */
+/* { dg-additional-options "-ftrack-macro-expansion=0" } */
 _Pragma("GCC dependency") /* { dg-error "#pragma dependency expects" } */
diff --git a/gcc/testsuite/gcc.dg/dfp/pragma-float-const-decimal64-4.c b/gcc/testsuite/gcc.dg/dfp/pragma-float-const-decimal64-4.c
index af0398daf79..42fc28a4384 100644
--- a/gcc/testsuite/gcc.dg/dfp/pragma-float-const-decimal64-4.c
+++ b/gcc/testsuite/gcc.dg/dfp/pragma-float-const-decimal64-4.c
@@ -1,4 +1,5 @@
 /* { dg-do compile } */
+/* { dg-additional-options -ftrack-macro-expansion=0 } */
 
 /* N1312 7.1.1: The FLOAT_CONST_DECIMAL64 pragma.
    C99 6.4.4.2a (New).
diff --git a/gcc/testsuite/gcc.dg/dfp/pragma-float-const-decimal64-5.c b/gcc/testsuite/gcc.dg/dfp/pragma-float-const-decimal64-5.c
index 75e9525dda0..3aefede7b5d 100644
--- a/gcc/testsuite/gcc.dg/dfp/pragma-float-const-decimal64-5.c
+++ b/gcc/testsuite/gcc.dg/dfp/pragma-float-const-decimal64-5.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-std=c99 -pedantic" } */
+/* { dg-options "-std=c99 -pedantic -ftrack-macro-expansion=0" } */
 
 /* N1312 7.1.1: The FLOAT_CONST_DECIMAL64 pragma.
    C99 6.4.4.2a (New).
diff --git a/gcc/testsuite/gcc.dg/dfp/pragma-float-const-decimal64-6.c b/gcc/testsuite/gcc.dg/dfp/pragma-float-const-decimal64-6.c
index 03c1715bee6..6d70ce2bb8d 100644
--- a/gcc/testsuite/gcc.dg/dfp/pragma-float-const-decimal64-6.c
+++ b/gcc/testsuite/gcc.dg/dfp/pragma-float-const-decimal64-6.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-std=c99 -pedantic-errors" } */
+/* { dg-options "-std=c99 -pedantic-errors -ftrack-macro-expansion=0" } */
 
 /* N1312 7.1.1: The FLOAT_CONST_DECIMAL64 pragma.
    C99 6.4.4.2a (New).
diff --git a/gcc/testsuite/gcc.dg/gomp/macro-4.c b/gcc/testsuite/gcc.dg/gomp/macro-4.c
index a4ed9a3980a..c6817d40125 100644
--- a/gcc/testsuite/gcc.dg/gomp/macro-4.c
+++ b/gcc/testsuite/gcc.dg/gomp/macro-4.c
@@ -1,6 +1,6 @@
 /* PR preprocessor/27746 */
 /* { dg-do compile } */
-/* { dg-options "-fopenmp -Wunknown-pragmas" } */
+/* { dg-options "-fopenmp -Wunknown-pragmas -ftrack-macro-expansion=0" } */
 
 #define p		_Pragma ("omp parallel")
 #define omp_p		_Pragma ("omp p")
diff --git a/gcc/testsuite/gcc.dg/pragma-message.c b/gcc/testsuite/gcc.dg/pragma-message.c
index 1b7cf09de0a..72fb0da6f44 100644
--- a/gcc/testsuite/gcc.dg/pragma-message.c
+++ b/gcc/testsuite/gcc.dg/pragma-message.c
@@ -45,8 +45,9 @@
 #define DO_PRAGMA(x) _Pragma (#x) /* { dg-line pragma_loc1 } */
 #define TODO(x) DO_PRAGMA(message ("TODO - " #x)) /* { dg-line pragma_loc2 } */
 TODO(Okay 4) /* { dg-message "in expansion of macro 'TODO'" } */
-/* { dg-message "TODO - Okay 4" "test4.1" { target *-*-* } pragma_loc1 } */
+/* { dg-message "1:TODO - Okay 4" "test4.1" { target *-*-* } 1 } */
 /* { dg-message "in expansion of macro 'DO_PRAGMA'" "test4.2" { target *-*-* } pragma_loc2 } */
+/* { dg-note {in <_Pragma directive>} "test4.3" { target *-*-* } pragma_loc1 } */
 
 #if 0
 #pragma message ("Not printed")
diff --git a/gcc/testsuite/lib/prune.exp b/gcc/testsuite/lib/prune.exp
index 04c6a1dd7a1..a5788842652 100644
--- a/gcc/testsuite/lib/prune.exp
+++ b/gcc/testsuite/lib/prune.exp
@@ -54,6 +54,7 @@ proc prune_gcc_output { text } {
 
     # Diagnostic inclusion stack
     regsub -all "(^|\n)(In file)?\[ \]+included from \[^\n\]*" $text "" text
+    regsub -all "(^|\n)In buffer generated from \[^\n\]*" $text "" text
     regsub -all "(^|\n)\[ \]+from \[^\n\]*" $text "" text
     regsub -all "(^|\n)(In|of) module( \[^\n \]*,)? imported at \[^\n\]*" $text "" text
 
diff --git a/gcc/tree-diagnostic.cc b/gcc/tree-diagnostic.cc
index 731e3559cd8..fd2773f3d8a 100644
--- a/gcc/tree-diagnostic.cc
+++ b/gcc/tree-diagnostic.cc
@@ -203,9 +203,12 @@ maybe_unwind_expanded_macro_loc (diagnostic_context *context,
 	const int resolved_def_loc_line = SOURCE_LINE (m, l0);
         if (ix == 0 && saved_location_line != resolved_def_loc_line)
           {
-            diagnostic_append_note (context, resolved_def_loc, 
-                                    "in definition of macro %qs",
-                                    linemap_map_get_macro_name (iter->map));
+	    const char *name = linemap_map_get_macro_name (iter->map);
+	    if (*name == '<')
+	      diagnostic_append_note (context, resolved_def_loc, "in %s", name);
+	    else
+	      diagnostic_append_note (context, resolved_def_loc,
+				      "in definition of macro %qs", name);
             /* At this step, as we've printed the context of the macro
                definition, we don't want to print the context of its
                expansion, otherwise, it'd be redundant.  */
@@ -220,9 +223,12 @@ maybe_unwind_expanded_macro_loc (diagnostic_context *context,
                                     MACRO_MAP_EXPANSION_POINT_LOCATION (iter->map),
                                     LRK_MACRO_DEFINITION_LOCATION, NULL);
 
-        diagnostic_append_note (context, resolved_exp_loc, 
-                                "in expansion of macro %qs",
-                                linemap_map_get_macro_name (iter->map));
+	const char *name = linemap_map_get_macro_name (iter->map);
+	if (*name == '<')
+	  diagnostic_append_note (context, resolved_exp_loc, "in %s", name);
+	else
+	  diagnostic_append_note (context, resolved_exp_loc,
+				  "in expansion of macro %qs", name);
       }
 }
 
diff --git a/libcpp/directives.cc b/libcpp/directives.cc
index a29f4abc3cc..c9cb8ad7e6b 100644
--- a/libcpp/directives.cc
+++ b/libcpp/directives.cc
@@ -127,10 +127,10 @@ static void do_pragma_warning_or_error (cpp_reader *, bool error);
 static void do_pragma_warning (cpp_reader *);
 static void do_pragma_error (cpp_reader *);
 static void do_linemarker (cpp_reader *);
-static const cpp_token *get_token_no_padding (cpp_reader *);
-static const cpp_token *get__Pragma_string (cpp_reader *);
-static void destringize_and_run (cpp_reader *, const cpp_string *,
-				 location_t);
+static const cpp_token *get_token_no_padding (cpp_reader *,
+					      location_t * = nullptr);
+static const cpp_token *get__Pragma_string (cpp_reader *,
+					    location_t * = nullptr);
 static bool parse_answer (cpp_reader *, int, location_t, cpp_macro **);
 static cpp_hashnode *parse_assertion (cpp_reader *, int, cpp_macro **);
 static cpp_macro **find_answer (cpp_hashnode *, const cpp_macro *);
@@ -1502,14 +1502,12 @@ do_pragma (cpp_reader *pfile)
 {
   const struct pragma_entry *p = NULL;
   const cpp_token *token, *pragma_token;
-  location_t pragma_token_virt_loc = 0;
   cpp_token ns_token;
   unsigned int count = 1;
 
   pfile->state.prevent_expansion++;
 
-  pragma_token = token = cpp_get_token_with_location (pfile,
-						      &pragma_token_virt_loc);
+  pragma_token = token = cpp_get_token (pfile);
   ns_token = *token;
   if (token->type == CPP_NAME)
     {
@@ -1535,7 +1533,7 @@ do_pragma (cpp_reader *pfile)
     {
       if (p->is_deferred)
 	{
-	  pfile->directive_result.src_loc = pragma_token_virt_loc;
+	  pfile->directive_result.src_loc = pragma_token->src_loc;
 	  pfile->directive_result.type = CPP_PRAGMA;
 	  pfile->directive_result.flags = pragma_token->flags;
 	  pfile->directive_result.val.pragma = p->u.ident;
@@ -1828,11 +1826,11 @@ do_pragma_error (cpp_reader *pfile)
 
 /* Get a token but skip padding.  */
 static const cpp_token *
-get_token_no_padding (cpp_reader *pfile)
+get_token_no_padding (cpp_reader *pfile, location_t *virt_loc)
 {
   for (;;)
     {
-      const cpp_token *result = cpp_get_token (pfile);
+      const cpp_token *result = cpp_get_token_with_location (pfile, virt_loc);
       if (result->type != CPP_PADDING)
 	return result;
     }
@@ -1841,7 +1839,7 @@ get_token_no_padding (cpp_reader *pfile)
 /* Check syntax is "(string-literal)".  Returns the string on success,
    or NULL on failure.  */
 static const cpp_token *
-get__Pragma_string (cpp_reader *pfile)
+get__Pragma_string (cpp_reader *pfile, location_t *string_virt_loc)
 {
   const cpp_token *string;
   const cpp_token *paren;
@@ -1852,7 +1850,7 @@ get__Pragma_string (cpp_reader *pfile)
   if (paren->type != CPP_OPEN_PAREN)
     return NULL;
 
-  string = get_token_no_padding (pfile);
+  string = get_token_no_padding (pfile, string_virt_loc);
   if (string->type == CPP_EOF)
     _cpp_backup_tokens (pfile, 1);
   if (string->type != CPP_STRING && string->type != CPP_WSTRING
@@ -1872,55 +1870,105 @@ get__Pragma_string (cpp_reader *pfile)
 /* Destringize IN into a temporary buffer, by removing the first \ of
    \" and \\ sequences, and process the result as a #pragma directive.  */
 static void
-destringize_and_run (cpp_reader *pfile, const cpp_string *in,
-		     location_t expansion_loc)
-{
-  const unsigned char *src, *limit;
-  char *dest, *result;
-  cpp_context *saved_context;
-  cpp_token *saved_cur_token;
-  tokenrun *saved_cur_run;
-  cpp_token *toks;
-  int count;
-  const struct directive *save_directive;
-
-  dest = result = (char *) alloca (in->len - 1);
-  src = in->text + 1 + (in->text[0] == 'L');
-  limit = in->text + in->len - 1;
-  while (src < limit)
+destringize_and_run (cpp_reader *pfile, _cpp__Pragma_state *pstate)
+{
+  uchar *dest, *result;
+
+  /* Determine where the data starts, and what kind of string it is.  */
+  const cpp_string *const in = &pstate->string_tok->val.str;
+  const uchar *src = in->text;
+  bool is_raw_string = false;
+  for (;;)
     {
-      /* We know there is a character following the backslash.  */
-      if (*src == '\\' && (src[1] == '\\' || src[1] == '"'))
-	src++;
-      *dest++ = *src++;
+      switch (*src++)
+	{
+	case '\"': break;
+	case 'R': is_raw_string = true; continue;
+	case '\0': gcc_assert (false);
+	default: continue;
+	}
+      break;
     }
-  *dest = '\n';
 
-  /* Ugh; an awful kludge.  We are really not set up to be lexing
-     tokens when in the middle of a macro expansion.  Use a new
-     context to force cpp_get_token to lex, and so skip_rest_of_line
-     doesn't go beyond the end of the text.  Also, remember the
-     current lexing position so we can return to it later.
+  /* If we were given a raw string literal, we don't need to destringize it,
+     but we do need to strip off the prefix and the suffix.  */
+  if (is_raw_string)
+    {
+      cpp_string buf;
+      const bool ok
+	= cpp_interpret_string_notranslate (pfile, in, 1, &buf, CPP_STRING);
+      gcc_assert (ok);
 
-     Something like line-at-a-time lexing should remove the need for
-     this.  */
-  saved_context = pfile->context;
-  saved_cur_token = pfile->cur_token;
-  saved_cur_run = pfile->cur_run;
+      /* BUF.TEXT ends with a terminating null (which is counted in BUF.LEN).
+	 We want to end with a newline as required by cpp_push_buffer.  While it
+	 is not strictly necessary to null terminate our buffer, it is useful to
+	 do so for safety, so we reserve one extra byte.  The \n\0 sequence is
+	 appended after the else block.  */
+      result = _cpp_unaligned_alloc (pfile, buf.len + 1);
+      memcpy (result, buf.text, buf.len - 1);
+      dest = result + (buf.len - 1);
+      XDELETEVEC (buf.text);
+    }
+  else
+    {
+      const auto last_ptr = in->text + in->len - 1;
+      /* +2 for the trailing \n\0 as above.  */
+      dest = result = _cpp_unaligned_alloc (pfile, last_ptr - src + 1 + 2);
+      while (src < last_ptr)
+	{
+	  /* We know there is a character following the backslash.  */
+	  if (*src == '\\' && (src[1] == '\\' || src[1] == '"'))
+	    src++;
+	  *dest++ = *src++;
+	}
+    }
+  *dest++ = '\n';
+  *dest++ = '\0';
 
-  pfile->context = XCNEW (cpp_context);
+  /* We will now ask PFILE to interrupt what it was doing (obtaining tokens
+     either from the main context via lexing, or from a macro context), and get
+     tokens from the string argument instead.  We create a new isolated
+     cpp_context so that cpp_get_token will think it is working on the main
+     buffer and call cpp_lex_token accordingly.  Save all the relevant state so
+     we can return to the previous task once that is completed.
 
-  /* Inline run_directive, since we need to delay the _cpp_pop_buffer
-     until we've read all of the tokens that we want.  */
-  cpp_push_buffer (pfile, (const uchar *) result, dest - result,
-		   /* from_stage3 */ true);
-  /* ??? Antique Disgusting Hack.  What does this do?  */
-  if (pfile->buffer->prev)
-    pfile->buffer->file = pfile->buffer->prev->file;
+     Doing things this way is a bit of a kludge, but the alternative would be
+     to create a new context type to support lexing from a string, and that
+     would add overhead to every token parse, while _Pragma is relatively rarely
+     needed.  */
 
+  const auto saved_context = pfile->context;
+  const auto saved_cur_token = pfile->cur_token;
+  const auto saved_cur_run = pfile->cur_run;
+  pfile->context = XCNEW (cpp_context);
   start_directive (pfile);
+
+  /* Set up an LC_GEN line map to get valid locations for the tokens we are
+     about to lex.  We need to do this after calling start_directive, because
+     historically pfile->directive_line is what's been passed to
+     pfile->cb.def_pragma, and we are not proposing to change that now.  To
+     decide if we are in a system header or not, look at the location of the
+     _Pragma token.  So for instance if we have _Pragma(S) in the main file,
+     where S is a macro defined in a system header, we will decide we are not in
+     a system location.  */
+  const unsigned int buf_len = dest - result;
+  const int sysp = linemap_location_in_system_header_p (pfile->line_table,
+							pstate->pragma_loc);
+  linemap_add (pfile->line_table, LC_GEN, sysp, (const char *)result, 1,
+	       buf_len);
+  const auto col_hint = (uchar *) memchr (result, '\n', buf_len) - result;
+  linemap_line_start (pfile->line_table, 1, col_hint);
+
+  /* Push the buffer.  */
+  cpp_push_buffer (pfile, result, buf_len - 2, true);
+
+  /* This is needed to make _Pragma("once") work correctly, as it needs
+     pfile->buffer->file to be set to the current source file.  */
+  pfile->buffer->file = pfile->buffer->prev->file;
+
+  /* We are ready to start handling the directive as normal.  */
   _cpp_clean_line (pfile);
-  save_directive = pfile->directive;
+  const auto save_directive = pfile->directive;
   pfile->directive = &dtable[T_PRAGMA];
   do_pragma (pfile);
   if (pfile->directive_result.type == CPP_PRAGMA)
@@ -1929,80 +1977,123 @@ destringize_and_run (cpp_reader *pfile, const cpp_string *in,
   pfile->directive = save_directive;
 
   /* We always insert at least one token, the directive result.  It'll
-     either be a CPP_PADDING or a CPP_PRAGMA.  In the later case, we 
+     either be a CPP_PADDING or a CPP_PRAGMA.  In the latter case, we
      need to insert *all* of the tokens, including the CPP_PRAGMA_EOL.  */
 
   /* If we're not handling the pragma internally, read all of the tokens from
-     the string buffer now, while the string buffer is still installed.  */
-  /* ??? Note that the token buffer allocated here is leaked.  It's not clear
-     to me what the true lifespan of the tokens are.  It would appear that
-     the lifespan is the entire parse of the main input stream, in which case
-     this may not be wrong.  */
-  if (pfile->directive_result.type == CPP_PRAGMA)
-    {
-      int maxcount;
-
-      count = 1;
-      maxcount = 50;
-      toks = XNEWVEC (cpp_token, maxcount);
-      toks[0] = pfile->directive_result;
-      toks[0].src_loc = expansion_loc;
-
-      do
+     the string buffer now, while the string buffer is still installed, and then
+     push them as a new token context after.  This way, we can clean up the
+     temporarily modified state of the lexer now.  */
+
+  const bool is_deferred = (pfile->directive_result.type == CPP_PRAGMA);
+  if (is_deferred)
+    {
+      /* Using _cpp_buff allows us to arrange for this buffer to be freed when
+	 the new token context is popped, without adding any additional space
+	 overhead to the cpp_context structure.  In order to support
+	 track_macro_expansion==0, we need to store the cpp_token objects
+	 contiguously, and the virt locs separately.  (Note that these tokens
+	 may acquire a virtual loc here, in case the pragma allows macro
+	 expansion.  But they will not yet have virtual locs representing them
+	 as part of the expansion of the _Pragma directive; this will be handled
+	 later in _cpp_push__Pragma_token_context.  */
+      const size_t init_count = 50;
+      _cpp_buff *tok_buff
+	= _cpp_get_buff (pfile, init_count * sizeof (cpp_token));
+      _cpp_buff *loc_buff
+	= _cpp_get_buff (pfile, init_count * sizeof (location_t));
+
+      /* Remember the base buffs so we can chain the final loc buff after it
+	 once we are done collecting tokens.  */
+      const auto tok_buff0 = tok_buff;
+      pstate->buff_chain = &loc_buff->next;
+
+      /* DIRECTIVE_RESULT is the first token we return (a CPP_PRAGMA).  This
+	 location cannot result from macro expansion, so there is no virtual
+	 location to worry about.  */
+      auto tok_out = (cpp_token *) tok_buff->base;
+      *tok_out++ = pfile->directive_result;
+      auto loc_out = (location_t *) loc_buff->base;
+      *loc_out++ = pfile->directive_result.src_loc;
+      unsigned int ntoks = 1;
+
+      /* Finally get all the tokens.  */
+      for (;;)
 	{
-	  if (count == maxcount)
+	  if (tok_buff->limit - (uchar *)tok_out < (int)sizeof (cpp_token))
 	    {
-	      maxcount = maxcount * 3 / 2;
-	      toks = XRESIZEVEC (cpp_token, toks, maxcount);
+	      _cpp_extend_buff (pfile, &tok_buff,
+				tok_buff->limit - tok_buff->base);
+	      tok_out = ((cpp_token *)tok_buff->base) + ntoks;
 	    }
-	  toks[count] = *cpp_get_token (pfile);
-	  /* _Pragma is a builtin, so we're not within a macro-map, and so
-	     the token locations are set to bogus ordinary locations
-	     near to, but after that of the "_Pragma".
-	     Paper over this by setting them equal to the location of the
-	     _Pragma itself (PR preprocessor/69126).  */
-	  toks[count].src_loc = expansion_loc;
+
+	  if (loc_buff->limit - (uchar *)loc_out < (int)sizeof (location_t))
+	    {
+	      _cpp_extend_buff (pfile, &loc_buff,
+				loc_buff->limit - loc_buff->base);
+	      loc_out = ((location_t *)loc_buff->base) + ntoks;
+	    }
+
+	  const auto this_tok = tok_out;
+	  *tok_out++ = *cpp_get_token_with_location (pfile, loc_out++);
+	  ++ntoks;
+
 	  /* Macros have been already expanded by cpp_get_token
 	     if the pragma allowed expansion.  */
-	  toks[count++].flags |= NO_EXPAND;
+	  this_tok->flags |= NO_EXPAND;
+	  if (this_tok->type == CPP_PRAGMA_EOL)
+	    break;
 	}
-      while (toks[count-1].type != CPP_PRAGMA_EOL);
+
+      /* Finalize the buffers so they can be stored as one chain in a
+	 cpp_context and freed when that context is popped.  */
+      tok_buff0->next = loc_buff;
+      pstate->ntoks = ntoks;
+      pstate->tok_buff = tok_buff;
+      pstate->loc_buff = loc_buff;
     }
   else
     {
-      count = 1;
-      toks = &pfile->avoid_paste;
-
       /* If we handled the entire pragma internally, make sure we get the
 	 line number correct for the next token.  */
       if (pfile->cb.line_change)
 	pfile->cb.line_change (pfile, pfile->cur_token, false);
     }
 
-  /* Finish inlining run_directive.  */
+  /* Reset the old state before...  */
+  const auto map = linemap_add (pfile->line_table, LC_LEAVE, 0, nullptr, 0);
+  linemap_line_start
+    (pfile->line_table,
+     ORDINARY_MAP_STARTING_LINE_NUMBER (linemap_check_ordinary (map)),
+     127);
   pfile->buffer->file = NULL;
   _cpp_pop_buffer (pfile);
-
-  /* Reset the old macro state before ...  */
   XDELETE (pfile->context);
   pfile->context = saved_context;
   pfile->cur_token = saved_cur_token;
   pfile->cur_run = saved_cur_run;
 
-  /* ... inserting the new tokens we collected.  */
-  _cpp_push_token_context (pfile, NULL, toks, count);
+  /* ...inserting the new tokens we collected.  This is not a simple call to
+     _cpp_push_token_context, because we need to create virtual locations
+     for the tokens and push an extended token context to return them.  */
+  if (is_deferred)
+    _cpp_push__Pragma_token_context (pfile, pstate);
+  else
+    _cpp_push_token_context (pfile, nullptr, &pfile->avoid_paste, 1);
 }
 
+
 /* Handle the _Pragma operator.  Return 0 on error, 1 if ok.  */
+
 int
-_cpp_do__Pragma (cpp_reader *pfile, location_t expansion_loc)
+_cpp_do__Pragma (cpp_reader *pfile, _cpp__Pragma_state *pstate)
 {
-  const cpp_token *string = get__Pragma_string (pfile);
-  pfile->directive_result.type = CPP_PADDING;
+  pstate->string_tok = get__Pragma_string (pfile, &pstate->string_loc);
 
-  if (string)
+  pfile->directive_result.type = CPP_PADDING;
+  if (pstate->string_tok)
     {
-      destringize_and_run (pfile, &string->val.str, expansion_loc);
+      destringize_and_run (pfile, pstate);
       return 1;
     }
   cpp_error (pfile, CPP_DL_ERROR,
diff --git a/libcpp/errors.cc b/libcpp/errors.cc
index df5f8d6fa32..351c7bc17e8 100644
--- a/libcpp/errors.cc
+++ b/libcpp/errors.cc
@@ -60,13 +60,11 @@ cpp_diagnostic_at (cpp_reader * pfile, enum cpp_diagnostic_level level,
 		   enum cpp_warning_reason reason, rich_location *richloc,
 		   const char *msgid, va_list *ap)
 {
-  bool ret;
-
   if (!pfile->cb.diagnostic)
     abort ();
-  ret = pfile->cb.diagnostic (pfile, level, reason, richloc, _(msgid), ap);
-
-  return ret;
+  if (pfile->diagnostic_rebase_loc)
+    _cpp_rebase_diagnostic_location (pfile, richloc);
+  return pfile->cb.diagnostic (pfile, level, reason, richloc, _(msgid), ap);
 }
 
 /* Print a diagnostic at the location of the previously lexed token.  */
@@ -197,16 +195,14 @@ cpp_diagnostic_with_line (cpp_reader * pfile, enum cpp_diagnostic_level level,
 			  location_t src_loc, unsigned int column,
 			  const char *msgid, va_list *ap)
 {
-  bool ret;
-  
   if (!pfile->cb.diagnostic)
     abort ();
   rich_location richloc (pfile->line_table, src_loc);
   if (column)
     richloc.override_column (column);
-  ret = pfile->cb.diagnostic (pfile, level, reason, &richloc, _(msgid), ap);
-
-  return ret;
+  if (pfile->diagnostic_rebase_loc)
+    _cpp_rebase_diagnostic_location (pfile, &richloc);
+  return pfile->cb.diagnostic (pfile, level, reason, &richloc, _(msgid), ap);
 }
 
 /* Print a warning or error, depending on the value of LEVEL.  */
diff --git a/libcpp/include/line-map.h b/libcpp/include/line-map.h
index 7bed55548c7..ff93d529e67 100644
--- a/libcpp/include/line-map.h
+++ b/libcpp/include/line-map.h
@@ -1758,6 +1758,7 @@ class rich_location
   location_range *get_range (unsigned int idx);
 
   expanded_location get_expanded_location (unsigned int idx);
+  void forget_cached_expanded_location () { m_have_expanded_location = false; }
 
   void
   override_column (int column);
diff --git a/libcpp/internal.h b/libcpp/internal.h
index badfd1b40da..fd0de49e302 100644
--- a/libcpp/internal.h
+++ b/libcpp/internal.h
@@ -292,6 +292,28 @@ struct lexer_state
   unsigned char ignore__Pragma;
 };
 
+/* Because handling of _Pragma bounces back and forth between macro.cc and
+   directives.cc, it is useful to keep the needed state in one place.  */
+struct _cpp__Pragma_state
+{
+  const cpp_token *string_tok; /* The token for the argument string.  */
+
+  /* These locations are the virtual locations returned by
+     cpp_get_token_with_location, if the relevant tokens came from macro
+     expansions.  */
+  location_t pragma_loc; /* Location of the _Pragma token.  */
+  location_t string_loc; /* Location of the string arg.  */
+
+  /* The tokens lexed from the _Pragma string.  */
+  unsigned int ntoks;
+  _cpp_buff *tok_buff;
+  _cpp_buff *loc_buff;
+  _cpp_buff **buff_chain;
+};
+
+/* In macro.cc, implements pstate->diagnostic_rebase_loc handling.  */
+void _cpp_rebase_diagnostic_location (cpp_reader *, rich_location *);
+
 /* Special nodes - identifiers with predefined significance.  */
 struct spec_nodes
 {
@@ -601,6 +623,12 @@ struct cpp_reader
      zero of said file.  */
   location_t main_loc;
 
+  /* Location from which we would like to pretend a given token was
+     macro-expanded, if a diagnostic is issued.  Useful for improving
+     _Pragma diagnostics.  */
+  location_t diagnostic_rebase_loc;
+  cpp_hashnode *diagnostic_rebase_node;
+
   /* Returns true iff we should warn about UTF-8 bidirectional control
      characters.  */
   bool warn_bidi_p () const
@@ -701,6 +729,8 @@ extern const unsigned char *_cpp_builtin_macro_text (cpp_reader *,
 extern int _cpp_warn_if_unused_macro (cpp_reader *, cpp_hashnode *, void *);
 extern void _cpp_push_token_context (cpp_reader *, cpp_hashnode *,
 				     const cpp_token *, unsigned int);
+extern void _cpp_push__Pragma_token_context (cpp_reader *,
+					     _cpp__Pragma_state *);
 extern void _cpp_backup_tokens_direct (cpp_reader *, unsigned int);
 
 /* In identifiers.cc */
@@ -772,7 +802,7 @@ extern int _cpp_handle_directive (cpp_reader *, bool);
 extern void _cpp_define_builtin (cpp_reader *, const char *);
 extern char ** _cpp_save_pragma_names (cpp_reader *);
 extern void _cpp_restore_pragma_names (cpp_reader *, char **);
-extern int _cpp_do__Pragma (cpp_reader *, location_t);
+extern int _cpp_do__Pragma (cpp_reader *, _cpp__Pragma_state *);
 extern void _cpp_init_directives (cpp_reader *);
 extern void _cpp_init_internal_pragmas (cpp_reader *);
 extern void _cpp_do_file_change (cpp_reader *, enum lc_reason, const char *,
diff --git a/libcpp/macro.cc b/libcpp/macro.cc
index 452e14a1e66..7591fab8632 100644
--- a/libcpp/macro.cc
+++ b/libcpp/macro.cc
@@ -93,6 +93,8 @@ struct macro_arg_saved_data {
 static const char *vaopt_paste_error =
   N_("'##' cannot appear at either end of __VA_OPT__");
 
+static const uchar pragma_str[] = N_("<_Pragma directive>");
+
 static void expand_arg (cpp_reader *, macro_arg *);
 
 /* A class for tracking __VA_OPT__ state while iterating over a
@@ -756,7 +758,31 @@ builtin_macro (cpp_reader *pfile, cpp_hashnode *node,
       if (pfile->state.in_directive || pfile->state.ignore__Pragma)
 	return 0;
 
-      return _cpp_do__Pragma (pfile, loc);
+      _cpp__Pragma_state pstate = {};
+      pstate.pragma_loc = loc;
+
+      /* The diagnostic_rebase stuff arranges that any diagnostics issued during
+	 lexing will point the user back to the _Pragma location.  */
+      const auto prev_rloc = pfile->diagnostic_rebase_loc;
+      const auto prev_rnode = pfile->diagnostic_rebase_node;
+      pfile->diagnostic_rebase_loc = loc;
+      pfile->diagnostic_rebase_node
+	= cpp_lookup (pfile, pragma_str, (sizeof pragma_str) - 1);
+
+      /* While lexing tokens, if we end up expanding some macros, we would
+	 like not to override top_most_macro_node; preserving it pointing
+	 to the _Pragma helps out the case of -ftrack-macro-expansion=0.
+	 Setting this flag causes in_macro_expansion_p to return TRUE,
+	 even though we are not technically in a macro context.  */
+      const bool prev_expand = pfile->about_to_expand_macro_p;
+      pfile->about_to_expand_macro_p = true;
+
+      /* Get the tokens, then reset everything back how it was.  */
+      const int res = _cpp_do__Pragma (pfile, &pstate);
+      pfile->about_to_expand_macro_p = prev_expand;
+      pfile->diagnostic_rebase_loc = prev_rloc;
+      pfile->diagnostic_rebase_node = prev_rnode;
+      return res;
     }
 
   buf = _cpp_builtin_macro_text (pfile, node, expand_loc);
@@ -2802,7 +2828,8 @@ _cpp_pop_context (cpp_reader *pfile)
 	  && macro_of_context (context->prev) != macro)
 	macro->flags &= ~NODE_DISABLED;
 
-      if (macro == pfile->top_most_macro_node && context->prev == NULL)
+      if (!pfile->about_to_expand_macro_p
+	  && context->prev == &pfile->base_context)
 	/* We are popping the context of the top-most macro node.  */
 	pfile->top_most_macro_node = NULL;
     }
@@ -2836,10 +2863,10 @@ reached_end_of_context (cpp_context *context)
 
 /* Consume the next token contained in the current context of PFILE,
    and return it in *TOKEN. It's "full location" is returned in
-   *LOCATION. If -ftrack-macro-location is in effeect, fFull location"
-   means the location encoding the locus of the token across macro
-   expansion; otherwise it's just is the "normal" location of the
-   token which (*TOKEN)->src_loc.  */
+   *LOCATION.  If -ftrack-macro-location is in effect, "full location"
+   means the virtual location encoding the locus of the token across macro
+   expansion; otherwise it's just the "normal" (spelling) location of the
+   token, which is (*TOKEN)->src_loc.  */
 static inline void
 consume_next_token_from_context (cpp_reader *pfile,
 				 const cpp_token ** token,
@@ -4129,3 +4156,90 @@ cpp_macro_definition (cpp_reader *pfile, cpp_hashnode *node,
   *buffer = '\0';
   return pfile->macro_buffer;
 }
+
+/* Handle the list of tokens lexed from a _Pragma string.  We need to create
+   virtual locations (reflecting the fact that these tokens are logically
+   within the expansion of the _Pragma string), and push an extended token
+   context.  */
+
+void
+_cpp_push__Pragma_token_context (cpp_reader *pfile,
+				 _cpp__Pragma_state *pstate)
+{
+  const auto node = cpp_lookup (pfile, pragma_str, (sizeof pragma_str) - 1);
+  const auto toks = (const cpp_token *) pstate->tok_buff->base;
+
+  /* If not tracking macro expansions, then just push a normal token context.
+     cpp_get_token () will return the user the location of the _Pragma
+     directive, so they will have a valid location for the _Pragma which is
+     outside the LC_GEN map.  */
+  if (!CPP_OPTION (pfile, track_macro_expansion))
+    {
+      _cpp_push_token_context (pfile, node, toks, pstate->ntoks);
+      /* Arrange to free the buffers when the context is popped.  */
+      pfile->context->buff = pstate->tok_buff;
+      return;
+    }
+
+  location_t *virt_locs = nullptr;
+  _cpp_buff *const macro_tokens = tokens_buff_new (pfile, pstate->ntoks,
+						   &virt_locs);
+  const auto map = linemap_enter_macro (pfile->line_table, node,
+					pstate->pragma_loc, pstate->ntoks);
+  const auto locs = (location_t *)pstate->loc_buff->base;
+  for (unsigned int i = 0; i != pstate->ntoks; ++i)
+    {
+      tokens_buff_add_token (macro_tokens, virt_locs, toks + i,
+			     locs[i], locs[i], map, i);
+    }
+
+  /* Chain tok_buff ahead of macro_tokens so both are freed together
+     when the context is popped.  pstate->buff_chain is the NEXT pointer
+     of the last buffer in the LOC_BUFF chain, so it looks like:
+     TOK_BUFF_1 -> ... -> TOK_BUFF_N -> ... -> LOC_BUFF_1 -> ... ->
+     LOC_BUFF_N -> MACRO_TOKENS_1 -> ... -> MACRO_TOKENS_N.  */
+  *pstate->buff_chain = macro_tokens;
+  push_extended_tokens_context (pfile, node, pstate->tok_buff, virt_locs,
+				(const cpp_token **) macro_tokens->base,
+				pstate->ntoks);
+}
+
+void
+_cpp_rebase_diagnostic_location (cpp_reader *pfile, rich_location *richloc)
+{
+  /* If we are here, it means a diagnostic is being generated while lexing
+     tokens outside a macro context, but pfile->diagnostic_rebase_loc indicates
+     a location from which we would like to pretend we are actually expanding a
+     macro.  This works around the fact that a macro map can only be generated
+     once we know how many tokens it will contain, but the number of tokens to
+     be lexed from, say, a _Pragma string, is not known ahead of time.  In the
+     case of _Pragma, _cpp_push__Pragma_token_context above handles creating the
+     proper macro map once all the tokens are available.  This function runs
+     earlier than that, while in the middle of lexing tokens, so it creates a
+     temporary macro map which serves only to improve the information content of
+     the diagnostic that's about to be generated.  */
+
+  const int nlocs = richloc->get_num_locations ();
+
+  if (CPP_OPTION (pfile, track_macro_expansion))
+    {
+      const auto map
+	= linemap_enter_macro (pfile->line_table, pfile->diagnostic_rebase_node,
+			       pfile->diagnostic_rebase_loc, nlocs);
+      for (int i = 0; i != nlocs; ++i)
+	{
+	  location_range& r = *richloc->get_range (i);
+	  r.m_loc = linemap_add_macro_token (map, i, r.m_loc, r.m_loc);
+	}
+    }
+  else
+    {
+      /* When not tracking macro expansion, then set the location to the
+	 expansion point for all tokens, which is what would be returned
+	 by cpp_get_token in the normal case.  */
+      for (int i = 0; i != nlocs; ++i)
+	richloc->get_range (i)->m_loc = pfile->invocation_location;
+    }
+
+  richloc->forget_cached_expanded_location ();
+}
diff --git a/libgomp/testsuite/libgomp.oacc-c-c++-common/reduction-5.c b/libgomp/testsuite/libgomp.oacc-c-c++-common/reduction-5.c
index ddccfe89e73..f518915492d 100644
--- a/libgomp/testsuite/libgomp.oacc-c-c++-common/reduction-5.c
+++ b/libgomp/testsuite/libgomp.oacc-c-c++-common/reduction-5.c
@@ -46,7 +46,8 @@ main (void)
   /* Nvptx targets require a vector_length or 32 in to allow spinlocks with
      gangs.  */
   check_reduction (num_workers (nw) vector_length (vl), worker); /* { dg-line check_reduction_loc } */
-  /* { dg-warning "22:region is vector partitioned but does not contain vector partitioned code" "" { target *-*-* } pragma_loc }
+  /* { dg-warning "1:region is vector partitioned but does not contain vector partitioned code" "" { target *-*-* } 1 }
+     { dg-note "22:in <_Pragma directive>" "" { target *-*-* xfail offloading_enabled} pragma_loc }
      { dg-note "1:in expansion of macro 'DO_PRAGMA'" "" { target *-*-* xfail offloading_enabled } DO_PRAGMA_loc }
      { dg-note "3:in expansion of macro 'check_reduction'" "" { target *-*-* xfail offloading_enabled } check_reduction_loc }
      TODO See PR101551 for 'offloading_enabled' XFAILs.  */
diff --git a/libgomp/testsuite/libgomp.oacc-c-c++-common/vred2d-128.c b/libgomp/testsuite/libgomp.oacc-c-c++-common/vred2d-128.c
index 84e6d51670b..bd2567d96f8 100644
--- a/libgomp/testsuite/libgomp.oacc-c-c++-common/vred2d-128.c
+++ b/libgomp/testsuite/libgomp.oacc-c-c++-common/vred2d-128.c
@@ -40,46 +40,54 @@ int a1[n], a2[n];
 
 gentest (test1, "acc parallel loop gang vector_length (128) firstprivate (t1, t2)",
 	 "acc loop vector reduction(+:t1) reduction(-:t2)")
-/* { dg-warning {'t1' is used uninitialized} {} { target *-*-* } outer }
+/* { dg-warning {'t1' is used uninitialized} {} { target *-*-* } 1 }
+   { dg-note {in <_Pragma directive>} {} { target { ! offloading_enabled } } outer }
    { dg-note {'t1' was declared here} {} { target *-*-* } vars }
-   { dg-note {in expansion of macro 'gentest'} {} { target { ! offloading_enabled } } .-4 }
+   { dg-note {in expansion of macro 'gentest'} {} { target { ! offloading_enabled } } .-5 }
      TODO See PR101551 for 'offloading_enabled' differences.  */
-/* { dg-warning {'t2' is used uninitialized} {} { target *-*-* } outer }
+/* { dg-warning {'t2' is used uninitialized} {} { target *-*-* } 1 }
+   { DUPdg-note {in <_Pragma directive>} {} { target { ! offloading_enabled } } outer }
    { dg-note {'t2' was declared here} {} { target *-*-* } vars }
-   { DUP_dg-note {in expansion of macro 'gentest'} {} { target { ! offloading_enabled } } .-8 }
+   { DUP_dg-note {in expansion of macro 'gentest'} {} { target { ! offloading_enabled } } .-10 }
      TODO See PR101551 for 'offloading_enabled' differences.  */
 
 gentest (test2, "acc parallel loop gang vector_length (128) firstprivate (t1, t2)",
 	 "acc loop worker vector reduction(+:t1) reduction(-:t2)")
-/* { DUPdg-warning {'t1' is used uninitialized} {} { target *-*-* } outer }
+/* { DUPdg-warning {'t1' is used uninitialized} {} { target *-*-* } 1 }
+   { DUPdg-note {in <_Pragma directive>} {} { target { ! offloading_enabled } } outer }
    { DUP_dg-note {'t1' was declared here} {} { target *-*-* } vars }
-   { dg-note {in expansion of macro 'gentest'} {} { target { ! offloading_enabled } } .-4 }
+   { dg-note {in expansion of macro 'gentest'} {} { target { ! offloading_enabled } } .-5 }
      TODO See PR101551 for 'offloading_enabled' differences.  */
-/* { DUPdg-warning {'t2' is used uninitialized} {} { target *-*-* } outer }
+/* { DUPdg-warning {'t2' is used uninitialized} {} { target *-*-* } 1 }
+   { DUPdg-note {in <_Pragma directive>} {} { target { ! offloading_enabled } } outer }
    { DUP_dg-note {'t2' was declared here} {} { target *-*-* } vars }
-   { DUP_dg-note {in expansion of macro 'gentest'} {} { target { ! offloading_enabled } } .-8 }
+   { DUP_dg-note {in expansion of macro 'gentest'} {} { target { ! offloading_enabled } } .-10 }
      TODO See PR101551 for 'offloading_enabled' differences.  */
 
 gentest (test3, "acc parallel loop gang worker vector_length (128) firstprivate (t1, t2)",
 	 "acc loop vector reduction(+:t1) reduction(-:t2)")
-/* { DUPdg-warning {'t1' is used uninitialized} {} { target *-*-* } outer }
+/* { DUPdg-warning {'t1' is used uninitialized} {} { target *-*-* } 1 }
+   { DUPdg-note {in <_Pragma directive>} {} { target { ! offloading_enabled } } outer }
    { DUP_dg-note {'t1' was declared here} {} { target *-*-* } vars }
-   { dg-note {in expansion of macro 'gentest'} {} { target { ! offloading_enabled } } .-4 }
+   { dg-note {in expansion of macro 'gentest'} {} { target { ! offloading_enabled } } .-5 }
      TODO See PR101551 for 'offloading_enabled' differences.  */
-/* { DUPdg-warning {'t2' is used uninitialized} {} { target *-*-* } outer }
+/* { DUPdg-warning {'t2' is used uninitialized} {} { target *-*-* } 1 }
+   { DUPdg-note {in <_Pragma directive>} {} { target { ! offloading_enabled } } outer }
    { DUP_dg-note {'t2' was declared here} {} { target *-*-* } vars }
-   { DUP_dg-note {in expansion of macro 'gentest'} {} { target { ! offloading_enabled } } .-8 }
+   { DUP_dg-note {in expansion of macro 'gentest'} {} { target { ! offloading_enabled } } .-10 }
      TODO See PR101551 for 'offloading_enabled' differences.  */
 
 gentest (test4, "acc parallel loop firstprivate (t1, t2)",
 	 "acc loop reduction(+:t1) reduction(-:t2)")
-/* { DUPdg-warning {'t1' is used uninitialized} {} { target *-*-* } outer }
+/* { DUPdg-warning {'t1' is used uninitialized} {} { target *-*-* } 1 }
+   { DUPdg-note {in <_Pragma directive>} {} { target { ! offloading_enabled } } outer }
    { DUP_dg-note {'t1' was declared here} {} { target *-*-* } vars }
-   { dg-note {in expansion of macro 'gentest'} {} { target { ! offloading_enabled } } .-4 }
+   { dg-note {in expansion of macro 'gentest'} {} { target { ! offloading_enabled } } .-5 }
      TODO See PR101551 for 'offloading_enabled' differences.  */
-/* { DUPdg-warning {'t2' is used uninitialized} {} { target *-*-* } outer }
+/* { DUPdg-warning {'t2' is used uninitialized} {} { target *-*-* } 1 }
+   { DUPdg-note {in <_Pragma directive>} {} { target { ! offloading_enabled } } outer }
    { DUP_dg-note {'t2' was declared here} {} { target *-*-* } vars }
-   { DUP_dg-note {in expansion of macro 'gentest'} {} { target { ! offloading_enabled } } .-8 }
+   { DUP_dg-note {in expansion of macro 'gentest'} {} { target { ! offloading_enabled } } .-10 }
      TODO See PR101551 for 'offloading_enabled' differences.  */
 
 
