diff options
author | Joseph Myers <jsm28@cam.ac.uk> | 2001-10-02 08:19:47 +0100 |
---|---|---|
committer | Joseph Myers <jsm28@gcc.gnu.org> | 2001-10-02 08:19:47 +0100 |
commit | 80a497e4e967ad5cdf59a1c4d2be2fdbba14ea3f (patch) | |
tree | 08f1b7a84cfb385dccaaf67b7162f37545d3fd09 /gcc/c-format.c | |
parent | b3b5d92c88835a1a64eae771baf053ff4c98dec7 (diff) | |
download | gcc-80a497e4e967ad5cdf59a1c4d2be2fdbba14ea3f.zip gcc-80a497e4e967ad5cdf59a1c4d2be2fdbba14ea3f.tar.gz gcc-80a497e4e967ad5cdf59a1c4d2be2fdbba14ea3f.tar.bz2 |
c-common.c (c_format_attribute_table): Make format and format_arg attributes apply to function types rather than to decls.
* c-common.c (c_format_attribute_table): Make format and
format_arg attributes apply to function types rather than to
decls.
(is_valid_printf_arglist): Construct an attribute list and pass
that to check_function_format rather than a name.
* c-common.h (check_function_format): Adjust prototype.
* c-decl.c (duplicate_decls): Preserve attributes from type of
built-in decl when allowing for harmless conflict in types.
* c-format.c (record_function_format,
record_international_format, function_format_list,
international_format_info, international_format_list): Remove.
(function_format_info): Remove next, name and assembler_name.
Make format_num and first_arg_num be unsigned HOST_WIDE_INT.
(decode_format_attr): New.
(handle_format_attribute): Handle receiving a type rather than a
decl. Call decode_format_attr. Store format information in a
function_format_info.
(handle_format_arg_attribute): Correct comment. Handle receiving
a type rather than a decl. Use unsigned HOST_WIDE_INT for
arg_num.
(check_format_info_recurse, check_format_info_main): Take argument
numbers as unsigned HOST_WIDE_INT.
(check_function_format): Take a list of attributes from the
function type rather than a name or assembler name. Check for
format attributes in that list and the attributes on the type of
the current function rather than looking through
function_format_list.
(check_format_info): Use unsigned HOST_WIDE_INT for argument
numbers.
(check_format_info_recurse): Take format_arg attributes from the
type of the function calls rather than using
international_format_list. Allow for multiple format_arg
attributes.
* c-typeck.c (build_function_call): Pass type attributes to
check_function_format rather than name or assembler name. Don't
require there to be a name or assembler name to check formats.
cp:
* call.c (build_over_call), typeck.c (build_function_call_real):
Pass type attributes to check_function_format rather than name or
assembler name. Don't require there to be a name or assembler
name to check formats.
testsuite:
* g++.dg/warn/format2.C, gcc.dg/format/attr-7.c,
gcc.dg/format/multattr-1.c, gcc.dg/format/multattr-2.c,
gcc.dg/format/multattr-3.c: New tests.
* gcc.dg/format/attr-3.c: Update expected error texts. Remove
tests for format attributes on function pointers being rejected.
From-SVN: r45945
Diffstat (limited to 'gcc/c-format.c')
-rw-r--r-- | gcc/c-format.c | 360 |
1 files changed, 142 insertions, 218 deletions
diff --git a/gcc/c-format.c b/gcc/c-format.c index 4ee3b3d..ea6ef57 100644 --- a/gcc/c-format.c +++ b/gcc/c-format.c @@ -77,10 +77,16 @@ enum format_type { printf_format_type, scanf_format_type, strftime_format_type, strfmon_format_type, format_type_error }; +typedef struct function_format_info +{ + enum format_type format_type; /* type of format (printf, scanf, etc.) */ + unsigned HOST_WIDE_INT format_num; /* number of format argument */ + unsigned HOST_WIDE_INT first_arg_num; /* number of first arg (zero for varargs) */ +} function_format_info; + +static bool decode_format_attr PARAMS ((tree, + function_format_info *, int)); static enum format_type decode_format_type PARAMS ((const char *)); -static void record_function_format PARAMS ((tree, tree, enum format_type, - int, int)); -static void record_international_format PARAMS ((tree, tree, int)); /* Handle a "format" attribute; arguments as in struct attribute_spec.handler. */ @@ -92,72 +98,13 @@ handle_format_attribute (node, name, args, flags, no_add_attrs) int flags; bool *no_add_attrs; { - tree decl = *node; - tree type = TREE_TYPE (decl); - tree format_type_id = TREE_VALUE (args); - tree format_num_expr = TREE_VALUE (TREE_CHAIN (args)); - tree first_arg_num_expr - = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (args))); - unsigned HOST_WIDE_INT format_num, first_arg_num; - enum format_type format_type; + tree type = *node; + function_format_info info; tree argument; - unsigned int arg_num; + unsigned HOST_WIDE_INT arg_num; - if (TREE_CODE (decl) != FUNCTION_DECL) + if (!decode_format_attr (args, &info, 0)) { - error_with_decl (decl, - "argument format specified for non-function `%s'"); - *no_add_attrs = true; - return NULL_TREE; - } - - if (TREE_CODE (format_type_id) != IDENTIFIER_NODE) - { - error ("unrecognized format specifier"); - *no_add_attrs = true; - return NULL_TREE; - } - else - { - const char *p = IDENTIFIER_POINTER (format_type_id); - - format_type = decode_format_type (p); - - if (format_type == format_type_error) - { - warning ("`%s' is an unrecognized format function type", p); - *no_add_attrs = true; - return NULL_TREE; - } - } - - /* Strip any conversions from the string index and first arg number - and verify they are constants. */ - while (TREE_CODE (format_num_expr) == NOP_EXPR - || TREE_CODE (format_num_expr) == CONVERT_EXPR - || TREE_CODE (format_num_expr) == NON_LVALUE_EXPR) - format_num_expr = TREE_OPERAND (format_num_expr, 0); - - while (TREE_CODE (first_arg_num_expr) == NOP_EXPR - || TREE_CODE (first_arg_num_expr) == CONVERT_EXPR - || TREE_CODE (first_arg_num_expr) == NON_LVALUE_EXPR) - first_arg_num_expr = TREE_OPERAND (first_arg_num_expr, 0); - - if (TREE_CODE (format_num_expr) != INTEGER_CST - || TREE_INT_CST_HIGH (format_num_expr) != 0 - || TREE_CODE (first_arg_num_expr) != INTEGER_CST - || TREE_INT_CST_HIGH (first_arg_num_expr) != 0) - { - error ("format string has invalid operand number"); - *no_add_attrs = true; - return NULL_TREE; - } - - format_num = TREE_INT_CST_LOW (format_num_expr); - first_arg_num = TREE_INT_CST_LOW (first_arg_num_expr); - if (first_arg_num != 0 && first_arg_num <= format_num) - { - error ("format string arg follows the args to be formatted"); *no_add_attrs = true; return NULL_TREE; } @@ -168,7 +115,7 @@ handle_format_attribute (node, name, args, flags, no_add_attrs) argument = TYPE_ARG_TYPES (type); if (argument) { - for (arg_num = 1; argument != 0 && arg_num != format_num; + for (arg_num = 1; argument != 0 && arg_num != info.format_num; ++arg_num, argument = TREE_CHAIN (argument)) ; @@ -183,14 +130,14 @@ handle_format_attribute (node, name, args, flags, no_add_attrs) return NULL_TREE; } - else if (first_arg_num != 0) + else if (info.first_arg_num != 0) { /* Verify that first_arg_num points to the last arg, the ... */ while (argument) arg_num++, argument = TREE_CHAIN (argument); - if (arg_num != first_arg_num) + if (arg_num != info.first_arg_num) { if (!(flags & (int) ATTR_FLAG_BUILT_IN)) error ("args to be formatted is not '...'"); @@ -200,20 +147,18 @@ handle_format_attribute (node, name, args, flags, no_add_attrs) } } - if (format_type == strftime_format_type && first_arg_num != 0) + if (info.format_type == strftime_format_type && info.first_arg_num != 0) { error ("strftime formats cannot format arguments"); *no_add_attrs = true; return NULL_TREE; } - record_function_format (DECL_NAME (decl), DECL_ASSEMBLER_NAME (decl), - format_type, format_num, first_arg_num); return NULL_TREE; } -/* Handle a "format" attribute; arguments as in +/* Handle a "format_arg" attribute; arguments as in struct attribute_spec.handler. */ tree handle_format_arg_attribute (node, name, args, flags, no_add_attrs) @@ -223,21 +168,12 @@ handle_format_arg_attribute (node, name, args, flags, no_add_attrs) int flags; bool *no_add_attrs; { - tree decl = *node; - tree type = TREE_TYPE (decl); + tree type = *node; tree format_num_expr = TREE_VALUE (args); unsigned HOST_WIDE_INT format_num; - unsigned int arg_num; + unsigned HOST_WIDE_INT arg_num; tree argument; - if (TREE_CODE (decl) != FUNCTION_DECL) - { - error_with_decl (decl, - "argument format specified for non-function `%s'"); - *no_add_attrs = true; - return NULL_TREE; - } - /* Strip any conversions from the first arg number and verify it is a constant. */ while (TREE_CODE (format_num_expr) == NOP_EXPR @@ -277,8 +213,8 @@ handle_format_arg_attribute (node, name, args, flags, no_add_attrs) } } - if (TREE_CODE (TREE_TYPE (TREE_TYPE (decl))) != POINTER_TYPE - || (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (TREE_TYPE (decl)))) + if (TREE_CODE (TREE_TYPE (type)) != POINTER_TYPE + || (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (type))) != char_type_node)) { if (!(flags & (int) ATTR_FLAG_BUILT_IN)) @@ -287,114 +223,85 @@ handle_format_arg_attribute (node, name, args, flags, no_add_attrs) return NULL_TREE; } - record_international_format (DECL_NAME (decl), DECL_ASSEMBLER_NAME (decl), - format_num); return NULL_TREE; } -typedef struct function_format_info -{ - struct function_format_info *next; /* next structure on the list */ - tree name; /* identifier such as "printf" */ - tree assembler_name; /* optional mangled identifier (for C++) */ - enum format_type format_type; /* type of format (printf, scanf, etc.) */ - int format_num; /* number of format argument */ - int first_arg_num; /* number of first arg (zero for varargs) */ -} function_format_info; - -static function_format_info *function_format_list = NULL; -typedef struct international_format_info -{ - struct international_format_info *next; /* next structure on the list */ - tree name; /* identifier such as "gettext" */ - tree assembler_name; /* optional mangled identifier (for C++) */ - int format_num; /* number of format argument */ -} international_format_info; - -static international_format_info *international_format_list = NULL; - -/* Record information for argument format checking. FUNCTION_IDENT is - the identifier node for the name of the function to check (its decl - need not exist yet). - FORMAT_TYPE specifies the type of format checking. FORMAT_NUM is the number - of the argument which is the format control string (starting from 1). - FIRST_ARG_NUM is the number of the first actual argument to check - against the format string, or zero if no checking is not be done - (e.g. for varargs such as vfprintf). */ +/* Decode the arguments to a "format" attribute into a function_format_info + structure. It is already known that the list is of the right length. + If VALIDATED_P is true, then these attributes have already been validated + and this function will abort if they are erroneous; if false, it + will give an error message. Returns true if the attributes are + successfully decoded, false otherwise. */ -static void -record_function_format (name, assembler_name, format_type, - format_num, first_arg_num) - tree name; - tree assembler_name; - enum format_type format_type; - int format_num; - int first_arg_num; +static bool +decode_format_attr (args, info, validated_p) + tree args; + function_format_info *info; + int validated_p; { - function_format_info *info; - - /* Re-use existing structure if it's there. */ + tree format_type_id = TREE_VALUE (args); + tree format_num_expr = TREE_VALUE (TREE_CHAIN (args)); + tree first_arg_num_expr + = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (args))); - for (info = function_format_list; info; info = info->next) + if (TREE_CODE (format_type_id) != IDENTIFIER_NODE) { - if (info->name == name && info->assembler_name == assembler_name) - break; + if (validated_p) + abort (); + error ("unrecognized format specifier"); + return false; } - if (! info) + else { - info = (function_format_info *) xmalloc (sizeof (function_format_info)); - info->next = function_format_list; - function_format_list = info; + const char *p = IDENTIFIER_POINTER (format_type_id); - info->name = name; - info->assembler_name = assembler_name; - } + info->format_type = decode_format_type (p); - info->format_type = format_type; - info->format_num = format_num; - info->first_arg_num = first_arg_num; -} - -/* Record information for the names of function that modify the format - argument to format functions. FUNCTION_IDENT is the identifier node for - the name of the function (its decl need not exist yet) and FORMAT_NUM is - the number of the argument which is the format control string (starting - from 1). */ + if (info->format_type == format_type_error) + { + if (validated_p) + abort (); + warning ("`%s' is an unrecognized format function type", p); + return false; + } + } -static void -record_international_format (name, assembler_name, format_num) - tree name; - tree assembler_name; - int format_num; -{ - international_format_info *info; + /* Strip any conversions from the string index and first arg number + and verify they are constants. */ + while (TREE_CODE (format_num_expr) == NOP_EXPR + || TREE_CODE (format_num_expr) == CONVERT_EXPR + || TREE_CODE (format_num_expr) == NON_LVALUE_EXPR) + format_num_expr = TREE_OPERAND (format_num_expr, 0); - /* Re-use existing structure if it's there. */ + while (TREE_CODE (first_arg_num_expr) == NOP_EXPR + || TREE_CODE (first_arg_num_expr) == CONVERT_EXPR + || TREE_CODE (first_arg_num_expr) == NON_LVALUE_EXPR) + first_arg_num_expr = TREE_OPERAND (first_arg_num_expr, 0); - for (info = international_format_list; info; info = info->next) + if (TREE_CODE (format_num_expr) != INTEGER_CST + || TREE_INT_CST_HIGH (format_num_expr) != 0 + || TREE_CODE (first_arg_num_expr) != INTEGER_CST + || TREE_INT_CST_HIGH (first_arg_num_expr) != 0) { - if (info->name == name && info->assembler_name == assembler_name) - break; + if (validated_p) + abort (); + error ("format string has invalid operand number"); + return false; } - if (! info) + info->format_num = TREE_INT_CST_LOW (format_num_expr); + info->first_arg_num = TREE_INT_CST_LOW (first_arg_num_expr); + if (info->first_arg_num != 0 && info->first_arg_num <= info->format_num) { - info - = (international_format_info *) - xmalloc (sizeof (international_format_info)); - info->next = international_format_list; - international_format_list = info; - - info->name = name; - info->assembler_name = assembler_name; + if (validated_p) + abort (); + error ("format string arg follows the args to be formatted"); + return false; } - info->format_num = format_num; + return true; } - - - /* Check a call to a format function against a parameter list. */ @@ -988,10 +895,11 @@ typedef struct static void check_format_info PARAMS ((int *, function_format_info *, tree)); static void check_format_info_recurse PARAMS ((int *, format_check_results *, function_format_info *, tree, - tree, int)); + tree, unsigned HOST_WIDE_INT)); static void check_format_info_main PARAMS ((int *, format_check_results *, function_format_info *, - const char *, int, tree, int)); + const char *, int, tree, + unsigned HOST_WIDE_INT)); static void status_warning PARAMS ((int *, const char *, ...)) ATTRIBUTE_PRINTF_2; @@ -1032,43 +940,42 @@ decode_format_type (s) /* Check the argument list of a call to printf, scanf, etc. - NAME is the function identifier. - ASSEMBLER_NAME is the function's assembler identifier. - (Either NAME or ASSEMBLER_NAME, but not both, may be NULL_TREE.) + ATTRS are the attributes on the function type. PARAMS is the list of argument values. Also, if -Wmissing-format-attribute, warn for calls to vprintf or vscanf in functions with no such format attribute themselves. */ void -check_function_format (status, name, assembler_name, params) +check_function_format (status, attrs, params) int *status; - tree name; - tree assembler_name; + tree attrs; tree params; { - function_format_info *info; + tree a; - /* See if this function is a format function. */ - for (info = function_format_list; info; info = info->next) + /* See if this function has any format attributes. */ + for (a = attrs; a; a = TREE_CHAIN (a)) { - if (info->assembler_name - ? (info->assembler_name == assembler_name) - : (info->name == name)) + if (is_attribute_p ("format", TREE_PURPOSE (a))) { /* Yup; check it. */ - check_format_info (status, info, params); - if (warn_missing_format_attribute && info->first_arg_num == 0 - && (format_types[info->format_type].flags + function_format_info info; + decode_format_attr (TREE_VALUE (a), &info, 1); + check_format_info (status, &info, params); + if (warn_missing_format_attribute && info.first_arg_num == 0 + && (format_types[info.format_type].flags & (int) FMT_FLAG_ARG_CONVERT)) { - function_format_info *info2; - for (info2 = function_format_list; info2; info2 = info2->next) - if ((info2->assembler_name - ? (info2->assembler_name == DECL_ASSEMBLER_NAME (current_function_decl)) - : (info2->name == DECL_NAME (current_function_decl))) - && info2->format_type == info->format_type) + tree c; + for (c = TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl)); + c; + c = TREE_CHAIN (c)) + if (is_attribute_p ("format", TREE_PURPOSE (c)) + && (decode_format_type (IDENTIFIER_POINTER + (TREE_VALUE (TREE_VALUE (c)))) + == info.format_type)) break; - if (info2 == NULL) + if (c == NULL_TREE) { /* Check if the current function has a parameter to which the format attribute could be attached; if not, it @@ -1086,10 +993,9 @@ check_function_format (status, name, assembler_name, params) } if (args != 0) warning ("function might be possible candidate for `%s' format attribute", - format_types[info->format_type].name); + format_types[info.format_type].name); } } - break; } } } @@ -1347,7 +1253,7 @@ check_format_info (status, info, params) function_format_info *info; tree params; { - int arg_num; + unsigned HOST_WIDE_INT arg_num; tree format_tree; format_check_results res; /* Skip to format argument. If the argument isn't available, there's @@ -1442,7 +1348,7 @@ check_format_info_recurse (status, res, info, format_tree, params, arg_num) function_format_info *info; tree format_tree; tree params; - int arg_num; + unsigned HOST_WIDE_INT arg_num; { int format_length; HOST_WIDE_INT offset; @@ -1459,40 +1365,58 @@ check_format_info_recurse (status, res, info, format_tree, params, arg_num) return; } - if (TREE_CODE (format_tree) == CALL_EXPR - && TREE_CODE (TREE_OPERAND (format_tree, 0)) == ADDR_EXPR - && (TREE_CODE (TREE_OPERAND (TREE_OPERAND (format_tree, 0), 0)) - == FUNCTION_DECL)) + if (TREE_CODE (format_tree) == CALL_EXPR) { - tree function = TREE_OPERAND (TREE_OPERAND (format_tree, 0), 0); + tree type = TREE_TYPE (TREE_TYPE (TREE_OPERAND (format_tree, 0))); + tree attrs; + bool found_format_arg = false; /* See if this is a call to a known internationalization function - that modifies the format arg. */ - international_format_info *iinfo; + that modifies the format arg. Such a function may have multiple + format_arg attributes (for example, ngettext). */ - for (iinfo = international_format_list; iinfo; iinfo = iinfo->next) - if (iinfo->assembler_name - ? (iinfo->assembler_name == DECL_ASSEMBLER_NAME (function)) - : (iinfo->name == DECL_NAME (function))) + for (attrs = TYPE_ATTRIBUTES (type); + attrs; + attrs = TREE_CHAIN (attrs)) + if (is_attribute_p ("format_arg", TREE_PURPOSE (attrs))) { tree inner_args; + tree format_num_expr; + int format_num; int i; + /* Extract the argument number, which was previously checked + to be valid. */ + format_num_expr = TREE_VALUE (TREE_VALUE (attrs)); + while (TREE_CODE (format_num_expr) == NOP_EXPR + || TREE_CODE (format_num_expr) == CONVERT_EXPR + || TREE_CODE (format_num_expr) == NON_LVALUE_EXPR) + format_num_expr = TREE_OPERAND (format_num_expr, 0); + + if (TREE_CODE (format_num_expr) != INTEGER_CST + || TREE_INT_CST_HIGH (format_num_expr) != 0) + abort (); + + format_num = TREE_INT_CST_LOW (format_num_expr); + for (inner_args = TREE_OPERAND (format_tree, 1), i = 1; inner_args != 0; inner_args = TREE_CHAIN (inner_args), i++) - if (i == iinfo->format_num) + if (i == format_num) { - /* FIXME: with Marc Espie's __attribute__((nonnull)) - patch in GCC, we will have chained attributes, - and be able to handle functions like ngettext - with multiple format_arg attributes properly. */ check_format_info_recurse (status, res, info, TREE_VALUE (inner_args), params, arg_num); - return; + found_format_arg = true; + break; } } + + /* If we found a format_arg attribute and did a recursive check, + we are done with checking this format string. Otherwise, we + continue and this will count as a non-literal format string. */ + if (found_format_arg) + return; } if (TREE_CODE (format_tree) == COND_EXPR) @@ -1666,7 +1590,7 @@ check_format_info_main (status, res, info, format_chars, format_length, const char *format_chars; int format_length; tree params; - int arg_num; + unsigned HOST_WIDE_INT arg_num; { const char *orig_format_chars = format_chars; tree first_fillin_param = params; |