From: jakub Date: Tue, 16 Sep 2003 07:58:27 +0000 (+0000) Subject: * c-common.c (handle_warn_unused_result_attribute): New function. X-Git-Tag: upstream/4.9.2~76732 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=8a8cdb8db5460d347fb62587e3becfe7f69e64ae;p=platform%2Fupstream%2Flinaro-gcc.git * 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) : 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. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@71424 138bc75d-0d04-0410-961f-82ee72b054a4 --- 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 + Jakub Jelinek + + * 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) : 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 * 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 diff --git a/gcc/expr.c b/gcc/expr.c index 0538707..584250b 100644 --- a/gcc/expr.c +++ b/gcc/expr.c @@ -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 + Jakub Jelinek + + * gcc.dg/attr-warn-unused-result.c: New test. + 2003-09-15 Nathan Sidwell 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