/* Check calls to formatted I/O functions (-Wformat).
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
- 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+ 2001, 2002, 2003, 2004, 2005, 2007 Free Software Foundation, Inc.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
-Software Foundation; either version 2, or (at your option) any later
+Software Foundation; either version 3, or (at your option) any later
version.
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
for more details.
You should have received a copy of the GNU General Public License
-along with GCC; see the file COPYING. If not, write to the Free
-Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
-02110-1301, USA. */
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
#include "config.h"
#include "system.h"
#include "diagnostic.h"
#include "langhooks.h"
#include "c-format.h"
+#include "alloc-pool.h"
\f
/* Set format warning options according to a -Wformat=n option. */
warn_format = setting;
warn_format_extra_args = setting;
warn_format_zero_length = setting;
+ warn_format_contains_nul = setting;
if (setting != 1)
{
warn_format_nonliteral = setting;
{ NULL, 0, 0, NULL, 0, 0 }
};
+
+/* For now, the Fortran front-end routines only use l as length modifier. */
+static const format_length_info gcc_gfc_length_specs[] =
+{
+ { "l", FMT_LEN_l, STD_C89, NULL, 0, 0 },
+ { NULL, 0, 0, NULL, 0, 0 }
+};
+
+
static const format_flag_spec printf_flag_specs[] =
{
{ ' ', 0, 0, N_("' ' flag"), N_("the ' ' printf flag"), STD_C89 },
static const format_char_info gcc_gfc_char_table[] =
{
/* C89 conversion specifiers. */
- { "di", 0, STD_C89, { T89_I, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "", "", NULL },
+ { "di", 0, STD_C89, { T89_I, BADLEN, BADLEN, T89_L, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "", "", NULL },
+ { "u", 0, STD_C89, { T89_UI, BADLEN, BADLEN, T89_UL, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "", "", NULL },
{ "c", 0, STD_C89, { T89_I, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "", "", NULL },
{ "s", 1, STD_C89, { T89_C, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN, BADLEN }, "", "cR", NULL },
{
/* C89 conversion specifiers. */
{ "ABZab", 0, STD_C89, NOLENGTHS, "^#", "", NULL },
- { "cx", 0, STD_C89, NOLENGTHS, "E", "3", NULL },
+ { "cx", 0, STD_C89, NOLENGTHS, "E", "3", NULL },
{ "HIMSUWdmw", 0, STD_C89, NOLENGTHS, "-_0Ow", "", NULL },
{ "j", 0, STD_C89, NOLENGTHS, "-_0Ow", "o", NULL },
{ "p", 0, STD_C89, NOLENGTHS, "#", "", NULL },
{ "X", 0, STD_C89, NOLENGTHS, "E", "", NULL },
- { "y", 0, STD_C89, NOLENGTHS, "EO-_0w", "4", NULL },
+ { "y", 0, STD_C89, NOLENGTHS, "EO-_0w", "4", NULL },
{ "Y", 0, STD_C89, NOLENGTHS, "-_0EOw", "o", NULL },
{ "%", 0, STD_C89, NOLENGTHS, "", "", NULL },
/* C99 conversion specifiers. */
{ "C", 0, STD_C99, NOLENGTHS, "-_0EOw", "o", NULL },
- { "D", 0, STD_C99, NOLENGTHS, "", "2", NULL },
+ { "D", 0, STD_C99, NOLENGTHS, "", "2", NULL },
{ "eVu", 0, STD_C99, NOLENGTHS, "-_0Ow", "", NULL },
{ "FRTnrt", 0, STD_C99, NOLENGTHS, "", "", NULL },
- { "g", 0, STD_C99, NOLENGTHS, "O-_0w", "2o", NULL },
+ { "g", 0, STD_C99, NOLENGTHS, "O-_0w", "2o", NULL },
{ "G", 0, STD_C99, NOLENGTHS, "-_0Ow", "o", NULL },
{ "h", 0, STD_C99, NOLENGTHS, "^#", "", NULL },
{ "z", 0, STD_C99, NOLENGTHS, "O", "o", NULL },
/* This must be in the same order as enum format_type. */
static const format_kind_info format_types_orig[] =
{
- { "printf", printf_length_specs, print_char_table, " +#0-'I", NULL,
+ { "printf", printf_length_specs, print_char_table, " +#0-'I", NULL,
printf_flag_specs, printf_flag_pairs,
FMT_FLAG_ARG_CONVERT|FMT_FLAG_DOLLAR_MULTIPLE|FMT_FLAG_USE_DOLLAR|FMT_FLAG_EMPTY_PREC_OK,
'w', 0, 'p', 0, 'L',
&integer_type_node, &integer_type_node
},
- { "asm_fprintf", asm_fprintf_length_specs, asm_fprintf_char_table, " +#0-", NULL,
+ { "asm_fprintf", asm_fprintf_length_specs, asm_fprintf_char_table, " +#0-", NULL,
asm_fprintf_flag_specs, asm_fprintf_flag_pairs,
FMT_FLAG_ARG_CONVERT|FMT_FLAG_EMPTY_PREC_OK,
'w', 0, 'p', 0, 'L',
NULL, NULL
},
- { "gcc_diag", gcc_diag_length_specs, gcc_diag_char_table, "q+", NULL,
+ { "gcc_diag", gcc_diag_length_specs, gcc_diag_char_table, "q+", NULL,
gcc_diag_flag_specs, gcc_diag_flag_pairs,
FMT_FLAG_ARG_CONVERT,
0, 0, 'p', 0, 'L',
NULL, &integer_type_node
},
- { "gcc_tdiag", gcc_tdiag_length_specs, gcc_tdiag_char_table, "q+", NULL,
+ { "gcc_tdiag", gcc_tdiag_length_specs, gcc_tdiag_char_table, "q+", NULL,
gcc_tdiag_flag_specs, gcc_tdiag_flag_pairs,
FMT_FLAG_ARG_CONVERT,
0, 0, 'p', 0, 'L',
NULL, &integer_type_node
},
- { "gcc_cdiag", gcc_cdiag_length_specs, gcc_cdiag_char_table, "q+", NULL,
+ { "gcc_cdiag", gcc_cdiag_length_specs, gcc_cdiag_char_table, "q+", NULL,
gcc_cdiag_flag_specs, gcc_cdiag_flag_pairs,
FMT_FLAG_ARG_CONVERT,
0, 0, 'p', 0, 'L',
NULL, &integer_type_node
},
- { "gcc_cxxdiag", gcc_cxxdiag_length_specs, gcc_cxxdiag_char_table, "q+#", NULL,
+ { "gcc_cxxdiag", gcc_cxxdiag_length_specs, gcc_cxxdiag_char_table, "q+#", NULL,
gcc_cxxdiag_flag_specs, gcc_cxxdiag_flag_pairs,
FMT_FLAG_ARG_CONVERT,
0, 0, 'p', 0, 'L',
NULL, &integer_type_node
},
- { "gcc_gfc", NULL, gcc_gfc_char_table, "", NULL,
+ { "gcc_gfc", gcc_gfc_length_specs, gcc_gfc_char_table, "", NULL,
NULL, gcc_gfc_flag_pairs,
FMT_FLAG_ARG_CONVERT,
0, 0, 0, 0, 0,
NULL, NULL
},
- { "scanf", scanf_length_specs, scan_char_table, "*'I", NULL,
+ { "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_DOLLAR_GAP_POINTER_OK,
'w', 0, 0, '*', 'L',
FMT_FLAG_FANCY_PERCENT_OK, 'w', 0, 0, 0, 0,
NULL, NULL
},
- { "strfmon", strfmon_length_specs, monetary_char_table, "=^+(!-", NULL,
+ { "strfmon", strfmon_length_specs, monetary_char_table, "=^+(!-", NULL,
strfmon_flag_specs, strfmon_flag_pairs,
FMT_FLAG_ARG_CONVERT, 'w', '#', 'p', 0, 'L',
NULL, NULL
static void check_format_info_main (format_check_results *,
function_format_info *,
const char *, int, tree,
- unsigned HOST_WIDE_INT);
+ unsigned HOST_WIDE_INT, alloc_pool);
static void init_dollar_format_checking (int, tree);
static int maybe_read_dollar_number (const char **, int,
\f
/* Check the argument list of a call to printf, scanf, etc.
- ATTRS are the attributes on the function type.
- PARAMS is the list of argument values. Also, if -Wmissing-format-attribute,
+ ATTRS are the attributes on the function type. There are NARGS argument
+ values in the array ARGARRAY.
+ Also, if -Wmissing-format-attribute,
warn for calls to vprintf or vscanf in functions with no such format
attribute themselves. */
void
-check_function_format (tree attrs, tree params)
+check_function_format (tree attrs, int nargs, tree *argarray)
{
tree a;
function_format_info info;
decode_format_attr (TREE_VALUE (a), &info, 1);
if (warn_format)
- check_format_info (&info, params);
+ {
+ /* FIXME: Rewrite all the internal functions in this file
+ to use the ARGARRAY directly instead of constructing this
+ temporary list. */
+ tree params = NULL_TREE;
+ int i;
+ for (i = nargs - 1; i >= 0; i--)
+ params = tree_cons (NULL_TREE, argarray[i], params);
+ check_format_info (&info, params);
+ }
if (warn_missing_format_attribute && info.first_arg_num == 0
&& (format_types[info.format_type].flags
& (int) FMT_FLAG_ARG_CONVERT))
const char *format_chars;
tree array_size = 0;
tree array_init;
+ alloc_pool fwt_pool;
if (integer_zerop (format_tree))
{
}
offset = 0;
- if (TREE_CODE (format_tree) == PLUS_EXPR)
+ if (TREE_CODE (format_tree) == POINTER_PLUS_EXPR)
{
tree arg0, arg1;
STRIP_NOPS (arg1);
if (TREE_CODE (arg1) == INTEGER_CST)
format_tree = arg0;
- else if (TREE_CODE (arg0) == INTEGER_CST)
- {
- format_tree = arg1;
- arg1 = arg0;
- }
else
{
res->number_non_literal++;
{
/* Variable length arrays can't be initialized. */
gcc_assert (TREE_CODE (array_size) == INTEGER_CST);
-
+
if (host_integerp (array_size, 0))
{
HOST_WIDE_INT array_size_value = TREE_INT_CST_LOW (array_size);
format_chars += offset;
format_length -= offset;
}
- if (format_length < 1)
+ if (format_length < 1 || format_chars[--format_length] != 0)
{
res->number_unterminated++;
return;
}
- if (format_length == 1)
+ if (format_length == 0)
{
res->number_empty++;
return;
}
- if (format_chars[--format_length] != 0)
- {
- res->number_unterminated++;
- return;
- }
/* Skip to first argument to check. */
while (arg_num + 1 < info->first_arg_num)
will decrement it if it finds there are extra arguments, but this way
need not adjust it for every return. */
res->number_other++;
+ fwt_pool = create_alloc_pool ("format_wanted_type pool",
+ sizeof (format_wanted_type), 10);
check_format_info_main (res, info, format_chars, format_length,
- params, arg_num);
+ params, arg_num, fwt_pool);
+ free_alloc_pool (fwt_pool);
}
check_format_info_main (format_check_results *res,
function_format_info *info, const char *format_chars,
int format_length, tree params,
- unsigned HOST_WIDE_INT arg_num)
+ unsigned HOST_WIDE_INT arg_num, alloc_pool fwt_pool)
{
const char *orig_format_chars = format_chars;
tree first_fillin_param = params;
if (*format_chars == 0)
{
if (format_chars - orig_format_chars != format_length)
- warning (OPT_Wformat, "embedded %<\\0%> in format");
+ warning (OPT_Wformat_contains_nul, "embedded %<\\0%> in format");
if (info->first_arg_num != 0 && params != 0
&& has_operand_number <= 0)
{
++fci;
if (fci->format_chars == 0)
{
- if (ISGRAPH (format_char))
+ if (ISGRAPH (format_char))
warning (OPT_Wformat, "unknown conversion type character %qc in format",
format_char);
else
fci = fci->chain;
if (fci)
{
- wanted_type_ptr = GGC_NEW (format_wanted_type);
+ wanted_type_ptr = (format_wanted_type *)
+ pool_alloc (fwt_pool);
arg_num++;
wanted_type = *fci->types[length_chars_val].type;
wanted_type_name = fci->types[length_chars_val].name;
if (first_wanted_type != 0)
check_format_types (first_wanted_type, format_start,
format_chars - format_start);
-
- if (main_wanted_type.next != NULL)
- {
- format_wanted_type *wanted_type_ptr = main_wanted_type.next;
- while (wanted_type_ptr)
- {
- format_wanted_type *next = wanted_type_ptr->next;
- ggc_free (wanted_type_ptr);
- wanted_type_ptr = next;
- }
- }
}
}
for (i = 0; fci->format_chars; i++, fci++)
if (strchr (fci->format_chars, c))
return i;
-
+
/* We shouldn't be looking for a non-existent specifier. */
gcc_unreachable ();
}
for (i = 0; fli->name; i++, fli++)
if (strchr (fli->name, c))
return i;
-
+
/* We shouldn't be looking for a non-existent modifier. */
gcc_unreachable ();
}
{
format_length_info *new_asm_fprintf_length_specs;
unsigned int i;
-
+
/* Find the underlying type for HOST_WIDE_INT. For the %w
length modifier to work, one must have issued: "typedef
HOST_WIDE_INT __gcc_host_wide_int__;" in one's source code
init_dynamic_gfc_info (void)
{
static tree locus;
-
+
if (!locus)
{
static format_char_info *gfc_fci;
sizeof (gcc_gfc_char_table),
sizeof (gcc_gfc_char_table));
if (locus)
- {
+ {
const unsigned i = find_char_info_specifier_index (gfc_fci, 'L');
gfc_fci[i].types[0].type = &locus;
gfc_fci[i].pointer_count = 1;
t = TREE_TYPE (TREE_TYPE (t));
}
}
-
+
/* Find the underlying type for HOST_WIDE_INT. For the %w
length modifier to work, one must have issued: "typedef
HOST_WIDE_INT __gcc_host_wide_int__;" in one's source code
}
}
}
-
+
/* Assign the new data for use. */
/* All the GCC diag formats use the same length specs. */
diag_ls = (format_length_info *)
xmemdup (gcc_diag_length_specs,
sizeof (gcc_diag_length_specs),
- sizeof (gcc_diag_length_specs));
+ sizeof (gcc_diag_length_specs));
if (hwi)
- {
+ {
/* HOST_WIDE_INT must be one of 'long' or 'long long'. */
i = find_length_info_modifier_index (diag_ls, 'w');
if (hwi == long_integer_type_node)
sizeof (gcc_diag_char_table),
sizeof (gcc_diag_char_table));
if (loc)
- {
+ {
i = find_char_info_specifier_index (diag_fci, 'H');
diag_fci[i].types[0].type = &loc;
diag_fci[i].pointer_count = 1;
}
if (t)
- {
+ {
i = find_char_info_specifier_index (diag_fci, 'J');
diag_fci[i].types[0].type = &t;
diag_fci[i].pointer_count = 1;
sizeof (gcc_tdiag_char_table),
sizeof (gcc_tdiag_char_table));
if (loc)
- {
+ {
i = find_char_info_specifier_index (tdiag_fci, 'H');
tdiag_fci[i].types[0].type = &loc;
tdiag_fci[i].pointer_count = 1;
}
if (t)
- {
+ {
/* All specifiers taking a tree share the same struct. */
i = find_char_info_specifier_index (tdiag_fci, 'D');
tdiag_fci[i].types[0].type = &t;
sizeof (gcc_cdiag_char_table),
sizeof (gcc_cdiag_char_table));
if (loc)
- {
+ {
i = find_char_info_specifier_index (cdiag_fci, 'H');
cdiag_fci[i].types[0].type = &loc;
cdiag_fci[i].pointer_count = 1;
}
if (t)
- {
+ {
/* All specifiers taking a tree share the same struct. */
i = find_char_info_specifier_index (cdiag_fci, 'D');
cdiag_fci[i].types[0].type = &t;
sizeof (gcc_cxxdiag_char_table),
sizeof (gcc_cxxdiag_char_table));
if (loc)
- {
+ {
i = find_char_info_specifier_index (cxxdiag_fci, 'H');
cxxdiag_fci[i].types[0].type = &loc;
cxxdiag_fci[i].pointer_count = 1;
}
if (t)
- {
+ {
/* All specifiers taking a tree share the same struct. */
i = find_char_info_specifier_index (cxxdiag_fci, 'D');
cxxdiag_fci[i].types[0].type = &t;
|| info.format_type == gcc_cxxdiag_format_type)
{
/* Our first time through, we have to make sure that our
- format_type data is allocated dynamically and is modifiable. */
+ format_type data is allocated dynamically and is modifiable. */
if (!dynamic_format_types)
format_types = dynamic_format_types = (format_kind_info *)
xmemdup (format_types_orig, sizeof (format_types_orig),
sizeof (format_types_orig));
/* If this is format __asm_fprintf__, we have to initialize
- GCC's notion of HOST_WIDE_INT for checking %wd. */
+ GCC's notion of HOST_WIDE_INT for checking %wd. */
if (info.format_type == asm_fprintf_format_type)
init_dynamic_asm_fprintf_info ();
/* If this is format __gcc_gfc__, we have to initialize GCC's
else if (info.format_type == gcc_gfc_format_type)
init_dynamic_gfc_info ();
/* If this is one of the diagnostic attributes, then we have to
- initialize 'location_t' and 'tree' at runtime. */
+ initialize 'location_t' and 'tree' at runtime. */
else if (info.format_type == gcc_diag_format_type
|| info.format_type == gcc_tdiag_format_type
|| info.format_type == gcc_cdiag_format_type