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:
parent
b3b5d92c88
commit
80a497e4e9
16 changed files with 421 additions and 255 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 *));
|
||||
|
|
|
@ -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)
|
||||
|
|
380
gcc/c-format.c
380
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
|
||||
|
||||
/* 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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
32
gcc/testsuite/g++.dg/warn/format2.C
Normal file
32
gcc/testsuite/g++.dg/warn/format2.C
Normal 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" }
|
||||
}
|
|
@ -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" } */
|
||||
|
|
34
gcc/testsuite/gcc.dg/format/attr-7.c
Normal file
34
gcc/testsuite/gcc.dg/format/attr-7.c
Normal 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" } */
|
||||
}
|
50
gcc/testsuite/gcc.dg/format/multattr-1.c
Normal file
50
gcc/testsuite/gcc.dg/format/multattr-1.c
Normal 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" } */
|
||||
}
|
39
gcc/testsuite/gcc.dg/format/multattr-2.c
Normal file
39
gcc/testsuite/gcc.dg/format/multattr-2.c
Normal 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" } */
|
||||
}
|
28
gcc/testsuite/gcc.dg/format/multattr-3.c
Normal file
28
gcc/testsuite/gcc.dg/format/multattr-3.c
Normal 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" } */
|
||||
}
|
Loading…
Add table
Reference in a new issue