aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoseph Myers <jsm28@cam.ac.uk>2001-10-02 08:19:47 +0100
committerJoseph Myers <jsm28@gcc.gnu.org>2001-10-02 08:19:47 +0100
commit80a497e4e967ad5cdf59a1c4d2be2fdbba14ea3f (patch)
tree08f1b7a84cfb385dccaaf67b7162f37545d3fd09
parentb3b5d92c88835a1a64eae771baf053ff4c98dec7 (diff)
downloadgcc-80a497e4e967ad5cdf59a1c4d2be2fdbba14ea3f.zip
gcc-80a497e4e967ad5cdf59a1c4d2be2fdbba14ea3f.tar.gz
gcc-80a497e4e967ad5cdf59a1c4d2be2fdbba14ea3f.tar.bz2
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
-rw-r--r--gcc/ChangeLog39
-rw-r--r--gcc/c-common.c19
-rw-r--r--gcc/c-common.h2
-rw-r--r--gcc/c-decl.c4
-rw-r--r--gcc/c-format.c360
-rw-r--r--gcc/c-typeck.c4
-rw-r--r--gcc/cp/ChangeLog7
-rw-r--r--gcc/cp/call.c6
-rw-r--r--gcc/cp/typeck.c4
-rw-r--r--gcc/testsuite/ChangeLog8
-rw-r--r--gcc/testsuite/g++.dg/warn/format2.C32
-rw-r--r--gcc/testsuite/gcc.dg/format/attr-3.c20
-rw-r--r--gcc/testsuite/gcc.dg/format/attr-7.c34
-rw-r--r--gcc/testsuite/gcc.dg/format/multattr-1.c50
-rw-r--r--gcc/testsuite/gcc.dg/format/multattr-2.c39
-rw-r--r--gcc/testsuite/gcc.dg/format/multattr-3.c28
16 files changed, 411 insertions, 245 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 7a10bfa..72e101e 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,5 +1,44 @@
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.
diff --git a/gcc/c-common.c b/gcc/c-common.c
index a3314ff..e6c81c3 100644
--- a/gcc/c-common.c
+++ b/gcc/c-common.c
@@ -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;
diff --git a/gcc/c-common.h b/gcc/c-common.h
index 66df266..aadbee8 100644
--- a/gcc/c-common.h
+++ b/gcc/c-common.h
@@ -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 *));
diff --git a/gcc/c-decl.c b/gcc/c-decl.c
index f3ad82e..ac09d26 100644
--- a/gcc/c-decl.c
+++ b/gcc/c-decl.c
@@ -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)
diff --git a/gcc/c-format.c b/gcc/c-format.c
index 4ee3b3d..ea6ef57 100644
--- a/gcc/c-format.c
+++ b/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
-{
- 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). */
+/* 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 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;
+static bool
+decode_format_attr (args, info, validated_p)
+ tree args;
+ function_format_info *info;
+ int validated_p;
{
- function_format_info *info;
-
- /* Re-use existing structure if it's there. */
+ 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)));
- 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);
- 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). */
+ if (info->format_type == format_type_error)
+ {
+ if (validated_p)
+ abort ();
+ warning ("`%s' is an unrecognized format function type", p);
+ return false;
+ }
+ }
-static void
-record_international_format (name, assembler_name, format_num)
- tree name;
- tree assembler_name;
- int format_num;
-{
- international_format_info *info;
+ /* 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);
- /* Re-use existing structure if it's there. */
+ while (TREE_CODE (first_arg_num_expr) == NOP_EXPR
+ || TREE_CODE (first_arg_num_expr) == CONVERT_EXPR
+ || TREE_CODE (first_arg_num_expr) == NON_LVALUE_EXPR)
+ first_arg_num_expr = TREE_OPERAND (first_arg_num_expr, 0);
- for (info = international_format_list; info; info = info->next)
+ if (TREE_CODE (format_num_expr) != INTEGER_CST
+ || TREE_INT_CST_HIGH (format_num_expr) != 0
+ || TREE_CODE (first_arg_num_expr) != INTEGER_CST
+ || TREE_INT_CST_HIGH (first_arg_num_expr) != 0)
{
- if (info->name == name && info->assembler_name == assembler_name)
- break;
+ if (validated_p)
+ abort ();
+ error ("format string has invalid operand number");
+ return false;
}
- if (! info)
+ info->format_num = TREE_INT_CST_LOW (format_num_expr);
+ info->first_arg_num = TREE_INT_CST_LOW (first_arg_num_expr);
+ if (info->first_arg_num != 0 && info->first_arg_num <= info->format_num)
{
- info
- = (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;
+ if (validated_p)
+ abort ();
+ error ("format string arg follows the args to be formatted");
+ return false;
}
- info->format_num = format_num;
+ return true;
}
-
-
-
/* 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;
diff --git a/gcc/c-typeck.c b/gcc/c-typeck.c
index c407f16..09ebf4d 100644
--- a/gcc/c-typeck.c
+++ b/gcc/c-typeck.c
@@ -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
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 6e3ebaf..54db0bd 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,5 +1,12 @@
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.
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 4273148..437d67c 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -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. */
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 54dc0ed..c57bb08 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -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
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 3c027b7..087feed 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,5 +1,13 @@
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>
diff --git a/gcc/testsuite/g++.dg/warn/format2.C b/gcc/testsuite/g++.dg/warn/format2.C
new file mode 100644
index 0000000..639bc66
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/format2.C
@@ -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" }
+}
diff --git a/gcc/testsuite/gcc.dg/format/attr-3.c b/gcc/testsuite/gcc.dg/format/attr-3.c
index 2e3c632..9627497 100644
--- a/gcc/testsuite/gcc.dg/format/attr-3.c
+++ b/gcc/testsuite/gcc.dg/format/attr-3.c
@@ -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" } */
diff --git a/gcc/testsuite/gcc.dg/format/attr-7.c b/gcc/testsuite/gcc.dg/format/attr-7.c
new file mode 100644
index 0000000..78526a5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/format/attr-7.c
@@ -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" } */
+}
diff --git a/gcc/testsuite/gcc.dg/format/multattr-1.c b/gcc/testsuite/gcc.dg/format/multattr-1.c
new file mode 100644
index 0000000..7691aba
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/format/multattr-1.c
@@ -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" } */
+}
diff --git a/gcc/testsuite/gcc.dg/format/multattr-2.c b/gcc/testsuite/gcc.dg/format/multattr-2.c
new file mode 100644
index 0000000..1d78840
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/format/multattr-2.c
@@ -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" } */
+}
diff --git a/gcc/testsuite/gcc.dg/format/multattr-3.c b/gcc/testsuite/gcc.dg/format/multattr-3.c
new file mode 100644
index 0000000..40467fe
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/format/multattr-3.c
@@ -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" } */
+}