From patchwork Fri Jun 11 01:42:52 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: DJ Delorie X-Patchwork-Id: 55281 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 CA29EB6EFF for ; Fri, 11 Jun 2010 11:43:11 +1000 (EST) Received: (qmail 31838 invoked by alias); 11 Jun 2010 01:43:07 -0000 Received: (qmail 31824 invoked by uid 22791); 11 Jun 2010 01:43:05 -0000 X-SWARE-Spam-Status: No, hits=-5.0 required=5.0 tests=AWL, BAYES_05, 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 01:42:59 +0000 Received: from int-mx05.intmail.prod.int.phx2.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.18]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id o5B1gvR4000582 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Thu, 10 Jun 2010 21:42:58 -0400 Received: from greed.delorie.com (ovpn01.gateway.prod.ext.phx2.redhat.com [10.5.9.1]) by int-mx05.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id o5B1gr2x031611 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO) for ; Thu, 10 Jun 2010 21:42:55 -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 o5B1grUp022121 for ; Thu, 10 Jun 2010 21:42:53 -0400 Received: (from dj@localhost) by greed.delorie.com (8.14.3/8.14.3/Submit) id o5B1gqcR022118; Thu, 10 Jun 2010 21:42:52 -0400 Date: Thu, 10 Jun 2010 21:42:52 -0400 Message-Id: <201006110142.o5B1gqcR022118@greed.delorie.com> From: DJ Delorie To: gcc-patches@gcc.gnu.org Subject: pragma diagnostic push/pop 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 This is long overdue... first try on location-sensitive pragma-based diagnostic control. * 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. * 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,30 +703,34 @@ 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); + return; + } + else if (strcmp (kind_string, "pop") == 0) + { + diagnostic_pop_diagnostics (global_dc); + 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++) Index: diagnostic.c =================================================================== --- diagnostic.c (revision 160588) +++ diagnostic.c (working copy) @@ -313,16 +313,72 @@ diagnostic_classify_diagnostic (diagnost 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 (input_location > 0) + { + 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 = input_location; + 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) +{ + 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) +{ + 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 = input_location; + 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 +427,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; + /* 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; @@ -226,12 +250,14 @@ extern void diagnostic_finish (diagnosti 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 */); +extern void diagnostic_push_diagnostics (diagnostic_context *); +extern void diagnostic_pop_diagnostics (diagnostic_context *); 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" } */ +}