From e9a0f285818ea3e0f7f5630ecb22d4b358f7eeed Mon Sep 17 00:00:00 2001 From: jsm28 Date: Tue, 2 Oct 2001 07:19:47 +0000 Subject: [PATCH] * 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. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@45945 138bc75d-0d04-0410-961f-82ee72b054a4 --- gcc/ChangeLog | 39 ++++ gcc/c-common.c | 19 +- gcc/c-common.h | 2 +- gcc/c-decl.c | 4 + gcc/c-format.c | 360 ++++++++++++------------------- gcc/c-typeck.c | 4 +- gcc/cp/ChangeLog | 7 + gcc/cp/call.c | 6 +- gcc/cp/typeck.c | 4 +- gcc/testsuite/ChangeLog | 8 + gcc/testsuite/g++.dg/warn/format2.C | 32 +++ gcc/testsuite/gcc.dg/format/attr-3.c | 20 +- gcc/testsuite/gcc.dg/format/attr-7.c | 34 +++ gcc/testsuite/gcc.dg/format/multattr-1.c | 50 +++++ gcc/testsuite/gcc.dg/format/multattr-2.c | 39 ++++ gcc/testsuite/gcc.dg/format/multattr-3.c | 28 +++ 16 files changed, 411 insertions(+), 245 deletions(-) create mode 100644 gcc/testsuite/g++.dg/warn/format2.C create mode 100644 gcc/testsuite/gcc.dg/format/attr-7.c create mode 100644 gcc/testsuite/gcc.dg/format/multattr-1.c create mode 100644 gcc/testsuite/gcc.dg/format/multattr-2.c create mode 100644 gcc/testsuite/gcc.dg/format/multattr-3.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 7a10bfa342c..72e101e6d4f 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,5 +1,44 @@ 2001-10-02 Joseph S. Myers + * 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. + +2001-10-02 Joseph S. Myers + * attribs.c (decl_attributes): Possibly call insert_default_attributes to insert default attributes on functions in a lazy manner. diff --git a/gcc/c-common.c b/gcc/c-common.c index a3314ff1fac..e6c81c385c0 100644 --- a/gcc/c-common.c +++ b/gcc/c-common.c @@ -2329,9 +2329,10 @@ c_alignof_expr (expr) static const struct attribute_spec c_format_attribute_table[] = { - { "format", 3, 3, true, false, false, + /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */ + { "format", 3, 3, false, true, true, handle_format_attribute }, - { "format_arg", 1, 1, true, false, false, + { "format_arg", 1, 1, false, true, true, handle_format_arg_attribute }, { NULL, 0, 0, false, false, false, NULL } }; @@ -3551,14 +3552,22 @@ is_valid_printf_arglist (arglist) /* Save this value so we can restore it later. */ const int SAVE_pedantic = pedantic; int diagnostic_occurred = 0; + tree attrs; /* Set this to a known value so the user setting won't affect code generation. */ pedantic = 1; /* Check to make sure there are no format specifier errors. */ - check_function_format (&diagnostic_occurred, - maybe_get_identifier("printf"), - NULL_TREE, arglist); + attrs = tree_cons (get_identifier ("format"), + tree_cons (NULL_TREE, + get_identifier ("printf"), + tree_cons (NULL_TREE, + integer_one_node, + tree_cons (NULL_TREE, + build_int_2 (2, 0), + NULL_TREE))), + NULL_TREE); + check_function_format (&diagnostic_occurred, attrs, arglist); /* Restore the value of `pedantic'. */ pedantic = SAVE_pedantic; diff --git a/gcc/c-common.h b/gcc/c-common.h index 66df266a1e0..aadbee8f3c4 100644 --- a/gcc/c-common.h +++ b/gcc/c-common.h @@ -503,7 +503,7 @@ extern const char *fname_as_string PARAMS ((int)); extern tree fname_decl PARAMS ((unsigned, tree)); extern const char *fname_string PARAMS ((unsigned)); -extern void check_function_format PARAMS ((int *, tree, tree, tree)); +extern void check_function_format PARAMS ((int *, tree, tree)); extern void set_Wformat PARAMS ((int)); extern tree handle_format_attribute PARAMS ((tree *, tree, tree, int, bool *)); diff --git a/gcc/c-decl.c b/gcc/c-decl.c index f3ad82ea79c..ac09d264db7 100644 --- a/gcc/c-decl.c +++ b/gcc/c-decl.c @@ -1498,6 +1498,8 @@ duplicate_decls (newdecl, olddecl, different_binding_level) tree trytype = build_function_type (newreturntype, TYPE_ARG_TYPES (oldtype)); + trytype = build_type_attribute_variant (trytype, + TYPE_ATTRIBUTES (oldtype)); types_match = comptypes (newtype, trytype); if (types_match) @@ -1519,6 +1521,8 @@ duplicate_decls (newdecl, olddecl, different_binding_level) tree_cons (NULL_TREE, TREE_VALUE (TYPE_ARG_TYPES (newtype)), TREE_CHAIN (TYPE_ARG_TYPES (oldtype)))); + trytype = build_type_attribute_variant (trytype, + TYPE_ATTRIBUTES (oldtype)); types_match = comptypes (newtype, trytype); if (types_match) diff --git a/gcc/c-format.c b/gcc/c-format.c index 4ee3b3d44a2..ea6ef57fa6d 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; diff --git a/gcc/c-typeck.c b/gcc/c-typeck.c index c407f16d640..09ebf4d4b9f 100644 --- a/gcc/c-typeck.c +++ b/gcc/c-typeck.c @@ -1510,8 +1510,8 @@ build_function_call (function, params) /* Check for errors in format strings. */ - if (warn_format && (name || assembler_name)) - check_function_format (NULL, name, assembler_name, coerced_params); + if (warn_format) + check_function_format (NULL, TYPE_ATTRIBUTES (fntype), coerced_params); /* Recognize certain built-in functions so we can make tree-codes other than CALL_EXPR. We do this when it enables fold-const.c diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 6e3ebaf2a08..54db0bdd9e3 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,5 +1,12 @@ 2001-10-02 Joseph S. Myers + * 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. + +2001-10-02 Joseph S. Myers + * decl.c (init_decl_processing): Don't call init_function_format_info. Initialize lang_attribute_table earlier. diff --git a/gcc/cp/call.c b/gcc/cp/call.c index 42731487892..437d67cf5b8 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -4204,9 +4204,9 @@ build_over_call (cand, args, flags) converted_args = nreverse (converted_args); - if (warn_format && (DECL_NAME (fn) || DECL_ASSEMBLER_NAME (fn))) - check_function_format (NULL, DECL_NAME (fn), DECL_ASSEMBLER_NAME (fn), - converted_args); + if (warn_format) + check_function_format (NULL, TYPE_ATTRIBUTES (TREE_TYPE (fn)), + converted_args); /* Avoid actually calling copy constructors and copy assignment operators, if possible. */ diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c index 54dc0ed3df5..c57bb08ab3e 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -3035,8 +3035,8 @@ build_function_call_real (function, params, require_complete, flags) /* Check for errors in format strings. */ - if (warn_format && (name || assembler_name)) - check_function_format (NULL, name, assembler_name, coerced_params); + if (warn_format) + check_function_format (NULL, TYPE_ATTRIBUTES (fntype), coerced_params); /* Recognize certain built-in functions so we can make tree-codes other than CALL_EXPR. We do this when it enables fold-const.c diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 3c027b7319b..087feed8da1 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,5 +1,13 @@ 2001-10-02 Joseph S. Myers + * 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. + +2001-10-02 Joseph S. Myers + * gcc.dg/format/attr-5.c, gcc.dg/format/attr-6.c: New tests. 2001-10-02 Joseph S. Myers diff --git a/gcc/testsuite/g++.dg/warn/format2.C b/gcc/testsuite/g++.dg/warn/format2.C new file mode 100644 index 00000000000..639bc668bc5 --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/format2.C @@ -0,0 +1,32 @@ +// Test for format attributes: test applying them to types in C++. +// Origin: Joseph Myers +// { dg-do compile } +// { dg-options "-Wformat" } + +__attribute__((format(printf, 1, 2))) void (*tformatprintf0) (const char *, ...); +void (*tformatprintf1) (const char *, ...) __attribute__((format(printf, 1, 2))); +void (__attribute__((format(printf, 1, 2))) *tformatprintf2) (const char *, ...); +void (__attribute__((format(printf, 1, 2))) ****tformatprintf3) (const char *, ...); + +char * (__attribute__((format_arg(1))) *tformat_arg) (const char *); + +void +baz (int i) +{ + (*tformatprintf0) ("%d", i); + (*tformatprintf0) ((*tformat_arg) ("%d"), i); + (*tformatprintf0) ("%"); // { dg-warning "format" "prefix" } + (*tformatprintf0) ((*tformat_arg) ("%")); // { dg-warning "format" "prefix" } + (*tformatprintf1) ("%d", i); + (*tformatprintf1) ((*tformat_arg) ("%d"), i); + (*tformatprintf1) ("%"); // { dg-warning "format" "postfix" } + (*tformatprintf1) ((*tformat_arg) ("%")); // { dg-warning "format" "postfix" } + (*tformatprintf2) ("%d", i); + (*tformatprintf2) ((*tformat_arg) ("%d"), i); + (*tformatprintf2) ("%"); // { dg-warning "format" "nested" } + (*tformatprintf2) ((*tformat_arg) ("%")); // { dg-warning "format" "nested" } + (****tformatprintf3) ("%d", i); + (****tformatprintf3) ((*tformat_arg) ("%d"), i); + (****tformatprintf3) ("%"); // { dg-warning "format" "nested 2" } + (****tformatprintf3) ((*tformat_arg) ("%")); // { dg-warning "format" "nested 2" } +} diff --git a/gcc/testsuite/gcc.dg/format/attr-3.c b/gcc/testsuite/gcc.dg/format/attr-3.c index 2e3c6326a9b..9627497ea52 100644 --- a/gcc/testsuite/gcc.dg/format/attr-3.c +++ b/gcc/testsuite/gcc.dg/format/attr-3.c @@ -25,21 +25,13 @@ extern void fc3 (const char *) __attribute__((format_arg(1, 2))); /* { dg-error /* These attributes presently only apply to declarations, not to types. Eventually, they should be usable with declarators for function types anywhere, but still not with structure/union/enum types. */ -struct s0 { int i; } __attribute__((format(printf, 1, 2))); /* { dg-error "does not apply" "format on struct" } */ -union u0 { int i; } __attribute__((format(printf, 1, 2))); /* { dg-error "does not apply" "format on union" } */ -enum e0 { E0V0 } __attribute__((format(printf, 1, 2))); /* { dg-error "does not apply" "format on enum" } */ +struct s0 { int i; } __attribute__((format(printf, 1, 2))); /* { dg-error "does not apply|only applies" "format on struct" } */ +union u0 { int i; } __attribute__((format(printf, 1, 2))); /* { dg-error "does not apply|only applies" "format on union" } */ +enum e0 { E0V0 } __attribute__((format(printf, 1, 2))); /* { dg-error "does not apply|only applies" "format on enum" } */ -struct s1 { int i; } __attribute__((format_arg(1))); /* { dg-error "does not apply" "format_arg on struct" } */ -union u1 { int i; } __attribute__((format_arg(1))); /* { dg-error "does not apply" "format_arg on union" } */ -enum e1 { E1V0 } __attribute__((format_arg(1))); /* { dg-error "does not apply" "format_arg on enum" } */ - -/* At present, only functions can be declared with these attributes. - Once they can be applied to function types in function pointers, etc., - these tests should be removed, and tests should be added (say in a new - testcase attr-.c) that such attributes work and calls through such - function pointers (etc.) get checked. */ -extern void (*fd0) (const char *, ...) __attribute__((format(printf, 1, 2))); /* { dg-error "non-function" "format on non-function" } */ -extern char *(*fd1) (const char *) __attribute__((format_arg(1))); /* { dg-error "non-function" "format on non-function" } */ +struct s1 { int i; } __attribute__((format_arg(1))); /* { dg-error "does not apply|only applies" "format_arg on struct" } */ +union u1 { int i; } __attribute__((format_arg(1))); /* { dg-error "does not apply|only applies" "format_arg on union" } */ +enum e1 { E1V0 } __attribute__((format_arg(1))); /* { dg-error "does not apply|only applies" "format_arg on enum" } */ /* The format type must be an identifier, one of those recognised. */ extern void fe0 (const char *, ...) __attribute__((format(12345, 1, 2))); /* { dg-error "format specifier" "non-id format" } */ diff --git a/gcc/testsuite/gcc.dg/format/attr-7.c b/gcc/testsuite/gcc.dg/format/attr-7.c new file mode 100644 index 00000000000..78526a5e9bb --- /dev/null +++ b/gcc/testsuite/gcc.dg/format/attr-7.c @@ -0,0 +1,34 @@ +/* Test for format attributes: test applying them to types. */ +/* Origin: Joseph Myers */ +/* { dg-do compile } */ +/* { dg-options "-std=gnu99 -Wformat" } */ + +#include "format.h" + +__attribute__((format(printf, 1, 2))) void (*tformatprintf0) (const char *, ...); +void (*tformatprintf1) (const char *, ...) __attribute__((format(printf, 1, 2))); +void (__attribute__((format(printf, 1, 2))) *tformatprintf2) (const char *, ...); +void (__attribute__((format(printf, 1, 2))) ****tformatprintf3) (const char *, ...); + +char * (__attribute__((format_arg(1))) *tformat_arg) (const char *); + +void +baz (int i) +{ + (*tformatprintf0) ("%d", i); + (*tformatprintf0) ((*tformat_arg) ("%d"), i); + (*tformatprintf0) ("%"); /* { dg-warning "format" "prefix" } */ + (*tformatprintf0) ((*tformat_arg) ("%")); /* { dg-warning "format" "prefix" } */ + (*tformatprintf1) ("%d", i); + (*tformatprintf1) ((*tformat_arg) ("%d"), i); + (*tformatprintf1) ("%"); /* { dg-warning "format" "postfix" } */ + (*tformatprintf1) ((*tformat_arg) ("%")); /* { dg-warning "format" "postfix" } */ + (*tformatprintf2) ("%d", i); + (*tformatprintf2) ((*tformat_arg) ("%d"), i); + (*tformatprintf2) ("%"); /* { dg-warning "format" "nested" } */ + (*tformatprintf2) ((*tformat_arg) ("%")); /* { dg-warning "format" "nested" } */ + (****tformatprintf3) ("%d", i); + (****tformatprintf3) ((*tformat_arg) ("%d"), i); + (****tformatprintf3) ("%"); /* { dg-warning "format" "nested 2" } */ + (****tformatprintf3) ((*tformat_arg) ("%")); /* { dg-warning "format" "nested 2" } */ +} diff --git a/gcc/testsuite/gcc.dg/format/multattr-1.c b/gcc/testsuite/gcc.dg/format/multattr-1.c new file mode 100644 index 00000000000..7691aba5bd8 --- /dev/null +++ b/gcc/testsuite/gcc.dg/format/multattr-1.c @@ -0,0 +1,50 @@ +/* Test for multiple format attributes. Test for printf and scanf attributes + together. */ +/* Origin: Joseph Myers */ +/* { dg-do compile } */ +/* { dg-options "-std=gnu99 -Wformat" } */ + +#include "format.h" + +/* If we specify multiple attributes for a single function, they should + all apply. This should apply whether they are on the same declaration + or on different declarations. */ + +extern void my_vprintf_scanf (const char *, va_list, const char *, ...) + __attribute__((__format__(__printf__, 1, 0))) + __attribute__((__format__(__scanf__, 3, 4))); + +extern void my_vprintf_scanf2 (const char *, va_list, const char *, ...) + __attribute__((__format__(__scanf__, 3, 4))) + __attribute__((__format__(__printf__, 1, 0))); + +extern void my_vprintf_scanf3 (const char *, va_list, const char *, ...) + __attribute__((__format__(__printf__, 1, 0))); +extern void my_vprintf_scanf3 (const char *, va_list, const char *, ...) + __attribute__((__format__(__scanf__, 3, 4))); + +extern void my_vprintf_scanf4 (const char *, va_list, const char *, ...) + __attribute__((__format__(__scanf__, 3, 4))); +extern void my_vprintf_scanf4 (const char *, va_list, const char *, ...) + __attribute__((__format__(__printf__, 1, 0))); + +void +foo (va_list ap, int *ip, long *lp) +{ + my_vprintf_scanf ("%d", ap, "%d", ip); + my_vprintf_scanf ("%d", ap, "%ld", lp); + my_vprintf_scanf ("%", ap, "%d", ip); /* { dg-warning "format" "printf" } */ + my_vprintf_scanf ("%d", ap, "%ld", ip); /* { dg-warning "format" "scanf" } */ + my_vprintf_scanf2 ("%d", ap, "%d", ip); + my_vprintf_scanf2 ("%d", ap, "%ld", lp); + my_vprintf_scanf2 ("%", ap, "%d", ip); /* { dg-warning "format" "printf" } */ + my_vprintf_scanf2 ("%d", ap, "%ld", ip); /* { dg-warning "format" "scanf" } */ + my_vprintf_scanf3 ("%d", ap, "%d", ip); + my_vprintf_scanf3 ("%d", ap, "%ld", lp); + my_vprintf_scanf3 ("%", ap, "%d", ip); /* { dg-warning "format" "printf" } */ + my_vprintf_scanf3 ("%d", ap, "%ld", ip); /* { dg-warning "format" "scanf" } */ + my_vprintf_scanf4 ("%d", ap, "%d", ip); + my_vprintf_scanf4 ("%d", ap, "%ld", lp); + my_vprintf_scanf4 ("%", ap, "%d", ip); /* { dg-warning "format" "printf" } */ + my_vprintf_scanf4 ("%d", ap, "%ld", ip); /* { dg-warning "format" "scanf" } */ +} diff --git a/gcc/testsuite/gcc.dg/format/multattr-2.c b/gcc/testsuite/gcc.dg/format/multattr-2.c new file mode 100644 index 00000000000..1d78840aecb --- /dev/null +++ b/gcc/testsuite/gcc.dg/format/multattr-2.c @@ -0,0 +1,39 @@ +/* Test for multiple format attributes. Test for printf and scanf attributes + together, in different places on the declarations. */ +/* Origin: Joseph Myers */ +/* { dg-do compile } */ +/* { dg-options "-std=gnu99 -Wformat" } */ + +#include "format.h" + +/* If we specify multiple attributes for a single function, they should + all apply, wherever they are placed on the declarations. */ + +extern __attribute__((__format__(__printf__, 1, 0))) void + my_vprintf_scanf (const char *, va_list, const char *, ...) + __attribute__((__format__(__scanf__, 3, 4))); + +extern void (__attribute__((__format__(__printf__, 1, 0))) my_vprintf_scanf2) + (const char *, va_list, const char *, ...) + __attribute__((__format__(__scanf__, 3, 4))); + +extern __attribute__((__format__(__scanf__, 3, 4))) void + (__attribute__((__format__(__printf__, 1, 0))) my_vprintf_scanf3) + (const char *, va_list, const char *, ...); + +void +foo (va_list ap, int *ip, long *lp) +{ + my_vprintf_scanf ("%d", ap, "%d", ip); + my_vprintf_scanf ("%d", ap, "%ld", lp); + my_vprintf_scanf ("%", ap, "%d", ip); /* { dg-warning "format" "printf" } */ + my_vprintf_scanf ("%d", ap, "%ld", ip); /* { dg-warning "format" "scanf" } */ + my_vprintf_scanf2 ("%d", ap, "%d", ip); + my_vprintf_scanf2 ("%d", ap, "%ld", lp); + my_vprintf_scanf2 ("%", ap, "%d", ip); /* { dg-warning "format" "printf" } */ + my_vprintf_scanf2 ("%d", ap, "%ld", ip); /* { dg-warning "format" "scanf" } */ + my_vprintf_scanf3 ("%d", ap, "%d", ip); + my_vprintf_scanf3 ("%d", ap, "%ld", lp); + my_vprintf_scanf3 ("%", ap, "%d", ip); /* { dg-warning "format" "printf" } */ + my_vprintf_scanf3 ("%d", ap, "%ld", ip); /* { dg-warning "format" "scanf" } */ +} diff --git a/gcc/testsuite/gcc.dg/format/multattr-3.c b/gcc/testsuite/gcc.dg/format/multattr-3.c new file mode 100644 index 00000000000..40467fe2d4c --- /dev/null +++ b/gcc/testsuite/gcc.dg/format/multattr-3.c @@ -0,0 +1,28 @@ +/* Test for multiple format_arg attributes. Test for both branches + getting checked. */ +/* Origin: Joseph Myers */ +/* { dg-do compile } */ +/* { dg-options "-std=gnu99 -Wformat" } */ + +#include "format.h" + +extern char *ngettext (const char *, const char *, unsigned long int) + __attribute__((__format_arg__(1))) __attribute__((__format_arg__(2))); + +void +foo (long l, int nfoo) +{ + printf (ngettext ("%d foo", "%d foos", nfoo), nfoo); + printf (ngettext ("%d foo", "%d foos", l), l); /* { dg-warning "int format" "wrong type in conditional expr" } */ + printf (ngettext ("%d foo", "%ld foos", l), l); /* { dg-warning "int format" "wrong type in conditional expr" } */ + printf (ngettext ("%ld foo", "%d foos", l), l); /* { dg-warning "int format" "wrong type in conditional expr" } */ + /* Should allow one case to have extra arguments. */ + printf (ngettext ("1 foo", "%d foos", nfoo), nfoo); + printf (ngettext ("1 foo", "many foos", nfoo), nfoo); /* { dg-warning "too many" "too many args in all branches" } */ + printf (ngettext ("", "%d foos", nfoo), nfoo); + printf (ngettext ("1 foo", (nfoo > 0) ? "%d foos" : "no foos", nfoo), nfoo); + printf (ngettext ("%d foo", (nfoo > 0) ? "%d foos" : "no foos", nfoo), nfoo); + printf (ngettext ("%ld foo", (nfoo > 0) ? "%d foos" : "no foos", nfoo), nfoo); /* { dg-warning "long int format" "wrong type" } */ + printf (ngettext ("%d foo", (nfoo > 0) ? "%ld foos" : "no foos", nfoo), nfoo); /* { dg-warning "long int format" "wrong type" } */ + printf (ngettext ("%d foo", (nfoo > 0) ? "%d foos" : "%ld foos", nfoo), nfoo); /* { dg-warning "long int format" "wrong type" } */ +} -- 2.11.4.GIT