(struct format_char_info...
authorRichard Kenner <kenner@gcc.gnu.org>
Tue, 5 Oct 1993 19:15:40 +0000 (15:15 -0400)
committerRichard Kenner <kenner@gcc.gnu.org>
Tue, 5 Oct 1993 19:15:40 +0000 (15:15 -0400)
(struct format_char_info, print_table, scan_table, struct
function_info, function_info_entries, function_info_table,
record_format_info, init_format_info_table, check_format): Moved to
c-common.c.
(build_function_call): Call check_function_format.

From-SVN: r5608

gcc/c-typeck.c

index 483119a60d270f6ef0f4e339eeb19ccea1d466bc..82d4fa4889f8d294b90f876ed75ccffcc4ab09ee 100644 (file)
@@ -1403,541 +1403,6 @@ build_array_ref (array, index)
   }
 }
 \f
-/* Check a printf/fprintf/sprintf/scanf/fscanf/sscanf format against PARAMS.  */
-
-#define ISDIGIT(c)     ((c) >= '0' && (c) <= '9')
-
-#define T_I    &integer_type_node
-#define T_L    &long_integer_type_node
-#define T_S    &short_integer_type_node
-#define T_UI   &unsigned_type_node
-#define T_UL   &long_unsigned_type_node
-#define T_US   &short_unsigned_type_node
-#define T_F    &float_type_node
-#define T_D    &double_type_node
-#define T_LD   &long_double_type_node
-#define T_C    &char_type_node
-#define T_V    &void_type_node
-#define T_W    &wchar_type_node
-
-typedef struct
-{
-  char *format_chars;
-  int pointer_count;
-  /* Type of argument if no length modifier is used.  */
-  tree *nolen;
-  /* Type of argument if length modifier for shortening is used.
-     If NULL, then this modifier is not allowed.  */
-  tree *hlen;
-  /* Type of argument if length modifier `l' is used.
-     If NULL, then this modifier is not allowed.  */
-  tree *llen;
-  /* Type of argument if length modifier `L' is used.
-     If NULL, then this modifier is not allowed.  */
-  tree *bigllen;
-  /* List of other modifier characters allowed with these options.  */
-  char *flag_chars;
-} format_char_info;
-
-static format_char_info print_table[]
-  = {
-      { "di",          0,      T_I,    T_I,    T_L,    NULL,   "-wp0 +" },
-      { "oxX",         0,      T_UI,   T_UI,   T_UL,   NULL,   "-wp0#" },
-      { "u",           0,      T_UI,   T_UI,   T_UL,   NULL,   "-wp0" },
-      { "feEgG",       0,      T_D,    NULL,   NULL,   T_LD,   "-wp0 +#" },
-      { "c",           0,      T_I,    NULL,   T_W,    NULL,   "-w" },
-      { "C",           0,      T_W,    NULL,   NULL,   NULL,   "-w" },
-      { "s",           1,      T_C,    NULL,   T_W,    NULL,   "-wp" },
-      { "S",           1,      T_W,    NULL,   NULL,   NULL,   "-wp" },
-      { "p",           1,      T_V,    NULL,   NULL,   NULL,   "-w" },
-      { "n",           1,      T_I,    T_S,    T_L,    NULL,   "" },
-      { NULL }
-    };
-
-static format_char_info scan_table[]
-  = {
-      { "di",          1,      T_I,    T_S,    T_L,    NULL,   "*" },
-      { "ouxX",                1,      T_UI,   T_US,   T_UL,   NULL,   "*" },  
-      { "efgEG",       1,      T_F,    NULL,   T_D,    T_LD,   "*" },
-      { "sc",          1,      T_C,    NULL,   T_W,    NULL,   "*" },
-      { "[",           1,      T_C,    NULL,   NULL,   NULL,   "*" },
-      { "C",           1,      T_W,    NULL,   NULL,   NULL,   "*" },
-      { "S",           1,      T_W,    NULL,   NULL,   NULL,   "*" },
-      { "p",           2,      T_V,    NULL,   NULL,   NULL,   "*" },
-      { "n",           1,      T_I,    T_S,    T_L,    NULL,   "" },
-      { NULL }
-    };
-
-typedef struct
-{
-  tree function_ident;         /* identifier such as "printf" */
-  int is_scan;                 /* TRUE if *scanf */
-  int format_num;              /* number of format argument */
-  int first_arg_num;           /* number of first arg (zero for varargs) */
-} function_info;
-
-static unsigned int function_info_entries = 0;
-static function_info *function_info_table = 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).  IS_SCAN is true for scanf-type format checking;
-   false indicates printf-style 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 teh format string, or zero if no checking is not be done
-   (e.g. for varargs such as vfprintf).  */
-
-void
-record_format_info (function_ident, is_scan, format_num, first_arg_num)
-      tree function_ident;
-      int is_scan;
-      int format_num;
-      int first_arg_num;
-{
-  function_info *info;
-  
-  function_info_entries++;
-  if (function_info_table)
-    function_info_table
-      = (function_info *) xrealloc (function_info_table,
-                                   function_info_entries * sizeof (function_info));
-  else
-    function_info_table = (function_info *) xmalloc (sizeof (function_info));
-
-  info = &function_info_table[function_info_entries - 1];
-  
-  info->function_ident = function_ident;
-  info->is_scan = is_scan;
-  info->format_num = format_num;
-  info->first_arg_num = first_arg_num;
-}
-
-/* Initialize the table of functions to perform format checking on.
-   The ANSI functions are always checked (whether <stdio.h> is
-   included or not), since it is common to call printf without
-   including <stdio.h>.  There shouldn't be a problem with this,
-   since ANSI reserves these function names whether you include the
-   header file or not.  In any case, the checking is harmless.  */
-
-void
-init_format_info_table ()
-{
-  record_format_info (get_identifier ("printf"), 0, 1, 2);
-  record_format_info (get_identifier ("fprintf"), 0, 2, 3);
-  record_format_info (get_identifier ("sprintf"), 0, 2, 3);
-  record_format_info (get_identifier ("scanf"), 1, 1, 2);
-  record_format_info (get_identifier ("fscanf"), 1, 2, 3);
-  record_format_info (get_identifier ("sscanf"), 1, 2, 3);
-  record_format_info (get_identifier ("vprintf"), 0, 1, 0);
-  record_format_info (get_identifier ("vfprintf"), 0, 2, 0);
-  record_format_info (get_identifier ("vsprintf"), 0, 2, 0);
-}
-
-static char    tfaff[] = "too few arguments for format";
-\f
-/* Check the argument list of a call to printf, scanf, etc.
-   INFO points to the element of function_info_table.
-   PARAMS is the list of argument values.  */
-
-static void
-check_format (info, params)
-     function_info *info;
-     tree params;
-{
-  int i;
-  int arg_num;
-  int suppressed, wide, precise;
-  int length_char;
-  int format_char;
-  int format_length;
-  tree format_tree;
-  tree cur_param;
-  tree cur_type;
-  tree wanted_type;
-  char *format_chars;
-  format_char_info *fci;
-  static char message[132];
-  char flag_chars[8];
-
-  /* Skip to format argument.  If the argument isn't available, there's
-     no work for us to do; prototype checking will catch the problem.  */
-  for (arg_num = 1; ; ++arg_num)
-    {
-      if (params == 0)
-       return;
-      if (arg_num == info->format_num)
-       break;
-      params = TREE_CHAIN (params);
-    }
-  format_tree = TREE_VALUE (params);
-  params = TREE_CHAIN (params);
-  if (format_tree == 0)
-    return;
-  /* We can only check the format if it's a string constant.  */
-  while (TREE_CODE (format_tree) == NOP_EXPR)
-    format_tree = TREE_OPERAND (format_tree, 0); /* strip coercion */
-  if (format_tree == null_pointer_node)
-    {
-      warning ("null format string");
-      return;
-    }
-  if (TREE_CODE (format_tree) != ADDR_EXPR)
-    return;
-  format_tree = TREE_OPERAND (format_tree, 0);
-  if (TREE_CODE (format_tree) != STRING_CST)
-    return;
-  format_chars = TREE_STRING_POINTER (format_tree);
-  format_length = TREE_STRING_LENGTH (format_tree);
-  if (format_length <= 1)
-    warning ("zero-length format string");
-  if (format_chars[--format_length] != 0)
-    {
-      warning ("unterminated format string");
-      return;
-    }
-  /* Skip to first argument to check.  */
-  while (arg_num + 1 < info->first_arg_num)
-    {
-      if (params == 0)
-       return;
-      params = TREE_CHAIN (params);
-      ++arg_num;
-    }
-  while (1)
-    {
-      if (*format_chars == 0)
-       {
-         if (format_chars - TREE_STRING_POINTER (format_tree) != format_length)
-           warning ("embedded `\\0' in format");
-         if (info->first_arg_num != 0 && params != 0)
-           warning ("too many arguments for format");
-         return;
-       }
-      if (*format_chars++ != '%')
-       continue;
-      if (*format_chars == 0)
-       {
-         warning ("spurious trailing `%%' in format");
-         continue;
-       }
-      if (*format_chars == '%')
-       {
-         ++format_chars;
-         continue;
-       }
-      flag_chars[0] = 0;
-      suppressed = wide = precise = FALSE;
-      if (info->is_scan)
-       {
-         suppressed = *format_chars == '*';
-         if (suppressed)
-           ++format_chars;
-         while (ISDIGIT (*format_chars))
-           ++format_chars;
-       }
-      else
-       {
-         while (*format_chars != 0 && index (" +#0-", *format_chars) != 0)
-           {
-             if (index (flag_chars, *format_chars) != 0)
-               {
-                 sprintf (message, "repeated `%c' flag in format",
-                          *format_chars);
-                 warning (message);
-               }
-             i = strlen (flag_chars);
-             flag_chars[i++] = *format_chars++;
-             flag_chars[i] = 0;
-           }
-         /* "If the space and + flags both appear, 
-            the space flag will be ignored."  */
-         if (index (flag_chars, ' ') != 0
-             && index (flag_chars, '+') != 0)
-           warning ("use of both ` ' and `+' flags in format");
-         /* "If the 0 and - flags both appear,
-            the 0 flag will be ignored."  */
-         if (index (flag_chars, '0') != 0
-             && index (flag_chars, '-') != 0)
-           warning ("use of both `0' and `-' flags in format");
-         if (*format_chars == '*')
-           {
-             wide = TRUE;
-             /* "...a field width...may be indicated by an asterisk.
-                In this case, an int argument supplies the field width..."  */
-             ++format_chars;
-             if (params == 0)
-               {
-                 warning (tfaff);
-                 return;
-               }
-             if (info->first_arg_num != 0)
-               {
-                 cur_param = TREE_VALUE (params);
-                 params = TREE_CHAIN (params);
-                 ++arg_num;
-                 /* size_t is generally not valid here.
-                    It will work on most machines, because size_t and int
-                    have the same mode.  But might as well warn anyway,
-                    since it will fail on other machines.  */
-                 if (TYPE_MAIN_VARIANT (TREE_TYPE (cur_param))
-                     != integer_type_node)
-                   {
-                     sprintf (message,
-                              "field width is not type int (arg %d)",
-                              arg_num);
-                     warning (message);
-                   }
-               }
-           }
-         else
-           {
-             while (ISDIGIT (*format_chars))
-               {
-                 wide = TRUE;
-                 ++format_chars;
-               }
-           }
-         if (*format_chars == '.')
-           {
-             precise = TRUE;
-             ++format_chars;
-             if (*format_chars != '*' && !ISDIGIT (*format_chars))
-               warning ("`.' not followed by `*' or digit in format");
-             /* "...a...precision...may be indicated by an asterisk.
-                In this case, an int argument supplies the...precision."  */
-             if (*format_chars == '*')
-               {
-                 if (info->first_arg_num != 0)
-                   {
-                     ++format_chars;
-                     if (params == 0)
-                       {
-                         warning (tfaff);
-                         return;
-                       }
-                     cur_param = TREE_VALUE (params);
-                     params = TREE_CHAIN (params);
-                     ++arg_num;
-                     if (TYPE_MAIN_VARIANT (TREE_TYPE (cur_param))
-                         != integer_type_node)
-                       {
-                         sprintf (message,
-                                  "field width is not type int (arg %d)",
-                                  arg_num);
-                         warning (message);
-                       }
-                   }
-               }
-             else
-               {
-                 while (ISDIGIT (*format_chars))
-                   ++format_chars;
-               }
-           }
-       }
-      if (*format_chars == 'h' || *format_chars == 'l' || *format_chars == 'L')
-       length_char = *format_chars++;
-      else
-       length_char = 0;
-      if (suppressed && length_char != 0)
-       {
-         sprintf (message,
-                  "use of `*' and `%c' together in format",
-                  length_char);
-         warning (message);
-       }
-      format_char = *format_chars;
-      if (format_char == 0)
-       {
-         warning ("conversion lacks type at end of format");
-         continue;
-       }
-      format_chars++;
-      fci = info->is_scan ? scan_table : print_table;
-      while (1)
-       {
-         if (fci->format_chars == 0
-             || index (fci->format_chars, format_char) != 0)
-           break;
-         ++fci;
-       }
-      if (fci->format_chars == 0)
-       {
-         if (format_char >= 040 && format_char < 0177)
-           sprintf (message,
-                    "unknown conversion type character `%c' in format",
-                    format_char);
-         else
-           sprintf (message,
-                    "unknown conversion type character 0x%x in format",
-                    format_char);
-         warning (message);
-         continue;
-       }
-      if (wide && index (fci->flag_chars, 'w') == 0)
-       {
-         sprintf (message, "width used with `%c' format",
-                  format_char);
-         warning (message);
-       }
-      if (precise && index (fci->flag_chars, 'p') == 0)
-       {
-         sprintf (message, "precision used with `%c' format",
-                  format_char);
-         warning (message);
-       }
-      if (info->is_scan && format_char == '[')
-       {
-         /* Skip over scan set, in case it happens to have '%' in it.  */
-         if (*format_chars == '^')
-           ++format_chars;
-         /* Find closing bracket; if one is hit immediately, then
-            it's part of the scan set rather than a terminator.  */
-         if (*format_chars == ']')
-           ++format_chars;
-         while (*format_chars && *format_chars != ']')
-           ++format_chars;
-         if (*format_chars != ']')
-             /* The end of the format string was reached.  */
-             warning ("no closing `]' for `%%[' format");
-       }
-      if (suppressed)
-       {
-         if (index (fci->flag_chars, '*') == 0)
-           {
-             sprintf (message,
-                      "suppression of `%c' conversion in format",
-                      format_char);
-             warning (message);
-           }
-         continue;
-       }
-      for (i = 0; flag_chars[i] != 0; ++i)
-       {
-         if (index (fci->flag_chars, flag_chars[i]) == 0)
-           {
-             sprintf (message, "flag `%c' used with type `%c'",
-                      flag_chars[i], format_char);
-             warning (message);
-           }
-       }
-      if (precise && index (flag_chars, '0') != 0
-         && (format_char == 'd' || format_char == 'i'
-             || format_char == 'o' || format_char == 'u'
-             || format_char == 'x' || format_char == 'x'))
-       {
-         sprintf (message,
-                  "precision and `0' flag not both allowed with `%c' format",
-                  format_char);
-         warning (message);
-       }
-      switch (length_char)
-       {
-       default: wanted_type = fci->nolen ? *(fci->nolen) : 0; break;
-       case 'h': wanted_type = fci->hlen ? *(fci->hlen) : 0; break;
-       case 'l': wanted_type = fci->llen ? *(fci->llen) : 0; break;
-       case 'L': wanted_type = fci->bigllen ? *(fci->bigllen) : 0; break;
-       }
-      if (wanted_type == 0)
-       {
-         sprintf (message,
-                  "use of `%c' length character with `%c' type character",
-                  length_char, format_char);
-         warning (message);
-       }
-
-      /*
-       ** XXX -- should kvetch about stuff such as
-       **      {
-       **              const int       i;
-       **
-       **              scanf ("%d", &i);
-       **      }
-       */
-
-      /* Finally. . .check type of argument against desired type!  */
-      if (info->first_arg_num == 0)
-       continue;
-      if (params == 0)
-       {
-         warning (tfaff);
-         return;
-       }
-      cur_param = TREE_VALUE (params);
-      params = TREE_CHAIN (params);
-      ++arg_num;
-      cur_type = TREE_TYPE (cur_param);
-
-      /* Check the types of any additional pointer arguments
-        that precede the "real" argument.  */
-      for (i = 0; i < fci->pointer_count; ++i)
-       {
-         if (TREE_CODE (cur_type) == POINTER_TYPE)
-           {
-             cur_type = TREE_TYPE (cur_type);
-             continue;
-           }
-         sprintf (message,
-                  "format argument is not a %s (arg %d)",
-                  ((fci->pointer_count == 1) ? "pointer" : "pointer to a pointer"),
-                  arg_num);
-         warning (message);
-         break;
-       }
-
-      /* Check the type of the "real" argument, if there's a type we want.  */
-      if (i == fci->pointer_count && wanted_type != 0
-         && wanted_type != TYPE_MAIN_VARIANT (cur_type)
-         /* If we want `void *', allow any pointer type.
-            (Anything else would already have got a warning.)  */
-         && ! (wanted_type == void_type_node
-               && fci->pointer_count > 0)
-         /* Don't warn about differences merely in signedness.  */
-         && !(TREE_CODE (wanted_type) == INTEGER_TYPE
-              && TREE_CODE (cur_type) == INTEGER_TYPE
-              && (wanted_type == (TREE_UNSIGNED (wanted_type)
-                                  ? unsigned_type : signed_type) (cur_type))))
-       {
-         register char *this;
-         register char *that;
-  
-         this = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (wanted_type)));
-         that = 0;
-         if (TREE_CODE (cur_type) != ERROR_MARK
-             && TYPE_NAME (cur_type) != 0
-             && TREE_CODE (cur_type) != INTEGER_TYPE
-             && !(TREE_CODE (cur_type) == POINTER_TYPE
-                  && TREE_CODE (TREE_TYPE (cur_type)) == INTEGER_TYPE))
-           {
-             if (TREE_CODE (TYPE_NAME (cur_type)) == TYPE_DECL
-                 && DECL_NAME (TYPE_NAME (cur_type)) != 0)
-               that = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (cur_type)));
-             else
-               that = IDENTIFIER_POINTER (TYPE_NAME (cur_type));
-           }
-
-         /* A nameless type can't possibly match what the format wants.
-            So there will be a warning for it.
-            Make up a string to describe vaguely what it is.  */
-         if (that == 0)
-           {
-             if (TREE_CODE (cur_type) == POINTER_TYPE)
-               that = "pointer";
-             else
-               that = "different type";
-           }
-
-         if (strcmp (this, that) != 0)
-           {
-             sprintf (message, "%s format, %s arg (arg %d)",
-                       this, that, arg_num);
-             warning (message);
-           }
-       }
-    }
-}
-\f
 /* Build a function call to function FUNCTION with parameters PARAMS.
    PARAMS is a list--a chain of TREE_LIST nodes--in which the
    TREE_VALUE of each node is a parameter-expression.
@@ -1949,7 +1414,7 @@ build_function_call (function, params)
 {
   register tree fntype, fundecl;
   register tree coerced_params;
-  tree name = NULL_TREE;
+  tree name = NULL_TREE, assembler_name = NULL_TREE;
 
   /* Strip NON_LVALUE_EXPRs, etc., since we aren't using as an lvalue.  */
   STRIP_TYPE_NOPS (function);
@@ -1958,6 +1423,8 @@ build_function_call (function, params)
   if (TREE_CODE (function) == FUNCTION_DECL)
     {
       name = DECL_NAME (function);
+      assembler_name = DECL_ASSEMBLER_NAME (function);
+
       /* Differs from default_conversion by not setting TREE_ADDRESSABLE
         (because calling an inline function does not mean the function
         needs to be separately compiled).  */
@@ -1992,21 +1459,9 @@ build_function_call (function, params)
     = convert_arguments (TYPE_ARG_TYPES (fntype), params, name, fundecl);
 
   /* Check for errors in format strings.  */
-  if (warn_format && name != 0)
-    {
-      unsigned int i;
-
-      /* See if this function is a format function.  */
-      for (i = 0; i < function_info_entries; i++)
-       if (function_info_table[i].function_ident == name)
-         {
-           register char *message;
 
-           /* If so, check it.  */
-           check_format (&function_info_table[i], coerced_params);
-           break;
-         }
-    }
+  if (warn_format && (name || assembler_name))
+    check_function_format (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