/* Subroutines shared by all languages that are variants of C.
- Copyright (C) 1992 Free Software Foundation, Inc.
+ Copyright (C) 1992, 1993 Free Software Foundation, Inc.
This file is part of GNU CC.
#include "flags.h"
#include "obstack.h"
#include <stdio.h>
+#include <ctype.h>
extern struct obstack permanent_obstack;
+static void declare_hidden_char_array PROTO((char *, char *));
+
/* Make bindings for __FUNCTION__ and __PRETTY_FUNCTION__. */
void
declare_function_name ()
{
- tree decl, type, init;
char *name, *printable_name;
- int len;
if (current_function_decl == NULL)
{
printable_name = (*decl_printable_name) (current_function_decl, &kind);
}
- /* If the default size of char arrays isn't big enough for the name,
- make a bigger one. */
- len = strlen (name) + 1;
- type = char_array_type_node;
- if (TREE_INT_CST_LOW (TYPE_MAX_VALUE (TREE_TYPE (char_array_type_node)))
- < len)
- type = build_array_type (char_type_node,
- build_index_type (build_int_2 (len, 0)));
+ declare_hidden_char_array ("__FUNCTION__", name);
+ declare_hidden_char_array ("__PRETTY_FUNCTION__", printable_name);
+}
- push_obstacks_nochange ();
- decl = build_decl (VAR_DECL, get_identifier ("__FUNCTION__"), type);
- TREE_STATIC (decl) = 1;
- TREE_READONLY (decl) = 1;
- DECL_SOURCE_LINE (decl) = 0;
- DECL_IN_SYSTEM_HEADER (decl) = 1;
- DECL_IGNORED_P (decl) = 1;
- init = build_string (len, name);
- TREE_TYPE (init) = type;
- DECL_INITIAL (decl) = init;
- finish_decl (pushdecl (decl), init, NULL_TREE);
+static void
+declare_hidden_char_array (name, value)
+ char *name, *value;
+{
+ tree decl, type, init;
+ int vlen;
- len = strlen (printable_name) + 1;
+ /* If the default size of char arrays isn't big enough for the name,
+ make a bigger one. */
+ vlen = strlen (value) + 1;
type = char_array_type_node;
- if (TREE_INT_CST_LOW (TYPE_MAX_VALUE (TREE_TYPE (char_array_type_node)))
- < len)
+ if (TREE_INT_CST_LOW (TYPE_MAX_VALUE (TREE_TYPE (type))) < vlen)
type = build_array_type (char_type_node,
- build_index_type (build_int_2 (len, 0)));
+ build_index_type (build_int_2 (vlen, 0)));
push_obstacks_nochange ();
- decl = build_decl (VAR_DECL, get_identifier ("__PRETTY_FUNCTION__"), type);
+ decl = build_decl (VAR_DECL, get_identifier (name), type);
TREE_STATIC (decl) = 1;
TREE_READONLY (decl) = 1;
+ TREE_ASM_WRITTEN (decl) = 1;
DECL_SOURCE_LINE (decl) = 0;
DECL_IN_SYSTEM_HEADER (decl) = 1;
DECL_IGNORED_P (decl) = 1;
- init = build_string (len, printable_name);
+ init = build_string (vlen, value);
TREE_TYPE (init) = type;
DECL_INITIAL (decl) = init;
finish_decl (pushdecl (decl), init, NULL_TREE);
/* We can't set DECL_PACKED for a VAR_DECL, because the bit is
used for DECL_REGISTER. It wouldn't mean anything anyway. */
}
+ else if (TREE_VALUE (a) == get_identifier ("noreturn")
+ || TREE_VALUE (a) == get_identifier ("volatile"))
+ {
+ if (TREE_CODE (decl) == FUNCTION_DECL)
+ TREE_THIS_VOLATILE (decl) = 1;
+ }
+ else if (TREE_VALUE (a) == get_identifier ("const"))
+ {
+ if (TREE_CODE (decl) == FUNCTION_DECL)
+ TREE_READONLY (decl) = 1;
+ }
else if (TREE_VALUE (a) != 0
&& TREE_CODE (TREE_VALUE (a)) == TREE_LIST
&& TREE_PURPOSE (TREE_VALUE (a)) == get_identifier ("mode"))
&& TREE_CODE (TREE_VALUE (a)) == TREE_LIST
&& TREE_PURPOSE (TREE_VALUE (a)) == get_identifier ("aligned"))
{
- int align = TREE_INT_CST_LOW (TREE_VALUE (TREE_VALUE (a)))
- * BITS_PER_UNIT;
+ tree align_expr = TREE_VALUE (TREE_VALUE (a));
+ int align;
+
+ /* Strip any NOPs of any kind. */
+ while (TREE_CODE (align_expr) == NOP_EXPR
+ || TREE_CODE (align_expr) == CONVERT_EXPR
+ || TREE_CODE (align_expr) == NON_LVALUE_EXPR)
+ align_expr = TREE_OPERAND (align_expr, 0);
+
+ if (TREE_CODE (align_expr) != INTEGER_CST)
+ {
+ error_with_decl (decl,
+ "requested alignment of `%s' is not a constant");
+ continue;
+ }
+
+ align = TREE_INT_CST_LOW (align_expr) * BITS_PER_UNIT;
if (exact_log2 (align) == -1)
error_with_decl (decl,
{
tree list = TREE_VALUE (TREE_VALUE (a));
tree format_type = TREE_PURPOSE (list);
- int format_num = TREE_INT_CST_LOW (TREE_PURPOSE (TREE_VALUE (list)));
- int first_arg_num = TREE_INT_CST_LOW (TREE_VALUE (TREE_VALUE (list)));
+ tree format_num_expr = TREE_PURPOSE (TREE_VALUE (list));
+ tree first_arg_num_expr = TREE_VALUE (TREE_VALUE (list));
+ int format_num;
+ int first_arg_num;
int is_scan;
tree argument;
int arg_num;
{
error_with_decl (decl,
"argument format specified for non-function `%s'");
- return;
+ continue;
}
if (format_type == get_identifier ("printf"))
else
{
error_with_decl (decl, "unrecognized format specifier for `%s'");
- return;
+ continue;
}
+ /* 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_CODE (first_arg_num_expr) != INTEGER_CST)
+ {
+ error_with_decl (decl,
+ "format string for `%s' has non-constant operand number");
+ continue;
+ }
+
+ 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_with_decl (decl,
- "format string arg follows the args to be formatted, for `%s'");
- return;
+ "format string arg follows the args to be formatted, for `%s'");
+ continue;
}
/* Verify that the format_num argument is actually a string, in case
{
error_with_decl (decl,
"format string arg not a string type, for `%s'");
- return;
+ continue;
}
if (first_arg_num != 0)
{
{
error_with_decl (decl,
"args to be formatted is not ..., for `%s'");
- return;
+ continue;
}
}
-
- record_format_info (DECL_NAME (decl), is_scan, format_num,
- first_arg_num);
+
+ record_function_format (DECL_NAME (decl), DECL_ASSEMBLER_NAME (decl),
+ is_scan, format_num, first_arg_num);
}
}
\f
+/* Check a printf/fprintf/sprintf/scanf/fscanf/sscanf format against
+ a parameter list. */
+
+#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
+#define T_ST &sizetype
+
+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_char_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" },
+/* Two GNU extensions. */
+ { "Z", 0, T_ST, NULL, NULL, NULL, "-wp0" },
+ { "m", 0, T_UI, T_UI, T_UL, NULL, "-wp" },
+ { "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_char_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, "*a" },
+ { "[", 1, T_C, NULL, NULL, NULL, "*a" },
+ { "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 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++) */
+ 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_format_info;
+
+static function_format_info *function_format_list = NULL;
+
+static void check_format_info PROTO((function_format_info *, tree));
+
+/* 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_function_format_info ()
+{
+ record_function_format (get_identifier ("printf"), NULL_TREE, 0, 1, 2);
+ record_function_format (get_identifier ("fprintf"), NULL_TREE, 0, 2, 3);
+ record_function_format (get_identifier ("sprintf"), NULL_TREE, 0, 2, 3);
+ record_function_format (get_identifier ("scanf"), NULL_TREE, 1, 1, 2);
+ record_function_format (get_identifier ("fscanf"), NULL_TREE, 1, 2, 3);
+ record_function_format (get_identifier ("sscanf"), NULL_TREE, 1, 2, 3);
+ record_function_format (get_identifier ("vprintf"), NULL_TREE, 0, 1, 0);
+ record_function_format (get_identifier ("vfprintf"), NULL_TREE, 0, 2, 0);
+ record_function_format (get_identifier ("vsprintf"), NULL_TREE, 0, 2, 0);
+}
+
+/* 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_function_format (name, assembler_name, is_scan,
+ format_num, first_arg_num)
+ tree name;
+ tree assembler_name;
+ int is_scan;
+ 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->is_scan = is_scan;
+ info->format_num = format_num;
+ info->first_arg_num = first_arg_num;
+}
+
+static char tfaff[] = "too few arguments for format";
+\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.)
+ PARAMS is the list of argument values. */
+
+void
+check_function_format (name, assembler_name, params)
+ 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 (info, params);
+ break;
+ }
+ }
+}
+
+/* Check the argument list of a call to printf, scanf, etc.
+ INFO points to the function_format_info structure.
+ PARAMS is the list of argument values. */
+
+static void
+check_format_info (info, params)
+ function_format_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;
+ tree first_fillin_param;
+ char *format_chars;
+ format_char_info *fci;
+ static char message[132];
+ char flag_chars[8];
+ int has_operand_number = 0;
+
+ /* 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;
+ }
+
+ first_fillin_param = params;
+ while (1)
+ {
+ int aflag;
+ 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 && ! has_operand_number)
+ 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
+ {
+ /* See if we have a number followed by a dollar sign. If we do,
+ it is an operand number, so set PARAMS to that operand. */
+ if (*format_chars >= '0' && *format_chars <= '9')
+ {
+ char *p = format_chars;
+
+ while (*p >= '0' && *p++ <= '9')
+ ;
+
+ if (*p == '$')
+ {
+ int opnum = atoi (format_chars);
+
+ params = first_fillin_param;
+ format_chars = p + 1;
+ has_operand_number = 1;
+
+ for (i = 1; i < opnum && params != 0; i++)
+ params = TREE_CHAIN (params);
+
+ if (opnum == 0 || params == 0)
+ {
+ warning ("operand number out of range in format");
+ return;
+ }
+ }
+ }
+
+ 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)
+ &&
+ (TYPE_MAIN_VARIANT (TREE_TYPE (cur_param))
+ != unsigned_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;
+ aflag = 0;
+ if (*format_chars == 'a')
+ {
+ aflag = 1;
+ format_chars++;
+ }
+ 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_char_table : print_char_table;
+ while (fci->format_chars != 0
+ && index (fci->format_chars, format_char) == 0)
+ ++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 (aflag && index (fci->flag_chars, 'a') == 0)
+ {
+ sprintf (message, "`a' flag 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 (TYPE_MAIN_VARIANT (cur_type)) == INTEGER_TYPE
+ && (TREE_UNSIGNED (wanted_type)
+ ? wanted_type == (cur_type = unsigned_type (cur_type))
+ : wanted_type == (cur_type = signed_type (cur_type))))
+ /* Likewise, "signed char", "unsigned char" and "char" are
+ equivalent but the above test won't consider them equivalent. */
+ && ! (wanted_type == char_type_node
+ && (TYPE_MAIN_VARIANT (cur_type) == signed_char_type_node
+ || TYPE_MAIN_VARIANT (cur_type) == unsigned_char_type_node)))
+ {
+ 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";
+ }
+
+ /* Make the warning better in case of mismatch of int vs long. */
+ if (TREE_CODE (cur_type) == INTEGER_TYPE
+ && TREE_CODE (wanted_type) == INTEGER_TYPE
+ && TYPE_PRECISION (cur_type) == TYPE_PRECISION (wanted_type)
+ && TYPE_NAME (cur_type) != 0
+ && TREE_CODE (TYPE_NAME (cur_type)) == TYPE_DECL)
+ that = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (cur_type)));
+
+ if (strcmp (this, that) != 0)
+ {
+ sprintf (message, "%s format, %s arg (arg %d)",
+ this, that, arg_num);
+ warning (message);
+ }
+ }
+ }
+}
+\f
/* Print a warning if a constant expression had overflow in folding.
Invoke this function on every expression that the language
requires to be a constant expression.
tree value;
{
if (TREE_CODE (value) == INTEGER_CST && TREE_CONSTANT_OVERFLOW (value))
- {
- /* ??? This is a warning, not a pedwarn, in 2.4,
- because it happens in contexts that are not
- "constant expressions" in ANSI C.
- Fix the problem differently in 2.5. */
- warning ("overflow in constant expression");
- /* Suppress duplicate warnings. */
- TREE_CONSTANT_OVERFLOW (value) = 0;
- }
+ if (pedantic)
+ pedwarn ("overflow in constant expression");
}
/* Print a warning if an expression had overflow in folding.
overflow_warning (value)
tree value;
{
- if (TREE_CODE (value) == INTEGER_CST && TREE_CONSTANT_OVERFLOW (value))
+ if (TREE_CODE (value) == INTEGER_CST && TREE_OVERFLOW (value))
{
- /* ??? This is a warning, not a pedwarn, in 2.4,
- because it happens in contexts that are not
- "constant expressions" in ANSI C.
- Fix the problem differently in 2.5. */
+ TREE_OVERFLOW (value) = 0;
warning ("integer overflow in expression");
- TREE_CONSTANT_OVERFLOW (value) = 0;
}
}
{
if (!int_fits_type_p (operand, signed_type (TREE_TYPE (result))))
/* This detects cases like converting -129 or 256 to unsigned char. */
- pedwarn ("large integer implicitly truncated to unsigned type");
+ warning ("large integer implicitly truncated to unsigned type");
else if (warn_conversion)
- pedwarn ("negative integer implicitly converted to unsigned type");
+ warning ("negative integer implicitly converted to unsigned type");
}
}
tree t = convert (type, expr);
if (TREE_CODE (t) == INTEGER_CST)
{
- if (TREE_UNSIGNED (TREE_TYPE (expr))
- && !TREE_UNSIGNED (type)
- && TREE_CODE (TREE_TYPE (expr)) == INTEGER_TYPE
- && TYPE_PRECISION (type) == TYPE_PRECISION (TREE_TYPE (expr)))
- /* No warning for converting 0x80000000 to int. */
- TREE_CONSTANT_OVERFLOW (t) = 0;
- else if (TREE_CONSTANT_OVERFLOW (t))
+ if (TREE_OVERFLOW (t))
{
- /* ??? This is a warning, not a pedwarn, in 2.4,
- because it happens in contexts that are not
- "constant expressions" in ANSI C.
- Fix the problem differently in 2.5. */
- warning ("overflow in implicit constant conversion");
- TREE_CONSTANT_OVERFLOW (t) = 0;
+ TREE_OVERFLOW (t) = 0;
+
+ /* No warning for converting 0x80000000 to int. */
+ if (!(TREE_UNSIGNED (type) < TREE_UNSIGNED (TREE_TYPE (expr))
+ && TREE_CODE (TREE_TYPE (expr)) == INTEGER_TYPE
+ && TYPE_PRECISION (type) == TYPE_PRECISION (TREE_TYPE (expr))))
+ /* If EXPR fits in the unsigned version of TYPE,
+ don't warn unless pedantic. */
+ if (pedantic
+ || TREE_UNSIGNED (type)
+ || ! int_fits_type_p (expr, unsigned_type (type)))
+ warning ("overflow in implicit constant conversion");
}
else
unsigned_conversion_warning (t, expr);
/* Subroutine of build_binary_op, used for comparison operations.
See if the operands have both been converted from subword integer types
and, if so, perhaps change them both back to their original type.
+ This function is also responsible for converting the two operands
+ to the proper common type for comparison.
The arguments of this function are all pointers to local variables
of build_binary_op: OP0_PTR is &OP0, OP1_PTR is &OP1,
type = unsigned_type (type);
}
- if (!max_gt && !unsignedp0)
+ if (!max_gt && !unsignedp0 && TREE_CODE (primop0) != INTEGER_CST)
{
/* This is the case of (char)x >?< 0x80, which people used to use
expecting old C compilers to change the 0x80 into -0x80. */
warning ("comparison is always 1 due to limited range of data type");
}
- if (!min_lt && unsignedp0)
+ if (!min_lt && unsignedp0 && TREE_CODE (primop0) != INTEGER_CST)
{
/* This is the case of (unsigned char)x >?< -1 or < 0. */
if (val == integer_zero_node)
case COMPLEX_EXPR:
return build_binary_op ((TREE_SIDE_EFFECTS (TREE_OPERAND (expr, 1))
- ? TRUTH_AND_EXPR : TRUTH_ANDIF_EXPR),
+ ? TRUTH_OR_EXPR : TRUTH_ORIF_EXPR),
truthvalue_conversion (TREE_OPERAND (expr, 0)),
truthvalue_conversion (TREE_OPERAND (expr, 1)),
0);
if (TREE_CODE (TREE_TYPE (expr)) == COMPLEX_TYPE)
return (build_binary_op
((TREE_SIDE_EFFECTS (expr)
- ? TRUTH_AND_EXPR : TRUTH_ANDIF_EXPR),
+ ? TRUTH_OR_EXPR : TRUTH_ORIF_EXPR),
truthvalue_conversion (build_unary_op (REALPART_EXPR, expr, 0)),
truthvalue_conversion (build_unary_op (IMAGPART_EXPR, expr, 0)),
0));
if (TREE_CODE (type) == ARRAY_TYPE)
{
tree real_main_variant = TYPE_MAIN_VARIANT (type);
- int permanent = TREE_PERMANENT (type);
- if (permanent)
- push_obstacks (&permanent_obstack, &permanent_obstack);
+ push_obstacks (TYPE_OBSTACK (type), TYPE_OBSTACK (type));
type = build_array_type (c_build_type_variant (TREE_TYPE (type),
constp, volatilep),
TYPE_DOMAIN (type));
TYPE_MAIN_VARIANT (type) = real_main_variant;
- if (permanent)
- pop_obstacks ();
+ pop_obstacks ();
}
return build_type_variant (type, constp, volatilep);
}