[RFC] parse #pragma GCC diagnostic in libcpp
diff mbox

Message ID CAESRpQAOyna4v8HDDHtMGUVPc7H5A=XFhJutRd8AkOzH4HC=GQ@mail.gmail.com
State New
Headers show

Commit Message

Manuel López-Ibáñez July 28, 2015, 10:46 p.m. UTC
Currently, #pragma GCC diagnostic is handled entirely by the FE. This
has several drawbacks:

* PR c++/53431 - C++ preprocessor ignores #pragma GCC diagnostic: The
C++ parser lexes (and preprocesses) before handling the pragmas.

* PR 53920 - "gcc -E" does not honor #pragma GCC diagnostic ignored
"-Wunused-macro": Because -E does not invoke the FE code that parses
the FE pragmas.

* PR 64698 - preprocessor ignores #pragma GCC diagnostic when using
-save-temps. Same issue as above.

The following patch moves the handling of #pragma GCC diagnostic to
libcpp but keeps the interface with the diagnostic machinery in the FE
by using a call-back function.

One serious problem with this approach is that the preprocessor will
delete the pragmas from the preprocessed output, thus '-E',
'-save-temps'  will not contain the pragmas and compiling the
preprocessed file will trigger the warnings that they were meant to
suppress.  Any ideas how to prevent libcpp from deleting the #pragmas?

No Changelog since this is not a request for approval, but comments are welcome.

Cheers,

Manuel.

Comments

Manuel López-Ibáñez Aug. 21, 2015, 5:41 p.m. UTC | #1
Any comments on this? https://gcc.gnu.org/ml/gcc-patches/2015-07/msg02414.html

I don't see any other way to fix these PRs, but I don't know how to
keep the pragmas from being deleted by the preprocessor.

Cheers,

Manuel.

On 29 July 2015 at 00:46, Manuel López-Ibáñez <lopezibanez@gmail.com> wrote:
> Currently, #pragma GCC diagnostic is handled entirely by the FE. This
> has several drawbacks:
>
> * PR c++/53431 - C++ preprocessor ignores #pragma GCC diagnostic: The
> C++ parser lexes (and preprocesses) before handling the pragmas.
>
> * PR 53920 - "gcc -E" does not honor #pragma GCC diagnostic ignored
> "-Wunused-macro": Because -E does not invoke the FE code that parses
> the FE pragmas.
>
> * PR 64698 - preprocessor ignores #pragma GCC diagnostic when using
> -save-temps. Same issue as above.
>
> The following patch moves the handling of #pragma GCC diagnostic to
> libcpp but keeps the interface with the diagnostic machinery in the FE
> by using a call-back function.
>
> One serious problem with this approach is that the preprocessor will
> delete the pragmas from the preprocessed output, thus '-E',
> '-save-temps'  will not contain the pragmas and compiling the
> preprocessed file will trigger the warnings that they were meant to
> suppress.  Any ideas how to prevent libcpp from deleting the #pragmas?
>
> No Changelog since this is not a request for approval, but comments are welcome.
>
> Cheers,
>
> Manuel.
Manuel López-Ibáñez Sept. 20, 2015, 6:18 p.m. UTC | #2
PING^2: https://gcc.gnu.org/ml/gcc-patches/2015-07/msg02414.html

