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:
Jason Merrill 2003-09-16 03:58:27 -04:00 committed by Jakub Jelinek
parent c9fbef12be
commit 72954a4f44
10 changed files with 316 additions and 7 deletions

View File

@ -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.

View File

@ -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

View File

@ -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)

View File

@ -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);

View File

@ -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

View File

@ -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;
}

View File

@ -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;

View File

@ -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

View 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" } */
}

View File

@ -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