Format attributes on types patch
Joseph S. Myers
jsm28@cam.ac.uk
Sat Sep 29 13:22:00 GMT 2001
This patch makes format and format_arg attributes apply to types
rather than to decls, thereby allowing them on function pointers, a
feature long in demand (see c/3481, Debian bug report 55298, etc.).
It also allows for multiple format_arg attributes on a single
function, already used in current glibc for ngettext.
This patch depends on my previous lazy default attributes patch
<URL: http://gcc.gnu.org/ml/gcc-patches/2001-09/msg01078.html >.
Bootstrapped with no regressions on i686-pc-linux-gnu. OK to commit
(both the lazy default attributes patch and this one)?
[The C++ testcase is called format2.C because format1.C is part of my
unreviewed patch
<URL: http://gcc.gnu.org/ml/gcc-patches/2001-09/msg00587.html >.]
2001-09-29 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.
cp:
2001-09-29 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.
testsuite:
2001-09-29 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.
diff -rcpN gcc.orig/c-common.c gcc/c-common.c
*** gcc.orig/c-common.c Sat Sep 29 10:30:10 2001
--- gcc/c-common.c Sat Sep 29 12:54:59 2001
*************** c_alignof_expr (expr)
*** 2329,2337 ****
static const struct attribute_spec c_format_attribute_table[] =
{
! { "format", 3, 3, true, false, false,
handle_format_attribute },
! { "format_arg", 1, 1, true, false, false,
handle_format_arg_attribute },
{ NULL, 0, 0, false, false, false, NULL }
};
--- 2329,2338 ----
static const struct attribute_spec c_format_attribute_table[] =
{
! /* { 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, false, true, true,
handle_format_arg_attribute },
{ NULL, 0, 0, false, false, false, NULL }
};
*************** is_valid_printf_arglist (arglist)
*** 3551,3564 ****
/* Save this value so we can restore it later. */
const int SAVE_pedantic = pedantic;
int diagnostic_occurred = 0;
/* 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);
/* Restore the value of `pedantic'. */
pedantic = SAVE_pedantic;
--- 3552,3573 ----
/* 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. */
! attrs = tree_cons (get_identifier ("format"),
! tree_cons (NULL_TREE,
! get_identifier ("printf"),
! tree_cons (NULL_TREE,
! integer_one_node,
! tree_cons (NULL_TREE,
! build_int_2 (2, 0),
! NULL_TREE))),
! NULL_TREE);
! check_function_format (&diagnostic_occurred, attrs, arglist);
/* Restore the value of `pedantic'. */
pedantic = SAVE_pedantic;
diff -rcpN gcc.orig/c-common.h gcc/c-common.h
*** gcc.orig/c-common.h Sat Sep 29 10:30:10 2001
--- gcc/c-common.h Sat Sep 29 12:03:11 2001
*************** extern const char *fname_as_string PARA
*** 503,509 ****
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 set_Wformat PARAMS ((int));
extern tree handle_format_attribute PARAMS ((tree *, tree, tree,
int, bool *));
--- 503,509 ----
extern tree fname_decl PARAMS ((unsigned, tree));
extern const char *fname_string PARAMS ((unsigned));
! extern void check_function_format PARAMS ((int *, tree, tree));
extern void set_Wformat PARAMS ((int));
extern tree handle_format_attribute PARAMS ((tree *, tree, tree,
int, bool *));
diff -rcpN gcc.orig/c-decl.c gcc/c-decl.c
*** gcc.orig/c-decl.c Sat Sep 29 10:30:10 2001
--- gcc/c-decl.c Sat Sep 29 18:00:37 2001
*************** duplicate_decls (newdecl, olddecl, diffe
*** 1498,1503 ****
--- 1498,1505 ----
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)
*************** duplicate_decls (newdecl, olddecl, diffe
*** 1519,1524 ****
--- 1521,1528 ----
tree_cons (NULL_TREE,
TREE_VALUE (TYPE_ARG_TYPES (newtype)),
TREE_CHAIN (TYPE_ARG_TYPES (oldtype))));
+ trytype = build_type_attribute_variant (trytype,
+ TYPE_ATTRIBUTES (oldtype));
types_match = comptypes (newtype, trytype);
if (types_match)
diff -rcpN gcc.orig/c-format.c gcc/c-format.c
*** gcc.orig/c-format.c Sat Sep 29 10:30:10 2001
--- gcc/c-format.c Sat Sep 29 16:23:27 2001
*************** enum format_type { printf_format_type, s
*** 77,86 ****
strftime_format_type, strfmon_format_type,
format_type_error };
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. */
--- 77,92 ----
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 *));
/* Handle a "format" attribute; arguments as in
struct attribute_spec.handler. */
*************** handle_format_attribute (node, name, arg
*** 92,163 ****
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 argument;
! unsigned int arg_num;
! if (TREE_CODE (decl) != FUNCTION_DECL)
{
- 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;
}
--- 98,110 ----
int flags;
bool *no_add_attrs;
{
! tree type = *node;
! function_format_info info;
tree argument;
! unsigned HOST_WIDE_INT arg_num;
! if (!decode_format_attr (args, &info, 0))
{
*no_add_attrs = true;
return NULL_TREE;
}
*************** handle_format_attribute (node, name, arg
*** 168,174 ****
argument = TYPE_ARG_TYPES (type);
if (argument)
{
! for (arg_num = 1; argument != 0 && arg_num != format_num;
++arg_num, argument = TREE_CHAIN (argument))
;
--- 115,121 ----
argument = TYPE_ARG_TYPES (type);
if (argument)
{
! for (arg_num = 1; argument != 0 && arg_num != info.format_num;
++arg_num, argument = TREE_CHAIN (argument))
;
*************** handle_format_attribute (node, name, arg
*** 183,196 ****
return NULL_TREE;
}
! else if (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 (!(flags & (int) ATTR_FLAG_BUILT_IN))
error ("args to be formatted is not '...'");
--- 130,143 ----
return NULL_TREE;
}
! 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 != info.first_arg_num)
{
if (!(flags & (int) ATTR_FLAG_BUILT_IN))
error ("args to be formatted is not '...'");
*************** handle_format_attribute (node, name, arg
*** 200,219 ****
}
}
! if (format_type == strftime_format_type && 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
struct attribute_spec.handler. */
tree
handle_format_arg_attribute (node, name, args, flags, no_add_attrs)
--- 147,164 ----
}
}
! 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;
}
return NULL_TREE;
}
! /* Handle a "format_arg" attribute; arguments as in
struct attribute_spec.handler. */
tree
handle_format_arg_attribute (node, name, args, flags, no_add_attrs)
*************** handle_format_arg_attribute (node, name,
*** 223,243 ****
int flags;
bool *no_add_attrs;
{
! tree decl = *node;
! tree type = TREE_TYPE (decl);
tree format_num_expr = TREE_VALUE (args);
unsigned HOST_WIDE_INT format_num;
! unsigned 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
--- 168,179 ----
int flags;
bool *no_add_attrs;
{
! tree type = *node;
tree format_num_expr = TREE_VALUE (args);
unsigned HOST_WIDE_INT format_num;
! unsigned HOST_WIDE_INT arg_num;
tree argument;
/* Strip any conversions from the first arg number and verify it
is a constant. */
while (TREE_CODE (format_num_expr) == NOP_EXPR
*************** handle_format_arg_attribute (node, name,
*** 277,284 ****
}
}
! if (TREE_CODE (TREE_TYPE (TREE_TYPE (decl))) != POINTER_TYPE
! || (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (TREE_TYPE (decl))))
!= char_type_node))
{
if (!(flags & (int) ATTR_FLAG_BUILT_IN))
--- 213,220 ----
}
}
! 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))
*************** handle_format_arg_attribute (node, name,
*** 287,400 ****
return NULL_TREE;
}
- record_international_format (DECL_NAME (decl), DECL_ASSEMBLER_NAME (decl),
- format_num);
return NULL_TREE;
}
- typedef struct function_format_info
- {
- struct function_format_info *next; /* next structure on the list */
- tree name; /* identifier such as "printf" */
- tree assembler_name; /* optional mangled identifier (for C++) */
- enum format_type format_type; /* type of format (printf, scanf, etc.) */
- int format_num; /* number of format argument */
- int first_arg_num; /* number of first arg (zero for varargs) */
- } function_format_info;
-
- static function_format_info *function_format_list = NULL;
! typedef struct international_format_info
! {
! struct international_format_info *next; /* next structure on the list */
! tree name; /* identifier such as "gettext" */
! tree assembler_name; /* optional mangled identifier (for C++) */
! int format_num; /* number of format argument */
! } international_format_info;
!
! static international_format_info *international_format_list = NULL;
!
! /* Record information for argument format checking. FUNCTION_IDENT is
! the identifier node for the name of the function to check (its decl
! need not exist yet).
! FORMAT_TYPE specifies the type of format checking. FORMAT_NUM is the number
! of the argument which is the format control string (starting from 1).
! FIRST_ARG_NUM is the number of the first actual argument to check
! against the format string, or zero if no checking is not be done
! (e.g. for varargs such as vfprintf). */
! 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 (info->name == name && info->assembler_name == assembler_name)
! break;
}
! if (! info)
{
! info = (function_format_info *) xmalloc (sizeof (function_format_info));
! info->next = function_format_list;
! function_format_list = info;
!
! info->name = name;
! info->assembler_name = assembler_name;
! }
! info->format_type = format_type;
! info->format_num = format_num;
! info->first_arg_num = first_arg_num;
! }
! /* Record information for the names of function that modify the format
! argument to format functions. FUNCTION_IDENT is the identifier node for
! the name of the function (its decl need not exist yet) and FORMAT_NUM is
! the number of the argument which is the format control string (starting
! from 1). */
! 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. */
--- 223,307 ----
return NULL_TREE;
}
return NULL_TREE;
}
! /* 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;
{
! 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)));
! if (TREE_CODE (format_type_id) != IDENTIFIER_NODE)
{
! if (validated_p)
! abort ();
! error ("unrecognized format specifier");
! return false;
}
! else
{
! const char *p = IDENTIFIER_POINTER (format_type_id);
! 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;
! }
! }
! /* 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;
}
/* Check a call to a format function against a parameter list. */
*************** typedef struct
*** 988,997 ****
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));
static void check_format_info_main PARAMS ((int *, format_check_results *,
function_format_info *,
! const char *, int, tree, int));
static void status_warning PARAMS ((int *, const char *, ...))
ATTRIBUTE_PRINTF_2;
--- 895,905 ----
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, unsigned HOST_WIDE_INT));
static void check_format_info_main PARAMS ((int *, format_check_results *,
function_format_info *,
! const char *, int, tree,
! unsigned HOST_WIDE_INT));
static void status_warning PARAMS ((int *, const char *, ...))
ATTRIBUTE_PRINTF_2;
*************** decode_format_type (s)
*** 1032,1074 ****
/* 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.)
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)
int *status;
! tree name;
! tree assembler_name;
tree params;
{
! function_format_info *info;
! /* See if this function is a format function. */
! for (info = function_format_list; info; info = info->next)
{
! if (info->assembler_name
! ? (info->assembler_name == assembler_name)
! : (info->name == name))
{
/* 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
& (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)
break;
! if (info2 == NULL)
{
/* Check if the current function has a parameter to which
the format attribute could be attached; if not, it
--- 940,981 ----
/* Check the argument list of a call to printf, scanf, etc.
! 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, attrs, params)
int *status;
! tree attrs;
tree params;
{
! tree a;
! /* See if this function has any format attributes. */
! for (a = attrs; a; a = TREE_CHAIN (a))
{
! if (is_attribute_p ("format", TREE_PURPOSE (a)))
{
/* Yup; check it. */
! 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))
{
! 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 (c == NULL_TREE)
{
/* Check if the current function has a parameter to which
the format attribute could be attached; if not, it
*************** check_function_format (status, name, ass
*** 1086,1095 ****
}
if (args != 0)
warning ("function might be possible candidate for `%s' format attribute",
! format_types[info->format_type].name);
}
}
- break;
}
}
}
--- 993,1001 ----
}
if (args != 0)
warning ("function might be possible candidate for `%s' format attribute",
! format_types[info.format_type].name);
}
}
}
}
}
*************** check_format_info (status, info, params)
*** 1347,1353 ****
function_format_info *info;
tree params;
{
! int arg_num;
tree format_tree;
format_check_results res;
/* Skip to format argument. If the argument isn't available, there's
--- 1253,1259 ----
function_format_info *info;
tree params;
{
! 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
*************** check_format_info_recurse (status, res,
*** 1442,1448 ****
function_format_info *info;
tree format_tree;
tree params;
! int arg_num;
{
int format_length;
HOST_WIDE_INT offset;
--- 1348,1354 ----
function_format_info *info;
tree format_tree;
tree params;
! unsigned HOST_WIDE_INT arg_num;
{
int format_length;
HOST_WIDE_INT offset;
*************** check_format_info_recurse (status, res,
*** 1459,1498 ****
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))
{
! tree function = TREE_OPERAND (TREE_OPERAND (format_tree, 0), 0);
/* See if this is a call to a known internationalization function
! that modifies the format arg. */
! international_format_info *iinfo;
! 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)))
{
tree inner_args;
int i;
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)
{
- /* 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;
}
}
}
if (TREE_CODE (format_tree) == COND_EXPR)
--- 1365,1422 ----
return;
}
! if (TREE_CODE (format_tree) == CALL_EXPR)
{
! 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. Such a function may have multiple
! format_arg attributes (for example, ngettext). */
! 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 == format_num)
{
check_format_info_recurse (status, res, info,
TREE_VALUE (inner_args), params,
arg_num);
! 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)
*************** check_format_info_main (status, res, inf
*** 1666,1672 ****
const char *format_chars;
int format_length;
tree params;
! int arg_num;
{
const char *orig_format_chars = format_chars;
tree first_fillin_param = params;
--- 1590,1596 ----
const char *format_chars;
int format_length;
tree params;
! unsigned HOST_WIDE_INT arg_num;
{
const char *orig_format_chars = format_chars;
tree first_fillin_param = params;
diff -rcpN gcc.orig/c-typeck.c gcc/c-typeck.c
*** gcc.orig/c-typeck.c Thu Sep 27 08:59:47 2001
--- gcc/c-typeck.c Sat Sep 29 12:48:05 2001
*************** build_function_call (function, params)
*** 1510,1517 ****
/* Check for errors in format strings. */
! if (warn_format && (name || assembler_name))
! check_function_format (NULL, name, assembler_name, 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
--- 1510,1517 ----
/* Check for errors in format strings. */
! if (warn_format)
! check_function_format (NULL, TYPE_ATTRIBUTES (fntype), coerced_params);
/* Recognize certain built-in functions so we can make tree-codes
other than CALL_EXPR. We do this when it enables fold-const.c
diff -rcpN gcc.orig/cp/call.c gcc/cp/call.c
*** gcc.orig/cp/call.c Fri Sep 21 17:05:33 2001
--- gcc/cp/call.c Sat Sep 29 12:56:19 2001
*************** build_over_call (cand, args, flags)
*** 4204,4212 ****
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);
/* Avoid actually calling copy constructors and copy assignment operators,
if possible. */
--- 4204,4212 ----
converted_args = nreverse (converted_args);
! if (warn_format)
! check_function_format (NULL, TYPE_ATTRIBUTES (TREE_TYPE (fn)),
! converted_args);
/* Avoid actually calling copy constructors and copy assignment operators,
if possible. */
diff -rcpN gcc.orig/cp/typeck.c gcc/cp/typeck.c
*** gcc.orig/cp/typeck.c Fri Sep 21 17:05:33 2001
--- gcc/cp/typeck.c Sat Sep 29 12:56:39 2001
*************** build_function_call_real (function, para
*** 3035,3042 ****
/* Check for errors in format strings. */
! if (warn_format && (name || assembler_name))
! check_function_format (NULL, name, assembler_name, 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
--- 3035,3042 ----
/* Check for errors in format strings. */
! if (warn_format)
! check_function_format (NULL, TYPE_ATTRIBUTES (fntype), coerced_params);
/* Recognize certain built-in functions so we can make tree-codes
other than CALL_EXPR. We do this when it enables fold-const.c
diff -rcpN gcc.orig/testsuite/g++.dg/warn/format2.C gcc/testsuite/g++.dg/warn/format2.C
*** gcc.orig/testsuite/g++.dg/warn/format2.C Thu Jan 1 00:00:00 1970
--- gcc/testsuite/g++.dg/warn/format2.C Sat Sep 29 15:57:17 2001
***************
*** 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" }
+ }
diff -rcpN gcc.orig/testsuite/gcc.dg/format/attr-3.c gcc/testsuite/gcc.dg/format/attr-3.c
*** gcc.orig/testsuite/gcc.dg/format/attr-3.c Mon Jan 8 11:27:44 2001
--- gcc/testsuite/gcc.dg/format/attr-3.c Sat Sep 29 19:31:16 2001
*************** extern void fc3 (const char *) __attribu
*** 25,45 ****
/* 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 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" } */
/* 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" } */
--- 25,37 ----
/* 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|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|only applies" "format_arg on struct" } */
! union u1 { int i; } __attribute__((format_arg(1))); /* { dg-error "does not apply|only applies" "format_arg on union" } */
! enum e1 { E1V0 } __attribute__((format_arg(1))); /* { dg-error "does not apply|only applies" "format_arg on enum" } */
/* The format type must be an identifier, one of those recognised. */
extern void fe0 (const char *, ...) __attribute__((format(12345, 1, 2))); /* { dg-error "format specifier" "non-id format" } */
diff -rcpN gcc.orig/testsuite/gcc.dg/format/attr-7.c gcc/testsuite/gcc.dg/format/attr-7.c
*** gcc.orig/testsuite/gcc.dg/format/attr-7.c Thu Jan 1 00:00:00 1970
--- gcc/testsuite/gcc.dg/format/attr-7.c Sat Sep 29 15:54:09 2001
***************
*** 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" } */
+ }
diff -rcpN gcc.orig/testsuite/gcc.dg/format/multattr-1.c gcc/testsuite/gcc.dg/format/multattr-1.c
*** gcc.orig/testsuite/gcc.dg/format/multattr-1.c Thu Jan 1 00:00:00 1970
--- gcc/testsuite/gcc.dg/format/multattr-1.c Sat Sep 29 13:03:27 2001
***************
*** 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" } */
+ }
diff -rcpN gcc.orig/testsuite/gcc.dg/format/multattr-2.c gcc/testsuite/gcc.dg/format/multattr-2.c
*** gcc.orig/testsuite/gcc.dg/format/multattr-2.c Thu Jan 1 00:00:00 1970
--- gcc/testsuite/gcc.dg/format/multattr-2.c Sat Sep 29 15:30:10 2001
***************
*** 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" } */
+ }
diff -rcpN gcc.orig/testsuite/gcc.dg/format/multattr-3.c gcc/testsuite/gcc.dg/format/multattr-3.c
*** gcc.orig/testsuite/gcc.dg/format/multattr-3.c Thu Jan 1 00:00:00 1970
--- gcc/testsuite/gcc.dg/format/multattr-3.c Sat Sep 29 15:44:34 2001
***************
*** 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" } */
+ }
--
Joseph S. Myers
jsm28@cam.ac.uk
More information about the Gcc-patches
mailing list