On 21 August 2015 at 19:41, Manuel López-Ibáñez <lopezibanez@gmail.com> wrote:
> Any comments on this? https://gcc.gnu.org/ml/gcc-patches/2015-07/msg02414.html
>
> I don't see any other way to fix these PRs, but I don't know how to
> keep the pragmas from being deleted by the preprocessor.
>
> Cheers,
>
> Manuel.
>
> On 29 July 2015 at 00:46, Manuel López-Ibáñez <lopezibanez@gmail.com> wrote:
>> Currently, #pragma GCC diagnostic is handled entirely by the FE. This
>> has several drawbacks:
>>
>> * PR c++/53431 - C++ preprocessor ignores #pragma GCC diagnostic: The
>> C++ parser lexes (and preprocesses) before handling the pragmas.
>>
>> * PR 53920 - "gcc -E" does not honor #pragma GCC diagnostic ignored
>> "-Wunused-macro": Because -E does not invoke the FE code that parses
>> the FE pragmas.
>>
>> * PR 64698 - preprocessor ignores #pragma GCC diagnostic when using
>> -save-temps. Same issue as above.
>>
>> The following patch moves the handling of #pragma GCC diagnostic to
>> libcpp but keeps the interface with the diagnostic machinery in the FE
>> by using a call-back function.
>>
>> One serious problem with this approach is that the preprocessor will
>> delete the pragmas from the preprocessed output, thus '-E',
>> '-save-temps'  will not contain the pragmas and compiling the
>> preprocessed file will trigger the warnings that they were meant to
>> suppress.  Any ideas how to prevent libcpp from deleting the #pragmas?
>>
>> No Changelog since this is not a request for approval, but comments are welcome.
>>
>> Cheers,
>>
>> Manuel.
Joseph Myers Sept. 23, 2015, 11:01 p.m. UTC | #3
On Sun, 20 Sep 2015, Manuel López-Ibáñez wrote:

> PING^2: https://gcc.gnu.org/ml/gcc-patches/2015-07/msg02414.html
> 
> On 21 August 2015 at 19:41, Manuel López-Ibáñez <lopezibanez@gmail.com> wrote:
> > Any comments on this? https://gcc.gnu.org/ml/gcc-patches/2015-07/msg02414.html
> >
> > I don't see any other way to fix these PRs, but I don't know how to
> > keep the pragmas from being deleted by the preprocessor.

I'd suppose you want a new type of pragma, that acts like a combination of 
a deferred one and one for which a handler is called immediately by 
libcpp.  libcpp would call the handler but also create a CPP_PRAGMA token.  
The front-end code calling pragma handlers would need to know to do 
nothing with such pragmas; the token would only be for textual 
preprocessor output.
Dodji Seketeli Sept. 25, 2015, 3:14 p.m. UTC | #4
Manuel López-Ibáñez <lopezibanez@gmail.com> writes:

> Currently, #pragma GCC diagnostic is handled entirely by the FE. This
> has several drawbacks:
>
> * PR c++/53431 - C++ preprocessor ignores #pragma GCC diagnostic: The
> C++ parser lexes (and preprocesses) before handling the pragmas.
>
> * PR 53920 - "gcc -E" does not honor #pragma GCC diagnostic ignored
> "-Wunused-macro": Because -E does not invoke the FE code that parses
> the FE pragmas.
>
> * PR 64698 - preprocessor ignores #pragma GCC diagnostic when using
> -save-temps. Same issue as above.
>
> The following patch moves the handling of #pragma GCC diagnostic to
> libcpp but keeps the interface with the diagnostic machinery in the FE
> by using a call-back function.
>
> One serious problem with this approach is that the preprocessor will
> delete the pragmas from the preprocessed output, thus '-E',
> '-save-temps'  will not contain the pragmas and compiling the
> preprocessed file will trigger the warnings that they were meant to
> suppress.  Any ideas how to prevent libcpp from deleting the #pragmas?

[...]

Joseph Myers <joseph@codesourcery.com> writes:

> On Sun, 20 Sep 2015, Manuel López-Ibáñez wrote:

>> > I don't see any other way to fix these PRs, but I don't know how to
>> > keep the pragmas from being deleted by the preprocessor.
>
> I'd suppose you want a new type of pragma, that acts like a combination of 
> a deferred one and one for which a handler is called immediately by 
> libcpp.  libcpp would call the handler but also create a CPP_PRAGMA token.  
> The front-end code calling pragma handlers would need to know to do 
> nothing with such pragmas; the token would only be for textual 
> preprocessor output.

Right.  I was thinking about something along those lines as well.

