diff --git a/gcc/testsuite/c-c++-common/cpp/pragma-push-pop-utf8.c b/gcc/testsuite/c-c++-common/cpp/pragma-push-pop-utf8.c
new file mode 100644
index 0000000000000000000000000000000000000000..8634481d447d9921e15a4009bebd39ec8679d0e8
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/cpp/pragma-push-pop-utf8.c
@@ -0,0 +1,203 @@
+/* { dg-do preprocess } */
+/* { dg-options "-std=c11 -pedantic" { target c } } */
+/* { dg-options "-std=c++11 -pedantic" { target c++ } } */
+/* { dg-additional-options "-Wall" } */
+
+/* PR preprocessor/109704 */
+
+/* Verify basic operations for different extended identifiers...  */
+
+/* ...dollar sign.  */
+#define $x 1
+#pragma push_macro("$x")
+#undef $x
+#define $x 0
+#pragma pop_macro("$x")
+#if !$x
+#error $x
+#endif
+#define $x 1
+_Pragma("push_macro(\"$x\")")
+#undef $x
+#define $x 0
+_Pragma("pop_macro(\"$x\")")
+#if !$x
+#error $x
+#endif
+#define x$ 1
+#pragma push_macro("x$")
+#undef x$
+#define x$ 0
+#pragma pop_macro("x$")
+#if !x$
+#error x$
+#endif
+#define x$ 1
+_Pragma("push_macro(\"x$\")")
+#undef x$
+#define x$ 0
+_Pragma("pop_macro(\"x$\")")
+#if !x$
+#error x$
+#endif
+
+/* ...UCN.  */
+#define \u03B1x 1
+#pragma push_macro("\u03B1x")
+#undef \u03B1x
+#define \u03B1x 0
+#pragma pop_macro("\u03B1x")
+#if !\u03B1x
+#error \u03B1x
+#endif
+#define \u03B1x 1
+_Pragma("push_macro(\"\\u03B1x\")")
+#undef \u03B1x
+#define \u03B1x 0
+_Pragma("pop_macro(\"\\u03B1x\")")
+#if !\u03B1x
+#error \u03B1x
+#endif
+#define x\u03B1 1
+#pragma push_macro("x\u03B1")
+#undef x\u03B1
+#define x\u03B1 0
+#pragma pop_macro("x\u03B1")
+#if !x\u03B1
+#error x\u03B1
+#endif
+#define x\u03B1 1
+_Pragma("push_macro(\"x\\u03B1\")")
+#undef x\u03B1
+#define x\u03B1 0
+_Pragma("pop_macro(\"x\\u03B1\")")
+#if !x\u03B1
+#error x\u03B1
+#endif
+
+/* ...UTF-8.  */
+#define πx 1
+#pragma push_macro("Ï€x")
+#undef πx
+#define πx 0
+#pragma pop_macro("Ï€x")
+#if !Ï€x
+#error πx
+#endif
+#define πx 1
+_Pragma("push_macro(\"Ï€x\")")
+#undef πx
+#define πx 0
+_Pragma("pop_macro(\"Ï€x\")")
+#if !Ï€x
+#error πx
+#endif
+#define xπ 1
+#pragma push_macro("xπ")
+#undef xπ
+#define xπ 0
+#pragma pop_macro("xπ")
+#if !xπ
+#error xπ
+#endif
+#define xπ 1
+_Pragma("push_macro(\"xπ\")")
+#undef xπ
+#define xπ 0
+_Pragma("pop_macro(\"xπ\")")
+#if !xπ
+#error xπ
+#endif
+
+/* Verify UCN and UTF-8 can be intermixed.  */
+#define ħ_0 1
+#pragma push_macro("ħ_0")
+#undef ħ_0
+#define ħ_0 0
+#if ħ_0
+#error ħ_0 ħ_0 \U00000127_0
+#endif
+#pragma pop_macro("\U00000127_0")
+#if !ħ_0
+#error ħ_0 ħ_0 \U00000127_0
+#endif
+#define ħ_1 1
+#pragma push_macro("\U00000127_1")
+#undef ħ_1
+#define ħ_1 0
+#if ħ_1
+#error ħ_1 \U00000127_1 ħ_1
+#endif
+#pragma pop_macro("ħ_1")
+#if !ħ_1
+#error ħ_1 \U00000127_1 ħ_1
+#endif
+#define ħ_2 1
+#pragma push_macro("\U00000127_2")
+#undef ħ_2
+#define ħ_2 0
+#if ħ_2
+#error ħ_2 \U00000127_2 \U00000127_2
+#endif
+#pragma pop_macro("\U00000127_2")
+#if !ħ_2
+#error ħ_2 \U00000127_2 \U00000127_2
+#endif
+#define \U00000127_3 1
+#pragma push_macro("ħ_3")
+#undef \U00000127_3
+#define \U00000127_3 0
+#if \U00000127_3
+#error \U00000127_3 ħ_3 ħ_3
+#endif
+#pragma pop_macro("ħ_3")
+#if !\U00000127_3
+#error \U00000127_3 ħ_3 ħ_3
+#endif
+#define \U00000127_4 1
+#pragma push_macro("ħ_4")
+#undef \U00000127_4
+#define \U00000127_4 0
+#if \U00000127_4
+#error \U00000127_4 ħ_4 \U00000127_4
+#endif
+#pragma pop_macro("\U00000127_4")
+#if !\U00000127_4
+#error \U00000127_4 ħ_4 \U00000127_4
+#endif
+#define \U00000127_5 1
+#pragma push_macro("\U00000127_5")
+#undef \U00000127_5
+#define \U00000127_5 0
+#if \U00000127_5
+#error \U00000127_5 \U00000127_5 ħ_5
+#endif
+#pragma pop_macro("ħ_5")
+#if !\U00000127_5
+#error \U00000127_5 \U00000127_5 ħ_5
+#endif
+
+/* Verify invalid input produces no diagnostics.  */
+#pragma push_macro("") /* { dg-bogus "." } */
+#pragma push_macro("\u") /* { dg-bogus "." } */
+#pragma push_macro("\u0000") /* { dg-bogus "." } */
+#pragma push_macro("not a single identifier") /* { dg-bogus "." } */
+#pragma push_macro("invalid╬character") /* { dg-bogus "." } */
+#pragma push_macro("\u0300invalid_start") /* { dg-bogus "." } */
+#pragma push_macro("#include <cstdlib>") /* { dg-bogus "." } */
+
+/* Verify end-of-line diagnostics for valid and invalid input.  */
+#pragma push_macro("ö") oops /* { dg-warning "extra tokens" } */
+#pragma push_macro("") oops /* { dg-warning "extra tokens" } */
+#pragma push_macro("\u") oops /* { dg-warning "extra tokens" } */
+#pragma push_macro("\u0000") oops /* { dg-warning "extra tokens" } */
+#pragma push_macro("not a single identifier") oops /* { dg-warning "extra tokens" } */
+#pragma push_macro("invalid╬character") oops /* { dg-warning "extra tokens" } */
+#pragma push_macro("\u0300invalid_start") oops /* { dg-warning "extra tokens" } */
+#pragma push_macro("#include <cstdlib>") oops /* { dg-warning "extra tokens" } */
+
+/* Verify expected diagnostics.  */
+#pragma push_macro() /* { dg-error {invalid '#pragma push_macro'} } */
+#pragma pop_macro() /* { dg-error {invalid '#pragma pop_macro'} } */
+_Pragma("push_macro(0)") /* { dg-error {invalid '#pragma push_macro'} } */
+_Pragma("pop_macro(\"oops\"") /* { dg-error {invalid '#pragma pop_macro'} } */
diff --git a/gcc/testsuite/g++.dg/pch/pushpop-2.C b/gcc/testsuite/g++.dg/pch/pushpop-2.C
new file mode 100644
index 0000000000000000000000000000000000000000..84886aea98550f2b38869102cdd071912d5c1ac1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pch/pushpop-2.C
@@ -0,0 +1,18 @@
+/* { dg-options -std=c++11 } */
+#include "pushpop-2.Hs"
+
+#if π != 4
+#error π != 4
+#endif
+#pragma pop_macro("\u03C0")
+#if π != 3
+#error π != 3
+#endif
+
+#if \u03B1 != 6
+#error α != 6
+#endif
+_Pragma("pop_macro(\"\\u03B1\")")
+#if α != 5
+#error α != 5
+#endif
diff --git a/gcc/testsuite/g++.dg/pch/pushpop-2.Hs b/gcc/testsuite/g++.dg/pch/pushpop-2.Hs
new file mode 100644
index 0000000000000000000000000000000000000000..797139a31965dc808938b0886e1ba9e949ed8c86
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pch/pushpop-2.Hs
@@ -0,0 +1,9 @@
+#define π 3
+#pragma push_macro ("Ï€")
+#undef π
+#define π 4
+
+#define \u03B1 5
+#pragma push_macro ("α")
+#undef α
+#define α 6
diff --git a/gcc/testsuite/gcc.dg/pch/pushpop-2.c b/gcc/testsuite/gcc.dg/pch/pushpop-2.c
new file mode 100644
index 0000000000000000000000000000000000000000..61b8430c6d2296267e702b9dc1f7921dcacb246c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pch/pushpop-2.c
@@ -0,0 +1,18 @@
+/* { dg-options -std=c11 } */
+#include "pushpop-2.hs"
+
+#if π != 4
+#error π != 4
+#endif
+#pragma pop_macro("\u03C0")
+#if π != 3
+#error π != 3
+#endif
+
+#if \u03B1 != 6
+#error α != 6
+#endif
+_Pragma("pop_macro(\"\\u03B1\")")
+#if α != 5
+#error α != 5
+#endif
diff --git a/gcc/testsuite/gcc.dg/pch/pushpop-2.hs b/gcc/testsuite/gcc.dg/pch/pushpop-2.hs
new file mode 100644
index 0000000000000000000000000000000000000000..797139a31965dc808938b0886e1ba9e949ed8c86
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pch/pushpop-2.hs
@@ -0,0 +1,9 @@
+#define π 3
+#pragma push_macro ("Ï€")
+#undef π
+#define π 4
+
+#define \u03B1 5
+#pragma push_macro ("α")
+#undef α
+#define α 6
diff --git a/libcpp/charset.cc b/libcpp/charset.cc
index c7d45088128fb70f536a94e53dbdade340820d9a..bcfc10cb965f51b16d7ad58cc5983cff7e9ee495 100644
--- a/libcpp/charset.cc
+++ b/libcpp/charset.cc
@@ -2600,19 +2600,6 @@ cpp_interpret_string (cpp_reader *pfile, const cpp_string *from, size_t count,
   return cpp_interpret_string_1 (pfile, from, count, to, type, NULL, NULL);
 }
 
