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