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