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>
* 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
insert_default_attributes to insert default attributes on
functions in a lazy manner.
......
......@@ -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)
......
......@@ -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
......
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
init_function_format_info. Initialize lang_attribute_table
earlier.
......
......@@ -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
......
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.
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
/* 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" } */
......
/* 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