OSDN Git Service

* diagnostic.h (diagnostic_classification_change_t): New.
authordj <dj@138bc75d-0d04-0410-961f-82ee72b054a4>
Mon, 21 Jun 2010 20:58:57 +0000 (20:58 +0000)
committerdj <dj@138bc75d-0d04-0410-961f-82ee72b054a4>
Mon, 21 Jun 2010 20:58:57 +0000 (20:58 +0000)
(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.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@161115 138bc75d-0d04-0410-961f-82ee72b054a4

gcc/ChangeLog
gcc/c-family/c-pragma.c
gcc/diagnostic-core.h
gcc/diagnostic.c
gcc/diagnostic.h
gcc/doc/extend.texi
gcc/opts.c
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/pragma-diag-1.c [new file with mode: 0644]

index a553a10..85041a1 100644 (file)
@@ -1,3 +1,24 @@
+2010-06-21  DJ Delorie  <dj@redhat.com>
+
+       * 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  <jakub@redhat.com>
 
        * dwarf2out.c (add_linkage_name): New function.  Don't add
index ab92269..cea0b26 100644 (file)
@@ -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
index b504716..674bad6 100644 (file)
@@ -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;
index 61401e6..34e9679 100644 (file)
@@ -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];
        }
index 062402f..d384074 100644 (file)
@@ -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
index 5f0d762..795b6bf 100644 (file)
@@ -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
 
index 006e0f1..55f8c77 100644 (file)
@@ -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;
index ba4ffa7..77228c1 100644 (file)
@@ -1,3 +1,7 @@
+2010-06-21  DJ Delorie  <dj@redhat.com>
+
+       * gcc.dg/pragma-diag-1.c: New.
+
 2010-06-21  H.J. Lu  <hongjiu.lu@intel.com>
 
        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 (file)
index 0000000..b6d294d
--- /dev/null
@@ -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" } */
+}