Patchwork pragma diagnostic push/pop

login
register
mail settings
Submitter DJ Delorie
Date June 11, 2010, 9:28 p.m.
Message ID <201006112128.o5BLSUlN026878@greed.delorie.com>
Download mbox | patch
Permalink /patch/55372/
State New
Headers show

Comments

DJ Delorie - June 11, 2010, 9:28 p.m.
> 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.
DJ Delorie - June 19, 2010, 12:30 a.m.
The lively conversation came to an abrupt end about a week ago, right
after I submitted a revised patch:

http://gcc.gnu.org/ml/gcc-patches/2010-06/msg01255.html
Manuel López-Ibáñez - June 19, 2010, 12:25 p.m.
On 19 June 2010 02:30, DJ Delorie <dj@redhat.com> wrote:
>
> The lively conversation came to an abrupt end about a week ago, right
> after I submitted a revised patch:
>
> http://gcc.gnu.org/ml/gcc-patches/2010-06/msg01255.html

As far as I see, the only remaining thing is someone with
approval-powers to approve the patch.

Cheers,

Manuel.
Gabriel Dos Reis - June 21, 2010, 1:38 p.m.
On Fri, Jun 18, 2010 at 7:30 PM, DJ Delorie <dj@redhat.com> wrote:
>
> The lively conversation came to an abrupt end about a week ago, right
> after I submitted a revised patch:
>
> http://gcc.gnu.org/ml/gcc-patches/2010-06/msg01255.html
>

I wish diagnostic kind enumeration weren't overloaded with DK_POP.
But I guess we can worry about that later with the understanding this
particular case is not meant to start a competition :-)

Patch OK; thanks!

-- Gaby
Gabriel Dos Reis - June 21, 2010, 1:39 p.m.
On Sat, Jun 19, 2010 at 7:25 AM, Manuel López-Ibáñez
<lopezibanez@gmail.com> wrote:
> On 19 June 2010 02:30, DJ Delorie <dj@redhat.com> wrote:
>>
>> The lively conversation came to an abrupt end about a week ago, right
>> after I submitted a revised patch:
>>
>> http://gcc.gnu.org/ml/gcc-patches/2010-06/msg01255.html
>
> As far as I see, the only remaining thing is someone with
> approval-powers to approve the patch.
>

thanks for the ping.

-- Gaby
DJ Delorie - June 21, 2010, 9:03 p.m.
> I wish diagnostic kind enumeration weren't overloaded with DK_POP.
> But I guess we can worry about that later with the understanding this
> particular case is not meant to start a competition :-)

Yeah, it was that or add another field to the struct, and I figured
there would be a lot of those in the chain if people start abusing the
pragma.

> Patch OK; thanks!

Committed.  Whew, another LONG term project done :-)
Manuel López-Ibáñez - June 21, 2010, 9:34 p.m.
On 21 June 2010 23:03, DJ Delorie <dj@redhat.com> wrote:
>
>> I wish diagnostic kind enumeration weren't overloaded with DK_POP.
>> But I guess we can worry about that later with the understanding this
>> particular case is not meant to start a competition :-)
>
> Yeah, it was that or add another field to the struct, and I figured
> there would be a lot of those in the chain if people start abusing the
> pragma.
>
>> Patch OK; thanks!
>
> Committed.  Whew, another LONG term project done :-)

Nice!

A note in:

http://gcc.gnu.org/gcc-4.6/changes.html

would make this new feature more visible to users.

Cheers,

Manuel.
DJ Delorie - June 21, 2010, 9:50 p.m.
> http://gcc.gnu.org/gcc-4.6/changes.html
> 
> would make this new feature more visible to users.

Try as I might, I could not find anywhere on the gcc web site that
said how to commit patches to the gcc web site itself...  or even
check out the tree so you could *make* patches.
Steven Bosscher - June 21, 2010, 9:53 p.m.
On Mon, Jun 21, 2010 at 11:50 PM, DJ Delorie <dj@redhat.com> wrote:
>
>> http://gcc.gnu.org/gcc-4.6/changes.html
>>
>> would make this new feature more visible to users.
>
> Try as I might, I could not find anywhere on the gcc web site that
> said how to commit patches to the gcc web site itself...  or even
> check out the tree so you could *make* patches.
>

http://gcc.gnu.org/about.html

Ciao!
Steven
DJ Delorie - June 21, 2010, 9:58 p.m.
> http://gcc.gnu.org/about.html

Totally non-obvious!  I kept looking under "Contributing..." or
"FAQ..." or "Wiki..." or "SVN write access..."

Why would a link called "The GCC Team" talk about cvs access?  Why
would a page called "About" be linked via text that reads "The GCC
Team"?

/me suspects more patches will come of this... ;-)

Thanks for the link, though.
Manuel López-Ibáñez - June 22, 2010, 7:21 a.m.
On 21 June 2010 23:58, DJ Delorie <dj@redhat.com> wrote:
>
>> http://gcc.gnu.org/about.html
>
> Totally non-obvious!  I kept looking under "Contributing..." or
> "FAQ..." or "Wiki..." or "SVN write access..."
>
> Why would a link called "The GCC Team" talk about cvs access?  Why
> would a page called "About" be linked via text that reads "The GCC
> Team"?
>
> /me suspects more patches will come of this... ;-)
>
> Thanks for the link, though.


Actually, there is:

http://gcc.gnu.org/contribute.html#webchanges

and if you go a bit down, you find the SVN access link:

http://gcc.gnu.org/svn.html

which links to:

http://gcc.gnu.org/cvs.html

An improvement would be to link to the last one from the first one. A
better fix would be to convert the CVS repo to SVN. I have heard that
this requires updating some scripts but I don't know which scripts or
where to find them.

Cheers,

Manuel.
Manuel López-Ibáñez - July 2, 2010, 11:05 a.m.
What happened with this?

Manuel.

On 11 June 2010 23:28, DJ Delorie <dj@redhat.com> wrote:
>
>> 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)
>
DJ Delorie - July 2, 2010, 3:56 p.m.
> What happened with this?

http://gcc.gnu.org/viewcvs?view=revision&revision=161115
Manuel López-Ibáñez - July 2, 2010, 4:05 p.m.
On 2 July 2010 17:56, DJ Delorie <dj@redhat.com> wrote:
>
>> What happened with this?
>
> http://gcc.gnu.org/viewcvs?view=revision&revision=161115

Oh, I missed that. Sorry for the noise.

I think this is worth mentioning in gcc-4.6/changes.html, this is yet
another feature where clang cannot longer claim to be superior! ;-)

Cheers,

Manuel.

Patch

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)