/* 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.
#include "c-common.h"
#include "intl.h"
#include "diagnostic.h"
-
+#include "langhooks.h"
\f
/* Command line options and their associated flags. */
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;
warn_format = setting;
warn_format_y2k = setting;
warn_format_extra_args = setting;
+ warn_format_zero_length = setting;
if (setting != 1)
{
warn_format_nonliteral = setting;
warn_format_security = setting;
}
+ /* Make sure not to disable -Wnonnull if -Wformat=0 is specified. */
+ if (setting)
+ warn_nonnull = setting;
}
\f
/* 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
} 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
{
#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
{
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 }
};
},
{ "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
},
int number_other;
} format_check_results;
+typedef struct
+{
+ format_check_results *res;
+ function_format_info *info;
+ tree params;
+ int *status;
+} format_check_context;
+
static void check_format_info PARAMS ((int *, function_format_info *, tree));
-static void check_format_info_recurse PARAMS ((int *, format_check_results *,
- function_format_info *, tree,
- tree, unsigned HOST_WIDE_INT));
+static void check_format_arg PARAMS ((void *, tree, unsigned HOST_WIDE_INT));
static void check_format_info_main PARAMS ((int *, format_check_results *,
function_format_info *,
const char *, 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 *));
static void
status_warning VPARAMS ((int *status, const char *msgid, ...))
{
- diagnostic_context dc;
+ diagnostic_info diagnostic ;
VA_OPEN (ap, msgid);
VA_FIXEDARG (ap, int *, status);
else
{
/* This duplicates the warning function behavior. */
- set_diagnostic_context
- (&dc, msgid, &ap, input_filename, lineno, /* warn = */ 1);
- report_diagnostic (&dc);
+ diagnostic_set_info (&diagnostic, _(msgid), &ap, input_filename, lineno,
+ DK_WARNING);
+ report_diagnostic (&diagnostic);
}
VA_CLOSE (ap);
/* 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;
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;
{
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++;
+ }
+ }
+ }
}
int argnum;
int overflow_flag;
const char *fcp = *format;
- if (*fcp < '0' || *fcp > '9')
+ if (! ISDIGIT (*fcp))
{
if (dollar_needed)
{
}
argnum = 0;
overflow_flag = 0;
- while (*fcp >= '0' && *fcp <= '9')
+ while (ISDIGIT (*fcp))
{
int nargnum;
nargnum = 10 * argnum + (*fcp - '0');
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;
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++;
function_format_info *info;
tree params;
{
+ format_check_context format_ctx;
unsigned HOST_WIDE_INT arg_num;
tree format_tree;
format_check_results res;
res.number_unterminated = 0;
res.number_other = 0;
- check_format_info_recurse (status, &res, info, format_tree, params, arg_num);
+ format_ctx.res = &res;
+ format_ctx.info = info;
+ format_ctx.params = params;
+ format_ctx.status = status;
+
+ check_function_arguments_recurse (check_format_arg, &format_ctx,
+ format_tree, arg_num);
if (res.number_non_literal > 0)
{
&& 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");
status_warning (status, "unterminated format string");
}
-
-/* Recursively check a call to a format function. FORMAT_TREE is the
- format parameter, which may be a conditional expression in which
- both halves should be checked. ARG_NUM is the number of the
- format argument; PARAMS points just after it in the argument list. */
+/* Callback from check_function_arguments_recurse to check a
+ format string. FORMAT_TREE is the format parameter. ARG_NUM
+ is the number of the format argument. CTX points to a
+ format_check_context. */
static void
-check_format_info_recurse (status, res, info, format_tree, params, arg_num)
- int *status;
- format_check_results *res;
- function_format_info *info;
+check_format_arg (ctx, format_tree, arg_num)
+ void *ctx;
tree format_tree;
- tree params;
unsigned HOST_WIDE_INT arg_num;
{
+ format_check_context *format_ctx = ctx;
+ format_check_results *res = format_ctx->res;
+ function_format_info *info = format_ctx->info;
+ tree params = format_ctx->params;
+ int *status = format_ctx->status;
+
int format_length;
HOST_WIDE_INT offset;
const char *format_chars;
tree array_size = 0;
tree array_init;
- if (TREE_CODE (format_tree) == NOP_EXPR)
- {
- /* Strip coercion. */
- check_format_info_recurse (status, res, info,
- TREE_OPERAND (format_tree, 0), params,
- arg_num);
- return;
- }
-
- if (TREE_CODE (format_tree) == CALL_EXPR)
- {
- tree type = TREE_TYPE (TREE_TYPE (TREE_OPERAND (format_tree, 0)));
- tree attrs;
- bool found_format_arg = false;
-
- /* See if this is a call to a known internationalization function
- that modifies the format arg. Such a function may have multiple
- format_arg attributes (for example, ngettext). */
-
- for (attrs = TYPE_ATTRIBUTES (type);
- attrs;
- attrs = TREE_CHAIN (attrs))
- if (is_attribute_p ("format_arg", TREE_PURPOSE (attrs)))
- {
- tree inner_args;
- tree format_num_expr;
- int format_num;
- int i;
-
- /* Extract the argument number, which was previously checked
- to be valid. */
- format_num_expr = TREE_VALUE (TREE_VALUE (attrs));
- while (TREE_CODE (format_num_expr) == NOP_EXPR
- || TREE_CODE (format_num_expr) == CONVERT_EXPR
- || TREE_CODE (format_num_expr) == NON_LVALUE_EXPR)
- format_num_expr = TREE_OPERAND (format_num_expr, 0);
-
- if (TREE_CODE (format_num_expr) != INTEGER_CST
- || TREE_INT_CST_HIGH (format_num_expr) != 0)
- abort ();
-
- format_num = TREE_INT_CST_LOW (format_num_expr);
-
- for (inner_args = TREE_OPERAND (format_tree, 1), i = 1;
- inner_args != 0;
- inner_args = TREE_CHAIN (inner_args), i++)
- if (i == format_num)
- {
- check_format_info_recurse (status, res, info,
- TREE_VALUE (inner_args), params,
- arg_num);
- found_format_arg = true;
- break;
- }
- }
-
- /* If we found a format_arg attribute and did a recursive check,
- we are done with checking this format string. Otherwise, we
- continue and this will count as a non-literal format string. */
- if (found_format_arg)
- return;
- }
-
- if (TREE_CODE (format_tree) == COND_EXPR)
- {
- /* Check both halves of the conditional expression. */
- check_format_info_recurse (status, res, info,
- TREE_OPERAND (format_tree, 1), params,
- arg_num);
- check_format_info_recurse (status, res, info,
- TREE_OPERAND (format_tree, 2), params,
- arg_num);
- return;
- }
-
if (integer_zerop (format_tree))
{
- /* FIXME: this warning should go away once Marc Espie's
- __attribute__((nonnull)) patch is in. Instead, checking for
- nonnull attributes should probably change this function to act
- specially if info == NULL and add a res->number_null entry for
- that case, or maybe add a function pointer to be called at
- the end instead of hardcoding check_format_info_main. */
- status_warning (status, "null format string");
-
/* Skip to first argument to check, so we can see if this format
has any arguments (it shouldn't). */
while (arg_num + 1 < info->first_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)
{
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++ != '%')
/* "...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;
}
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)
{
tree cur_type;
tree orig_cur_type;
tree wanted_type;
- tree promoted_type;
int arg_num;
int i;
int char_type_flag;
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);
&& 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. */
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. */