Commit 80a497e4 by Joseph Myers Committed by Joseph Myers

c-common.c (c_format_attribute_table): Make format and format_arg attributes…

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
parent b3b5d92c
2001-10-02 Joseph S. Myers <jsm28@cam.ac.uk> 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 * attribs.c (decl_attributes): Possibly call
insert_default_attributes to insert default attributes on insert_default_attributes to insert default attributes on
functions in a lazy manner. functions in a lazy manner.
......
...@@ -2329,9 +2329,10 @@ c_alignof_expr (expr) ...@@ -2329,9 +2329,10 @@ c_alignof_expr (expr)
static const struct attribute_spec c_format_attribute_table[] = 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 }, handle_format_attribute },
{ "format_arg", 1, 1, true, false, false, { "format_arg", 1, 1, false, true, true,
handle_format_arg_attribute }, handle_format_arg_attribute },
{ NULL, 0, 0, false, false, false, NULL } { NULL, 0, 0, false, false, false, NULL }
}; };
...@@ -3551,14 +3552,22 @@ is_valid_printf_arglist (arglist) ...@@ -3551,14 +3552,22 @@ is_valid_printf_arglist (arglist)
/* Save this value so we can restore it later. */ /* Save this value so we can restore it later. */
const int SAVE_pedantic = pedantic; const int SAVE_pedantic = pedantic;
int diagnostic_occurred = 0; int diagnostic_occurred = 0;
tree attrs;
/* Set this to a known value so the user setting won't affect code /* Set this to a known value so the user setting won't affect code
generation. */ generation. */
pedantic = 1; pedantic = 1;
/* Check to make sure there are no format specifier errors. */ /* Check to make sure there are no format specifier errors. */
check_function_format (&diagnostic_occurred, attrs = tree_cons (get_identifier ("format"),
maybe_get_identifier("printf"), tree_cons (NULL_TREE,
NULL_TREE, arglist); 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'. */ /* Restore the value of `pedantic'. */
pedantic = SAVE_pedantic; pedantic = SAVE_pedantic;
......
...@@ -503,7 +503,7 @@ extern const char *fname_as_string PARAMS ((int)); ...@@ -503,7 +503,7 @@ extern const char *fname_as_string PARAMS ((int));
extern tree fname_decl PARAMS ((unsigned, tree)); extern tree fname_decl PARAMS ((unsigned, tree));
extern const char *fname_string PARAMS ((unsigned)); 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 void set_Wformat PARAMS ((int));
extern tree handle_format_attribute PARAMS ((tree *, tree, tree, extern tree handle_format_attribute PARAMS ((tree *, tree, tree,
int, bool *)); int, bool *));
......
...@@ -1498,6 +1498,8 @@ duplicate_decls (newdecl, olddecl, different_binding_level) ...@@ -1498,6 +1498,8 @@ duplicate_decls (newdecl, olddecl, different_binding_level)
tree trytype tree trytype
= build_function_type (newreturntype, = build_function_type (newreturntype,
TYPE_ARG_TYPES (oldtype)); TYPE_ARG_TYPES (oldtype));
trytype = build_type_attribute_variant (trytype,
TYPE_ATTRIBUTES (oldtype));
types_match = comptypes (newtype, trytype); types_match = comptypes (newtype, trytype);
if (types_match) if (types_match)
...@@ -1519,6 +1521,8 @@ duplicate_decls (newdecl, olddecl, different_binding_level) ...@@ -1519,6 +1521,8 @@ duplicate_decls (newdecl, olddecl, different_binding_level)
tree_cons (NULL_TREE, tree_cons (NULL_TREE,
TREE_VALUE (TYPE_ARG_TYPES (newtype)), TREE_VALUE (TYPE_ARG_TYPES (newtype)),
TREE_CHAIN (TYPE_ARG_TYPES (oldtype)))); TREE_CHAIN (TYPE_ARG_TYPES (oldtype))));
trytype = build_type_attribute_variant (trytype,
TYPE_ATTRIBUTES (oldtype));
types_match = comptypes (newtype, trytype); types_match = comptypes (newtype, trytype);
if (types_match) if (types_match)
......
...@@ -77,10 +77,16 @@ enum format_type { printf_format_type, scanf_format_type, ...@@ -77,10 +77,16 @@ enum format_type { printf_format_type, scanf_format_type,
strftime_format_type, strfmon_format_type, strftime_format_type, strfmon_format_type,
format_type_error }; 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 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 /* Handle a "format" attribute; arguments as in
struct attribute_spec.handler. */ struct attribute_spec.handler. */
...@@ -92,72 +98,13 @@ handle_format_attribute (node, name, args, flags, no_add_attrs) ...@@ -92,72 +98,13 @@ handle_format_attribute (node, name, args, flags, no_add_attrs)
int flags; int flags;
bool *no_add_attrs; bool *no_add_attrs;
{ {
tree decl = *node; tree type = *node;
tree type = TREE_TYPE (decl); function_format_info 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)));
unsigned HOST_WIDE_INT format_num, first_arg_num;
enum format_type format_type;
tree argument; 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; *no_add_attrs = true;
return NULL_TREE; return NULL_TREE;
} }
...@@ -168,7 +115,7 @@ handle_format_attribute (node, name, args, flags, no_add_attrs) ...@@ -168,7 +115,7 @@ handle_format_attribute (node, name, args, flags, no_add_attrs)
argument = TYPE_ARG_TYPES (type); argument = TYPE_ARG_TYPES (type);
if (argument) 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)) ++arg_num, argument = TREE_CHAIN (argument))
; ;
...@@ -183,14 +130,14 @@ handle_format_attribute (node, name, args, flags, no_add_attrs) ...@@ -183,14 +130,14 @@ handle_format_attribute (node, name, args, flags, no_add_attrs)
return NULL_TREE; 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, /* Verify that first_arg_num points to the last arg,
the ... */ the ... */
while (argument) while (argument)
arg_num++, argument = TREE_CHAIN (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)) if (!(flags & (int) ATTR_FLAG_BUILT_IN))
error ("args to be formatted is not '...'"); error ("args to be formatted is not '...'");
...@@ -200,20 +147,18 @@ handle_format_attribute (node, name, args, flags, no_add_attrs) ...@@ -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"); error ("strftime formats cannot format arguments");
*no_add_attrs = true; *no_add_attrs = true;
return NULL_TREE; return NULL_TREE;
} }
record_function_format (DECL_NAME (decl), DECL_ASSEMBLER_NAME (decl),
format_type, format_num, first_arg_num);
return NULL_TREE; return NULL_TREE;
} }
/* Handle a "format" attribute; arguments as in /* Handle a "format_arg" attribute; arguments as in
struct attribute_spec.handler. */ struct attribute_spec.handler. */
tree tree
handle_format_arg_attribute (node, name, args, flags, no_add_attrs) 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) ...@@ -223,21 +168,12 @@ handle_format_arg_attribute (node, name, args, flags, no_add_attrs)
int flags; int flags;
bool *no_add_attrs; bool *no_add_attrs;
{ {
tree decl = *node; tree type = *node;
tree type = TREE_TYPE (decl);
tree format_num_expr = TREE_VALUE (args); tree format_num_expr = TREE_VALUE (args);
unsigned HOST_WIDE_INT format_num; unsigned HOST_WIDE_INT format_num;
unsigned int arg_num; unsigned HOST_WIDE_INT arg_num;
tree argument; 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 /* Strip any conversions from the first arg number and verify it
is a constant. */ is a constant. */
while (TREE_CODE (format_num_expr) == NOP_EXPR while (TREE_CODE (format_num_expr) == NOP_EXPR
...@@ -277,8 +213,8 @@ handle_format_arg_attribute (node, name, args, flags, no_add_attrs) ...@@ -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 if (TREE_CODE (TREE_TYPE (type)) != POINTER_TYPE
|| (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (TREE_TYPE (decl)))) || (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (type)))
!= char_type_node)) != char_type_node))
{ {
if (!(flags & (int) ATTR_FLAG_BUILT_IN)) if (!(flags & (int) ATTR_FLAG_BUILT_IN))
...@@ -287,114 +223,85 @@ handle_format_arg_attribute (node, name, args, flags, no_add_attrs) ...@@ -287,114 +223,85 @@ handle_format_arg_attribute (node, name, args, flags, no_add_attrs)
return NULL_TREE; return NULL_TREE;
} }
record_international_format (DECL_NAME (decl), DECL_ASSEMBLER_NAME (decl),
format_num);
return NULL_TREE; 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 /* 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.
struct international_format_info *next; /* next structure on the list */ If VALIDATED_P is true, then these attributes have already been validated
tree name; /* identifier such as "gettext" */ and this function will abort if they are erroneous; if false, it
tree assembler_name; /* optional mangled identifier (for C++) */ will give an error message. Returns true if the attributes are
int format_num; /* number of format argument */ successfully decoded, false otherwise. */
} 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 static bool
record_function_format (name, assembler_name, format_type, decode_format_attr (args, info, validated_p)
format_num, first_arg_num) tree args;
tree name; function_format_info *info;
tree assembler_name; int validated_p;
enum format_type format_type;
int format_num;
int first_arg_num;
{ {
function_format_info *info; tree format_type_id = TREE_VALUE (args);
tree format_num_expr = TREE_VALUE (TREE_CHAIN (args));
/* Re-use existing structure if it's there. */ 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) if (validated_p)
break; abort ();
error ("unrecognized format specifier");
return false;
} }
if (! info) else
{ {
info = (function_format_info *) xmalloc (sizeof (function_format_info)); const char *p = IDENTIFIER_POINTER (format_type_id);
info->next = function_format_list;
function_format_list = info;
info->name = name; info->format_type = decode_format_type (p);
info->assembler_name = assembler_name;
}
info->format_type = format_type; if (info->format_type == format_type_error)
info->format_num = format_num; {
info->first_arg_num = first_arg_num; if (validated_p)
} abort ();
warning ("`%s' is an unrecognized format function type", p);
/* Record information for the names of function that modify the format return false;
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 /* Strip any conversions from the string index and first arg number
record_international_format (name, assembler_name, format_num) and verify they are constants. */
tree name; while (TREE_CODE (format_num_expr) == NOP_EXPR
tree assembler_name; || TREE_CODE (format_num_expr) == CONVERT_EXPR
int format_num; || TREE_CODE (format_num_expr) == NON_LVALUE_EXPR)
{ format_num_expr = TREE_OPERAND (format_num_expr, 0);
international_format_info *info;
/* 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) if (validated_p)
break; 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 if (validated_p)
= (international_format_info *) abort ();
xmalloc (sizeof (international_format_info)); error ("format string arg follows the args to be formatted");
info->next = international_format_list; return false;
international_format_list = info;
info->name = name;
info->assembler_name = assembler_name;
} }
info->format_num = format_num; return true;
} }
/* Check a call to a format function against a parameter list. */ /* Check a call to a format function against a parameter list. */
...@@ -988,10 +895,11 @@ typedef struct ...@@ -988,10 +895,11 @@ typedef struct
static void check_format_info PARAMS ((int *, function_format_info *, tree)); static void check_format_info PARAMS ((int *, function_format_info *, tree));
static void check_format_info_recurse PARAMS ((int *, format_check_results *, static void check_format_info_recurse PARAMS ((int *, format_check_results *,
function_format_info *, tree, function_format_info *, tree,
tree, int)); tree, unsigned HOST_WIDE_INT));
static void check_format_info_main PARAMS ((int *, format_check_results *, static void check_format_info_main PARAMS ((int *, format_check_results *,
function_format_info *, function_format_info *,
const char *, int, tree, int)); const char *, int, tree,
unsigned HOST_WIDE_INT));
static void status_warning PARAMS ((int *, const char *, ...)) static void status_warning PARAMS ((int *, const char *, ...))
ATTRIBUTE_PRINTF_2; ATTRIBUTE_PRINTF_2;
...@@ -1032,43 +940,42 @@ decode_format_type (s) ...@@ -1032,43 +940,42 @@ decode_format_type (s)
/* Check the argument list of a call to printf, scanf, etc. /* Check the argument list of a call to printf, scanf, etc.
NAME is the function identifier. ATTRS are the attributes on the function type.
ASSEMBLER_NAME is the function's assembler identifier.
(Either NAME or ASSEMBLER_NAME, but not both, may be NULL_TREE.)
PARAMS is the list of argument values. Also, if -Wmissing-format-attribute, 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 warn for calls to vprintf or vscanf in functions with no such format
attribute themselves. */ attribute themselves. */
void void
check_function_format (status, name, assembler_name, params) check_function_format (status, attrs, params)
int *status; int *status;
tree name; tree attrs;
tree assembler_name;
tree params; tree params;
{ {
function_format_info *info; tree a;
/* See if this function is a format function. */ /* See if this function has any format attributes. */
for (info = function_format_list; info; info = info->next) for (a = attrs; a; a = TREE_CHAIN (a))
{ {
if (info->assembler_name if (is_attribute_p ("format", TREE_PURPOSE (a)))
? (info->assembler_name == assembler_name)
: (info->name == name))
{ {
/* Yup; check it. */ /* Yup; check it. */
check_format_info (status, info, params); function_format_info info;
if (warn_missing_format_attribute && info->first_arg_num == 0 decode_format_attr (TREE_VALUE (a), &info, 1);
&& (format_types[info->format_type].flags 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)) & (int) FMT_FLAG_ARG_CONVERT))
{ {
function_format_info *info2; tree c;
for (info2 = function_format_list; info2; info2 = info2->next) for (c = TYPE_ATTRIBUTES (TREE_TYPE (current_function_decl));
if ((info2->assembler_name c;
? (info2->assembler_name == DECL_ASSEMBLER_NAME (current_function_decl)) c = TREE_CHAIN (c))
: (info2->name == DECL_NAME (current_function_decl))) if (is_attribute_p ("format", TREE_PURPOSE (c))
&& info2->format_type == info->format_type) && (decode_format_type (IDENTIFIER_POINTER
(TREE_VALUE (TREE_VALUE (c))))
== info.format_type))
break; break;
if (info2 == NULL) if (c == NULL_TREE)
{ {
/* Check if the current function has a parameter to which /* Check if the current function has a parameter to which
the format attribute could be attached; if not, it the format attribute could be attached; if not, it
...@@ -1086,10 +993,9 @@ check_function_format (status, name, assembler_name, params) ...@@ -1086,10 +993,9 @@ check_function_format (status, name, assembler_name, params)
} }
if (args != 0) if (args != 0)
warning ("function might be possible candidate for `%s' format attribute", 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) ...@@ -1347,7 +1253,7 @@ check_format_info (status, info, params)
function_format_info *info; function_format_info *info;
tree params; tree params;
{ {
int arg_num; unsigned HOST_WIDE_INT arg_num;
tree format_tree; tree format_tree;
format_check_results res; format_check_results res;
/* Skip to format argument. If the argument isn't available, there's /* 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) ...@@ -1442,7 +1348,7 @@ check_format_info_recurse (status, res, info, format_tree, params, arg_num)
function_format_info *info; function_format_info *info;
tree format_tree; tree format_tree;
tree params; tree params;
int arg_num; unsigned HOST_WIDE_INT arg_num;
{ {
int format_length; int format_length;
HOST_WIDE_INT offset; HOST_WIDE_INT offset;
...@@ -1459,40 +1365,58 @@ check_format_info_recurse (status, res, info, format_tree, params, arg_num) ...@@ -1459,40 +1365,58 @@ check_format_info_recurse (status, res, info, format_tree, params, arg_num)
return; return;
} }
if (TREE_CODE (format_tree) == CALL_EXPR 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))
{ {
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 /* See if this is a call to a known internationalization function
that modifies the format arg. */ that modifies the format arg. Such a function may have multiple
international_format_info *iinfo; format_arg attributes (for example, ngettext). */
for (iinfo = international_format_list; iinfo; iinfo = iinfo->next) for (attrs = TYPE_ATTRIBUTES (type);
if (iinfo->assembler_name attrs;
? (iinfo->assembler_name == DECL_ASSEMBLER_NAME (function)) attrs = TREE_CHAIN (attrs))
: (iinfo->name == DECL_NAME (function))) if (is_attribute_p ("format_arg", TREE_PURPOSE (attrs)))
{ {
tree inner_args; tree inner_args;
tree format_num_expr;
int format_num;
int i; 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; for (inner_args = TREE_OPERAND (format_tree, 1), i = 1;
inner_args != 0; inner_args != 0;
inner_args = TREE_CHAIN (inner_args), i++) 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, check_format_info_recurse (status, res, info,
TREE_VALUE (inner_args), params, TREE_VALUE (inner_args), params,
arg_num); 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) if (TREE_CODE (format_tree) == COND_EXPR)
...@@ -1666,7 +1590,7 @@ check_format_info_main (status, res, info, format_chars, format_length, ...@@ -1666,7 +1590,7 @@ check_format_info_main (status, res, info, format_chars, format_length,
const char *format_chars; const char *format_chars;
int format_length; int format_length;
tree params; tree params;
int arg_num; unsigned HOST_WIDE_INT arg_num;
{ {
const char *orig_format_chars = format_chars; const char *orig_format_chars = format_chars;
tree first_fillin_param = params; tree first_fillin_param = params;
......
...@@ -1510,8 +1510,8 @@ build_function_call (function, params) ...@@ -1510,8 +1510,8 @@ build_function_call (function, params)
/* Check for errors in format strings. */ /* Check for errors in format strings. */
if (warn_format && (name || assembler_name)) if (warn_format)
check_function_format (NULL, name, assembler_name, coerced_params); check_function_format (NULL, TYPE_ATTRIBUTES (fntype), coerced_params);
/* Recognize certain built-in functions so we can make tree-codes /* Recognize certain built-in functions so we can make tree-codes
other than CALL_EXPR. We do this when it enables fold-const.c other than CALL_EXPR. We do this when it enables fold-const.c
......
2001-10-02 Joseph S. Myers <jsm28@cam.ac.uk> 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 * decl.c (init_decl_processing): Don't call
init_function_format_info. Initialize lang_attribute_table init_function_format_info. Initialize lang_attribute_table
earlier. earlier.
......
...@@ -4204,9 +4204,9 @@ build_over_call (cand, args, flags) ...@@ -4204,9 +4204,9 @@ build_over_call (cand, args, flags)
converted_args = nreverse (converted_args); converted_args = nreverse (converted_args);
if (warn_format && (DECL_NAME (fn) || DECL_ASSEMBLER_NAME (fn))) if (warn_format)
check_function_format (NULL, DECL_NAME (fn), DECL_ASSEMBLER_NAME (fn), check_function_format (NULL, TYPE_ATTRIBUTES (TREE_TYPE (fn)),
converted_args); converted_args);
/* Avoid actually calling copy constructors and copy assignment operators, /* Avoid actually calling copy constructors and copy assignment operators,
if possible. */ if possible. */
......
...@@ -3035,8 +3035,8 @@ build_function_call_real (function, params, require_complete, flags) ...@@ -3035,8 +3035,8 @@ build_function_call_real (function, params, require_complete, flags)
/* Check for errors in format strings. */ /* Check for errors in format strings. */
if (warn_format && (name || assembler_name)) if (warn_format)
check_function_format (NULL, name, assembler_name, coerced_params); check_function_format (NULL, TYPE_ATTRIBUTES (fntype), coerced_params);
/* Recognize certain built-in functions so we can make tree-codes /* Recognize certain built-in functions so we can make tree-codes
other than CALL_EXPR. We do this when it enables fold-const.c other than CALL_EXPR. We do this when it enables fold-const.c
......
2001-10-02 Joseph S. Myers <jsm28@cam.ac.uk> 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. * gcc.dg/format/attr-5.c, gcc.dg/format/attr-6.c: New tests.
2001-10-02 Joseph S. Myers <jsm28@cam.ac.uk> 2001-10-02 Joseph S. Myers <jsm28@cam.ac.uk>
......
// 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" }
}
...@@ -25,21 +25,13 @@ extern void fc3 (const char *) __attribute__((format_arg(1, 2))); /* { dg-error ...@@ -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. /* These attributes presently only apply to declarations, not to types.
Eventually, they should be usable with declarators for function types Eventually, they should be usable with declarators for function types
anywhere, but still not with structure/union/enum 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" } */ 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" "format on union" } */ 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" "format on enum" } */ 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" } */ 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" "format_arg on union" } */ 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" "format_arg on enum" } */ enum e1 { E1V0 } __attribute__((format_arg(1))); /* { dg-error "does not apply|only applies" "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" } */
/* The format type must be an identifier, one of those recognised. */ /* 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" } */ extern void fe0 (const char *, ...) __attribute__((format(12345, 1, 2))); /* { dg-error "format specifier" "non-id format" } */
......
/* 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" } */
}
/* 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" } */
}
/* 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" } */
}
/* 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" } */
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment