mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-03-18 14:30:42 +08:00
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
This commit is contained in:
parent
c9fbef12be
commit
72954a4f44
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
15
gcc/calls.c
15
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);
|
||||
|
||||
|
@ -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
|
||||
|
18
gcc/expr.c
18
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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
188
gcc/testsuite/gcc.dg/attr-warn-unused-result.c
Normal file
188
gcc/testsuite/gcc.dg/attr-warn-unused-result.c
Normal file
@ -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" } */
|
||||
}
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user