Commit 1c4d457e by David Malcolm Committed by David Malcolm

c-format.c: cleanup of check_format_info_main

gcc/c-family/ChangeLog:
	* c-format.c (class flag_chars_t): New class.
	(struct length_modifier): New struct.
	(class argument_parser): New class.
	(flag_chars_t::flag_chars_t): New ctor.
	(flag_chars_t::has_char_p): New method.
	(flag_chars_t::add_char): New method.
	(flag_chars_t::validate): New method.
	(flag_chars_t::get_alloc_flag): New method.
	(flag_chars_t::assignment_suppression_p): New method.
	(argument_parser::argument_parser): New ctor.
	(argument_parser::read_any_dollar): New method.
	(argument_parser::read_format_flags): New method.
	(argument_parser::read_any_format_width): New method.
	(argument_parser::read_any_format_left_precision): New method.
	(argument_parser::read_any_format_precision): New method.
	(argument_parser::handle_alloc_chars): New method.
	(argument_parser::read_any_length_modifier): New method.
	(argument_parser::read_any_other_modifier): New method.
	(argument_parser::find_format_char_info): New method.
	(argument_parser::validate_flag_pairs): New method.
	(argument_parser::give_y2k_warnings): New method.
	(argument_parser::parse_any_scan_set): New method.
	(argument_parser::handle_conversions): New method.
	(argument_parser::check_argument_type): New method.
	(check_format_info_main): Introduce classes argument_parser
	and flag_chars_t, moving the code within the loop into methods
	of these classes.  Make various locals "const".

