From patchwork Fri Jun 11 21:28:30 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: DJ Delorie X-Patchwork-Id: 55372 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) by ozlabs.org (Postfix) with SMTP id 9A77B1007D1 for ; Sat, 12 Jun 2010 07:28:44 +1000 (EST) Received: (qmail 18973 invoked by alias); 11 Jun 2010 21:28:42 -0000 Received: (qmail 18960 invoked by uid 22791); 11 Jun 2010 21:28:39 -0000 X-SWARE-Spam-Status: No, hits=-5.7 required=5.0 tests=AWL, BAYES_00, RCVD_IN_DNSWL_HI, SPF_HELO_PASS, T_RP_MATCHES_RCVD X-Spam-Check-By: sourceware.org Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Fri, 11 Jun 2010 21:28:34 +0000 Received: from int-mx03.intmail.prod.int.phx2.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.16]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id o5BLSWR6004790 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Fri, 11 Jun 2010 17:28:32 -0400 Received: from greed.delorie.com (ovpn01.gateway.prod.ext.phx2.redhat.com [10.5.9.1]) by int-mx03.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id o5BLSUJZ031191 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO); Fri, 11 Jun 2010 17:28:32 -0400 Received: from greed.delorie.com (greed.delorie.com [127.0.0.1] (may be forged)) by greed.delorie.com (8.14.3/8.14.3) with ESMTP id o5BLSUim026881; Fri, 11 Jun 2010 17:28:30 -0400 Received: (from dj@localhost) by greed.delorie.com (8.14.3/8.14.3/Submit) id o5BLSUlN026878; Fri, 11 Jun 2010 17:28:30 -0400 Date: Fri, 11 Jun 2010 17:28:30 -0400 Message-Id: <201006112128.o5BLSUlN026878@greed.delorie.com> From: DJ Delorie To: Tom Tromey CC: gcc-patches@gcc.gnu.org In-reply-to: (message from Tom Tromey on Fri, 11 Jun 2010 08:44:00 -0600) Subject: Re: pragma diagnostic push/pop References: <201006110142.o5B1gqcR022118@greed.delorie.com> <201006110349.o5B3n2A7032685@greed.delorie.com> X-IsSubscribed: yes Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org > I forgot to mention: at the very least I think the diagnostic routines > should take the location as a parameter. If we must use input_location, > let it be in the callers. I did it that way. I assume there are FAR more callers of pragma_lex() than the three diagnostic*() calls here. * 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. Index: doc/extend.texi =================================================================== --- doc/extend.texi (revision 160588) +++ doc/extend.texi (working copy) @@ -12587,21 +12587,36 @@ option. @example #pragma GCC diagnostic warning "-Wformat" #pragma GCC diagnostic error "-Wformat" #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 GCC also offers a simple mechanism for printing messages during compilation. Index: c-family/c-pragma.c =================================================================== --- c-family/c-pragma.c (revision 160588) +++ c-family/c-pragma.c (working copy) @@ -703,40 +703,44 @@ handle_pragma_diagnostic(cpp_reader *ARG const char *kind_string, *option_string; unsigned int option_index; enum cpp_ttype token; 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%>"); 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); + 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) GCC_BAD ("missing option after %<#pragma GCC diagnostic%> kind"); option_string = TREE_STRING_POINTER (x); for (option_index = 0; option_index < cl_options_count; option_index++) 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 && kind != DK_IGNORED) *(int *) cl_options[option_index].flag_var = 1; return; Index: diagnostic.c =================================================================== --- diagnostic.c (revision 160588) +++ diagnostic.c (working copy) @@ -303,26 +303,83 @@ default_diagnostic_finalizer (diagnostic /* Interface to specify diagnostic kind overrides. Returns the previous setting, or DK_UNSPECIFIED if the parameters are out of range. */ 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; if (option_index <= 0 || option_index >= context->n_opts || new_kind >= DK_LAST_DIAGNOSTIC_KIND) 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 front-end independent format specifiers are exactly those described in the documentation of output_format. Return true if a diagnostic was printed, false otherwise. */ @@ -371,19 +428,47 @@ diagnostic_report_diagnostic (diagnostic { diagnostic->kind = DK_ERROR; } 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]; } /* This allows for future extensions, like temporarily disabling warnings for ranges of source code. */ if (diagnostic->kind == DK_IGNORED) Index: diagnostic.h =================================================================== --- diagnostic.h (revision 160588) +++ diagnostic.h (working copy) @@ -38,12 +38,22 @@ typedef struct diagnostic_info /* The kind of diagnostic it is about. */ diagnostic_t kind; /* Which OPT_* directly controls this diagnostic. */ 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 *, diagnostic_info *); typedef diagnostic_starter_fn diagnostic_finalizer_fn; @@ -73,12 +83,26 @@ struct diagnostic_context options), this array may contain a new kind that the diagnostic should be changed to before reporting, or DK_UNSPECIFIED to leave it as the reported kind, or DK_IGNORED to not report it at 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; /* True if we should raise a SIGABRT on errors. */ bool abort_on_error; @@ -225,13 +249,16 @@ extern void diagnostic_initialize (diagn extern void diagnostic_finish (diagnostic_context *); 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 extern void diagnostic_set_info (diagnostic_info *, const char *, va_list *, location_t, diagnostic_t) ATTRIBUTE_GCC_DIAG(2,0); extern void diagnostic_set_info_translated (diagnostic_info *, const char *, Index: diagnostic-core.h =================================================================== --- diagnostic-core.h (revision 160588) +++ diagnostic-core.h (working copy) @@ -29,13 +29,16 @@ along with GCC; see the file COPYING3. /* Constants used to discriminate diagnostics. */ 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; extern const char *trim_filename (const char *); Index: testsuite/gcc.dg/pragma-diag-1.c =================================================================== --- testsuite/gcc.dg/pragma-diag-1.c (revision 0) +++ testsuite/gcc.dg/pragma-diag-1.c (revision 0) @@ -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" } */ +} Index: opts.c =================================================================== --- opts.c (revision 160588) +++ opts.c (working copy) @@ -2473,13 +2473,14 @@ set_option (int opt_index, int value, co case CLVC_STRING: *(const char **) option->flag_var = arg; break; } 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); } /* Callback function, called when -Werror= enables a warning. */ static void (*warning_as_error_callback) (int) = NULL; @@ -2511,13 +2512,14 @@ enable_warning_as_error (const char *arg error ("-Werror=%s: No option -%s", arg, new_option); } else { 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; /* -Werror=foo implies -Wfoo. */ if (option->var_type == CLVC_BOOLEAN)