This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] Add warn_unused_result attribute (take 2)


On Sat, Sep 13, 2003 at 10:52:54PM +0100, Joseph S. Myers wrote:
> On Sat, 13 Sep 2003, Jakub Jelinek wrote:
> 
> > a) function type attribute instead of decl type
> 
> So there should be corresponding tests that you do get the warning (which
> in this case can't name the particular function called) when the call
> isn't directly to a named function (e.g. through a function pointer
> variable with the appropriate type, or (x ? check1 : check2)()).

Added.  Also, warning string arguments in the source split into two lines
as requested by Gaby.
Ok to commit?

2003-09-14  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.

	* gcc.dg/attr-warn-unused-result.c: New test.

--- gcc/c-common.c.jj	2003-09-11 18:26:38.000000000 -0400
+++ gcc/c-common.c	2003-09-14 07:50:45.000000000 -0400
@@ -769,6 +769,8 @@ static tree handle_vector_size_attribute
 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);
@@ -846,6 +848,8 @@ const struct attribute_spec c_common_att
   { "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 }
 };
 
@@ -4003,6 +4007,26 @@ c_expand_expr (tree exp, rtx target, enu
 	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
@@ -5492,6 +5516,23 @@ handle_cleanup_attribute (tree *node, tr
 
   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
--- gcc/calls.c.jj	2003-09-08 02:47:50.000000000 -0400
+++ gcc/calls.c	2003-09-14 07:49:59.000000000 -0400
@@ -2167,13 +2167,26 @@ expand_call (tree exp, rtx target, int i
 	  (*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);
 
--- gcc/c-common.h.jj	2003-09-11 18:26:38.000000000 -0400
+++ gcc/c-common.h	2003-09-13 14:58:24.000000000 -0400
@@ -40,6 +40,7 @@ Software Foundation, 59 Temple Place - S
    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)
 */
 
@@ -1050,6 +1051,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)
--- gcc/expr.c.jj	2003-09-10 11:13:25.000000000 -0400
+++ gcc/expr.c	2003-09-11 18:35:06.000000000 -0400
@@ -235,6 +235,9 @@ enum insn_code movstr_optab[NUM_MACHINE_
 /* 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 
     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;
       }
 
--- gcc/tree-inline.c.jj	2003-09-11 18:26:39.000000000 -0400
+++ gcc/tree-inline.c	2003-09-13 15:01:10.000000000 -0400
@@ -1327,6 +1327,9 @@ expand_call_inline (tree *tp, int *walk_
   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
--- gcc/input.h.jj	2003-08-26 10:53:04.000000000 -0400
+++ gcc/input.h	2003-09-11 18:35:06.000000000 -0400
@@ -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;
 
--- gcc/doc/extend.texi.jj	2003-09-11 18:26:48.000000000 -0400
+++ gcc/doc/extend.texi	2003-09-13 15:03:44.000000000 -0400
@@ -1980,9 +1980,9 @@ attributes are currently defined for fun
 @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 l
 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
--- gcc/testsuite/gcc.dg/attr-warn-unused-result.c.jj	2003-09-11 18:35:06.000000000 -0400
+++ gcc/testsuite/gcc.dg/attr-warn-unused-result.c	2003-09-14 07:55:38.000000000 -0400
@@ -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" } */
+}


	Jakub


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]