So in concrete terms, in libcpp, once do_pragma() has handled the
pragma, it does two things that are interesting to us:

  * If the pragma is an internal one -- one that is handled immediately by
    libcpp, then it keeps going, chewing away at more tokens.

  * If the pragma is a deffered one -- one that is to be handled later
    by the FE, it updates pfile->directive_result.type to CPP_PRAGMA
    (and sets up some ancillary state there too).

The caller of do_pragma(), which is destringize_and_run() then detects
that pfile->directive_result.type is set, and then puts the tokens of
the pragma back into the input stream again.  So next time the FE
requests more tokens, it's going to get the same pragma tokens.

So, maybe you could alter pragma_entry::is_deferred; change it into a
flag which type is an enum that says how the the pragma is to be
handled; either internally and its tokens shouldn't be visible to the FE
(this is what the current pragma_entry::is_internal means), internally
and the tokens would be visible to the FE, or deferred.

Then do do_pragma() would be adjusted to change the if (p->is_deferred)
clause to allow the third handling kind I just talked about.

I hope this helps.

Cheers,
Manuel López-Ibáñez Sept. 26, 2015, 10:10 p.m. UTC | #5
On 25 September 2015 at 17:14, Dodji Seketeli <dodji@redhat.com> wrote:
> The caller of do_pragma(), which is destringize_and_run() then detects
> that pfile->directive_result.type is set, and then puts the tokens of
> the pragma back into the input stream again.  So next time the FE
> requests more tokens, it's going to get the same pragma tokens.
>
> So, maybe you could alter pragma_entry::is_deferred; change it into a
> flag which type is an enum that says how the the pragma is to be
> handled; either internally and its tokens shouldn't be visible to the FE
> (this is what the current pragma_entry::is_internal means), internally
> and the tokens would be visible to the FE, or deferred.
>
> Then do do_pragma() would be adjusted to change the if (p->is_deferred)
> clause to allow the third handling kind I just talked about.

I could not make it work by touching directive_result.type. However,
behaving as if the pragma was unknown did work:

@@ -1414,11 +1435,11 @@ do_pragma (cpp_reader *pfile)
        }
     }

   if (p)
     {
-      if (p->is_deferred)
+      if (p->type == DEFERRED)
        {
          pfile->directive_result.src_loc = pragma_token_virt_loc;
          pfile->directive_result.type = CPP_PRAGMA;
          pfile->directive_result.flags = pragma_token->flags;
          pfile->directive_result.val.pragma = p->u.ident;
@@ -1439,11 +1460,12 @@ do_pragma (cpp_reader *pfile)
          (*p->u.handler) (pfile);
          if (p->allow_expansion)
            pfile->state.prevent_expansion++;
        }
     }
