OSDN Git Service

* c-common.c (c_format_attribute_table): Make format and
authorjsm28 <jsm28@138bc75d-0d04-0410-961f-82ee72b054a4>
Tue, 2 Oct 2001 07:19:47 +0000 (07:19 +0000)
committerjsm28 <jsm28@138bc75d-0d04-0410-961f-82ee72b054a4>
Tue, 2 Oct 2001 07:19:47 +0000 (07:19 +0000)
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.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@45945 138bc75d-0d04-0410-961f-82ee72b054a4

16 files changed:
gcc/ChangeLog
gcc/c-common.c
gcc/c-common.h
gcc/c-decl.c
gcc/c-format.c
gcc/c-typeck.c
gcc/cp/ChangeLog
gcc/cp/call.c
gcc/cp/typeck.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/warn/format2.C [new file with mode: 0644]
gcc/testsuite/gcc.dg/format/attr-3.c
gcc/testsuite/gcc.dg/format/attr-7.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/format/multattr-1.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/format/multattr-2.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/format/multattr-3.c [new file with mode: 0644]

index 7a10bfa..72e101e 100644 (file)
@@ -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.
index a3314ff..e6c81c3 100644 (file)
@@ -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;
index 66df266..aadbee8 100644 (file)
@@ -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 *));
index f3ad82e..ac09d26 100644 (file)
@@ -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)
index 4ee3b3d..ea6ef57 100644 (file)
@@ -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;
 }
-
-
-
 \f
 /* 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)
 
 \f
 /* 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;
index c407f16..09ebf4d 100644 (file)
@@ -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
index 6e3ebaf..54db0bd 100644 (file)
@@ -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.
index 4273148..437d67c 100644 (file)
@@ -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.  */
index 54dc0ed..c57bb08 100644 (file)
@@ -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
index 3c027b7..087feed 100644 (file)
@@ -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 (file)
index 0000000..639bc66
--- /dev/null
@@ -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" }
+}
index 2e3c632..9627497 100644 (file)
@@ -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 (file)
index 0000000..78526a5
--- /dev/null
@@ -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 (file)
index 0000000..7691aba
--- /dev/null
@@ -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 (file)
index 0000000..1d78840
--- /dev/null
@@ -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 (file)
index 0000000..40467fe
--- /dev/null
@@ -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" } */
+}