summaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authordj <dj@138bc75d-0d04-0410-961f-82ee72b054a4>2010-06-21 20:58:57 +0000
committerdj <dj@138bc75d-0d04-0410-961f-82ee72b054a4>2010-06-21 20:58:57 +0000
commit0955be6537c2fd3d21d5a0b385a21fdfc6b5634b (patch)
treebed45f50f0a58fddadbef5bd923786107ade4104 /gcc
parent99820dd9a9a14ed27bc7d1f2f452eb24ebfa6ebe (diff)
downloadgcc-0955be6537c2fd3d21d5a0b385a21fdfc6b5634b.tar.gz
* 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. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@161115 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc')
-rw-r--r--gcc/ChangeLog21
-rw-r--r--gcc/c-family/c-pragma.c20
-rw-r--r--gcc/diagnostic-core.h5
-rw-r--r--gcc/diagnostic.c91
-rw-r--r--gcc/diagnostic.h29
-rw-r--r--gcc/doc/extend.texi33
-rw-r--r--gcc/opts.c6
-rw-r--r--gcc/testsuite/ChangeLog4
-rw-r--r--gcc/testsuite/gcc.dg/pragma-diag-1.c21
9 files changed, 206 insertions, 24 deletions
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 <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
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 <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
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" } */
+}