-  else if (pfile->cb.def_pragma)
+
+  if ((!p || p->type == INTERNAL_VISIBLE) && pfile->cb.def_pragma)
     {
       if (count == 1 || pfile->context->prev == NULL)
        _cpp_backup_tokens (pfile, count);
       else
        {

Yet, there is another problem. Now the FE sees the pragma and it warns
with -Wunknown-pragma. But if we register the pragma in the FE to
ignore it, then we get

cc1plus: internal compiler error: #pragma GCC diagnostic is already registered

Cheers,

Manuel.
Dodji Seketeli Sept. 27, 2015, 1:29 a.m. UTC | #6
Manuel López-Ibáñez <lopezibanez@gmail.com> writes:

> On 25 September 2015 at 17:14, Dodji Seketeli <dodji@redhat.com> wrote:
>> The caller of do_pragma(), which is destringize_and_run() then detects
>> that pfile->directive_result.type is set, and then puts the tokens of
>> the pragma back into the input stream again.  So next time the FE
>> requests more tokens, it's going to get the same pragma tokens.
>>
>> So, maybe you could alter pragma_entry::is_deferred; change it into a
>> flag which type is an enum that says how the the pragma is to be
>> handled; either internally and its tokens shouldn't be visible to the FE
>> (this is what the current pragma_entry::is_internal means), internally
>> and the tokens would be visible to the FE, or deferred.
>>
>> Then do do_pragma() would be adjusted to change the if (p->is_deferred)
>> clause to allow the third handling kind I just talked about.

[...]

> behaving as if the pragma was unknown did work:
>
> @@ -1414,11 +1435,11 @@ do_pragma (cpp_reader *pfile)
>         }
>      }
>
>    if (p)
>      {
> -      if (p->is_deferred)
> +      if (p->type == DEFERRED)
>         {
>           pfile->directive_result.src_loc = pragma_token_virt_loc;
>           pfile->directive_result.type = CPP_PRAGMA;
>           pfile->directive_result.flags = pragma_token->flags;
>           pfile->directive_result.val.pragma = p->u.ident;
> @@ -1439,11 +1460,12 @@ do_pragma (cpp_reader *pfile)
>           (*p->u.handler) (pfile);
>           if (p->allow_expansion)
>             pfile->state.prevent_expansion++;
>         }
>      }
> -  else if (pfile->cb.def_pragma)
> +
> +  if ((!p || p->type == INTERNAL_VISIBLE) && pfile->cb.def_pragma)
>      {
>        if (count == 1 || pfile->context->prev == NULL)
>         _cpp_backup_tokens (pfile, count);
>        else
>         {
>
> Yet, there is another problem. Now the FE sees the pragma and it warns
> with -Wunknown-pragma.

Couldn't we change the FE to make it not warn on pragma entries of type
INTERNAL_VISIBLE?

Thank you for looking into this.

Patch
diff mbox

Index: gcc/c-family/c-opts.c
===================================================================
--- gcc/c-family/c-opts.c	(revision 226219)
+++ gcc/c-family/c-opts.c	(working copy)
@@ -969,10 +969,11 @@  c_common_post_options (const char **pfil
     }
 
   cb = cpp_get_callbacks (parse_in);
   cb->file_change = cb_file_change;
   cb->dir_change = cb_dir_change;
+  cb->handle_pragma_diagnostic = cb_handle_pragma_diagnostic;
   cpp_post_options (parse_in);
   init_global_opts_from_cpp (&global_options, cpp_get_options (parse_in));
 
   input_location = UNKNOWN_LOCATION;
 
Index: gcc/c-family/c-common.h
===================================================================
--- gcc/c-family/c-common.h	(revision 226219)
+++ gcc/c-family/c-common.h	(working copy)
@@ -769,10 +769,12 @@  extern void check_function_arguments_rec
 					      unsigned HOST_WIDE_INT);
 extern bool check_builtin_function_arguments (tree, int, tree *);
 extern void check_function_format (tree, int, tree *);
 extern tree handle_format_attribute (tree *, tree, tree, int, bool *);
 extern tree handle_format_arg_attribute (tree *, tree, tree, int, bool *);
+extern void cb_handle_pragma_diagnostic (location_t, const char *,
+					 location_t, const char *);
 extern bool attribute_takes_identifier_p (const_tree);
 extern bool c_common_handle_option (size_t, const char *, int, int, location_t,
 				    const struct cl_option_handlers *);
 extern bool default_handle_c_option (size_t, const char *, int);
 extern tree c_common_type_for_mode (machine_mode, int);
Index: gcc/c-family/c-pragma.c
===================================================================
--- gcc/c-family/c-pragma.c	(revision 226219)
+++ gcc/c-family/c-pragma.c	(working copy)
@@ -699,58 +699,75 @@  handle_pragma_visibility (cpp_reader *du
     }
   if (pragma_lex (&x) != CPP_EOF)
     warning (OPT_Wpragmas, "junk at end of %<#pragma GCC visibility%>");
 }
 