-/* A "do nothing" diagnostic-handling callback for use by
-   cpp_interpret_string_ranges, so that it can temporarily suppress
-   diagnostic-handling.  */
-
-static bool
-noop_diagnostic_cb (cpp_reader *, enum cpp_diagnostic_level,
-		    enum cpp_warning_reason, rich_location *,
-		    const char *, va_list *)
-{
-  /* no-op.  */
-  return true;
-}
-
 /* This function mimics the behavior of cpp_interpret_string, but
    rather than generating a string in the execution character set,
    *OUT is written to with the source code ranges of the characters
@@ -2652,20 +2639,9 @@ cpp_interpret_string_ranges (cpp_reader *pfile, const cpp_string *from,
      failing, rather than being emitted as a user-visible diagnostic.
      If an diagnostic does occur, we should see it via the return value of
      cpp_interpret_string_1.  */
-  bool (*saved_diagnostic_handler) (cpp_reader *, enum cpp_diagnostic_level,
-				    enum cpp_warning_reason, rich_location *,
-				    const char *, va_list *)
-    ATTRIBUTE_CPP_PPDIAG (5, 0);
-
-  saved_diagnostic_handler = pfile->cb.diagnostic;
-  pfile->cb.diagnostic = noop_diagnostic_cb;
-
+  cpp_auto_suppress_diagnostics suppress {pfile};
   bool result = cpp_interpret_string_1 (pfile, from, count, NULL, type,
 					loc_readers, out);
