OSDN Git Service

* reload1.c (reload_cse_simplify): Fix typo in rtx code check.
[pf3gnuchains/gcc-fork.git] / gcc / c-format.c
index 19bbcce..dc73f4d 100644 (file)
@@ -1,6 +1,6 @@
 /* Check calls to formatted I/O functions (-Wformat).
-   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
-   Free Software Foundation, Inc.
+   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
+   2001, 2002 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -27,7 +27,7 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #include "c-common.h"
 #include "intl.h"
 #include "diagnostic.h"
-
+#include "langhooks.h"
 \f
 /* Command line options and their associated flags.  */
 
@@ -44,6 +44,10 @@ int warn_format_y2k;
 
 int warn_format_extra_args;
 
+/* Warn about zero-length formats.  */
+
+int warn_format_zero_length;
+
 /* Warn about non-literal format arguments.  */
 
 int warn_format_nonliteral;
@@ -61,6 +65,7 @@ set_Wformat (setting)
   warn_format = setting;
   warn_format_y2k = setting;
   warn_format_extra_args = setting;
+  warn_format_zero_length = setting;
   if (setting != 1)
     {
       warn_format_nonliteral = setting;
@@ -374,7 +379,10 @@ enum
   /* Zero width is bad in this type of format (scanf).  */
   FMT_FLAG_ZERO_WIDTH_BAD = 32,
   /* Empty precision specification is OK in this type of format (printf).  */
-  FMT_FLAG_EMPTY_PREC_OK = 64
+  FMT_FLAG_EMPTY_PREC_OK = 64,
+  /* Gaps are allowed in the arguments with $ operand numbers if all
+     arguments are pointers (scanf).  */
+  FMT_FLAG_DOLLAR_GAP_POINTER_OK = 128
   /* Not included here: details of whether width or precision may occur
      (controlled by width_char and precision_char); details of whether
      '*' can be used for these (width_type and precision_type); details
@@ -399,7 +407,7 @@ typedef struct
 } format_length_info;
 
 
-/* Structure desribing the combination of a conversion specifier
+/* Structure describing the combination of a conversion specifier
    (or a set of specifiers which act identically) and a length modifier.  */
 typedef struct
 {
@@ -420,7 +428,7 @@ typedef struct
 #define NOLENGTHS      { BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }
 
 
-/* Structure desribing a format conversion specifier (or a set of specifiers
+/* Structure describing a format conversion specifier (or a set of specifiers
    which act identically), and the length modifiers used with it.  */
 typedef struct
 {
@@ -639,12 +647,12 @@ static const format_flag_pair printf_flag_pairs[] =
 
 static const format_flag_spec scanf_flag_specs[] =
 {
-  { '*',  0, 0, N_("assignment suppression"), N_("assignment suppression"),          STD_C89 },
-  { 'a',  0, 0, N_("`a' flag"),               N_("the `a' scanf flag"),              STD_EXT },
-  { 'w',  0, 0, N_("field width"),            N_("field width in scanf format"),     STD_C89 },
-  { 'L',  0, 0, N_("length modifier"),        N_("length modifier in scanf format"), STD_C89 },
-  { '\'', 0, 0, N_("`'' flag"),               N_("the `'' scanf flag"),              STD_EXT },
-  { 'I',  0, 0, N_("`I' flag"),               N_("the `I' scanf flag"),              STD_EXT },
+  { '*',  0, 0, N_("assignment suppression"), N_("the assignment suppression scanf feature"), STD_C89 },
+  { 'a',  0, 0, N_("`a' flag"),               N_("the `a' scanf flag"),                       STD_EXT },
+  { 'w',  0, 0, N_("field width"),            N_("field width in scanf format"),              STD_C89 },
+  { 'L',  0, 0, N_("length modifier"),        N_("length modifier in scanf format"),          STD_C89 },
+  { '\'', 0, 0, N_("`'' flag"),               N_("the `'' scanf flag"),                       STD_EXT },
+  { 'I',  0, 0, N_("`I' flag"),               N_("the `I' scanf flag"),                       STD_EXT },
   { 0, 0, 0, NULL, NULL, 0 }
 };
 
@@ -848,7 +856,7 @@ static const format_kind_info format_types[] =
   },
   { "scanf",    scanf_length_specs,   scan_char_table,  "*'I", NULL, 
     scanf_flag_specs, scanf_flag_pairs,
-    FMT_FLAG_ARG_CONVERT|FMT_FLAG_SCANF_A_KLUDGE|FMT_FLAG_USE_DOLLAR|FMT_FLAG_ZERO_WIDTH_BAD,
+    FMT_FLAG_ARG_CONVERT|FMT_FLAG_SCANF_A_KLUDGE|FMT_FLAG_USE_DOLLAR|FMT_FLAG_ZERO_WIDTH_BAD|FMT_FLAG_DOLLAR_GAP_POINTER_OK,
     'w', 0, 0, '*', 'L',
     NULL, NULL
   },
@@ -907,7 +915,7 @@ static void init_dollar_format_checking             PARAMS ((int, tree));
 static int maybe_read_dollar_number            PARAMS ((int *, const char **, int,
                                                         tree, tree *,
                                                         const format_kind_info *));
-static void finish_dollar_format_checking      PARAMS ((int *, format_check_results *));
+static void finish_dollar_format_checking      PARAMS ((int *, format_check_results *, int));
 
 static const format_flag_spec *get_flag_spec   PARAMS ((const format_flag_spec *,
                                                         int, const char *));
@@ -1029,6 +1037,7 @@ status_warning VPARAMS ((int *status, const char *msgid, ...))
 
 /* Variables used by the checking of $ operand number formats.  */
 static char *dollar_arguments_used = NULL;
+static char *dollar_arguments_pointer_p = NULL;
 static int dollar_arguments_alloc = 0;
 static int dollar_arguments_count;
 static int dollar_first_arg_num;
@@ -1046,6 +1055,8 @@ init_dollar_format_checking (first_arg_num, params)
      int first_arg_num;
      tree params;
 {
+  tree oparams = params;
+
   dollar_first_arg_num = first_arg_num;
   dollar_arguments_count = 0;
   dollar_max_arg_used = 0;
@@ -1062,11 +1073,28 @@ init_dollar_format_checking (first_arg_num, params)
     {
       if (dollar_arguments_used)
        free (dollar_arguments_used);
+      if (dollar_arguments_pointer_p)
+       free (dollar_arguments_pointer_p);
       dollar_arguments_alloc = dollar_arguments_count;
       dollar_arguments_used = xmalloc (dollar_arguments_alloc);
+      dollar_arguments_pointer_p = xmalloc (dollar_arguments_alloc);
     }
   if (dollar_arguments_alloc)
-    memset (dollar_arguments_used, 0, dollar_arguments_alloc);
+    {
+      memset (dollar_arguments_used, 0, dollar_arguments_alloc);
+      if (first_arg_num > 0)
+       {
+         int i = 0;
+         params = oparams;
+         while (params)
+           {
+             dollar_arguments_pointer_p[i] = (TREE_CODE (TREE_TYPE (TREE_VALUE (params)))
+                                              == POINTER_TYPE);
+             params = TREE_CHAIN (params);
+             i++;
+           }
+       }
+    }
 }
 
 
@@ -1092,7 +1120,7 @@ maybe_read_dollar_number (status, format, dollar_needed, params, param_ptr,
   int argnum;
   int overflow_flag;
   const char *fcp = *format;
-  if (*fcp < '0' || *fcp > '9')
+  if (! ISDIGIT (*fcp))
     {
       if (dollar_needed)
        {
@@ -1104,7 +1132,7 @@ maybe_read_dollar_number (status, format, dollar_needed, params, param_ptr,
     }
   argnum = 0;
   overflow_flag = 0;
-  while (*fcp >= '0' && *fcp <= '9')
+  while (ISDIGIT (*fcp))
     {
       int nargnum;
       nargnum = 10 * argnum + (*fcp - '0');
@@ -1146,6 +1174,8 @@ maybe_read_dollar_number (status, format, dollar_needed, params, param_ptr,
       int nalloc;
       nalloc = 2 * dollar_arguments_alloc + 16;
       dollar_arguments_used = xrealloc (dollar_arguments_used, nalloc);
+      dollar_arguments_pointer_p = xrealloc (dollar_arguments_pointer_p,
+                                            nalloc);
       memset (dollar_arguments_used + dollar_arguments_alloc, 0,
              nalloc - dollar_arguments_alloc);
       dollar_arguments_alloc = nalloc;
@@ -1186,21 +1216,32 @@ maybe_read_dollar_number (status, format, dollar_needed, params, param_ptr,
    and for unused operands at the end of the format (if we know how many
    arguments the format had, so not for vprintf).  If there were operand
    numbers out of range on a non-vprintf-style format, we won't have reached
-   here.  */
+   here.  If POINTER_GAP_OK, unused arguments are OK if all arguments are
+   pointers.  */
 
 static void
-finish_dollar_format_checking (status, res)
+finish_dollar_format_checking (status, res, pointer_gap_ok)
      int *status;
      format_check_results *res;
+     int pointer_gap_ok;
 {
   int i;
+  bool found_pointer_gap = false;
   for (i = 0; i < dollar_max_arg_used; i++)
     {
       if (!dollar_arguments_used[i])
-       status_warning (status, "format argument %d unused before used argument %d in $-style format",
-                i + 1, dollar_max_arg_used);
+       {
+         if (pointer_gap_ok && (dollar_first_arg_num == 0
+                                || dollar_arguments_pointer_p[i]))
+           found_pointer_gap = true;
+         else
+           status_warning (status, "format argument %d unused before used argument %d in $-style format",
+                           i + 1, dollar_max_arg_used);
+       }
     }
-  if (dollar_first_arg_num && dollar_max_arg_used < dollar_arguments_count)
+  if (found_pointer_gap
+      || (dollar_first_arg_num
+         && dollar_max_arg_used < dollar_arguments_count))
     {
       res->number_other--;
       res->number_dollar_extra_args++;
@@ -1325,8 +1366,9 @@ check_format_info (status, info, params)
       && res.number_other == 0 && warn_format_extra_args)
     status_warning (status, "unused arguments in $-style format");
   if (res.number_empty > 0 && res.number_non_literal == 0
-      && res.number_other == 0)
-    status_warning (status, "zero-length format string");
+      && res.number_other == 0 && warn_format_zero_length)
+    status_warning (status, "zero-length %s format string",
+                   format_types[info->format_type].name);
 
   if (res.number_wide > 0)
     status_warning (status, "format is a wide character string");
@@ -1480,13 +1522,12 @@ check_format_info_recurse (status, res, info, format_tree, params, arg_num)
          res->number_non_literal++;
          return;
        }
-      if (!host_integerp (arg1, 1))
+      if (!host_integerp (arg1, 0)
+         || (offset = tree_low_cst (arg1, 0)) < 0)
        {
          res->number_non_literal++;
          return;
        }
-
-      offset = TREE_INT_CST_LOW (arg1);
     }
   if (TREE_CODE (format_tree) != ADDR_EXPR)
     {
@@ -1639,7 +1680,7 @@ check_format_info_main (status, res, info, format_chars, format_length,
              res->number_extra_args++;
            }
          if (has_operand_number > 0)
-           finish_dollar_format_checking (status, res);
+           finish_dollar_format_checking (status, res, fki->flags & (int) FMT_FLAG_DOLLAR_GAP_POINTER_OK);
          return;
        }
       if (*format_chars++ != '%')
@@ -1716,11 +1757,6 @@ check_format_info_main (status, res, info, format_chars, format_length,
              /* "...a field width...may be indicated by an asterisk.
                 In this case, an int argument supplies the field width..."  */
              ++format_chars;
-             if (params == 0)
-               {
-                 status_warning (status, "too few arguments for format");
-                 return;
-               }
              if (has_operand_number != 0)
                {
                  int opnum;
@@ -1740,6 +1776,11 @@ check_format_info_main (status, res, info, format_chars, format_length,
                }
              if (info->first_arg_num != 0)
                {
+                 if (params == 0)
+                   {
+                     status_warning (status, "too few arguments for format");
+                     return;
+                   }
                  cur_param = TREE_VALUE (params);
                  if (has_operand_number <= 0)
                    {
@@ -2223,7 +2264,6 @@ check_format_types (status, types)
       tree cur_type;
       tree orig_cur_type;
       tree wanted_type;
-      tree promoted_type;
       int arg_num;
       int i;
       int char_type_flag;
@@ -2242,11 +2282,7 @@ check_format_types (status, types)
        abort ();
 
       if (types->pointer_count == 0)
-       {
-         promoted_type = simple_type_promotes_to (wanted_type);
-         if (promoted_type != NULL_TREE)
-           wanted_type = promoted_type;
-       }
+       wanted_type = (*lang_hooks.types.type_promotes_to) (wanted_type);
 
       STRIP_NOPS (cur_param);
 
@@ -2351,8 +2387,8 @@ check_format_types (status, types)
          && TREE_CODE (cur_type) == INTEGER_TYPE
          && (! pedantic || i == 0 || (i == 1 && char_type_flag))
          && (TREE_UNSIGNED (wanted_type)
-             ? wanted_type == unsigned_type (cur_type)
-             : wanted_type == signed_type (cur_type)))
+             ? wanted_type == c_common_unsigned_type (cur_type)
+             : wanted_type == c_common_signed_type (cur_type)))
        continue;
       /* Likewise, "signed char", "unsigned char" and "char" are
         equivalent but the above test won't consider them equivalent.  */
@@ -2362,8 +2398,8 @@ check_format_types (status, types)
        continue;
       /* Now we have a type mismatch.  */
       {
-       register const char *this;
-       register const char *that;
+       const char *this;
+       const char *that;
 
        this = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (wanted_type)));
        that = 0;
@@ -2385,9 +2421,9 @@ check_format_types (status, types)
        if (that == 0)
          {
            if (TREE_CODE (orig_cur_type) == POINTER_TYPE)
-             that = "pointer";
+             that = _("pointer");
            else
-             that = "different type";
+             that = _("different type");
          }
 
        /* Make the warning better in case of mismatch of int vs long.  */