-static void
-handle_pragma_diagnostic(cpp_reader *ARG_UNUSED(dummy))
-{
-  const char *kind_string, *option_string;
-  unsigned int option_index;
-  enum cpp_ttype token;
+/* CPP call-back to handle "#pragma GCC diagnostic KIND_STRING
+   OPTION_STRING", where KIND_STRING is error, warning, ignored, push
+   or pop. LOC_KIND is the location of the KIND_STRING. LOC_OPTION is
+   the location of the warning option string.  */
+
+extern void
+cb_handle_pragma_diagnostic (location_t loc_kind, const char * kind_string,
+			     location_t loc_option, const char * option_string)
+{
+  if (!kind_string)
+    {
+      warning_at (loc_kind, OPT_Wpragmas,
+		  "missing [error|warning|ignored|push|pop]"
+		  " after %<#pragma GCC diagnostic%>");
+      return;
+    }
+
   diagnostic_t kind;
-  tree x;
-  struct cl_option_handlers handlers;
 
-  token = pragma_lex (&x);
-  if (token != CPP_NAME)
-    GCC_BAD ("missing [error|warning|ignored] after %<#pragma GCC diagnostic%>");
-  kind_string = IDENTIFIER_POINTER (x);
   if (strcmp (kind_string, "error") == 0)
     kind = DK_ERROR;
   else if (strcmp (kind_string, "warning") == 0)
     kind = DK_WARNING;
   else if (strcmp (kind_string, "ignored") == 0)
     kind = DK_IGNORED;
   else if (strcmp (kind_string, "push") == 0)
     {
-      diagnostic_push_diagnostics (global_dc, input_location);
+      diagnostic_push_diagnostics (global_dc, loc_kind);
       return;
     }
   else if (strcmp (kind_string, "pop") == 0)
     {
-      diagnostic_pop_diagnostics (global_dc, input_location);
+      diagnostic_pop_diagnostics (global_dc, loc_kind);
       return;
     }
   else
-    GCC_BAD ("expected [error|warning|ignored|push|pop] after %<#pragma GCC diagnostic%>");
+    {
+      warning_at (loc_kind, OPT_Wpragmas,
+		  "expected [error|warning|ignored|push|pop]"
+		  " after %<#pragma GCC diagnostic%>");
+      return;
+    }
 
-  token = pragma_lex (&x);
-  if (token != CPP_STRING)
-    GCC_BAD ("missing option after %<#pragma GCC diagnostic%> kind");
-  option_string = TREE_STRING_POINTER (x);
+  if (!option_string)
+    {
+      warning_at (loc_option, OPT_Wpragmas, "missing option"
+		  " after %<#pragma GCC diagnostic%> kind");
+      return;
+    }
+  /* option_string + 1 to skip the initial '-' */
+  unsigned int option_index =  find_opt (option_string + 1, c_family_lang_mask);
+  if (option_index == OPT_SPECIAL_unknown
+      || !(cl_options[option_index].flags & CL_WARNING))
+    {
+      warning_at (loc_option, OPT_Wpragmas, "unknown warning option"
+		  " after %<#pragma GCC diagnostic%> kind");
+      return;
+    }
+
+  struct cl_option_handlers handlers;
   set_default_handlers (&handlers);
-  for (option_index = 0; option_index < cl_options_count; option_index++)
-    if (strcmp (cl_options[option_index].opt_text, option_string) == 0)
-      {
-	control_warning_option (option_index, (int) kind, kind != DK_IGNORED,
-				input_location, c_family_lang_mask, &handlers,
-				&global_options, &global_options_set,
-				global_dc);
-	return;
-      }
-  GCC_BAD ("unknown option after %<#pragma GCC diagnostic%> kind");
+  control_warning_option (option_index, (int) kind, kind != DK_IGNORED,
+			  loc_kind, c_family_lang_mask, &handlers,
+			  &global_options, &global_options_set,
+			  global_dc);
 }
 
 /*  Parse #pragma GCC target (xxx) to set target specific options.  */
 static void
 handle_pragma_target(cpp_reader *ARG_UNUSED(dummy))
@@ -1451,11 +1468,10 @@  init_pragma (void)
   c_register_pragma (0, "pack", handle_pragma_pack);
 #endif
   c_register_pragma (0, "weak", handle_pragma_weak);
   c_register_pragma ("GCC", "visibility", handle_pragma_visibility);
 
-  c_register_pragma ("GCC", "diagnostic", handle_pragma_diagnostic);
   c_register_pragma ("GCC", "target", handle_pragma_target);
   c_register_pragma ("GCC", "optimize", handle_pragma_optimize);
   c_register_pragma ("GCC", "push_options", handle_pragma_push_options);
   c_register_pragma ("GCC", "pop_options", handle_pragma_pop_options);
   c_register_pragma ("GCC", "reset_options", handle_pragma_reset_options);
Index: gcc/testsuite/c-c++-common/cpp/pragma-gcc-diag.c
===================================================================
--- gcc/testsuite/c-c++-common/cpp/pragma-gcc-diag.c	(revision 0)
+++ gcc/testsuite/c-c++-common/cpp/pragma-gcc-diag.c	(revision 0)
@@ -0,0 +1,5 @@ 
+/* { dg-do preprocess } */
+/* { dg-options "-Wunused-macros -E" } */
+/* PR preprocessor/53920 */
+#pragma GCC diagnostic ignored "-Wunused-macros"
+#define FOO
Index: gcc/testsuite/c-c++-common/pragma-diag-5.c
===================================================================
--- gcc/testsuite/c-c++-common/pragma-diag-5.c	(revision 0)
+++ gcc/testsuite/c-c++-common/pragma-diag-5.c	(revision 0)
@@ -0,0 +1,6 @@ 
+/* { dg-do compile } */
+/* { dg-options "-Wundef" } */
+#pragma GCC diagnostic ignored "-Wundef"
+#if FOO
+#endif
+int main (void) { return 42; }
Index: gcc/testsuite/c-c++-common/pragma-diag-3.c
===================================================================
--- gcc/testsuite/c-c++-common/pragma-diag-3.c	(revision 0)
+++ gcc/testsuite/c-c++-common/pragma-diag-3.c	(revision 0)
@@ -0,0 +1,4 @@ 
+/* { dg-do compile } */
+#pragma GCC diagnostic /* { dg-warning "missing" "missing" } */
+#pragma GCC diagnostic warn /* { dg-warning "24:expected" } */
+#pragma GCC diagnostic ignored "-Wfoo" /* { dg-warning "32:unknown" } */
Index: gcc/testsuite/c-c++-common/pragma-diag-4.c
===================================================================
--- gcc/testsuite/c-c++-common/pragma-diag-4.c	(revision 0)
+++ gcc/testsuite/c-c++-common/pragma-diag-4.c	(revision 0)
@@ -0,0 +1,4 @@ 
+/* { dg-do compile } */
+#pragma GCC diagnostic error "-fstrict-aliasing" /* { dg-warning "unknown" } */
+#pragma GCC diagnostic error "-Werror" /* { dg-warning "unknown" } */
+
Index: libcpp/directives.c
===================================================================
--- libcpp/directives.c	(revision 226219)
+++ libcpp/directives.c	(working copy)
@@ -42,12 +42,11 @@  typedef void (*pragma_cb) (cpp_reader *)
 struct pragma_entry
 {
   struct pragma_entry *next;
   const cpp_hashnode *pragma;	/* Name and length.  */
   bool is_nspace;
-  bool is_internal;
-  bool is_deferred;
+  bool is_deferred; /* !is_deferred means that it is internal.  */
   bool allow_expansion;
   union {
     pragma_cb handler;
     struct pragma_entry *space;
     unsigned int ident;
@@ -114,10 +113,11 @@  static char ** restore_registered_pragma
                                            char **);
 static void do_pragma_once (cpp_reader *);
 static void do_pragma_poison (cpp_reader *);
 static void do_pragma_system_header (cpp_reader *);
 static void do_pragma_dependency (cpp_reader *);
+static void do_pragma_diagnostic (cpp_reader *pfile);
 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 *);
@@ -1234,11 +1234,11 @@  register_pragma_internal (cpp_reader *pf
 			  const char *name, pragma_cb handler)
 {
   struct pragma_entry *entry;
 
   entry = register_pragma_1 (pfile, space, name, false);
-  entry->is_internal = true;
+  entry->is_deferred = false; /* it is internal.  */
   entry->u.handler = handler;
 }
 
 /* Register a pragma NAME in namespace SPACE.  If SPACE is null, it
    goes in the global namespace.  HANDLER is the handler it will call,
@@ -1264,11 +1264,11 @@  cpp_register_pragma (cpp_reader *pfile, 
       entry->u.handler = handler;
     }
 }
 
 /* Similarly, but create mark the pragma for deferred processing.
-   When found, a CPP_PRAGMA token will be insertted into the stream
+   When found, a CPP_PRAGMA token will be inserted into the stream
    with IDENT in the token->u.pragma slot.  */
 void
 cpp_register_deferred_pragma (cpp_reader *pfile, const char *space,
 			      const char *name, unsigned int ident,
 			      bool allow_expansion, bool allow_name_expansion)
@@ -1298,10 +1298,11 @@  _cpp_init_internal_pragmas (cpp_reader *
   register_pragma_internal (pfile, "GCC", "system_header",
 			    do_pragma_system_header);
   register_pragma_internal (pfile, "GCC", "dependency", do_pragma_dependency);
   register_pragma_internal (pfile, "GCC", "warning", do_pragma_warning);
   register_pragma_internal (pfile, "GCC", "error", do_pragma_error);
+  register_pragma_internal (pfile, "GCC", "diagnostic", do_pragma_diagnostic);
 }
 
 /* Return the number of registered pragmas in PE.  */
 
 static int
@@ -1669,10 +1670,46 @@  do_pragma_dependency (cpp_reader *pfile)
     }
 
   free ((void *) fname);
 }
 
