diff options
author | Jason Merrill <jason@redhat.com> | 2003-09-16 03:58:27 -0400 |
---|---|---|
committer | Jakub Jelinek <jakub@gcc.gnu.org> | 2003-09-16 09:58:27 +0200 |
commit | 72954a4f4456cbdc8e1ff01e27f488d304ba32ad (patch) | |
tree | a6e02ea18c49961a7fc38f54f53192e537f6e3af /gcc | |
parent | c9fbef12bee8504ff48a45e3c67099af6772857d (diff) | |
download | gcc-72954a4f4456cbdc8e1ff01e27f488d304ba32ad.zip gcc-72954a4f4456cbdc8e1ff01e27f488d304ba32ad.tar.gz gcc-72954a4f4456cbdc8e1ff01e27f488d304ba32ad.tar.bz2 |
c-common.c (handle_warn_unused_result_attribute): New function.
* c-common.c (handle_warn_unused_result_attribute): New function.
(c_common_attribute_table): Add warn_unused_result.
(c_expand_expr): Issue warning when result of inlined function
with warn_unused_result attribute is ignored.
* calls.c (expand_call): Issue warning when result of function
with warn_unused_result attribute is ignored.
* c-common.h (STMT_EXPR_WARN_UNUSED_RESULT): Define.
* expr.c (expr_wfl_stack): Define.
(expand_expr) <case EXPR_WITH_FILE_LOCATION>: If ignore,
pass const0_rtx as target. Chain locations into expr_wfl_stack.
* tree-inline.c (expand_call_inline): Set STMT_EXPR_WARN_UNUSED_RESULT
bit if inlined function has warn_unused_result attribute.
* input.h (expr_wfl_stack): Declare.
* doc/extend.texi: Document warn_unused_result attribute.
* gcc.dg/attr-warn-unused-result.c: New test.
Co-Authored-By: Jakub Jelinek <jakub@redhat.com>
From-SVN: r71424
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/ChangeLog | 18 | ||||
-rw-r--r-- | gcc/c-common.c | 41 | ||||
-rw-r--r-- | gcc/c-common.h | 6 | ||||
-rw-r--r-- | gcc/calls.c | 15 | ||||
-rw-r--r-- | gcc/doc/extend.texi | 26 | ||||
-rw-r--r-- | gcc/expr.c | 18 | ||||
-rw-r--r-- | gcc/input.h | 3 | ||||
-rw-r--r-- | gcc/testsuite/ChangeLog | 5 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/attr-warn-unused-result.c | 188 | ||||
-rw-r--r-- | gcc/tree-inline.c | 3 |
10 files changed, 316 insertions, 7 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index dfa2b63..04c27b0 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,21 @@ +2003-09-16 Jason Merrill <jason@redhat.com> + Jakub Jelinek <jakub@redhat.com> + + * c-common.c (handle_warn_unused_result_attribute): New function. + (c_common_attribute_table): Add warn_unused_result. + (c_expand_expr): Issue warning when result of inlined function + with warn_unused_result attribute is ignored. + * calls.c (expand_call): Issue warning when result of function + with warn_unused_result attribute is ignored. + * c-common.h (STMT_EXPR_WARN_UNUSED_RESULT): Define. + * expr.c (expr_wfl_stack): Define. + (expand_expr) <case EXPR_WITH_FILE_LOCATION>: If ignore, + pass const0_rtx as target. Chain locations into expr_wfl_stack. + * tree-inline.c (expand_call_inline): Set STMT_EXPR_WARN_UNUSED_RESULT + bit if inlined function has warn_unused_result attribute. + * input.h (expr_wfl_stack): Declare. + * doc/extend.texi: Document warn_unused_result attribute. + 2003-09-15 Alexandre Oliva <aoliva@redhat.com> * cpplib.c (do_pragma): Remove unnecessary cb_line_change. diff --git a/gcc/c-common.c b/gcc/c-common.c index 5268806..49c6aca 100644 --- a/gcc/c-common.c +++ b/gcc/c-common.c @@ -773,6 +773,8 @@ static tree handle_vector_size_attribute (tree *, tree, tree, int, static tree handle_nonnull_attribute (tree *, tree, tree, int, bool *); static tree handle_nothrow_attribute (tree *, tree, tree, int, bool *); static tree handle_cleanup_attribute (tree *, tree, tree, int, bool *); +static tree handle_warn_unused_result_attribute (tree *, tree, tree, int, + bool *); static tree vector_size_helper (tree, tree); static void check_function_nonnull (tree, tree); @@ -850,6 +852,8 @@ const struct attribute_spec c_common_attribute_table[] = { "may_alias", 0, 0, false, true, false, NULL }, { "cleanup", 1, 1, true, false, false, handle_cleanup_attribute }, + { "warn_unused_result", 0, 0, false, true, true, + handle_warn_unused_result_attribute }, { NULL, 0, 0, false, false, false, NULL } }; @@ -4007,6 +4011,26 @@ c_expand_expr (tree exp, rtx target, enum machine_mode tmode, int modifier) bool preserve_result = false; bool return_target = false; + if (STMT_EXPR_WARN_UNUSED_RESULT (exp) && target == const0_rtx) + { + tree stmt = STMT_EXPR_STMT (exp); + tree scope; + + for (scope = COMPOUND_BODY (stmt); + scope && TREE_CODE (scope) != SCOPE_STMT; + scope = TREE_CHAIN (scope)); + + if (scope && SCOPE_STMT_BLOCK (scope)) + warning ("%Hignoring return value of `%D', " + "declared with attribute warn_unused_result", + &expr_wfl_stack->location, + BLOCK_ABSTRACT_ORIGIN (SCOPE_STMT_BLOCK (scope))); + else + warning ("%Hignoring return value of function " + "declared with attribute warn_unused_result", + &expr_wfl_stack->location); + } + /* Since expand_expr_stmt calls free_temp_slots after every expression statement, we must call push_temp_slots here. Otherwise, any temporaries in use now would be considered @@ -5496,6 +5520,23 @@ handle_cleanup_attribute (tree *node, tree name, tree args, return NULL_TREE; } + +/* Handle a "warn_unused_result" attribute. No special handling. */ + +static tree +handle_warn_unused_result_attribute (tree *node, tree name, + tree args ATTRIBUTE_UNUSED, + int flags ATTRIBUTE_UNUSED, bool *no_add_attrs) +{ + /* Ignore the attribute for functions not returning any value. */ + if (VOID_TYPE_P (TREE_TYPE (*node))) + { + warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name)); + *no_add_attrs = true; + } + + return NULL_TREE; +} /* Check for valid arguments being passed to a function. */ void diff --git a/gcc/c-common.h b/gcc/c-common.h index d9cbb2c..0bbc955 100644 --- a/gcc/c-common.h +++ b/gcc/c-common.h @@ -40,6 +40,7 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA 2: STMT_LINENO_FOR_FN_P (in _STMT) 3: SCOPE_NO_CLEANUPS_P (in SCOPE_STMT) COMPOUND_STMT_BODY_BLOCK (in COMPOUND_STMT) + STMT_EXPR_WARN_UNUSED_RESULT (in STMT_EXPR) 4: SCOPE_PARTIAL_P (in SCOPE_STMT) */ @@ -1054,6 +1055,11 @@ extern void finish_file (void); #define STMT_EXPR_NO_SCOPE(NODE) \ TREE_LANG_FLAG_0 (STMT_EXPR_CHECK (NODE)) +/* Nonzero if this statement-expression should cause warning if its result + is not used. */ +#define STMT_EXPR_WARN_UNUSED_RESULT(NODE) \ + TREE_LANG_FLAG_3 (STMT_EXPR_CHECK (NODE)) + /* LABEL_STMT accessor. This gives access to the label associated with the given label statement. */ #define LABEL_STMT_LABEL(NODE) TREE_OPERAND (LABEL_STMT_CHECK (NODE), 0) diff --git a/gcc/calls.c b/gcc/calls.c index 482d487..f8e7ea0 100644 --- a/gcc/calls.c +++ b/gcc/calls.c @@ -2167,13 +2167,26 @@ expand_call (tree exp, rtx target, int ignore) (*lang_hooks.mark_addressable) (fndecl); } + if (ignore + && lookup_attribute ("warn_unused_result", + TYPE_ATTRIBUTES (TREE_TYPE (fndecl)))) + warning ("ignoring return value of `%D', " + "declared with attribute warn_unused_result", fndecl); + flags |= flags_from_decl_or_type (fndecl); } /* If we don't have specific function to call, see if we have a attributes set in the type. */ else - flags |= flags_from_decl_or_type (TREE_TYPE (TREE_TYPE (p))); + { + if (ignore + && lookup_attribute ("warn_unused_result", + TYPE_ATTRIBUTES (TREE_TYPE (TREE_TYPE (p))))) + warning ("ignoring return value of function " + "declared with attribute warn_unused_result"); + flags |= flags_from_decl_or_type (TREE_TYPE (TREE_TYPE (p))); + } struct_value = targetm.calls.struct_value_rtx (fndecl ? TREE_TYPE (fndecl) : 0, 0); diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index 788df63..ce31713 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -1980,9 +1980,9 @@ attributes are currently defined for functions on all targets: @code{format}, @code{format_arg}, @code{no_instrument_function}, @code{section}, @code{constructor}, @code{destructor}, @code{used}, @code{unused}, @code{deprecated}, @code{weak}, @code{malloc}, -@code{alias}, and @code{nonnull}. Several other attributes are defined -for functions on particular target systems. Other attributes, including -@code{section} are supported for variables declarations +@code{alias}, @code{warn_unused_result} and @code{nonnull}. Several other +attributes are defined for functions on particular target systems. Other +attributes, including @code{section} are supported for variables declarations (@pxref{Variable Attributes}) and for types (@pxref{Type Attributes}). You may also specify attributes with @samp{__} preceding and following @@ -2312,6 +2312,26 @@ results in a warning on line 3 but not line 2. The @code{deprecated} attribute can also be used for variables and types (@pxref{Variable Attributes}, @pxref{Type Attributes}.) +@item warn_unused_result +@cindex @code{warn_unused_result} attribute +The @code{warn_unused_result} attribute causes a warning to be emitted +if a caller of the function with this attribute does not use its +return value. This is useful for functions where not checking +the result is either a security problem or always a bug, such as +@code{realloc}. + +@smallexample +int fn () __attribute__ ((warn_unused_result)); +int foo () +@{ + if (fn () < 0) return -1; + fn (); + return 0; +@} +@end smallexample + +results in warning on line 5. + @item weak @cindex @code{weak} attribute The @code{weak} attribute causes the declaration to be emitted as a weak @@ -235,6 +235,9 @@ enum insn_code movstr_optab[NUM_MACHINE_MODES]; /* This array records the insn_code of insns to perform block clears. */ enum insn_code clrstr_optab[NUM_MACHINE_MODES]; +/* Stack of EXPR_WITH_FILE_LOCATION nested expressions. */ +struct file_stack *expr_wfl_stack; + /* SLOW_UNALIGNED_ACCESS is nonzero if unaligned accesses are very slow. */ #ifndef SLOW_UNALIGNED_ACCESS @@ -6959,14 +6962,23 @@ expand_expr (tree exp, rtx target, enum machine_mode tmode, case EXPR_WITH_FILE_LOCATION: { rtx to_return; - location_t saved_loc = input_location; + struct file_stack fs; + + fs.location = input_location; + fs.next = expr_wfl_stack; input_filename = EXPR_WFL_FILENAME (exp); input_line = EXPR_WFL_LINENO (exp); + expr_wfl_stack = &fs; if (EXPR_WFL_EMIT_LINE_NOTE (exp)) emit_line_note (input_location); /* Possibly avoid switching back and forth here. */ - to_return = expand_expr (EXPR_WFL_NODE (exp), target, tmode, modifier); - input_location = saved_loc; + to_return = expand_expr (EXPR_WFL_NODE (exp), + (ignore ? const0_rtx : target), + tmode, modifier); + if (expr_wfl_stack != &fs) + abort (); + input_location = fs.location; + expr_wfl_stack = fs.next; return to_return; } diff --git a/gcc/input.h b/gcc/input.h index fba597e..ff014f6 100644 --- a/gcc/input.h +++ b/gcc/input.h @@ -51,6 +51,9 @@ extern location_t input_location; The line member is not accurate for the innermost file on the stack. */ extern struct file_stack *input_file_stack; +/* Stack of EXPR_WITH_FILE_LOCATION nested expressions. */ +extern struct file_stack *expr_wfl_stack; + /* Incremented on each change to input_file_stack. */ extern int input_file_stack_tick; diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index db50d89..9af03e7 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2003-09-16 Jason Merrill <jason@redhat.com> + Jakub Jelinek <jakub@redhat.com> + + * gcc.dg/attr-warn-unused-result.c: New test. + 2003-09-15 Nathan Sidwell <nathan@codesourcery.com> PR c++/12184 diff --git a/gcc/testsuite/gcc.dg/attr-warn-unused-result.c b/gcc/testsuite/gcc.dg/attr-warn-unused-result.c new file mode 100644 index 0000000..0404cec --- /dev/null +++ b/gcc/testsuite/gcc.dg/attr-warn-unused-result.c @@ -0,0 +1,188 @@ +/* warn_unused_result attribute tests. */ +/* { dg-do compile } */ +/* { dg-options "-O" } */ + +#define WUR __attribute__((warn_unused_result)) +#define WURAI __attribute__((warn_unused_result, always_inline)) inline +typedef WUR int (*fnt) (void); + +typedef struct { long i; } A; +typedef struct { long i; long j; } B; +typedef struct { char big[1024]; fnt fn; } C; + +WUR int check1 (void); +WUR void check2 (void); /* { dg-warning "attribute ignored" } */ +int foo WUR; /* { dg-warning "only applies" } */ +int bar (void); +extern WURAI int check3 (void) { return bar (); } +WUR A check4 (void); +WUR B check5 (void); +WUR C check6 (void); +A bar7 (void); +B bar8 (void); +C bar9 (void); +extern WURAI A check7 (void) { return bar7 (); } +extern WURAI B check8 (void) { return bar8 (); } +extern WURAI C check9 (void) { return bar9 (); } +/* This is useful for checking whether return value of statement + expressions (returning int in this case) is used. */ +extern WURAI int check_int_result (int res) { return res; } +#define GU(v) ({ int e = 0; (v) = bar (); if ((v) < 23) e = 14; e; }) +fnt fnptr; +WUR int check10 (void); +int baz (void); +extern WURAI int check11 (void) { return baz (); } +int k; + +void +test (void) +{ + int i = 0, j; + const fnt pcheck1 = check1; + const fnt pcheck3 = check3; + A a; + B b; + C c; + if (check1 ()) + return; + i += check1 (); + i += ({ check1 (); }); + check1 (); /* { dg-warning "ignoring return value of" } */ + (void) check1 (); /* { dg-warning "ignoring return value of" } */ + check1 (), bar (); /* { dg-warning "ignoring return value of" } */ + check2 (); + (void) check2 (); + check2 (), bar (); + if (check3 ()) + return; + i += check3 (); + i += ({ check3 (); }); + check3 (); /* { dg-warning "ignoring return value of" } */ + (void) check3 (); /* { dg-warning "ignoring return value of" } */ + check3 (), bar (); /* { dg-warning "ignoring return value of" } */ + a = check4 (); + if (a.i) + return; + if (check4 ().i) + return; + if (({ check4 (); }).i) + return; + check4 (); /* { dg-warning "ignoring return value of" } */ + (void) check4 (); /* { dg-warning "ignoring return value of" } */ + check4 (), bar (); /* { dg-warning "ignoring return value of" } */ + b = check5 (); + if (b.i + b.j) + return; + if (check5 ().j) + return; + if (({ check5 (); }).j) + return; + check5 (); /* { dg-warning "ignoring return value of" } */ + (void) check5 (); /* { dg-warning "ignoring return value of" } */ + check5 (), bar (); /* { dg-warning "ignoring return value of" } */ + c = check6 (); + if (c.big[12] + c.big[29]) + return; + if (check6 ().big[27]) + return; + if (({ check6 (); }).big[0]) + return; + check6 (); /* { dg-warning "ignoring return value of" } */ + (void) check6 (); /* { dg-warning "ignoring return value of" } */ + check6 (), bar (); /* { dg-warning "ignoring return value of" } */ + a = check7 (); + if (a.i) + return; + if (check7 ().i) + return; + if (({ check7 (); }).i) + return; + check7 (); /* { dg-warning "ignoring return value of" } */ + (void) check7 (); /* { dg-warning "ignoring return value of" } */ + check7 (), bar (); /* { dg-warning "ignoring return value of" } */ + b = check8 (); + if (b.i + b.j) + return; + if (check8 ().j) + return; + if (({ check8 (); }).j) + return; + check8 (); /* { dg-warning "ignoring return value of" } */ + (void) check8 (); /* { dg-warning "ignoring return value of" } */ + check8 (), bar (); /* { dg-warning "ignoring return value of" } */ + c = check9 (); + if (c.big[12] + c.big[29]) + return; + if (check9 ().big[27]) + return; + if (({ check9 (); }).big[0]) + return; + check9 (); /* { dg-warning "ignoring return value of" } */ + (void) check9 (); /* { dg-warning "ignoring return value of" } */ + check9 (), bar (); /* { dg-warning "ignoring return value of" } */ + if (check_int_result (GU (j))) + return; + i += check_int_result (GU (j)); + i += ({ check_int_result (GU (j)); }); + check_int_result (GU (j)); /* { dg-warning "ignoring return value of" } */ + (void) check_int_result (GU (j)); /* { dg-warning "ignoring return value of" } */ + check_int_result (GU (j)), bar (); /* { dg-warning "ignoring return value of" } */ + if (fnptr ()) + return; + i += fnptr (); + i += ({ fnptr (); }); + fnptr (); /* { dg-warning "ignoring return value of" } */ + (void) fnptr (); /* { dg-warning "ignoring return value of" } */ + fnptr (), bar (); /* { dg-warning "ignoring return value of" } */ + fnptr = check1; + if (fnptr ()) + return; + i += fnptr (); + i += ({ fnptr (); }); + fnptr (); /* { dg-warning "ignoring return value of" } */ + (void) fnptr (); /* { dg-warning "ignoring return value of" } */ + fnptr (), bar (); /* { dg-warning "ignoring return value of" } */ + fnptr = check3; + if (fnptr ()) + return; + i += fnptr (); + i += ({ fnptr (); }); + fnptr (); /* { dg-warning "ignoring return value of" } */ + (void) fnptr (); /* { dg-warning "ignoring return value of" } */ + fnptr (), bar (); /* { dg-warning "ignoring return value of" } */ + if (bar9 ().fn ()) + return; + i += bar9 ().fn (); + i += ({ bar9 ().fn (); }); + bar9 ().fn (); /* { dg-warning "ignoring return value of" } */ + (void) bar9 ().fn (); /* { dg-warning "ignoring return value of" } */ + bar9 ().fn (), bar (); /* { dg-warning "ignoring return value of" } */ + if ((k ? check1 : check10) ()) + return; + i += (k ? check1 : check10) (); + i += ({ (k ? check1 : check10) (); }); + (k ? check1 : check10) (); /* { dg-warning "ignoring return value of" } */ + (void) (k ? check1 : check10) (); /* { dg-warning "ignoring return value of" } */ + (k ? check1 : check10) (), bar (); /* { dg-warning "ignoring return value of" } */ + if ((k ? check3 : check11) ()) + return; + i += (k ? check3 : check11) (); + i += ({ (k ? check3 : check11) (); }); + (k ? check3 : check11) (); /* { dg-warning "ignoring return value of" } */ + (void) (k ? check3 : check11) (); /* { dg-warning "ignoring return value of" } */ + (k ? check3 : check11) (), bar (); /* { dg-warning "ignoring return value of" } */ + if (pcheck1 ()) + return; + i += pcheck1 (); + i += ({ pcheck1 (); }); + pcheck1 (); /* { dg-warning "ignoring return value of" } */ + (void) pcheck1 (); /* { dg-warning "ignoring return value of" } */ + pcheck1 (), bar (); /* { dg-warning "ignoring return value of" } */ + if (pcheck3 ()) + return; + i += pcheck3 (); + i += ({ pcheck3 (); }); + pcheck3 (); /* { dg-warning "ignoring return value of" } */ + (void) pcheck3 (); /* { dg-warning "ignoring return value of" } */ + pcheck3 (), bar (); /* { dg-warning "ignoring return value of" } */ +} diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c index bd027eb..8a435a6 100644 --- a/gcc/tree-inline.c +++ b/gcc/tree-inline.c @@ -1328,6 +1328,9 @@ expand_call_inline (tree *tp, int *walk_subtrees, void *data) expr = build1 (STMT_EXPR, TREE_TYPE (TREE_TYPE (fn)), make_node (COMPOUND_STMT)); /* There is no scope associated with the statement-expression. */ STMT_EXPR_NO_SCOPE (expr) = 1; + if (lookup_attribute ("warn_unused_result", + TYPE_ATTRIBUTES (TREE_TYPE (fn)))) + STMT_EXPR_WARN_UNUSED_RESULT (expr) = 1; stmt = STMT_EXPR_STMT (expr); #else /* INLINER_FOR_JAVA */ /* Build a block containing code to initialize the arguments, the |