From cd7fe53b723e8f24dc2efc21bc68a2ac174ee882 Mon Sep 17 00:00:00 2001 From: DJ Delorie Date: Mon, 21 Jun 2010 16:58:57 -0400 Subject: [PATCH] diagnostic.h (diagnostic_classification_change_t): New. * diagnostic.h (diagnostic_classification_change_t): New. (diagnostic_context): Add history and push/pop list. (diagnostic_push_diagnostics): Declare. (diagnostic_pop_diagnostics): Declare. * diagnostic.c (diagnostic_classify_diagnostic): Store changes from pragmas in a history chain instead of the global table. (diagnostic_push_diagnostics): New. (diagnostic_pop_diagnostics): New. (diagnostic_report_diagnostic): Scan history chain to find state of diagnostics as of the diagnostic location. * opts.c (set_option): Pass UNKNOWN_LOCATION to diagnostic_classify_diagnostic. (enable_warning_as_error): Likewise. * diagnostic-core.h (DK_POP): Add after "real" diagnostics, for use in the history chain. * c-family/c-pragma.c (handle_pragma_diagnostic): Add push/pop, allow these pragmas anywhere. * doc/extend.texi: Document pragma GCC diagnostic changes. * gcc.dg/pragma-diag-1.c: New. From-SVN: r161115 --- gcc/ChangeLog | 21 +++++++ gcc/c-family/c-pragma.c | 20 +++--- gcc/diagnostic-core.h | 5 +- gcc/diagnostic.c | 91 +++++++++++++++++++++++++++- gcc/diagnostic.h | 29 ++++++++- gcc/doc/extend.texi | 33 +++++++--- gcc/opts.c | 6 +- gcc/testsuite/ChangeLog | 4 ++ gcc/testsuite/gcc.dg/pragma-diag-1.c | 21 +++++++ 9 files changed, 206 insertions(+), 24 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/pragma-diag-1.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index a553a107294..85041a1ac82 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,24 @@ +2010-06-21 DJ Delorie + + * diagnostic.h (diagnostic_classification_change_t): New. + (diagnostic_context): Add history and push/pop list. + (diagnostic_push_diagnostics): Declare. + (diagnostic_pop_diagnostics): Declare. + * diagnostic.c (diagnostic_classify_diagnostic): Store changes + from pragmas in a history chain instead of the global table. + (diagnostic_push_diagnostics): New. + (diagnostic_pop_diagnostics): New. + (diagnostic_report_diagnostic): Scan history chain to find state + of diagnostics as of the diagnostic location. + * opts.c (set_option): Pass UNKNOWN_LOCATION to + diagnostic_classify_diagnostic. + (enable_warning_as_error): Likewise. + * diagnostic-core.h (DK_POP): Add after "real" diagnostics, for + use in the history chain. + * c-family/c-pragma.c (handle_pragma_diagnostic): Add push/pop, + allow these pragmas anywhere. + * doc/extend.texi: Document pragma GCC diagnostic changes. + 2010-06-21 Jakub Jelinek * dwarf2out.c (add_linkage_name): New function. Don't add diff --git a/gcc/c-family/c-pragma.c b/gcc/c-family/c-pragma.c index ab922696ddd..cea0b267b92 100644 --- a/gcc/c-family/c-pragma.c +++ b/gcc/c-family/c-pragma.c @@ -706,12 +706,6 @@ handle_pragma_diagnostic(cpp_reader *ARG_UNUSED(dummy)) diagnostic_t kind; tree x; - if (cfun) - { - error ("#pragma GCC diagnostic not allowed inside functions"); - return; - } - token = pragma_lex (&x); if (token != CPP_NAME) GCC_BAD ("missing [error|warning|ignored] after %<#pragma GCC diagnostic%>"); @@ -722,8 +716,18 @@ handle_pragma_diagnostic(cpp_reader *ARG_UNUSED(dummy)) 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); + return; + } + else if (strcmp (kind_string, "pop") == 0) + { + diagnostic_pop_diagnostics (global_dc, input_location); + return; + } else - GCC_BAD ("expected [error|warning|ignored] after %<#pragma GCC diagnostic%>"); + GCC_BAD ("expected [error|warning|ignored|push|pop] after %<#pragma GCC diagnostic%>"); token = pragma_lex (&x); if (token != CPP_STRING) @@ -733,7 +737,7 @@ handle_pragma_diagnostic(cpp_reader *ARG_UNUSED(dummy)) if (strcmp (cl_options[option_index].opt_text, option_string) == 0) { /* This overrides -Werror, for example. */ - diagnostic_classify_diagnostic (global_dc, option_index, kind); + diagnostic_classify_diagnostic (global_dc, option_index, kind, input_location); /* This makes sure the option is enabled, like -Wfoo would do. */ if (cl_options[option_index].var_type == CLVC_BOOLEAN && cl_options[option_index].flag_var diff --git a/gcc/diagnostic-core.h b/gcc/diagnostic-core.h index b5047164002..674bad608aa 100644 --- a/gcc/diagnostic-core.h +++ b/gcc/diagnostic-core.h @@ -32,7 +32,10 @@ typedef enum #define DEFINE_DIAGNOSTIC_KIND(K, msgid) K, #include "diagnostic.def" #undef DEFINE_DIAGNOSTIC_KIND - DK_LAST_DIAGNOSTIC_KIND + DK_LAST_DIAGNOSTIC_KIND, + /* This is used for tagging pragma pops in the diagnostic + classification history chain. */ + DK_POP } diagnostic_t; extern const char *progname; diff --git a/gcc/diagnostic.c b/gcc/diagnostic.c index 61401e62a80..34e9679693f 100644 --- a/gcc/diagnostic.c +++ b/gcc/diagnostic.c @@ -306,7 +306,8 @@ default_diagnostic_finalizer (diagnostic_context *context, diagnostic_t diagnostic_classify_diagnostic (diagnostic_context *context, int option_index, - diagnostic_t new_kind) + diagnostic_t new_kind, + location_t where) { diagnostic_t old_kind; @@ -316,10 +317,66 @@ diagnostic_classify_diagnostic (diagnostic_context *context, return DK_UNSPECIFIED; old_kind = context->classify_diagnostic[option_index]; - context->classify_diagnostic[option_index] = new_kind; + + /* Handle pragmas separately, since we need to keep track of *where* + the pragmas were. */ + if (where != UNKNOWN_LOCATION) + { + int i; + + for (i = context->n_classification_history - 1; i >= 0; i --) + if (context->classification_history[i].option == option_index) + { + old_kind = context->classification_history[i].kind; + break; + } + + i = context->n_classification_history; + context->classification_history = + (diagnostic_classification_change_t *) xrealloc (context->classification_history, (i + 1) + * sizeof (diagnostic_classification_change_t)); + context->classification_history[i].location = where; + context->classification_history[i].option = option_index; + context->classification_history[i].kind = new_kind; + context->n_classification_history ++; + } + else + context->classify_diagnostic[option_index] = new_kind; + return old_kind; } +/* Save all diagnostic classifications in a stack. */ +void +diagnostic_push_diagnostics (diagnostic_context *context, location_t where ATTRIBUTE_UNUSED) +{ + context->push_list = (int *) xrealloc (context->push_list, (context->n_push + 1) * sizeof (int)); + context->push_list[context->n_push ++] = context->n_classification_history; +} + +/* Restore the topmost classification set off the stack. If the stack + is empty, revert to the state based on command line parameters. */ +void +diagnostic_pop_diagnostics (diagnostic_context *context, location_t where) +{ + int jump_to; + int i; + + if (context->n_push) + jump_to = context->push_list [-- context->n_push]; + else + jump_to = 0; + + i = context->n_classification_history; + context->classification_history = + (diagnostic_classification_change_t *) xrealloc (context->classification_history, (i + 1) + * sizeof (diagnostic_classification_change_t)); + context->classification_history[i].location = where; + context->classification_history[i].option = jump_to; + context->classification_history[i].kind = DK_POP; + context->n_classification_history ++; +} + /* Report a diagnostic message (an error or a warning) as specified by DC. This function is *the* subroutine in terms of which front-ends should implement their specific diagnostic handling modules. The @@ -374,13 +431,41 @@ diagnostic_report_diagnostic (diagnostic_context *context, if (diagnostic->option_index) { + diagnostic_t diag_class = DK_UNSPECIFIED; + /* This tests if the user provided the appropriate -Wfoo or -Wno-foo option. */ if (! context->option_enabled (diagnostic->option_index)) return false; + + /* This tests for #pragma diagnostic changes. */ + if (context->n_classification_history > 0) + { + int i; + /* FIXME: Stupid search. Optimize later. */ + for (i = context->n_classification_history - 1; i >= 0; i --) + { + if (context->classification_history[i].location <= location) + { + if (context->classification_history[i].kind == (int) DK_POP) + { + i = context->classification_history[i].option; + continue; + } + if (context->classification_history[i].option == diagnostic->option_index) + { + diag_class = context->classification_history[i].kind; + if (diag_class != DK_UNSPECIFIED) + diagnostic->kind = diag_class; + break; + } + } + } + } /* This tests if the user provided the appropriate -Werror=foo option. */ - if (context->classify_diagnostic[diagnostic->option_index] != DK_UNSPECIFIED) + if (diag_class == DK_UNSPECIFIED + && context->classify_diagnostic[diagnostic->option_index] != DK_UNSPECIFIED) { diagnostic->kind = context->classify_diagnostic[diagnostic->option_index]; } diff --git a/gcc/diagnostic.h b/gcc/diagnostic.h index 062402fa3f9..d3840749e91 100644 --- a/gcc/diagnostic.h +++ b/gcc/diagnostic.h @@ -41,6 +41,16 @@ typedef struct diagnostic_info int option_index; } diagnostic_info; +/* Each time a diagnostic's classification is changed with a pragma, + we record the change and the location of the change in an array of + these structs. */ +typedef struct diagnostic_classification_change_t +{ + location_t location; + int option; + diagnostic_t kind; +} diagnostic_classification_change_t; + /* Forward declarations. */ typedef struct diagnostic_context diagnostic_context; typedef void (*diagnostic_starter_fn) (diagnostic_context *, @@ -76,6 +86,20 @@ struct diagnostic_context all. */ diagnostic_t *classify_diagnostic; + /* History of all changes to the classifications above. This list + is stored in location-order, so we can search it, either + binary-wise or end-to-front, to find the most recent + classification for a given diagnostic, given the location of the + diagnostic. */ + diagnostic_classification_change_t *classification_history; + + /* The size of the above array. */ + int n_classification_history; + + /* For pragma push/pop. */ + int *push_list; + int n_push; + /* True if we should print the command line option which controls each diagnostic, if known. */ bool show_option_requested; @@ -228,7 +252,10 @@ extern void diagnostic_report_current_module (diagnostic_context *); /* Force diagnostics controlled by OPTIDX to be kind KIND. */ extern diagnostic_t diagnostic_classify_diagnostic (diagnostic_context *, int /* optidx */, - diagnostic_t /* kind */); + diagnostic_t /* kind */, + location_t); +extern void diagnostic_push_diagnostics (diagnostic_context *, location_t); +extern void diagnostic_pop_diagnostics (diagnostic_context *, location_t); extern bool diagnostic_report_diagnostic (diagnostic_context *, diagnostic_info *); #ifdef ATTRIBUTE_GCC_DIAG diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index 5f0d7624a04..795b6bf7a34 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -12590,15 +12590,30 @@ option. #pragma GCC diagnostic ignored "-Wformat" @end example -Note that these pragmas override any command-line options. Also, -while it is syntactically valid to put these pragmas anywhere in your -sources, the only supported location for them is before any data or -functions are defined. Doing otherwise may result in unpredictable -results depending on how the optimizer manages your sources. If the -same option is listed multiple times, the last one specified is the -one that is in effect. This pragma is not intended to be a general -purpose replacement for command-line options, but for implementing -strict control over project policies. +Note that these pragmas override any command-line options. GCC keeps +track of the location of each pragma, and issues diagnostics according +to the state as of that point in the source file. Thus, pragmas occurring +after a line do not affect diagnostics caused by that line. + +@item #pragma GCC diagnostic push +@itemx #pragma GCC diagnostic pop + +Causes GCC to remember the state of the diagnostics as of each +@code{push}, and restore to that point at each @code{pop}. If a +@code{pop} has no matching @code{push}, the command line options are +restored. + +@example +#pragma GCC diagnostic error "-Wuninitialized" + foo(a); /* error is given for this one */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wuninitialized" + foo(b); /* no diagnostic for this one */ +#pragma GCC diagnostic pop + foo(c); /* error is given for this one */ +#pragma GCC diagnostic pop + foo(d); /* depends on command line options */ +@end example @end table diff --git a/gcc/opts.c b/gcc/opts.c index 006e0f18b16..55f8c77d7f1 100644 --- a/gcc/opts.c +++ b/gcc/opts.c @@ -2396,7 +2396,8 @@ set_option (int opt_index, int value, const char *arg, int kind) } if ((diagnostic_t)kind != DK_UNSPECIFIED) - diagnostic_classify_diagnostic (global_dc, opt_index, (diagnostic_t)kind); + diagnostic_classify_diagnostic (global_dc, opt_index, (diagnostic_t)kind, + UNKNOWN_LOCATION); } @@ -2434,7 +2435,8 @@ enable_warning_as_error (const char *arg, int value, unsigned int lang_mask) { const diagnostic_t kind = value ? DK_ERROR : DK_WARNING; - diagnostic_classify_diagnostic (global_dc, option_index, kind); + diagnostic_classify_diagnostic (global_dc, option_index, kind, + UNKNOWN_LOCATION); if (kind == DK_ERROR) { const struct cl_option * const option = cl_options + option_index; diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index ba4ffa7ed43..77228c14ff1 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2010-06-21 DJ Delorie + + * gcc.dg/pragma-diag-1.c: New. + 2010-06-21 H.J. Lu PR target/44615 diff --git a/gcc/testsuite/gcc.dg/pragma-diag-1.c b/gcc/testsuite/gcc.dg/pragma-diag-1.c new file mode 100644 index 00000000000..b6d294d9ab5 --- /dev/null +++ b/gcc/testsuite/gcc.dg/pragma-diag-1.c @@ -0,0 +1,21 @@ +/* { dg-do compile } */ +/* { dg-options "-Wuninitialized -O2" } */ +/* { dg-message "warnings being treated as errors" "" {target "*-*-*"} 0 } */ + +main() +{ + int a; + int b; + int c; + int d; + +#pragma GCC diagnostic error "-Wuninitialized" + foo(a); /* { dg-error "uninitialized" } */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wuninitialized" + foo(b); +#pragma GCC diagnostic pop + foo(c); /* { dg-error "uninitialized" } */ +#pragma GCC diagnostic pop + foo(d); /* { dg-warning "uninitialized" } */ +} -- 2.30.2