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.
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