* c-common.c (handle_warn_unused_result_attribute): New function.
authorjakub <jakub@138bc75d-0d04-0410-961f-82ee72b054a4>
Tue, 16 Sep 2003 07:58:27 +0000 (07:58 +0000)
committerjakub <jakub@138bc75d-0d04-0410-961f-82ee72b054a4>
Tue, 16 Sep 2003 07:58:27 +0000 (07:58 +0000)
(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.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@71424 138bc75d-0d04-0410-961f-82ee72b054a4

gcc/ChangeLog
gcc/c-common.c
gcc/c-common.h
gcc/calls.c
gcc/doc/extend.texi
gcc/expr.c
gcc/input.h
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/attr-warn-unused-result.c [new file with mode: 0644]
gcc/tree-inline.c

index dfa2b63..04c27b0 100644 (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.
index 5268806..49c6aca 100644 (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;
+}
 \f
 /* Check for valid arguments being passed to a function.  */
 void
index d9cbb2c..0bbc955 100644 (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)
index 482d487..f8e7ea0 100644 (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);
 
index 788df63..ce31713 100644 (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
index 0538707..584250b 100644 (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;
       }
 
index fba597e..ff014f6 100644 (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;
 
index db50d89..9af03e7 100644 (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
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 (file)
index 0000000..0404cec
--- /dev/null
@@ -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" } */
+}
index bd027eb..8a435a6 100644 (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