-
-  /* Restore the saved diagnostic-handler.  */
-  pfile->cb.diagnostic = saved_diagnostic_handler;
-
   if (!result)
     return "cpp_interpret_string_1 failed";
 
@@ -2701,17 +2677,11 @@ static unsigned
 count_source_chars (cpp_reader *pfile, cpp_string str, cpp_ttype type)
 {
   cpp_string str2 = { 0, 0 };
-  bool (*saved_diagnostic_handler) (cpp_reader *, enum cpp_diagnostic_level,
-				    enum cpp_warning_reason, rich_location *,
-				    const char *, va_list *)
-    ATTRIBUTE_CPP_PPDIAG (5, 0);
-  saved_diagnostic_handler = pfile->cb.diagnostic;
-  pfile->cb.diagnostic = noop_diagnostic_cb;
+  cpp_auto_suppress_diagnostics suppress {pfile};
   convert_f save_func = pfile->narrow_cset_desc.func;
   pfile->narrow_cset_desc.func = convert_count_chars;
   bool ret = cpp_interpret_string (pfile, &str, 1, &str2, type);
   pfile->narrow_cset_desc.func = save_func;
-  pfile->cb.diagnostic = saved_diagnostic_handler;
   if (ret)
     {
       if (str2.text != str.text)
diff --git a/libcpp/directives.cc b/libcpp/directives.cc
index 50fa8ace2f681041302e7e991143f76544de0416..bc4a95b66feba49728ce17a47b1fd8c8ca24e286 100644
--- a/libcpp/directives.cc
+++ b/libcpp/directives.cc
@@ -141,7 +141,8 @@ static cpp_macro **find_answer (cpp_hashnode *, const cpp_macro *);
 static void handle_assertion (cpp_reader *, const char *, int);
 static void do_pragma_push_macro (cpp_reader *);
 static void do_pragma_pop_macro (cpp_reader *);
-static void cpp_pop_definition (cpp_reader *, struct def_pragma_macro *);
+static void cpp_pop_definition (cpp_reader *, def_pragma_macro *,
+				cpp_hashnode *);
 
 /* This is the table of directive handlers.  All extensions other than
    #warning, #include_next, and #import are deprecated.  The name is
@@ -2085,55 +2086,95 @@ do_pragma_once (cpp_reader *pfile)
   _cpp_mark_file_once_only (pfile, pfile->buffer->file);
 }
 
-/* Handle #pragma push_macro(STRING).  */
-static void
-do_pragma_push_macro (cpp_reader *pfile)
+/* Helper for #pragma {push,pop}_macro.  Destringize STR and
+   lex it into an identifier, returning the hash node for it.  */
+
+static cpp_hashnode *
+lex_identifier_from_string (cpp_reader *pfile, cpp_string str)
 {
+  auto src = (const uchar *) memchr (str.text, '"', str.len);
+  gcc_checking_assert (src);
+  ++src;
+  const auto limit = str.text + str.len - 1;
+  gcc_checking_assert (*limit == '"' && limit >= src);
+  const auto ident = XALLOCAVEC (uchar, limit - src + 1);
+  auto dest = ident;
+  while (src != limit)
+    {
+      /* We know there is a character following the backslash.  */
+      if (*src == '\\' && (src[1] == '\\' || src[1] == '"'))
+	src++;
+      *dest++ = *src++;
+    }
+
+  /* We reserved a spot for the newline with the + 1 when allocating IDENT.
+     Push a buffer containing the identifier to lex.  */
+  *dest = '\n';
+  cpp_push_buffer (pfile, ident, dest - ident, true);
+  _cpp_clean_line (pfile);
+  pfile->cur_token = _cpp_temp_token (pfile);
+  cpp_token *tok;
+  {
+    /* Suppress diagnostics during lexing so that we silently ignore invalid
+       input, as seems to be the common practice for this pragma.  */
+    cpp_auto_suppress_diagnostics suppress {pfile};
+    tok = _cpp_lex_direct (pfile);
+  }
+
   cpp_hashnode *node;
-  size_t defnlen;
-  const uchar *defn = NULL;
-  char *macroname, *dest;
-  const char *limit, *src;
-  const cpp_token *txt;
-  struct def_pragma_macro *c;
+  if (tok->type != CPP_NAME || pfile->buffer->cur != pfile->buffer->rlimit)
+    node = nullptr;
+  else
+    node = tok->val.node.node;
 
-  txt = get__Pragma_string (pfile);
-  if (!txt)
+  _cpp_pop_buffer (pfile);
+  return node;
+}
+
+/* Common processing for #pragma {push,pop}_macro.  */
+
+static cpp_hashnode *
+push_pop_macro_common (cpp_reader *pfile, const char *type)
+{
+  const cpp_token *const txt = get__Pragma_string (pfile);
+  ++pfile->keep_tokens;
+  cpp_hashnode *node;
+  if (txt)
     {
-      location_t src_loc = pfile->cur_token[-1].src_loc;
-      cpp_error_with_line (pfile, CPP_DL_ERROR, src_loc, 0,
-			   "invalid %<#pragma push_macro%> directive");
       check_eol (pfile, false);
       skip_rest_of_line (pfile);
-      return;
+      node = lex_identifier_from_string (pfile, txt->val.str);
     }
-  dest = macroname = (char *) alloca (txt->val.str.len + 2);
-  src = (const char *) (txt->val.str.text + 1 + (txt->val.str.text[0] == 'L'));
-  limit = (const char *) (txt->val.str.text + txt->val.str.len - 1);
-  while (src < limit)
+  else
     {
-      /* We know there is a character following the backslash.  */
-      if (*src == '\\' && (src[1] == '\\' || src[1] == '"'))
-	src++;
-      *dest++ = *src++;
+      node = nullptr;
+      location_t src_loc = pfile->cur_token[-1].src_loc;
+      cpp_error_with_line (pfile, CPP_DL_ERROR, src_loc, 0,
+			   "invalid %<#pragma %s_macro%> directive", type);
+      skip_rest_of_line (pfile);
     }
-  *dest = 0;
-  check_eol (pfile, false);
-  skip_rest_of_line (pfile);
-  c = XNEW (struct def_pragma_macro);
-  memset (c, 0, sizeof (struct def_pragma_macro));
-  c->name = XNEWVAR (char, strlen (macroname) + 1);
-  strcpy (c->name, macroname);
+  --pfile->keep_tokens;
+  return node;
+}
+
+/* Handle #pragma push_macro(STRING).  */
+static void
+do_pragma_push_macro (cpp_reader *pfile)
+{
+  const auto node = push_pop_macro_common (pfile, "push");
+  if (!node)
+    return;
+  const auto c = XCNEW (def_pragma_macro);
+  c->name = xstrdup ((const char *) NODE_NAME (node));
   c->next = pfile->pushed_macros;
-  node = _cpp_lex_identifier (pfile, c->name);
   if (node->type == NT_VOID)
     c->is_undef = 1;
   else if (node->type == NT_BUILTIN_MACRO)
     c->is_builtin = 1;
   else
     {
-      defn = cpp_macro_definition (pfile, node);
-      defnlen = ustrlen (defn);
+      const auto defn = cpp_macro_definition (pfile, node);
+      const size_t defnlen = ustrlen (defn);
       c->definition = XNEWVEC (uchar, defnlen + 2);
       c->definition[defnlen] = '\n';
       c->definition[defnlen + 1] = 0;
@@ -2150,50 +2191,24 @@ do_pragma_push_macro (cpp_reader *pfile)
 static void
 do_pragma_pop_macro (cpp_reader *pfile)
 {
-  char *macroname, *dest;
-  const char *limit, *src;
-  const cpp_token *txt;
-  struct def_pragma_macro *l = NULL, *c = pfile->pushed_macros;
-  txt = get__Pragma_string (pfile);
-  if (!txt)
-    {
-      location_t src_loc = pfile->cur_token[-1].src_loc;
-      cpp_error_with_line (pfile, CPP_DL_ERROR, src_loc, 0,
-			   "invalid %<#pragma pop_macro%> directive");
-      check_eol (pfile, false);
-      skip_rest_of_line (pfile);
-      return;
-    }
-  dest = macroname = (char *) alloca (txt->val.str.len + 2);
-  src = (const char *) (txt->val.str.text + 1 + (txt->val.str.text[0] == 'L'));
-  limit = (const char *) (txt->val.str.text + txt->val.str.len - 1);
-  while (src < limit)
-    {
-      /* We know there is a character following the backslash.  */
-      if (*src == '\\' && (src[1] == '\\' || src[1] == '"'))
-	src++;
-      *dest++ = *src++;
-    }
-  *dest = 0;
-  check_eol (pfile, false);
-  skip_rest_of_line (pfile);
-
-  while (c != NULL)
+  const auto node = push_pop_macro_common (pfile, "pop");
+  if (!node)
+    return;
+  for (def_pragma_macro *c = pfile->pushed_macros, *l = nullptr; c; c = c->next)
     {
-      if (!strcmp (c->name, macroname))
+      if (!strcmp (c->name, (const char *) NODE_NAME (node)))
 	{
 	  if (!l)
 	    pfile->pushed_macros = c->next;
 	  else
 	    l->next = c->next;
-	  cpp_pop_definition (pfile, c);
+	  cpp_pop_definition (pfile, c, node);
 	  free (c->definition);
 	  free (c->name);
 	  free (c);
 	  break;
 	}
       l = c;
-      c = c->next;
     }
 }
 
@@ -3109,12 +3124,8 @@ cpp_undef (cpp_reader *pfile, const char *macro)
 /* Replace a previous definition DEF of the macro STR.  If DEF is NULL,
    or first element is zero, then the macro should be undefined.  */
 static void
-cpp_pop_definition (cpp_reader *pfile, struct def_pragma_macro *c)
+cpp_pop_definition (cpp_reader *pfile, def_pragma_macro *c, cpp_hashnode *node)
 {
-  cpp_hashnode *node = _cpp_lex_identifier (pfile, c->name);
-  if (node == NULL)
-    return;
-
   if (pfile->cb.before_define)
     pfile->cb.before_define (pfile);
 
@@ -3136,29 +3147,23 @@ cpp_pop_definition (cpp_reader *pfile, struct def_pragma_macro *c)
     }
 
   {
-    size_t namelen;
-    const uchar *dn;
-    cpp_hashnode *h = NULL;
-    cpp_buffer *nbuf;
-
-    namelen = ustrcspn (c->definition, "( \n");
-    h = cpp_lookup (pfile, c->definition, namelen);
-    dn = c->definition + namelen;
-
-    nbuf = cpp_push_buffer (pfile, dn, ustrchr (dn, '\n') - dn, true);
+    const auto namelen = ustrcspn (c->definition, "( \n");
+    const auto dn = c->definition + namelen;
+    const auto nbuf = cpp_push_buffer (pfile, dn, ustrchr (dn, '\n') - dn,
+				       true);
     if (nbuf != NULL)
       {
 	_cpp_clean_line (pfile);
 	nbuf->sysp = 1;
-	if (!_cpp_create_definition (pfile, h, 0))
+	if (!_cpp_create_definition (pfile, node, 0))
 	  abort ();
 	_cpp_pop_buffer (pfile);
       }
     else
       abort ();
-    h->value.macro->line = c->line;
-    h->value.macro->syshdr = c->syshdr;
-    h->value.macro->used = c->used;
+    node->value.macro->line = c->line;
+    node->value.macro->syshdr = c->syshdr;
+    node->value.macro->used = c->used;
   }
 }
 
diff --git a/libcpp/errors.cc b/libcpp/errors.cc
index 5d8ceb936f92a50f7aa2b583507bd6fc94ab87aa..ad45f61913c313ecd1b8553b6bc9ca4829e2c005 100644
--- a/libcpp/errors.cc
+++ b/libcpp/errors.cc
@@ -350,3 +350,19 @@ cpp_errno_filename (cpp_reader *pfile, enum cpp_diagnostic_level level,
   return cpp_error_at (pfile, level, loc, "%s: %s", filename,
 		       xstrerror (errno));
 }
+
+cpp_auto_suppress_diagnostics::cpp_auto_suppress_diagnostics (cpp_reader *pfile)
+  : m_pfile (pfile), m_cb (pfile->cb.diagnostic)
+{
+  m_pfile->cb.diagnostic
+    = [] (cpp_reader *, cpp_diagnostic_level, cpp_warning_reason,
+	  rich_location *, const char *, va_list *)
+    {
+      return true;
+    };
+}
+
+cpp_auto_suppress_diagnostics::~cpp_auto_suppress_diagnostics ()
+{
+  m_pfile->cb.diagnostic = m_cb;
+}
diff --git a/libcpp/include/cpplib.h b/libcpp/include/cpplib.h
index e64c2b6b20baacb3bfbe1ebec10fd9f7efcafd5e..be367b1f54afeccf3e26cf4717845e0905411bb0 100644
--- a/libcpp/include/cpplib.h
+++ b/libcpp/include/cpplib.h
@@ -1662,4 +1662,17 @@ enum cpp_xid_property {
 
 unsigned int cpp_check_xid_property (cppchar_t c);
 
+/* In errors.cc */
+
+/* RAII class to suppress CPP diagnostics in the current scope.  */
+class cpp_auto_suppress_diagnostics
+{
+ public:
+  explicit cpp_auto_suppress_diagnostics (cpp_reader *pfile);
+  ~cpp_auto_suppress_diagnostics ();
+ private:
+  cpp_reader *const m_pfile;
+  const decltype (cpp_callbacks::diagnostic) m_cb;
+};
+
 #endif /* ! LIBCPP_CPPLIB_H */
diff --git a/libcpp/internal.h b/libcpp/internal.h
index 2379fbba89968d3c9b30a97c26da47839095196a..6c2a36ecd0ab5d43473f98ddde98ddc23320e3f2 100644
--- a/libcpp/internal.h
+++ b/libcpp/internal.h
@@ -794,7 +794,6 @@ extern cpp_token *_cpp_lex_direct (cpp_reader *);
 extern unsigned char *_cpp_spell_ident_ucns (unsigned char *, cpp_hashnode *);
 extern int _cpp_equiv_tokens (const cpp_token *, const cpp_token *);
 extern void _cpp_init_tokenrun (tokenrun *, unsigned int);
-extern cpp_hashnode *_cpp_lex_identifier (cpp_reader *, const char *);
 extern int _cpp_remaining_tokens_num_in_context (cpp_context *);
 extern void _cpp_init_lexer (void);
 static inline void *_cpp_reserve_room (cpp_reader *pfile, size_t have,
diff --git a/libcpp/lex.cc b/libcpp/lex.cc
index 66ff6cedb4b4d99e87a7c28b1f5c09c6152320cd..40c53181bfc9c704c85e141c1a7918b0f800ad84 100644
--- a/libcpp/lex.cc
+++ b/libcpp/lex.cc
@@ -2091,39 +2091,6 @@ identifier_diagnostics_on_lex (cpp_reader *pfile, cpp_hashnode *node)
 		 NODE_NAME (node));
 }
 
-/* Helper function to get the cpp_hashnode of the identifier BASE.  */
-static cpp_hashnode *
-lex_identifier_intern (cpp_reader *pfile, const uchar *base)
-{
-  cpp_hashnode *result;
-  const uchar *cur;
-  unsigned int len;
-  unsigned int hash = HT_HASHSTEP (0, *base);
-
-  cur = base + 1;
-  while (ISIDNUM (*cur))
-    {
-      hash = HT_HASHSTEP (hash, *cur);
-      cur++;
-    }
-  len = cur - base;
-  hash = HT_HASHFINISH (hash, len);
-  result = CPP_HASHNODE (ht_lookup_with_hash (pfile->hash_table,
-					      base, len, hash, HT_ALLOC));
-  identifier_diagnostics_on_lex (pfile, result);
-  return result;
-}
-
-/* Get the cpp_hashnode of an identifier specified by NAME in
-   the current cpp_reader object.  If none is found, NULL is returned.  */
-cpp_hashnode *
-_cpp_lex_identifier (cpp_reader *pfile, const char *name)
-{
-  cpp_hashnode *result;
-  result = lex_identifier_intern (pfile, (uchar *) name);
-  return result;
-}
-
 /* Lex an identifier starting at BASE.  BUFFER->CUR is expected to point
    one past the first character at BASE, which may be a (possibly multi-byte)
    character if STARTS_UCN is true.  */