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> 2016-08-05 David Malcolm <dmalcolm@redhat.com>
* c-common.c: Include "substring-locations.h". * c-common.c: Include "substring-locations.h".
......
...@@ -1688,78 +1688,284 @@ check_format_arg (void *ctx, tree format_tree, ...@@ -1688,78 +1688,284 @@ check_format_arg (void *ctx, tree format_tree,
params, arg_num, fwt_pool); 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 class flag_chars_t
is the NUL-terminated format string (which at this point may contain {
internal NUL characters); FORMAT_LENGTH is its length (excluding the public:
terminating NUL character). ARG_NUM is one less than the number of flag_chars_t ();
the first format argument to check; PARAMS points to that format bool has_char_p (char ch) const;
argument in the list of arguments. */ 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 /* Support struct for argument_parser and check_format_info_main.
check_format_info_main (format_check_results *res, Encapsulates any length modifier applied to the current argument. */
function_format_info *info, const char *format_chars,
int format_length, tree params, struct length_modifier
unsigned HOST_WIDE_INT arg_num,
object_allocator <format_wanted_type> &fwt_pool)
{ {
const char *orig_format_chars = format_chars; length_modifier ()
tree first_fillin_param = params; : chars (NULL), val (FMT_LEN_none), std (STD_C89),
scalar_identity_flag (0)
{
}
const format_kind_info *fki = &format_types[info->format_type]; length_modifier (const char *chars_,
const format_flag_spec *flag_specs = fki->flag_specs; enum format_lengths val_,
const format_flag_pair *bad_flag_pairs = fki->bad_flag_pairs; enum format_std_version std_,
location_t format_string_loc = res->format_string_loc; 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 const char *chars;
and it didn't use $; 1 if $ formats are in use. */ enum format_lengths val;
int has_operand_number = -1; 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) class argument_parser
{ {
int i; public:
int suppressed = FALSE; argument_parser (function_format_info *info, const char *&format_chars,
const char *length_chars = NULL; const char * const orig_format_chars,
enum format_lengths length_chars_val = FMT_LEN_none; location_t format_string_loc, flag_chars_t &flag_chars,
enum format_std_version length_chars_std = STD_C89; int &has_operand_number, tree first_fillin_param,
int format_char; object_allocator <format_wanted_type> &fwt_pool_);
tree cur_param;
tree wanted_type; bool read_any_dollar ();
int main_arg_num = 0;
tree main_arg_params = 0; bool read_format_flags ();
enum format_std_version wanted_type_std;
const char *wanted_type_name; 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 width_wanted_type;
format_wanted_type precision_wanted_type; format_wanted_type precision_wanted_type;
public:
format_wanted_type main_wanted_type; format_wanted_type main_wanted_type;
format_wanted_type *first_wanted_type = NULL; private:
format_wanted_type *last_wanted_type = NULL; format_wanted_type *first_wanted_type;
const format_length_info *fli = NULL; format_wanted_type *last_wanted_type;
const format_char_info *fci = NULL; };
char flag_chars[256];
int alloc_flag = 0;
int scalar_identity_flag = 0;
const char *format_start;
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; continue;
if (*format_chars == 0) if (strchr (fci->flag_chars, m_flag_chars[i]) == 0)
{ {
warning_at (location_from_offset (format_string_loc, warning_at (location_from_offset (format_string_loc,
format_chars - orig_format_chars), format_chars
OPT_Wformat_, - orig_format_chars),
"spurious trailing %<%%%> in format"); OPT_Wformat_, "%s used with %<%%%c%> %s format",
_(s->name), format_char, fki->name);
d++;
continue; continue;
} }
if (*format_chars == '%') if (pedantic)
{ {
++format_chars; const format_flag_spec *t;
continue; 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) if ((fki->flags & (int) FMT_FLAG_USE_DOLLAR) && has_operand_number != 0)
{ {
/* Possibly read a $ operand number at the start of the format. /* Possibly read a $ operand number at the start of the format.
...@@ -1771,7 +1977,7 @@ check_format_info_main (format_check_results *res, ...@@ -1771,7 +1977,7 @@ check_format_info_main (format_check_results *res,
first_fillin_param, first_fillin_param,
&main_arg_params, fki); &main_arg_params, fki);
if (opnum == -1) if (opnum == -1)
return; return false;
else if (opnum > 0) else if (opnum > 0)
{ {
has_operand_number = 1; has_operand_number = 1;
...@@ -1781,18 +1987,26 @@ check_format_info_main (format_check_results *res, ...@@ -1781,18 +1987,26 @@ check_format_info_main (format_check_results *res,
else if (fki->flags & FMT_FLAG_USE_DOLLAR) else if (fki->flags & FMT_FLAG_USE_DOLLAR)
{ {
if (avoid_dollar_number (format_chars)) 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 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 while (*format_chars != 0
&& strchr (fki->flag_chars, *format_chars) != 0) && strchr (fki->flag_chars, *format_chars) != 0)
{ {
const format_flag_spec *s = get_flag_spec (flag_specs, const format_flag_spec *s = get_flag_spec (flag_specs,
*format_chars, NULL); *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, warning_at (location_from_offset (format_string_loc,
format_chars + 1 format_chars + 1
...@@ -1801,11 +2015,8 @@ check_format_info_main (format_check_results *res, ...@@ -1801,11 +2015,8 @@ check_format_info_main (format_check_results *res,
"repeated %s in format", _(s->name)); "repeated %s in format", _(s->name));
} }
else else
{ flag_chars.add_char (*format_chars);
i = strlen (flag_chars);
flag_chars[i++] = *format_chars;
flag_chars[i] = 0;
}
if (s->skip_next_char) if (s->skip_next_char)
{ {
++format_chars; ++format_chars;
...@@ -1813,20 +2024,30 @@ check_format_info_main (format_check_results *res, ...@@ -1813,20 +2024,30 @@ check_format_info_main (format_check_results *res,
{ {
warning_at (format_string_loc, OPT_Wformat_, warning_at (format_string_loc, OPT_Wformat_,
"missing fill character at end of strfmon format"); "missing fill character at end of strfmon format");
return; return false;
} }
} }
++format_chars; ++format_chars;
} }
/* Read any format width, possibly * or *m$. */ return true;
if (fki->width_char != 0) }
{
/* 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 == '*') if (fki->width_type != NULL && *format_chars == '*')
{ {
i = strlen (flag_chars); flag_chars.add_char (fki->width_char);
flag_chars[i++] = fki->width_char;
flag_chars[i] = 0;
/* "...a field width...may be indicated by an asterisk. /* "...a field width...may be indicated by an asterisk.
In this case, an int argument supplies the field width..." */ In this case, an int argument supplies the field width..." */
++format_chars; ++format_chars;
...@@ -1838,7 +2059,7 @@ check_format_info_main (format_check_results *res, ...@@ -1838,7 +2059,7 @@ check_format_info_main (format_check_results *res,
first_fillin_param, first_fillin_param,
&params, fki); &params, fki);
if (opnum == -1) if (opnum == -1)
return; return false;
else if (opnum > 0) else if (opnum > 0)
{ {
has_operand_number = 1; has_operand_number = 1;
...@@ -1850,10 +2071,11 @@ check_format_info_main (format_check_results *res, ...@@ -1850,10 +2071,11 @@ check_format_info_main (format_check_results *res,
else else
{ {
if (avoid_dollar_number (format_chars)) if (avoid_dollar_number (format_chars))
return; return false;
} }
if (info->first_arg_num != 0) if (info->first_arg_num != 0)
{ {
tree cur_param;
if (params == 0) if (params == 0)
cur_param = NULL; cur_param = NULL;
else else
...@@ -1905,21 +2127,23 @@ check_format_info_main (format_check_results *res, ...@@ -1905,21 +2127,23 @@ check_format_info_main (format_check_results *res,
warning_at (format_string_loc, OPT_Wformat_, warning_at (format_string_loc, OPT_Wformat_,
"zero width in %s format", fki->name); "zero width in %s format", fki->name);
if (found_width) if (found_width)
{ flag_chars.add_char (fki->width_char);
i = strlen (flag_chars);
flag_chars[i++] = fki->width_char;
flag_chars[i] = 0;
}
}
} }
/* Read any format left precision (must be a number, not *). */ return true;
if (fki->left_precision_char != 0 && *format_chars == '#') }
{
/* 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; ++format_chars;
i = strlen (flag_chars); flag_chars.add_char (fki->left_precision_char);
flag_chars[i++] = fki->left_precision_char;
flag_chars[i] = 0;
if (!ISDIGIT (*format_chars)) if (!ISDIGIT (*format_chars))
warning_at (location_from_offset (format_string_loc, warning_at (location_from_offset (format_string_loc,
format_chars - orig_format_chars), format_chars - orig_format_chars),
...@@ -1927,15 +2151,24 @@ check_format_info_main (format_check_results *res, ...@@ -1927,15 +2151,24 @@ check_format_info_main (format_check_results *res,
"empty left precision in %s format", fki->name); "empty left precision in %s format", fki->name);
while (ISDIGIT (*format_chars)) while (ISDIGIT (*format_chars))
++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; ++format_chars;
i = strlen (flag_chars); flag_chars.add_char (fki->precision_char);
flag_chars[i++] = fki->precision_char;
flag_chars[i] = 0;
if (fki->precision_type != NULL && *format_chars == '*') if (fki->precision_type != NULL && *format_chars == '*')
{ {
/* "...a...precision...may be indicated by an asterisk. /* "...a...precision...may be indicated by an asterisk.
...@@ -1949,7 +2182,7 @@ check_format_info_main (format_check_results *res, ...@@ -1949,7 +2182,7 @@ check_format_info_main (format_check_results *res,
first_fillin_param, first_fillin_param,
&params, fki); &params, fki);
if (opnum == -1) if (opnum == -1)
return; return false;
else if (opnum > 0) else if (opnum > 0)
{ {
has_operand_number = 1; has_operand_number = 1;
...@@ -1961,10 +2194,11 @@ check_format_info_main (format_check_results *res, ...@@ -1961,10 +2194,11 @@ check_format_info_main (format_check_results *res,
else else
{ {
if (avoid_dollar_number (format_chars)) if (avoid_dollar_number (format_chars))
return; return false;
} }
if (info->first_arg_num != 0) if (info->first_arg_num != 0)
{ {
tree cur_param;
if (params == 0) if (params == 0)
cur_param = NULL; cur_param = NULL;
else else
...@@ -2009,14 +2243,22 @@ check_format_info_main (format_check_results *res, ...@@ -2009,14 +2243,22 @@ check_format_info_main (format_check_results *res,
while (ISDIGIT (*format_chars)) while (ISDIGIT (*format_chars))
++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) if (fki->alloc_char && fki->alloc_char == *format_chars)
{ {
i = strlen (flag_chars); flag_chars.add_char (fki->alloc_char);
flag_chars[i++] = fki->alloc_char;
flag_chars[i] = 0;
format_chars++; format_chars++;
} }
...@@ -2029,22 +2271,28 @@ check_format_info_main (format_check_results *res, ...@@ -2029,22 +2271,28 @@ check_format_info_main (format_check_results *res,
|| format_chars[1] == '[') || format_chars[1] == '[')
{ {
/* 'a' is used as a flag. */ /* 'a' is used as a flag. */
i = strlen (flag_chars); flag_chars.add_char ('a');
flag_chars[i++] = 'a';
flag_chars[i] = 0;
format_chars++; 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 while (fli->name != 0
&& strncmp (fli->name, format_chars, strlen (fli->name))) && strncmp (fli->name, format_chars, strlen (fli->name)))
fli++; fli++;
...@@ -2054,39 +2302,41 @@ check_format_info_main (format_check_results *res, ...@@ -2054,39 +2302,41 @@ check_format_info_main (format_check_results *res,
if (fli->double_name != 0 && fli->name[0] == *format_chars) if (fli->double_name != 0 && fli->name[0] == *format_chars)
{ {
format_chars++; format_chars++;
length_chars = fli->double_name; result = length_modifier (fli->double_name, fli->double_index,
length_chars_val = fli->double_index; fli->double_std, 0);
length_chars_std = fli->double_std;
} }
else else
{ {
length_chars = fli->name; result = length_modifier (fli->name, fli->index, fli->std,
length_chars_val = fli->index; fli->scalar_identity_flag);
length_chars_std = fli->std;
scalar_identity_flag = fli->scalar_identity_flag;
} }
i = strlen (flag_chars); flag_chars.add_char (fki->length_code_char);
flag_chars[i++] = fki->length_code_char;
flag_chars[i] = 0;
} }
if (pedantic) if (pedantic)
{ {
/* Warn if the length modifier is non-standard. */ /* 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_, warning_at (format_string_loc, OPT_Wformat_,
"%s does not support the %qs %s length modifier", "%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); fki->name);
} }
}
/* Read any modifier (strftime E/O). */ return result;
if (fki->modifier_chars != NULL) }
{
/* Read any other modifier (strftime E/O). */
void
argument_parser::read_any_other_modifier ()
{
if (fki->modifier_chars == NULL)
return;
while (*format_chars != 0 while (*format_chars != 0
&& strchr (fki->modifier_chars, *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, const format_flag_spec *s = get_flag_spec (flag_specs,
*format_chars, NULL); *format_chars, NULL);
...@@ -2097,28 +2347,25 @@ check_format_info_main (format_check_results *res, ...@@ -2097,28 +2347,25 @@ check_format_info_main (format_check_results *res,
"repeated %s in format", _(s->name)); "repeated %s in format", _(s->name));
} }
else else
{ flag_chars.add_char (*format_chars);
i = strlen (flag_chars);
flag_chars[i++] = *format_chars;
flag_chars[i] = 0;
}
++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 while (fci->format_chars != 0
&& strchr (fci->format_chars, format_char) == 0) && strchr (fci->format_chars, format_char) == 0)
++fci; ++fci;
...@@ -2136,8 +2383,9 @@ check_format_info_main (format_check_results *res, ...@@ -2136,8 +2383,9 @@ check_format_info_main (format_check_results *res,
OPT_Wformat_, OPT_Wformat_,
"unknown conversion type character 0x%x in format", "unknown conversion type character 0x%x in format",
format_char); format_char);
continue; return NULL;
} }
if (pedantic) if (pedantic)
{ {
if (ADJ_STD (fci->std) > C_STD_VER) if (ADJ_STD (fci->std) > C_STD_VER)
...@@ -2148,67 +2396,24 @@ check_format_info_main (format_check_results *res, ...@@ -2148,67 +2396,24 @@ check_format_info_main (format_check_results *res,
C_STD_NAME (fci->std), format_char, fki->name); C_STD_NAME (fci->std), format_char, fki->name);
} }
/* Validate the individual flags used, removing any that are invalid. */ return fci;
{ }
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;
}
if ((fki->flags & (int) FMT_FLAG_SCANF_A_KLUDGE) /* Validate the pairs of flags used.
&& strchr (flag_chars, 'a') != 0) Issue warnings about incompatible combinations of flags. */
alloc_flag = 1;
if (fki->alloc_char && strchr (flag_chars, fki->alloc_char) != 0)
alloc_flag = 1;
if (fki->suppression_char void
&& strchr (flag_chars, fki->suppression_char) != 0) argument_parser::validate_flag_pairs (const format_char_info *fci,
suppressed = 1; char format_char)
{
const format_flag_pair * const bad_flag_pairs = fki->bad_flag_pairs;
/* Validate the pairs of flags used. */ for (int i = 0; bad_flag_pairs[i].flag_char1 != 0; i++)
for (i = 0; bad_flag_pairs[i].flag_char1 != 0; i++)
{ {
const format_flag_spec *s, *t; 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; 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; continue;
if (bad_flag_pairs[i].predicate != 0 if (bad_flag_pairs[i].predicate != 0
&& strchr (fci->flags2, 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, ...@@ -2240,13 +2445,20 @@ check_format_info_main (format_check_results *res,
_(s->name), _(t->name), fki->name); _(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; int y2k_level = 0;
if (strchr (fci->flags2, '4') != 0) if (strchr (fci->flags2, '4') != 0)
if (strchr (flag_chars, 'E') != 0) if (flag_chars.has_char_p ('E'))
y2k_level = 3; y2k_level = 3;
else else
y2k_level = 2; y2k_level = 2;
...@@ -2262,10 +2474,17 @@ check_format_info_main (format_check_results *res, ...@@ -2262,10 +2474,17 @@ check_format_info_main (format_check_results *res,
warning_at (format_string_loc, OPT_Wformat_y2k, warning_at (format_string_loc, OPT_Wformat_y2k,
"%<%%%c%> yields only last 2 digits of year", "%<%%%c%> yields only last 2 digits of year",
format_char); 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. */ /* Skip over scan set, in case it happens to have '%' in it. */
if (*format_chars == '^') if (*format_chars == '^')
++format_chars; ++format_chars;
...@@ -2281,16 +2500,29 @@ check_format_info_main (format_check_results *res, ...@@ -2281,16 +2500,29 @@ check_format_info_main (format_check_results *res,
format_chars - orig_format_chars), format_chars - orig_format_chars),
OPT_Wformat_, OPT_Wformat_,
"no closing %<]%> for %<%%[%> format"); "no closing %<]%> for %<%%[%> format");
} }
wanted_type = 0; /* Return true if this argument is to be continued to be parsed,
wanted_type_name = 0; false to skip to next argument. */
if (fki->flags & (int) FMT_FLAG_ARG_CONVERT)
{ bool
wanted_type = (fci->types[length_chars_val].type argument_parser::handle_conversions (const format_char_info *fci,
? *fci->types[length_chars_val].type : 0); const length_modifier &len_modifier,
wanted_type_name = fci->types[length_chars_val].name; tree &wanted_type,
wanted_type_std = fci->types[length_chars_val].std; 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) if (wanted_type == 0)
{ {
warning_at (location_from_offset (format_string_loc, warning_at (location_from_offset (format_string_loc,
...@@ -2298,19 +2530,19 @@ check_format_info_main (format_check_results *res, ...@@ -2298,19 +2530,19 @@ check_format_info_main (format_check_results *res,
OPT_Wformat_, OPT_Wformat_,
"use of %qs length modifier with %qc type character" "use of %qs length modifier with %qc type character"
" has either no effect or undefined behavior", " 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 /* Heuristic: skip one argument when an invalid length/type
combination is encountered. */ combination is encountered. */
arg_num++; arg_num++;
if (params != 0) if (params != 0)
params = TREE_CHAIN (params); params = TREE_CHAIN (params);
continue; return false;
} }
else if (pedantic else if (pedantic
/* Warn if non-standard, provided it is more non-standard /* Warn if non-standard, provided it is more non-standard
than the length and type characters that may already than the length and type characters that may already
have been warned for. */ 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)) && ADJ_STD (wanted_type_std) > ADJ_STD (fci->std))
{ {
if (ADJ_STD (wanted_type_std) > C_STD_VER) if (ADJ_STD (wanted_type_std) > C_STD_VER)
...@@ -2318,16 +2550,32 @@ check_format_info_main (format_check_results *res, ...@@ -2318,16 +2550,32 @@ check_format_info_main (format_check_results *res,
format_chars - orig_format_chars), format_chars - orig_format_chars),
OPT_Wformat_, OPT_Wformat_,
"%s does not support the %<%%%s%c%> %s format", "%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); 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) if (info->first_arg_num == 0)
continue; return true;
if ((fci->pointer_count == 0 && wanted_type == void_type_node) if ((fci->pointer_count == 0 && wanted_type == void_type_node)
|| suppressed) || suppressed)
{ {
...@@ -2359,7 +2607,7 @@ check_format_info_main (format_check_results *res, ...@@ -2359,7 +2607,7 @@ check_format_info_main (format_check_results *res,
{ {
warning_at (format_string_loc, OPT_Wformat_, warning_at (format_string_loc, OPT_Wformat_,
"missing $ operand number in format"); "missing $ operand number in format");
return; return false;
} }
else else
has_operand_number = 0; has_operand_number = 0;
...@@ -2368,6 +2616,7 @@ check_format_info_main (format_check_results *res, ...@@ -2368,6 +2616,7 @@ check_format_info_main (format_check_results *res,
wanted_type_ptr = &main_wanted_type; wanted_type_ptr = &main_wanted_type;
while (fci) while (fci)
{ {
tree cur_param;
if (params == 0) if (params == 0)
cur_param = NULL; cur_param = NULL;
else else
...@@ -2383,7 +2632,7 @@ check_format_info_main (format_check_results *res, ...@@ -2383,7 +2632,7 @@ check_format_info_main (format_check_results *res,
if (strchr (fci->flags2, 'c') != 0) if (strchr (fci->flags2, 'c') != 0)
wanted_type_ptr->char_lenient_flag = 1; wanted_type_ptr->char_lenient_flag = 1;
wanted_type_ptr->scalar_identity_flag = 0; 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->scalar_identity_flag = 1;
wanted_type_ptr->writing_in_flag = 0; wanted_type_ptr->writing_in_flag = 0;
wanted_type_ptr->reading_from_flag = 0; wanted_type_ptr->reading_from_flag = 0;
...@@ -2414,14 +2663,148 @@ check_format_info_main (format_check_results *res, ...@@ -2414,14 +2663,148 @@ check_format_info_main (format_check_results *res,
{ {
wanted_type_ptr = fwt_pool.allocate (); wanted_type_ptr = fwt_pool.allocate ();
arg_num++; arg_num++;
wanted_type = *fci->types[length_chars_val].type; wanted_type = *fci->types[len_modifier.val].type;
wanted_type_name = fci->types[length_chars_val].name; wanted_type_name = fci->types[len_modifier.val].name;
} }
} }
} }
if (first_wanted_type != 0) if (first_wanted_type != 0)
check_format_types (format_string_loc, first_wanted_type); 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) 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