From-SVN: r239247
parent 66c3dd8c
2016-08-08 David Malcolm <dmalcolm@redhat.com>
* c-format.c (class flag_chars_t): New class.
(struct length_modifier): New struct.
(class argument_parser): New class.
(flag_chars_t::flag_chars_t): New ctor.
(flag_chars_t::has_char_p): New method.
(flag_chars_t::add_char): New method.
(flag_chars_t::validate): New method.
(flag_chars_t::get_alloc_flag): New method.
(flag_chars_t::assignment_suppression_p): New method.
(argument_parser::argument_parser): New ctor.
(argument_parser::read_any_dollar): New method.
(argument_parser::read_format_flags): New method.
(argument_parser::read_any_format_width): New method.
(argument_parser::read_any_format_left_precision): New method.
(argument_parser::read_any_format_precision): New method.
(argument_parser::handle_alloc_chars): New method.
(argument_parser::read_any_length_modifier): New method.
(argument_parser::read_any_other_modifier): New method.
(argument_parser::find_format_char_info): New method.
(argument_parser::validate_flag_pairs): New method.
(argument_parser::give_y2k_warnings): New method.
(argument_parser::parse_any_scan_set): New method.
(argument_parser::handle_conversions): New method.
(argument_parser::check_argument_type): New method.
(check_format_info_main): Introduce classes argument_parser
and flag_chars_t, moving the code within the loop into methods
of these classes. Make various locals "const".
2016-08-05 David Malcolm <dmalcolm@redhat.com>
* c-common.c: Include "substring-locations.h".
......
......@@ -1688,78 +1688,284 @@ check_format_arg (void *ctx, tree format_tree,
params, arg_num, fwt_pool);
}
/* Support class for argument_parser and check_format_info_main.
Tracks any flag characters that have been applied to the
current argument. */
/* Do the main part of checking a call to a format function. FORMAT_CHARS
is the NUL-terminated format string (which at this point may contain
internal NUL characters); FORMAT_LENGTH is its length (excluding the
terminating NUL character). ARG_NUM is one less than the number of
the first format argument to check; PARAMS points to that format
argument in the list of arguments. */
class flag_chars_t
{
public:
flag_chars_t ();
bool has_char_p (char ch) const;
void add_char (char ch);
void validate (const format_kind_info *fki,
const format_char_info *fci,
const format_flag_spec *flag_specs,
const char * const format_chars,
location_t format_string_loc,
const char * const orig_format_chars,
char format_char);
int get_alloc_flag (const format_kind_info *fki);
int assignment_suppression_p (const format_kind_info *fki);
private:
char m_flag_chars[256];
};
static void
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,
object_allocator <format_wanted_type> &fwt_pool)
/* Support struct for argument_parser and check_format_info_main.
Encapsulates any length modifier applied to the current argument. */
struct length_modifier
{
const char *orig_format_chars = format_chars;
tree first_fillin_param = params;
length_modifier ()
: chars (NULL), val (FMT_LEN_none), std (STD_C89),
scalar_identity_flag (0)
{
}
const format_kind_info *fki = &format_types[info->format_type];
const format_flag_spec *flag_specs = fki->flag_specs;
const format_flag_pair *bad_flag_pairs = fki->bad_flag_pairs;
location_t format_string_loc = res->format_string_loc;
length_modifier (const char *chars_,
enum format_lengths val_,
enum format_std_version std_,
int scalar_identity_flag_)
: chars (chars_), val (val_), std (std_),
scalar_identity_flag (scalar_identity_flag_)
{
}
/* -1 if no conversions taking an operand have been found; 0 if one has
and it didn't use $; 1 if $ formats are in use. */
int has_operand_number = -1;
const char *chars;
enum format_lengths val;
enum format_std_version std;
int scalar_identity_flag;
};
init_dollar_format_checking (info->first_arg_num, first_fillin_param);
/* Parsing one argument within a format string. */
while (*format_chars != 0)
{
int i;
int suppressed = FALSE;
const char *length_chars = NULL;
enum format_lengths length_chars_val = FMT_LEN_none;
enum format_std_version length_chars_std = STD_C89;
int format_char;
tree cur_param;
tree wanted_type;
int main_arg_num = 0;
tree main_arg_params = 0;
enum format_std_version wanted_type_std;
const char *wanted_type_name;
class argument_parser
{
public:
argument_parser (function_format_info *info, const char *&format_chars,
const char * const orig_format_chars,
location_t format_string_loc, flag_chars_t &flag_chars,
int &has_operand_number, tree first_fillin_param,
object_allocator <format_wanted_type> &fwt_pool_);
bool read_any_dollar ();
bool read_format_flags ();
bool
read_any_format_width (tree &params,
unsigned HOST_WIDE_INT &arg_num);
void
read_any_format_left_precision ();
bool
read_any_format_precision (tree &params,
unsigned HOST_WIDE_INT &arg_num);
void handle_alloc_chars ();
length_modifier read_any_length_modifier ();
void read_any_other_modifier ();
const format_char_info *find_format_char_info (char format_char);
void
validate_flag_pairs (const format_char_info *fci,
char format_char);
void
give_y2k_warnings (const format_char_info *fci,
char format_char);
void parse_any_scan_set (const format_char_info *fci);
bool handle_conversions (const format_char_info *fci,
const length_modifier &len_modifier,
tree &wanted_type,
const char *&wanted_type_name,
unsigned HOST_WIDE_INT &arg_num,
tree &params,
char format_char);
bool
check_argument_type (const format_char_info *fci,
const length_modifier &len_modifier,
tree &wanted_type,
const char *&wanted_type_name,
const bool suppressed,
unsigned HOST_WIDE_INT &arg_num,
tree &params,
const int alloc_flag,
const char * const format_start);
private:
const function_format_info *const info;
const format_kind_info * const fki;
const format_flag_spec * const flag_specs;
const char *&format_chars;
const char * const orig_format_chars;
const location_t format_string_loc;
object_allocator <format_wanted_type> &fwt_pool;
flag_chars_t &flag_chars;
int main_arg_num;
tree main_arg_params;
int &has_operand_number;
const tree first_fillin_param;
format_wanted_type width_wanted_type;
format_wanted_type precision_wanted_type;
public:
format_wanted_type main_wanted_type;
format_wanted_type *first_wanted_type = NULL;
format_wanted_type *last_wanted_type = NULL;
const format_length_info *fli = NULL;
const format_char_info *fci = NULL;
char flag_chars[256];
int alloc_flag = 0;
int scalar_identity_flag = 0;
const char *format_start;
private:
format_wanted_type *first_wanted_type;
format_wanted_type *last_wanted_type;
};
if (*format_chars++ != '%')
/* flag_chars_t's constructor. */
flag_chars_t::flag_chars_t ()
{
m_flag_chars[0] = 0;
}
/* Has CH been seen as a flag within the current argument? */
bool
flag_chars_t::has_char_p (char ch) const
{
return strchr (m_flag_chars, ch) != 0;
}
/* Add CH to the flags seen within the current argument. */
void
flag_chars_t::add_char (char ch)
{
int i = strlen (m_flag_chars);
m_flag_chars[i++] = ch;
m_flag_chars[i] = 0;
}
/* Validate the individual flags used, removing any that are invalid. */
void
flag_chars_t::validate (const format_kind_info *fki,
const format_char_info *fci,
const format_flag_spec *flag_specs,
const char * const format_chars,
location_t format_string_loc,
const char * const orig_format_chars,
char format_char)
{
int i;
int d = 0;
for (i = 0; m_flag_chars[i] != 0; i++)
{
const format_flag_spec *s = get_flag_spec (flag_specs,
m_flag_chars[i], NULL);
m_flag_chars[i - d] = m_flag_chars[i];
if (m_flag_chars[i] == fki->length_code_char)
continue;
if (*format_chars == 0)
if (strchr (fci->flag_chars, m_flag_chars[i]) == 0)
{
warning_at (location_from_offset (format_string_loc,
format_chars - orig_format_chars),
OPT_Wformat_,
"spurious trailing %<%%%> in format");
format_chars
- orig_format_chars),
OPT_Wformat_, "%s used with %<%%%c%> %s format",
_(s->name), format_char, fki->name);
d++;
continue;
}
if (*format_chars == '%')
if (pedantic)
{
++format_chars;
continue;
const format_flag_spec *t;
if (ADJ_STD (s->std) > C_STD_VER)
warning_at (format_string_loc, OPT_Wformat_,
"%s does not support %s",
C_STD_NAME (s->std), _(s->long_name));
t = get_flag_spec (flag_specs, m_flag_chars[i], fci->flags2);
if (t != NULL && ADJ_STD (t->std) > ADJ_STD (s->std))
{
const char *long_name = (t->long_name != NULL
? t->long_name
: s->long_name);
if (ADJ_STD (t->std) > C_STD_VER)
warning_at (format_string_loc, OPT_Wformat_,
"%s does not support %s with"
" the %<%%%c%> %s format",
C_STD_NAME (t->std), _(long_name),
format_char, fki->name);
}
flag_chars[0] = 0;
}
}
m_flag_chars[i - d] = 0;
}
/* Determine if an assignment-allocation has been set, requiring
an extra char ** for writing back a dynamically-allocated char *.
This is for handling the optional 'm' character in scanf. */
int
flag_chars_t::get_alloc_flag (const format_kind_info *fki)
{
if ((fki->flags & (int) FMT_FLAG_SCANF_A_KLUDGE)
&& has_char_p ('a'))
return 1;
if (fki->alloc_char && has_char_p (fki->alloc_char))
return 1;
return 0;
}
/* Determine if an assignment-suppression character was seen.
('*' in scanf, for discarding the converted input). */
int
flag_chars_t::assignment_suppression_p (const format_kind_info *fki)
{
if (fki->suppression_char
&& has_char_p (fki->suppression_char))
return 1;
return 0;
}
/* Constructor for argument_parser. Initialize for parsing one
argument within a format string. */
argument_parser::
argument_parser (function_format_info *info_, const char *&format_chars_,
const char * const orig_format_chars_,
location_t format_string_loc_,
flag_chars_t &flag_chars_,
int &has_operand_number_,
tree first_fillin_param_,
object_allocator <format_wanted_type> &fwt_pool_)
: info (info_),
fki (&format_types[info->format_type]),
flag_specs (fki->flag_specs),
format_chars (format_chars_),
orig_format_chars (orig_format_chars_),
format_string_loc (format_string_loc_),
fwt_pool (fwt_pool_),
flag_chars (flag_chars_),
main_arg_num (0),
main_arg_params (NULL),
has_operand_number (has_operand_number_),
first_fillin_param (first_fillin_param_),
first_wanted_type (NULL),
last_wanted_type (NULL)
{
}
/* Handle dollars at the start of format arguments, setting up main_arg_params
and main_arg_num.
Return true if format parsing is to continue, false otherwise. */
bool
argument_parser::read_any_dollar ()
{
if ((fki->flags & (int) FMT_FLAG_USE_DOLLAR) && has_operand_number != 0)
{
/* Possibly read a $ operand number at the start of the format.
......@@ -1771,7 +1977,7 @@ check_format_info_main (format_check_results *res,
first_fillin_param,
&main_arg_params, fki);
if (opnum == -1)
return;
return false;
else if (opnum > 0)
{
has_operand_number = 1;
......@@ -1781,18 +1987,26 @@ check_format_info_main (format_check_results *res,
else if (fki->flags & FMT_FLAG_USE_DOLLAR)
{
if (avoid_dollar_number (format_chars))
return;
return false;
}
return true;
}
/* Read any format flags, but do not yet validate them beyond removing
/* Read any format flags, but do not yet validate them beyond removing
duplicates, since in general validation depends on the rest of
the format. */
the format.
Return true if format parsing is to continue, false otherwise. */
bool
argument_parser::read_format_flags ()
{
while (*format_chars != 0
&& strchr (fki->flag_chars, *format_chars) != 0)
{
const format_flag_spec *s = get_flag_spec (flag_specs,
*format_chars, NULL);
if (strchr (flag_chars, *format_chars) != 0)
if (flag_chars.has_char_p (*format_chars))
{
warning_at (location_from_offset (format_string_loc,
format_chars + 1
......@@ -1801,11 +2015,8 @@ check_format_info_main (format_check_results *res,
"repeated %s in format", _(s->name));
}
else
{
i = strlen (flag_chars);
flag_chars[i++] = *format_chars;
flag_chars[i] = 0;
}
flag_chars.add_char (*format_chars);
if (s->skip_next_char)
{
++format_chars;
......@@ -1813,20 +2024,30 @@ check_format_info_main (format_check_results *res,
{
warning_at (format_string_loc, OPT_Wformat_,
"missing fill character at end of strfmon format");
return;
return false;
}
}
++format_chars;
}
/* Read any format width, possibly * or *m$. */
if (fki->width_char != 0)
{
return true;
}
/* Read any format width, possibly * or *m$.
Return true if format parsing is to continue, false otherwise. */
bool
argument_parser::
read_any_format_width (tree &params,
unsigned HOST_WIDE_INT &arg_num)
{
if (!fki->width_char)
return true;
if (fki->width_type != NULL && *format_chars == '*')
{
i = strlen (flag_chars);
flag_chars[i++] = fki->width_char;
flag_chars[i] = 0;
flag_chars.add_char (fki->width_char);
/* "...a field width...may be indicated by an asterisk.
In this case, an int argument supplies the field width..." */
++format_chars;
......@@ -1838,7 +2059,7 @@ check_format_info_main (format_check_results *res,
first_fillin_param,
&params, fki);
if (opnum == -1)
return;
return false;
else if (opnum > 0)
{
has_operand_number = 1;
......@@ -1850,10 +2071,11 @@ check_format_info_main (format_check_results *res,
else
{
if (avoid_dollar_number (format_chars))
return;
return false;
}
if (info->first_arg_num != 0)
{
tree cur_param;
if (params == 0)
cur_param = NULL;
else
......@@ -1905,21 +2127,23 @@ check_format_info_main (format_check_results *res,
warning_at (format_string_loc, OPT_Wformat_,
"zero width in %s format", fki->name);
if (found_width)
{
i = strlen (flag_chars);
flag_chars[i++] = fki->width_char;
flag_chars[i] = 0;
}
}
flag_chars.add_char (fki->width_char);
}
/* Read any format left precision (must be a number, not *). */
if (fki->left_precision_char != 0 && *format_chars == '#')
{
return true;
}
/* Read any format left precision (must be a number, not *). */
void
argument_parser::read_any_format_left_precision ()
{
if (fki->left_precision_char == 0)
return;
if (*format_chars != '#')
return;
++format_chars;
i = strlen (flag_chars);
flag_chars[i++] = fki->left_precision_char;
flag_chars[i] = 0;
flag_chars.add_char (fki->left_precision_char);
if (!ISDIGIT (*format_chars))
warning_at (location_from_offset (format_string_loc,
format_chars - orig_format_chars),
......@@ -1927,15 +2151,24 @@ check_format_info_main (format_check_results *res,
"empty left precision in %s format", fki->name);
while (ISDIGIT (*format_chars))
++format_chars;
}
}
/* Read any format precision, possibly * or *m$.
Return true if format parsing is to continue, false otherwise. */
bool
argument_parser::
read_any_format_precision (tree &params,
unsigned HOST_WIDE_INT &arg_num)
{
if (fki->precision_char == 0)
return true;
if (*format_chars != '.')
return true;
/* Read any format precision, possibly * or *m$. */
if (fki->precision_char != 0 && *format_chars == '.')
{
++format_chars;
i = strlen (flag_chars);
flag_chars[i++] = fki->precision_char;
flag_chars[i] = 0;
flag_chars.add_char (fki->precision_char);
if (fki->precision_type != NULL && *format_chars == '*')
{
/* "...a...precision...may be indicated by an asterisk.
......@@ -1949,7 +2182,7 @@ check_format_info_main (format_check_results *res,
first_fillin_param,
&params, fki);
if (opnum == -1)
return;
return false;
else if (opnum > 0)
{
has_operand_number = 1;
......@@ -1961,10 +2194,11 @@ check_format_info_main (format_check_results *res,
else
{
if (avoid_dollar_number (format_chars))
return;
return false;
}
if (info->first_arg_num != 0)
{
tree cur_param;
if (params == 0)
cur_param = NULL;
else
......@@ -2009,14 +2243,22 @@ check_format_info_main (format_check_results *res,
while (ISDIGIT (*format_chars))
++format_chars;
}
}
format_start = format_chars;
return true;
}
/* Parse any assignment-allocation flags, which request an extra
char ** for writing back a dynamically-allocated char *.
This is for handling the optional 'm' character in scanf,
and, before C99, 'a' (for compatibility with a non-standard
GNU libc extension). */
void
argument_parser::handle_alloc_chars ()
{
if (fki->alloc_char && fki->alloc_char == *format_chars)
{
i = strlen (flag_chars);
flag_chars[i++] = fki->alloc_char;
flag_chars[i] = 0;
flag_chars.add_char (fki->alloc_char);
format_chars++;
}
......@@ -2029,22 +2271,28 @@ check_format_info_main (format_check_results *res,
|| format_chars[1] == '[')
{
/* 'a' is used as a flag. */
i = strlen (flag_chars);
flag_chars[i++] = 'a';
flag_chars[i] = 0;
flag_chars.add_char ('a');
format_chars++;
}
}
}
}
/* Look for length modifiers within the current format argument,
returning a length_modifier instance describing it (or the
default if one is not found).
Issue warnings about non-standard modifiers. */
length_modifier
argument_parser::read_any_length_modifier ()
{
length_modifier result;
const format_length_info *fli = fki->length_char_specs;
if (!fli)
return result;
/* Read any length modifier, if this kind of format has them. */
fli = fki->length_char_specs;
length_chars = NULL;
length_chars_val = FMT_LEN_none;
length_chars_std = STD_C89;
scalar_identity_flag = 0;
if (fli)
{
while (fli->name != 0
&& strncmp (fli->name, format_chars, strlen (fli->name)))
fli++;
......@@ -2054,39 +2302,41 @@ check_format_info_main (format_check_results *res,
if (fli->double_name != 0 && fli->name[0] == *format_chars)
{
format_chars++;
length_chars = fli->double_name;
length_chars_val = fli->double_index;
length_chars_std = fli->double_std;
result = length_modifier (fli->double_name, fli->double_index,
fli->double_std, 0);
}
else
{
length_chars = fli->name;
length_chars_val = fli->index;
length_chars_std = fli->std;
scalar_identity_flag = fli->scalar_identity_flag;
result = length_modifier (fli->name, fli->index, fli->std,
fli->scalar_identity_flag);
}
i = strlen (flag_chars);
flag_chars[i++] = fki->length_code_char;
flag_chars[i] = 0;
flag_chars.add_char (fki->length_code_char);
}
if (pedantic)
{
/* Warn if the length modifier is non-standard. */
if (ADJ_STD (length_chars_std) > C_STD_VER)
if (ADJ_STD (result.std) > C_STD_VER)
warning_at (format_string_loc, OPT_Wformat_,
"%s does not support the %qs %s length modifier",
C_STD_NAME (length_chars_std), length_chars,
C_STD_NAME (result.std), result.chars,
fki->name);
}
}
/* Read any modifier (strftime E/O). */
if (fki->modifier_chars != NULL)
{
return result;
}
/* Read any other modifier (strftime E/O). */
void
argument_parser::read_any_other_modifier ()
{
if (fki->modifier_chars == NULL)
return;
while (*format_chars != 0
&& strchr (fki->modifier_chars, *format_chars) != 0)
{
if (strchr (flag_chars, *format_chars) != 0)
if (flag_chars.has_char_p (*format_chars))
{
const format_flag_spec *s = get_flag_spec (flag_specs,
*format_chars, NULL);
......@@ -2097,28 +2347,25 @@ check_format_info_main (format_check_results *res,
"repeated %s in format", _(s->name));
}
else
{
i = strlen (flag_chars);
flag_chars[i++] = *format_chars;
flag_chars[i] = 0;
}
flag_chars.add_char (*format_chars);
++format_chars;
}
}
}
/* Return the format_char_info corresponding to FORMAT_CHAR,
potentially issuing a warning if the format char is
not supported in the C standard version we are checking
against.
Issue a warning and return NULL if it is not found.
Issue warnings about non-standard modifiers. */
const format_char_info *
argument_parser::find_format_char_info (char format_char)
{
const format_char_info *fci = fki->conversion_specs;
format_char = *format_chars;
if (format_char == 0
|| (!(fki->flags & (int) FMT_FLAG_FANCY_PERCENT_OK)
&& format_char == '%'))
{
warning_at (location_from_offset (format_string_loc,
format_chars - orig_format_chars),
OPT_Wformat_,
"conversion lacks type at end of format");
continue;
}
format_chars++;
fci = fki->conversion_specs;
while (fci->format_chars != 0
&& strchr (fci->format_chars, format_char) == 0)
++fci;
......@@ -2136,8 +2383,9 @@ check_format_info_main (format_check_results *res,
OPT_Wformat_,
"unknown conversion type character 0x%x in format",
format_char);
continue;
return NULL;
}
if (pedantic)
{
if (ADJ_STD (fci->std) > C_STD_VER)
......@@ -2148,67 +2396,24 @@ check_format_info_main (format_check_results *res,
C_STD_NAME (fci->std), format_char, fki->name);
}
/* Validate the individual flags used, removing any that are invalid. */
{
int d = 0;
for (i = 0; flag_chars[i] != 0; i++)
{
const format_flag_spec *s = get_flag_spec (flag_specs,
flag_chars[i], NULL);
flag_chars[i - d] = flag_chars[i];
if (flag_chars[i] == fki->length_code_char)
continue;
if (strchr (fci->flag_chars, flag_chars[i]) == 0)
{
warning_at (location_from_offset (format_string_loc,
format_chars
- orig_format_chars),
OPT_Wformat_, "%s used with %<%%%c%> %s format",
_(s->name), format_char, fki->name);
d++;
continue;
}
if (pedantic)
{
const format_flag_spec *t;
if (ADJ_STD (s->std) > C_STD_VER)
warning_at (format_string_loc, OPT_Wformat_,
"%s does not support %s",
C_STD_NAME (s->std), _(s->long_name));
t = get_flag_spec (flag_specs, flag_chars[i], fci->flags2);
if (t != NULL && ADJ_STD (t->std) > ADJ_STD (s->std))
{
const char *long_name = (t->long_name != NULL
? t->long_name
: s->long_name);
if (ADJ_STD (t->std) > C_STD_VER)
warning_at (format_string_loc, OPT_Wformat_,
"%s does not support %s with the %<%%%c%> %s format",
C_STD_NAME (t->std), _(long_name),
format_char, fki->name);
}
}
}
flag_chars[i - d] = 0;
}
return fci;
}
if ((fki->flags & (int) FMT_FLAG_SCANF_A_KLUDGE)
&& strchr (flag_chars, 'a') != 0)
alloc_flag = 1;
if (fki->alloc_char && strchr (flag_chars, fki->alloc_char) != 0)
alloc_flag = 1;
/* Validate the pairs of flags used.
Issue warnings about incompatible combinations of flags. */
if (fki->suppression_char
&& strchr (flag_chars, fki->suppression_char) != 0)
suppressed = 1;
void
argument_parser::validate_flag_pairs (const format_char_info *fci,
char format_char)
{
const format_flag_pair * const bad_flag_pairs = fki->bad_flag_pairs;
/* Validate the pairs of flags used. */
for (i = 0; bad_flag_pairs[i].flag_char1 != 0; i++)
for (int i = 0; bad_flag_pairs[i].flag_char1 != 0; i++)
{
const format_flag_spec *s, *t;
if (strchr (flag_chars, bad_flag_pairs[i].flag_char1) == 0)
if (!flag_chars.has_char_p (bad_flag_pairs[i].flag_char1))
continue;
if (strchr (flag_chars, bad_flag_pairs[i].flag_char2) == 0)
if (!flag_chars.has_char_p (bad_flag_pairs[i].flag_char2))
continue;
if (bad_flag_pairs[i].predicate != 0
&& strchr (fci->flags2, bad_flag_pairs[i].predicate) == 0)
......@@ -2240,13 +2445,20 @@ check_format_info_main (format_check_results *res,
_(s->name), _(t->name), fki->name);
}
}
}
/* Give Y2K warnings. */
void
argument_parser::give_y2k_warnings (const format_char_info *fci,
char format_char)
{
if (!warn_format_y2k)
return;
/* Give Y2K warnings. */
if (warn_format_y2k)
{
int y2k_level = 0;
if (strchr (fci->flags2, '4') != 0)
if (strchr (flag_chars, 'E') != 0)
if (flag_chars.has_char_p ('E'))
y2k_level = 3;
else
y2k_level = 2;
......@@ -2262,10 +2474,17 @@ check_format_info_main (format_check_results *res,
warning_at (format_string_loc, OPT_Wformat_y2k,
"%<%%%c%> yields only last 2 digits of year",
format_char);
}
}
/* Parse any "scan sets" enclosed in square brackets, e.g.
for scanf-style calls. */
void
argument_parser::parse_any_scan_set (const format_char_info *fci)
{
if (strchr (fci->flags2, '[') == NULL)
return;
if (strchr (fci->flags2, '[') != 0)
{
/* Skip over scan set, in case it happens to have '%' in it. */
if (*format_chars == '^')
++format_chars;
......@@ -2281,16 +2500,29 @@ check_format_info_main (format_check_results *res,
format_chars - orig_format_chars),
OPT_Wformat_,
"no closing %<]%> for %<%%[%> format");
}
}
wanted_type = 0;
wanted_type_name = 0;
if (fki->flags & (int) FMT_FLAG_ARG_CONVERT)
{
wanted_type = (fci->types[length_chars_val].type
? *fci->types[length_chars_val].type : 0);
wanted_type_name = fci->types[length_chars_val].name;
wanted_type_std = fci->types[length_chars_val].std;
/* Return true if this argument is to be continued to be parsed,
false to skip to next argument. */
bool
argument_parser::handle_conversions (const format_char_info *fci,
const length_modifier &len_modifier,
tree &wanted_type,
const char *&wanted_type_name,
unsigned HOST_WIDE_INT &arg_num,
tree &params,
char format_char)
{
enum format_std_version wanted_type_std;
if (!(fki->flags & (int) FMT_FLAG_ARG_CONVERT))
return true;
wanted_type = (fci->types[len_modifier.val].type
? *fci->types[len_modifier.val].type : 0);
wanted_type_name = fci->types[len_modifier.val].name;
wanted_type_std = fci->types[len_modifier.val].std;
if (wanted_type == 0)
{
warning_at (location_from_offset (format_string_loc,
......@@ -2298,19 +2530,19 @@ check_format_info_main (format_check_results *res,
OPT_Wformat_,
"use of %qs length modifier with %qc type character"
" has either no effect or undefined behavior",
length_chars, format_char);
len_modifier.chars, format_char);
/* Heuristic: skip one argument when an invalid length/type
combination is encountered. */
arg_num++;
if (params != 0)
params = TREE_CHAIN (params);
continue;
return false;
}
else if (pedantic
/* Warn if non-standard, provided it is more non-standard
than the length and type characters that may already
have been warned for. */
&& ADJ_STD (wanted_type_std) > ADJ_STD (length_chars_std)
&& ADJ_STD (wanted_type_std) > ADJ_STD (len_modifier.std)
&& ADJ_STD (wanted_type_std) > ADJ_STD (fci->std))
{
if (ADJ_STD (wanted_type_std) > C_STD_VER)
......@@ -2318,16 +2550,32 @@ check_format_info_main (format_check_results *res,
format_chars - orig_format_chars),
OPT_Wformat_,
"%s does not support the %<%%%s%c%> %s format",
C_STD_NAME (wanted_type_std), length_chars,
C_STD_NAME (wanted_type_std), len_modifier.chars,
format_char, fki->name);
}
}
main_wanted_type.next = NULL;
return true;
}
/* Finally. . .check type of argument against desired type! */
/* Check type of argument against desired type.
Return true if format parsing is to continue, false otherwise. */
bool
argument_parser::
check_argument_type (const format_char_info *fci,
const length_modifier &len_modifier,
tree &wanted_type,
const char *&wanted_type_name,
const bool suppressed,
unsigned HOST_WIDE_INT &arg_num,
tree &params,
const int alloc_flag,
const char * const format_start)
{
if (info->first_arg_num == 0)
continue;
return true;
if ((fci->pointer_count == 0 && wanted_type == void_type_node)
|| suppressed)
{
......@@ -2359,7 +2607,7 @@ check_format_info_main (format_check_results *res,
{
warning_at (format_string_loc, OPT_Wformat_,
"missing $ operand number in format");
return;
return false;
}
else
has_operand_number = 0;
......@@ -2368,6 +2616,7 @@ check_format_info_main (format_check_results *res,
wanted_type_ptr = &main_wanted_type;
while (fci)
{
tree cur_param;
if (params == 0)
cur_param = NULL;
else
......@@ -2383,7 +2632,7 @@ check_format_info_main (format_check_results *res,
if (strchr (fci->flags2, 'c') != 0)
wanted_type_ptr->char_lenient_flag = 1;
wanted_type_ptr->scalar_identity_flag = 0;
if (scalar_identity_flag)
if (len_modifier.scalar_identity_flag)
wanted_type_ptr->scalar_identity_flag = 1;
wanted_type_ptr->writing_in_flag = 0;
wanted_type_ptr->reading_from_flag = 0;
......@@ -2414,14 +2663,148 @@ check_format_info_main (format_check_results *res,
{
wanted_type_ptr = fwt_pool.allocate ();
arg_num++;
wanted_type = *fci->types[length_chars_val].type;
wanted_type_name = fci->types[length_chars_val].name;
wanted_type = *fci->types[len_modifier.val].type;
wanted_type_name = fci->types[len_modifier.val].name;
}
}
}
if (first_wanted_type != 0)
check_format_types (format_string_loc, first_wanted_type);
return true;
}
/* Do the main part of checking a call to a format function. FORMAT_CHARS
is the NUL-terminated format string (which at this point may contain
internal NUL characters); FORMAT_LENGTH is its length (excluding the
terminating NUL character). ARG_NUM is one less than the number of
the first format argument to check; PARAMS points to that format
argument in the list of arguments. */
static void
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,
object_allocator <format_wanted_type> &fwt_pool)
{
const char * const orig_format_chars = format_chars;
const tree first_fillin_param = params;
const format_kind_info * const fki = &format_types[info->format_type];
const format_flag_spec * const flag_specs = fki->flag_specs;
const location_t format_string_loc = res->format_string_loc;
/* -1 if no conversions taking an operand have been found; 0 if one has
and it didn't use $; 1 if $ formats are in use. */
int has_operand_number = -1;
init_dollar_format_checking (info->first_arg_num, first_fillin_param);
while (*format_chars != 0)
{
if (*format_chars++ != '%')
continue;
if (*format_chars == 0)
{
warning_at (location_from_offset (format_string_loc,
format_chars - orig_format_chars),
OPT_Wformat_,
"spurious trailing %<%%%> in format");
continue;
}
if (*format_chars == '%')
{
++format_chars;
continue;
}
flag_chars_t flag_chars;
argument_parser arg_parser (info, format_chars, orig_format_chars,
format_string_loc,
flag_chars, has_operand_number,
first_fillin_param, fwt_pool);
if (!arg_parser.read_any_dollar ())
return;
if (!arg_parser.read_format_flags ())
return;
/* Read any format width, possibly * or *m$. */
if (!arg_parser.read_any_format_width (params, arg_num))
return;
/* Read any format left precision (must be a number, not *). */
arg_parser.read_any_format_left_precision ();
/* Read any format precision, possibly * or *m$. */
if (!arg_parser.read_any_format_precision (params, arg_num))
return;
const char *format_start = format_chars;
arg_parser.handle_alloc_chars ();
/* Read any length modifier, if this kind of format has them. */
const length_modifier len_modifier
= arg_parser.read_any_length_modifier ();
/* Read any modifier (strftime E/O). */
arg_parser.read_any_other_modifier ();
char format_char = *format_chars;
if (format_char == 0
|| (!(fki->flags & (int) FMT_FLAG_FANCY_PERCENT_OK)
&& format_char == '%'))
{
warning_at (location_from_offset (format_string_loc,
format_chars - orig_format_chars),
OPT_Wformat_,
"conversion lacks type at end of format");
continue;
}
format_chars++;
const format_char_info * const fci
= arg_parser.find_format_char_info (format_char);
if (!fci)
continue;
flag_chars.validate (fki, fci, flag_specs, format_chars,
format_string_loc, orig_format_chars, format_char);
const int alloc_flag = flag_chars.get_alloc_flag (fki);
const bool suppressed = flag_chars.assignment_suppression_p (fki);
/* Validate the pairs of flags used. */
arg_parser.validate_flag_pairs (fci, format_char);
arg_parser.give_y2k_warnings (fci, format_char);
arg_parser.parse_any_scan_set (fci);
tree wanted_type = NULL;
const char *wanted_type_name = NULL;
if (!arg_parser.handle_conversions (fci, len_modifier,
wanted_type, wanted_type_name,
arg_num,
params,
format_char))
continue;
arg_parser.main_wanted_type.next = NULL;
/* Finally. . .check type of argument against desired type! */
if (!arg_parser.check_argument_type (fci, len_modifier,
wanted_type, wanted_type_name,
suppressed,
arg_num, params,
alloc_flag,
format_start))
return;
}
if (format_chars - orig_format_chars != format_length)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment