From 3d091dac56f97cc79cc2e9a31796219fba99764c Mon Sep 17 00:00:00 2001 From: Kaveh Ghazi Date: Sun, 5 Sep 2004 02:50:09 +0000 Subject: [PATCH] builtin-attrs.def (ATTR_SENTINEL, [...]): New. gcc: * builtin-attrs.def (ATTR_SENTINEL, ATTR_SENTINEL_NOTHROW_LIST): New. * builtins.def (BUILT_IN_EXECL, BUILT_IN_EXECLP): Add `sentinel' attribute. * c-common.c (handle_sentinel_attribute, check_function_sentinel): New functions. (c_common_attribute_table): Add `sentinel' attribute. (check_function_arguments): Handle `sentinel' attribute. * doc/extend.texi: Document `sentinel' attribute. gcc/testsuite: * gcc.dg/format/sentinel-1.c: New test. include: * ansidecl.h (ATTRIBUTE_SENTINEL): Define. * libiberty.h (concat, reconcat, concat_length, concat_copy, concat_copy2): Use ATTRIBUTE_SENTINEL. From-SVN: r87096 --- gcc/ChangeLog | 13 ++++- gcc/builtin-attrs.def | 3 ++ gcc/builtins.def | 4 +- gcc/c-common.c | 61 +++++++++++++++++++++++- gcc/doc/extend.texi | 14 +++++- gcc/testsuite/ChangeLog | 4 ++ gcc/testsuite/gcc.dg/format/sentinel-1.c | 37 ++++++++++++++ include/ChangeLog | 6 +++ include/ansidecl.h | 9 ++++ include/libiberty.h | 10 ++-- 10 files changed, 151 insertions(+), 10 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/format/sentinel-1.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index be21a7f7e66..95f0e026050 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,8 +1,19 @@ +2004-09-04 Kaveh R. Ghazi - * configure: Regenerated. + * builtin-attrs.def (ATTR_SENTINEL, ATTR_SENTINEL_NOTHROW_LIST): + New. + * builtins.def (BUILT_IN_EXECL, BUILT_IN_EXECLP): Add `sentinel' + attribute. + * c-common.c (handle_sentinel_attribute, check_function_sentinel): + New functions. + (c_common_attribute_table): Add `sentinel' attribute. + (check_function_arguments): Handle `sentinel' attribute. + * doc/extend.texi: Document `sentinel' attribute. 2004-09-04 Richard Kenner + * configure: Regenerated. + * gimplify.c (internal_get_tmp_var): Remove unused var CLASS. * tree.c (save_expr): No longer TREE_READONLY. diff --git a/gcc/builtin-attrs.def b/gcc/builtin-attrs.def index 10d58649a82..67cf193e4fd 100644 --- a/gcc/builtin-attrs.def +++ b/gcc/builtin-attrs.def @@ -84,6 +84,7 @@ DEF_ATTR_IDENT (ATTR_GCC_CDIAG, "gcc_cdiag") DEF_ATTR_IDENT (ATTR_GCC_CXXDIAG, "gcc_cxxdiag") DEF_ATTR_IDENT (ATTR_PURE, "pure") DEF_ATTR_IDENT (ATTR_SCANF, "scanf") +DEF_ATTR_IDENT (ATTR_SENTINEL, "sentinel") DEF_ATTR_IDENT (ATTR_STRFMON, "strfmon") DEF_ATTR_IDENT (ATTR_STRFTIME, "strftime") @@ -97,6 +98,8 @@ DEF_ATTR_TREE_LIST (ATTR_NORETURN_NOTHROW_LIST, ATTR_NORETURN, \ ATTR_NULL, ATTR_NOTHROW_LIST) DEF_ATTR_TREE_LIST (ATTR_MALLOC_NOTHROW_LIST, ATTR_MALLOC, \ ATTR_NULL, ATTR_NOTHROW_LIST) +DEF_ATTR_TREE_LIST (ATTR_SENTINEL_NOTHROW_LIST, ATTR_SENTINEL, \ + ATTR_NULL, ATTR_NOTHROW_LIST) DEF_ATTR_TREE_LIST (ATTR_NOTHROW_NONNULL_1, ATTR_NONNULL, ATTR_LIST_1, \ ATTR_NOTHROW_LIST) diff --git a/gcc/builtins.def b/gcc/builtins.def index 1cfbf712999..e89509fcebf 100644 --- a/gcc/builtins.def +++ b/gcc/builtins.def @@ -554,8 +554,8 @@ DEF_GCC_BUILTIN (BUILT_IN_DWARF_CFA, "dwarf_cfa", BT_FN_PTR, ATTR_NULL) DEF_GCC_BUILTIN (BUILT_IN_DWARF_SP_COLUMN, "dwarf_sp_column", BT_FN_UINT, ATTR_NULL) DEF_GCC_BUILTIN (BUILT_IN_EH_RETURN, "eh_return", BT_FN_VOID_PTRMODE_PTR, ATTR_NORETURN_NOTHROW_LIST) DEF_GCC_BUILTIN (BUILT_IN_EH_RETURN_DATA_REGNO, "eh_return_data_regno", BT_FN_INT_INT, ATTR_NULL) -DEF_EXT_LIB_BUILTIN (BUILT_IN_EXECL, "execl", BT_FN_INT_CONST_STRING_CONST_STRING_VAR, ATTR_NOTHROW_LIST) -DEF_EXT_LIB_BUILTIN (BUILT_IN_EXECLP, "execlp", BT_FN_INT_CONST_STRING_CONST_STRING_VAR, ATTR_NOTHROW_LIST) +DEF_EXT_LIB_BUILTIN (BUILT_IN_EXECL, "execl", BT_FN_INT_CONST_STRING_CONST_STRING_VAR, ATTR_SENTINEL_NOTHROW_LIST) +DEF_EXT_LIB_BUILTIN (BUILT_IN_EXECLP, "execlp", BT_FN_INT_CONST_STRING_CONST_STRING_VAR, ATTR_SENTINEL_NOTHROW_LIST) DEF_EXT_LIB_BUILTIN (BUILT_IN_EXECLE, "execle", BT_FN_INT_CONST_STRING_CONST_STRING_VAR, ATTR_NOTHROW_LIST) DEF_EXT_LIB_BUILTIN (BUILT_IN_EXECV, "execv", BT_FN_INT_CONST_STRING_PTR_CONST_STRING, ATTR_NOTHROW_LIST) DEF_EXT_LIB_BUILTIN (BUILT_IN_EXECVP, "execvp", BT_FN_INT_CONST_STRING_PTR_CONST_STRING, ATTR_NOTHROW_LIST) diff --git a/gcc/c-common.c b/gcc/c-common.c index 499829bd0e3..3af1e6408ed 100644 --- a/gcc/c-common.c +++ b/gcc/c-common.c @@ -557,6 +557,7 @@ 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 handle_sentinel_attribute (tree *, tree, tree, int, bool *); static void check_function_nonnull (tree, tree); static void check_nonnull_arg (void *, tree, unsigned HOST_WIDE_INT); @@ -635,6 +636,8 @@ const struct attribute_spec c_common_attribute_table[] = handle_cleanup_attribute }, { "warn_unused_result", 0, 0, false, true, true, handle_warn_unused_result_attribute }, + { "sentinel", 0, 0, false, true, true, + handle_sentinel_attribute }, { NULL, 0, 0, false, false, false, NULL } }; @@ -5044,6 +5047,29 @@ check_function_nonnull (tree attrs, tree params) } } +/* Check the last argument of a function call is (pointer)0. */ + +static void +check_function_sentinel (tree attrs, tree params) +{ + tree attr = lookup_attribute ("sentinel", attrs); + + if (attr) + { + if (!params) + warning ("missing sentinel in function call"); + else + { + /* Find the last parameter. */ + while (TREE_CHAIN (params)) + params = TREE_CHAIN (params); + if (!POINTER_TYPE_P (TREE_TYPE (TREE_VALUE (params))) + || !integer_zerop (TREE_VALUE (params))) + warning ("missing sentinel in function call"); + } + } +} + /* Helper for check_function_nonnull; given a list of operands which must be non-null in ARGS, determine if operand PARAM_NUM should be checked. */ @@ -5185,6 +5211,36 @@ handle_warn_unused_result_attribute (tree *node, tree name, return NULL_TREE; } + +/* Handle a "sentinel" attribute. */ + +static tree +handle_sentinel_attribute (tree *node, tree name, + tree ARG_UNUSED (args), + int ARG_UNUSED (flags), bool *no_add_attrs) +{ + tree params = TYPE_ARG_TYPES (*node); + + if (!params) + { + warning ("`%s' attribute requires prototypes with named arguments", + IDENTIFIER_POINTER (name)); + *no_add_attrs = true; + return NULL_TREE; + } + + while (TREE_CHAIN (params)) + params = TREE_CHAIN (params); + + if (VOID_TYPE_P (TREE_VALUE (params))) + { + warning ("`%s' attribute only applies to variadic functions", + IDENTIFIER_POINTER (name)); + *no_add_attrs = true; + } + + return NULL_TREE; +} /* Check for valid arguments being passed to a function. */ void @@ -5199,7 +5255,10 @@ check_function_arguments (tree attrs, tree params) /* Check for errors in format strings. */ if (warn_format) - check_function_format (attrs, params); + { + check_function_format (attrs, params); + check_function_sentinel (attrs, params); + } } /* Generic argument checking recursion routine. PARAM is the argument to diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index fe25cd1b066..4c27320f2b9 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -1496,7 +1496,7 @@ attributes when making a declaration. This keyword is followed by an attribute specification inside double parentheses. The following attributes are currently defined for functions on all targets: @code{noreturn}, @code{noinline}, @code{always_inline}, -@code{pure}, @code{const}, @code{nothrow}, +@code{pure}, @code{const}, @code{nothrow}, @code{sentinel}, @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}, @@ -2111,6 +2111,18 @@ attribute is not available on all platforms. If you need to map the entire contents of a module to a particular section, consider using the facilities of the linker instead. +@item sentinel +@cindex @code{sentinel} function attribute +This function attribute ensures that the last parameter in a function +call is an explicit @code{NULL}. The attribute is only valid on +variadic functions. For example the attribute is automatically set for +the built-in functions @code{execl} and @code{execlp} where @code{NULL} +is the marker for argument list termination. A valid @code{NULL} in +this context is defined as zero with any pointer type. If your system +defines the @code{NULL} macro with an integer type then you need to add +an explicit cast. The warnings for missing or incorrect sentinels are +enabled with @option{-Wformat}. + @item short_call See long_call/short_call. diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index e0f86f13f7e..4fb58cb251b 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2004-09-04 Kaveh R. Ghazi + + * gcc.dg/format/sentinel-1.c: New test. + 2004-09-04 Uros Bizjak * testsuite/gcc.dg/builtins-46.c: New. diff --git a/gcc/testsuite/gcc.dg/format/sentinel-1.c b/gcc/testsuite/gcc.dg/format/sentinel-1.c new file mode 100644 index 00000000000..e1e127dca38 --- /dev/null +++ b/gcc/testsuite/gcc.dg/format/sentinel-1.c @@ -0,0 +1,37 @@ +/* Test for attribute sentinel. */ +/* Origin: Kaveh Ghazi */ +/* { dg-do compile } */ +/* { dg-options "-Wformat" } */ + +#include /* For NULL, which must be (ptr)0. */ + +extern int execl (const char *, const char *, ...); +extern int execlp (const char *, const char *, ...); + +#define ATTR __attribute__ ((__sentinel__)) + +extern int a ATTR; /* { dg-warning "applies to function types" "sentinel" } */ + +extern void foo1 (const char *, ...) ATTR; +extern void foo2 (...) ATTR; /* { dg-error "ISO C requires|named arguments" "sentinel" } */ +extern void foo3 () ATTR; /* { dg-warning "named arguments" "sentinel" } */ +extern void foo4 (const char *, int) ATTR; /* { dg-warning "variadic functions" "sentinel" } */ + +extern void bar(void) +{ + foo1 (); /* { dg-error "missing sentinel|too few arguments" "sentinel" } */ + foo1 ("a"); /* { dg-warning "missing sentinel" "sentinel" } */ + foo1 ("a", 1); /* { dg-warning "missing sentinel" "sentinel" } */ + foo1 ("a", 0); /* { dg-warning "missing sentinel" "sentinel" } */ + foo1 ("a", (void*)1); /* { dg-warning "missing sentinel" "sentinel" } */ + foo1 ("a", NULL, 1); /* { dg-warning "missing sentinel" "sentinel" } */ + foo1 ("a", NULL); + + execl ("/bin/ls", "-aFC"); /* { dg-warning "missing sentinel" "sentinel" } */ + execl ("/bin/ls", "-aFC", 0); /* { dg-warning "missing sentinel" "sentinel" } */ + execl ("/bin/ls", "-aFC", NULL); + + execlp ("ls", "-aFC"); /* { dg-warning "missing sentinel" "sentinel" } */ + execlp ("ls", "-aFC", 0); /* { dg-warning "missing sentinel" "sentinel" } */ + execlp ("ls", "-aFC", NULL); +} diff --git a/include/ChangeLog b/include/ChangeLog index 1e180da613c..19d2f5099e4 100644 --- a/include/ChangeLog +++ b/include/ChangeLog @@ -1,3 +1,9 @@ +2004-09-04 Kaveh R. Ghazi + + * ansidecl.h (ATTRIBUTE_SENTINEL): Define. + * libiberty.h (concat, reconcat, concat_length, concat_copy, + concat_copy2): Use ATTRIBUTE_SENTINEL. + 2004-08-02 Gabriel Dos Reis * libiberty.h (XDELETE, XDELETEVEC, XRESIZEVEC): Remove any diff --git a/include/ansidecl.h b/include/ansidecl.h index ccf0b2757ae..04c3a30bb53 100644 --- a/include/ansidecl.h +++ b/include/ansidecl.h @@ -322,6 +322,15 @@ So instead we use the macro below and test it against specific values. */ # define ATTRIBUTE_NULL_PRINTF_5 ATTRIBUTE_NULL_PRINTF(5, 6) #endif /* ATTRIBUTE_NULL_PRINTF */ +/* Attribute `sentinel' was valid as of gcc 3.5. */ +#ifndef ATTRIBUTE_SENTINEL +# if (GCC_VERSION >= 3005) +# define ATTRIBUTE_SENTINEL __attribute__ ((__sentinel__)) +# else +# define ATTRIBUTE_SENTINEL +# endif /* GNUC >= 3.5 */ +#endif /* ATTRIBUTE_SENTINEL */ + /* We use __extension__ in some places to suppress -pedantic warnings about GCC extensions. This feature didn't work properly before gcc 2.8. */ diff --git a/include/libiberty.h b/include/libiberty.h index a90b4ddcbe8..4aab80b2150 100644 --- a/include/libiberty.h +++ b/include/libiberty.h @@ -93,7 +93,7 @@ extern char *lrealpath PARAMS ((const char *)); the last argument of this function, to terminate the list of strings. Allocates memory using xmalloc. */ -extern char *concat PARAMS ((const char *, ...)) ATTRIBUTE_MALLOC; +extern char *concat PARAMS ((const char *, ...)) ATTRIBUTE_MALLOC ATTRIBUTE_SENTINEL; /* Concatenate an arbitrary number of strings. You must pass NULL as the last argument of this function, to terminate the list of @@ -102,27 +102,27 @@ extern char *concat PARAMS ((const char *, ...)) ATTRIBUTE_MALLOC; pointer to be freed after the new string is created, similar to the way xrealloc works. */ -extern char *reconcat PARAMS ((char *, const char *, ...)) ATTRIBUTE_MALLOC; +extern char *reconcat PARAMS ((char *, const char *, ...)) ATTRIBUTE_MALLOC ATTRIBUTE_SENTINEL; /* Determine the length of concatenating an arbitrary number of strings. You must pass NULL as the last argument of this function, to terminate the list of strings. */ -extern unsigned long concat_length PARAMS ((const char *, ...)); +extern unsigned long concat_length PARAMS ((const char *, ...)) ATTRIBUTE_SENTINEL; /* Concatenate an arbitrary number of strings into a SUPPLIED area of memory. You must pass NULL as the last argument of this function, to terminate the list of strings. The supplied memory is assumed to be large enough. */ -extern char *concat_copy PARAMS ((char *, const char *, ...)); +extern char *concat_copy PARAMS ((char *, const char *, ...)) ATTRIBUTE_SENTINEL; /* Concatenate an arbitrary number of strings into a GLOBAL area of memory. You must pass NULL as the last argument of this function, to terminate the list of strings. The supplied memory is assumed to be large enough. */ -extern char *concat_copy2 PARAMS ((const char *, ...)); +extern char *concat_copy2 PARAMS ((const char *, ...)) ATTRIBUTE_SENTINEL; /* This is the global area used by concat_copy2. */