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
This commit is contained in:
Joseph Myers 2001-10-02 08:19:47 +01:00 committed by Joseph Myers
parent b3b5d92c88
commit 80a497e4e9
16 changed files with 421 additions and 255 deletions

View file

@ -1,3 +1,42 @@
2001-10-02 Joseph S. Myers <jsm28@cam.ac.uk>
* 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 <jsm28@cam.ac.uk>
* attribs.c (decl_attributes): Possibly call

View file

@ -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;

View file

@ -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 *));

View file

@ -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)

View file

@ -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
/* 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 bool
decode_format_attr (args, info, validated_p)
tree args;
function_format_info *info;
int validated_p;
{
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;
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)));
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). */
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;
{
function_format_info *info;
/* Re-use existing structure if it's there. */
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);
if (info->format_type == format_type_error)
{
if (validated_p)
abort ();
warning ("`%s' is an unrecognized format function type", p);
return false;
}
}
info->format_type = format_type;
info->format_num = format_num;
info->first_arg_num = first_arg_num;
/* 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)
{
if (validated_p)
abort ();
error ("format string has invalid operand number");
return false;
}
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)
{
if (validated_p)
abort ();
error ("format string arg follows the args to be formatted");
return false;
}
return true;
}
/* 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). */
static void
record_international_format (name, assembler_name, format_num)
tree name;
tree assembler_name;
int format_num;
{
international_format_info *info;
/* Re-use existing structure if it's there. */
for (info = international_format_list; info; info = info->next)
{
if (info->name == name && info->assembler_name == assembler_name)
break;
}
if (! info)
{
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;
}
info->format_num = format_num;
}
/* 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;

View file

@ -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

View file

@ -1,3 +1,10 @@
2001-10-02 Joseph S. Myers <jsm28@cam.ac.uk>
* 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 <jsm28@cam.ac.uk>
* decl.c (init_decl_processing): Don't call

View file

@ -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. */

View file

@ -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

View file

@ -1,3 +1,11 @@
2001-10-02 Joseph S. Myers <jsm28@cam.ac.uk>
* 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 <jsm28@cam.ac.uk>
* gcc.dg/format/attr-5.c, gcc.dg/format/attr-6.c: New tests.

View file

@ -0,0 +1,32 @@
// Test for format attributes: test applying them to types in C++.
// Origin: Joseph Myers <jsm28@cam.ac.uk>
// { 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" }
}

View file

@ -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-<num>.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" } */

View file

@ -0,0 +1,34 @@
/* Test for format attributes: test applying them to types. */
/* Origin: Joseph Myers <jsm28@cam.ac.uk> */
/* { 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" } */
}

View file

@ -0,0 +1,50 @@
/* Test for multiple format attributes. Test for printf and scanf attributes
together. */
/* Origin: Joseph Myers <jsm28@cam.ac.uk> */
/* { 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" } */
}

View file

@ -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 <jsm28@cam.ac.uk> */
/* { 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" } */
}

View file

@ -0,0 +1,28 @@
/* Test for multiple format_arg attributes. Test for both branches
getting checked. */
/* Origin: Joseph Myers <jsm28@cam.ac.uk> */
/* { 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" } */
}