+/* Handle a "#pragma GCC diagnostic".  We parse the #pragma here to
+   set up the classification as early as possible, but we let the FEs
+   handle the actual implementation because CPP does not have access
+   to the diagnostic interfaces.  */
+static void
+do_pragma_diagnostic (cpp_reader *pfile)
+{
+  const cpp_token *tok = _cpp_lex_token (pfile);
+  source_location loc_kind = tok->src_loc;
+  const unsigned char * kind_string = 
+    (tok->type == CPP_NAME || tok->type == CPP_KEYWORD) 
+    ? cpp_token_as_text (pfile, tok)
+    : NULL;
+
+  const unsigned char * option_string = NULL;
+  source_location loc_option = loc_kind;
+  if (kind_string)
+    {
+      tok = _cpp_lex_token (pfile);
+      loc_option = tok->src_loc;
+      cpp_string str;
+      if (tok->type == CPP_STRING
+	  && cpp_interpret_string_notranslate (pfile, &tok->val.str, 1, &str,
+						CPP_STRING)
+	  && str.len > 0)
+	{
+	  option_string = str.text;
+	}
+    }
+  if (pfile->cb.handle_pragma_diagnostic)
+    pfile->cb.handle_pragma_diagnostic (loc_kind, (const char *) kind_string,
+					loc_option, (const char *) option_string);
+  if (option_string)
+    free ((void *) option_string);
+}
+
 /* Issue a diagnostic with the message taken from the pragma.  If
    ERROR is true, the diagnostic is a warning, otherwise, it is an
    error.  */
 static void
 do_pragma_warning_or_error (cpp_reader *pfile, bool error)
Index: libcpp/include/cpplib.h
===================================================================
--- libcpp/include/cpplib.h	(revision 226219)
+++ libcpp/include/cpplib.h	(working copy)
@@ -591,10 +591,13 @@  struct cpp_callbacks
   /* Callback to identify whether an attribute exists.  */
   int (*has_attribute) (cpp_reader *);
 
   /* Callback that can change a user builtin into normal macro.  */
   bool (*user_builtin_macro) (cpp_reader *, cpp_hashnode *);
+
+  void (*handle_pragma_diagnostic) (source_location, const char*,
+				    source_location, const char*);
 };
 
 #ifdef VMS
 #define INO_T_CPP ino_t ino[3]
 #else