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,740 +1688,1123 @@ check_format_arg (void *ctx, tree format_tree, ...@@ -1688,740 +1688,1123 @@ 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
format_wanted_type width_wanted_type; read_any_format_width (tree &params,
format_wanted_type precision_wanted_type; unsigned HOST_WIDE_INT &arg_num);
format_wanted_type main_wanted_type;
format_wanted_type *first_wanted_type = NULL; void
format_wanted_type *last_wanted_type = NULL; read_any_format_left_precision ();
const format_length_info *fli = NULL;
const format_char_info *fci = NULL; bool
char flag_chars[256]; read_any_format_precision (tree &params,
int alloc_flag = 0; unsigned HOST_WIDE_INT &arg_num);
int scalar_identity_flag = 0;
const char *format_start; 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;
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; 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)
{
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);
}
}
}
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.
If one was previously used, one is required here. If one
is not used here, we can't immediately conclude this is a
format without them, since it could be printf %m or scanf %*. */
int opnum;
opnum = maybe_read_dollar_number (&format_chars, 0,
first_fillin_param,
&main_arg_params, fki);
if (opnum == -1)
return false;
else if (opnum > 0)
{
has_operand_number = 1;
main_arg_num = opnum + info->first_arg_num - 1;
}
}
else if (fki->flags & FMT_FLAG_USE_DOLLAR)
{
if (avoid_dollar_number (format_chars))
return false;
}
return true;
}
/* Read any format flags, but do not yet validate them beyond removing
duplicates, since in general validation depends on the rest of
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 (flag_chars.has_char_p (*format_chars))
{
warning_at (location_from_offset (format_string_loc,
format_chars + 1
- orig_format_chars),
OPT_Wformat_,
"repeated %s in format", _(s->name));
}
else
flag_chars.add_char (*format_chars);
if (s->skip_next_char)
{ {
++format_chars; ++format_chars;
continue; if (*format_chars == 0)
{
warning_at (format_string_loc, OPT_Wformat_,
"missing fill character at end of strfmon format");
return false;
}
} }
flag_chars[0] = 0; ++format_chars;
}
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->flags & (int) FMT_FLAG_USE_DOLLAR) && has_operand_number != 0) if (fki->width_type != NULL && *format_chars == '*')
{
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;
if (has_operand_number != 0)
{ {
/* Possibly read a $ operand number at the start of the format.
If one was previously used, one is required here. If one
is not used here, we can't immediately conclude this is a
format without them, since it could be printf %m or scanf %*. */
int opnum; int opnum;
opnum = maybe_read_dollar_number (&format_chars, 0, opnum = maybe_read_dollar_number (&format_chars,
has_operand_number == 1,
first_fillin_param, first_fillin_param,
&main_arg_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;
main_arg_num = opnum + info->first_arg_num - 1; arg_num = opnum + info->first_arg_num - 1;
} }
else
has_operand_number = 0;
} }
else if (fki->flags & FMT_FLAG_USE_DOLLAR) else
{ {
if (avoid_dollar_number (format_chars)) if (avoid_dollar_number (format_chars))
return; return false;
} }
if (info->first_arg_num != 0)
/* Read any format flags, but do not yet validate them beyond removing
duplicates, since in general validation depends on the rest of
the format. */
while (*format_chars != 0
&& strchr (fki->flag_chars, *format_chars) != 0)
{ {
const format_flag_spec *s = get_flag_spec (flag_specs, tree cur_param;
*format_chars, NULL); if (params == 0)
if (strchr (flag_chars, *format_chars) != 0) cur_param = NULL;
{
warning_at (location_from_offset (format_string_loc,
format_chars + 1
- orig_format_chars),
OPT_Wformat_,
"repeated %s in format", _(s->name));
}
else else
{ {
i = strlen (flag_chars); cur_param = TREE_VALUE (params);
flag_chars[i++] = *format_chars; if (has_operand_number <= 0)
flag_chars[i] = 0;
}
if (s->skip_next_char)
{
++format_chars;
if (*format_chars == 0)
{ {
warning_at (format_string_loc, OPT_Wformat_, params = TREE_CHAIN (params);
"missing fill character at end of strfmon format"); ++arg_num;
return;
} }
} }
width_wanted_type.wanted_type = *fki->width_type;
width_wanted_type.wanted_type_name = NULL;
width_wanted_type.pointer_count = 0;
width_wanted_type.char_lenient_flag = 0;
width_wanted_type.scalar_identity_flag = 0;
width_wanted_type.writing_in_flag = 0;
width_wanted_type.reading_from_flag = 0;
width_wanted_type.kind = CF_KIND_FIELD_WIDTH;
width_wanted_type.format_start = format_chars - 1;
width_wanted_type.format_length = 1;
width_wanted_type.param = cur_param;
width_wanted_type.arg_num = arg_num;
width_wanted_type.offset_loc =
format_chars - orig_format_chars;
width_wanted_type.next = NULL;
if (last_wanted_type != 0)
last_wanted_type->next = &width_wanted_type;
if (first_wanted_type == 0)
first_wanted_type = &width_wanted_type;
last_wanted_type = &width_wanted_type;
}
}
else
{
/* Possibly read a numeric width. If the width is zero,
we complain if appropriate. */
int non_zero_width_char = FALSE;
int found_width = FALSE;
while (ISDIGIT (*format_chars))
{
found_width = TRUE;
if (*format_chars != '0')
non_zero_width_char = TRUE;
++format_chars; ++format_chars;
} }
if (found_width && !non_zero_width_char &&
(fki->flags & (int) FMT_FLAG_ZERO_WIDTH_BAD))
warning_at (format_string_loc, OPT_Wformat_,
"zero width in %s format", fki->name);
if (found_width)
flag_chars.add_char (fki->width_char);
}
/* Read any format width, possibly * or *m$. */ return true;
if (fki->width_char != 0) }
/* 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;
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),
OPT_Wformat_,
"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;
++format_chars;
flag_chars.add_char (fki->precision_char);
if (fki->precision_type != NULL && *format_chars == '*')
{
/* "...a...precision...may be indicated by an asterisk.
In this case, an int argument supplies the...precision." */
++format_chars;
if (has_operand_number != 0)
{ {
if (fki->width_type != NULL && *format_chars == '*') int opnum;
opnum = maybe_read_dollar_number (&format_chars,
has_operand_number == 1,
first_fillin_param,
&params, fki);
if (opnum == -1)
return false;
else if (opnum > 0)
{ {
i = strlen (flag_chars); has_operand_number = 1;
flag_chars[i++] = fki->width_char; arg_num = opnum + info->first_arg_num - 1;
flag_chars[i] = 0;
/* "...a field width...may be indicated by an asterisk.
In this case, an int argument supplies the field width..." */
++format_chars;
if (has_operand_number != 0)
{
int opnum;
opnum = maybe_read_dollar_number (&format_chars,
has_operand_number == 1,
first_fillin_param,
&params, fki);
if (opnum == -1)
return;
else if (opnum > 0)
{
has_operand_number = 1;
arg_num = opnum + info->first_arg_num - 1;
}
else
has_operand_number = 0;
}
else
{
if (avoid_dollar_number (format_chars))
return;
}
if (info->first_arg_num != 0)
{
if (params == 0)
cur_param = NULL;
else
{
cur_param = TREE_VALUE (params);
if (has_operand_number <= 0)
{
params = TREE_CHAIN (params);
++arg_num;
}
}
width_wanted_type.wanted_type = *fki->width_type;
width_wanted_type.wanted_type_name = NULL;
width_wanted_type.pointer_count = 0;
width_wanted_type.char_lenient_flag = 0;
width_wanted_type.scalar_identity_flag = 0;
width_wanted_type.writing_in_flag = 0;
width_wanted_type.reading_from_flag = 0;
width_wanted_type.kind = CF_KIND_FIELD_WIDTH;
width_wanted_type.format_start = format_chars - 1;
width_wanted_type.format_length = 1;
width_wanted_type.param = cur_param;
width_wanted_type.arg_num = arg_num;
width_wanted_type.offset_loc =
format_chars - orig_format_chars;
width_wanted_type.next = NULL;
if (last_wanted_type != 0)
last_wanted_type->next = &width_wanted_type;
if (first_wanted_type == 0)
first_wanted_type = &width_wanted_type;
last_wanted_type = &width_wanted_type;
}
} }
else else
{ has_operand_number = 0;
/* Possibly read a numeric width. If the width is zero,
we complain if appropriate. */
int non_zero_width_char = FALSE;
int found_width = FALSE;
while (ISDIGIT (*format_chars))
{
found_width = TRUE;
if (*format_chars != '0')
non_zero_width_char = TRUE;
++format_chars;
}
if (found_width && !non_zero_width_char &&
(fki->flags & (int) FMT_FLAG_ZERO_WIDTH_BAD))
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;
}
}
} }
else
/* Read any format left precision (must be a number, not *). */
if (fki->left_precision_char != 0 && *format_chars == '#')
{ {
++format_chars; if (avoid_dollar_number (format_chars))
i = strlen (flag_chars); return false;
flag_chars[i++] = fki->left_precision_char;
flag_chars[i] = 0;
if (!ISDIGIT (*format_chars))
warning_at (location_from_offset (format_string_loc,
format_chars - orig_format_chars),
OPT_Wformat_,
"empty left precision in %s format", fki->name);
while (ISDIGIT (*format_chars))
++format_chars;
} }
if (info->first_arg_num != 0)
/* Read any format precision, possibly * or *m$. */
if (fki->precision_char != 0 && *format_chars == '.')
{ {
++format_chars; tree cur_param;
i = strlen (flag_chars); if (params == 0)
flag_chars[i++] = fki->precision_char; cur_param = NULL;
flag_chars[i] = 0; else
if (fki->precision_type != NULL && *format_chars == '*')
{ {
/* "...a...precision...may be indicated by an asterisk. cur_param = TREE_VALUE (params);
In this case, an int argument supplies the...precision." */ if (has_operand_number <= 0)
++format_chars;
if (has_operand_number != 0)
{ {
int opnum; params = TREE_CHAIN (params);
opnum = maybe_read_dollar_number (&format_chars, ++arg_num;
has_operand_number == 1,
first_fillin_param,
&params, fki);
if (opnum == -1)
return;
else if (opnum > 0)
{
has_operand_number = 1;
arg_num = opnum + info->first_arg_num - 1;
}
else
has_operand_number = 0;
}
else
{
if (avoid_dollar_number (format_chars))
return;
}
if (info->first_arg_num != 0)
{
if (params == 0)
cur_param = NULL;
else
{
cur_param = TREE_VALUE (params);
if (has_operand_number <= 0)
{
params = TREE_CHAIN (params);
++arg_num;
}
}
precision_wanted_type.wanted_type = *fki->precision_type;
precision_wanted_type.wanted_type_name = NULL;
precision_wanted_type.pointer_count = 0;
precision_wanted_type.char_lenient_flag = 0;
precision_wanted_type.scalar_identity_flag = 0;
precision_wanted_type.writing_in_flag = 0;
precision_wanted_type.reading_from_flag = 0;
precision_wanted_type.kind = CF_KIND_FIELD_PRECISION;
precision_wanted_type.param = cur_param;
precision_wanted_type.format_start = format_chars - 2;
precision_wanted_type.format_length = 2;
precision_wanted_type.arg_num = arg_num;
precision_wanted_type.offset_loc =
format_chars - orig_format_chars;
precision_wanted_type.next = NULL;
if (last_wanted_type != 0)
last_wanted_type->next = &precision_wanted_type;
if (first_wanted_type == 0)
first_wanted_type = &precision_wanted_type;
last_wanted_type = &precision_wanted_type;
} }
} }
else precision_wanted_type.wanted_type = *fki->precision_type;
{ precision_wanted_type.wanted_type_name = NULL;
if (!(fki->flags & (int) FMT_FLAG_EMPTY_PREC_OK) precision_wanted_type.pointer_count = 0;
&& !ISDIGIT (*format_chars)) precision_wanted_type.char_lenient_flag = 0;
warning_at (location_from_offset (format_string_loc, precision_wanted_type.scalar_identity_flag = 0;
format_chars - orig_format_chars), precision_wanted_type.writing_in_flag = 0;
OPT_Wformat_, precision_wanted_type.reading_from_flag = 0;
"empty precision in %s format", fki->name); precision_wanted_type.kind = CF_KIND_FIELD_PRECISION;
while (ISDIGIT (*format_chars)) precision_wanted_type.param = cur_param;
++format_chars; precision_wanted_type.format_start = format_chars - 2;
} precision_wanted_type.format_length = 2;
precision_wanted_type.arg_num = arg_num;
precision_wanted_type.offset_loc =
format_chars - orig_format_chars;
precision_wanted_type.next = NULL;
if (last_wanted_type != 0)
last_wanted_type->next = &precision_wanted_type;
if (first_wanted_type == 0)
first_wanted_type = &precision_wanted_type;
last_wanted_type = &precision_wanted_type;
} }
}
else
{
if (!(fki->flags & (int) FMT_FLAG_EMPTY_PREC_OK)
&& !ISDIGIT (*format_chars))
warning_at (location_from_offset (format_string_loc,
format_chars - orig_format_chars),
OPT_Wformat_,
"empty precision in %s format", fki->name);
while (ISDIGIT (*format_chars))
++format_chars;
}
format_start = format_chars; return true;
if (fki->alloc_char && fki->alloc_char == *format_chars) }
{
i = strlen (flag_chars);
flag_chars[i++] = fki->alloc_char;
flag_chars[i] = 0;
format_chars++;
}
/* Handle the scanf allocation kludge. */ /* Parse any assignment-allocation flags, which request an extra
if (fki->flags & (int) FMT_FLAG_SCANF_A_KLUDGE) 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)
{
flag_chars.add_char (fki->alloc_char);
format_chars++;
}
/* Handle the scanf allocation kludge. */
if (fki->flags & (int) FMT_FLAG_SCANF_A_KLUDGE)
{
if (*format_chars == 'a' && !flag_isoc99)
{ {
if (*format_chars == 'a' && !flag_isoc99) if (format_chars[1] == 's' || format_chars[1] == 'S'
|| format_chars[1] == '[')
{ {
if (format_chars[1] == 's' || format_chars[1] == 'S' /* 'a' is used as a flag. */
|| format_chars[1] == '[') flag_chars.add_char ('a');
{ format_chars++;
/* 'a' is used as a flag. */
i = strlen (flag_chars);
flag_chars[i++] = 'a';
flag_chars[i] = 0;
format_chars++;
}
} }
} }
}
}
/* Read any length modifier, if this kind of format has them. */ /* Look for length modifiers within the current format argument,
fli = fki->length_char_specs; returning a length_modifier instance describing it (or the
length_chars = NULL; default if one is not found).
length_chars_val = FMT_LEN_none;
length_chars_std = STD_C89; Issue warnings about non-standard modifiers. */
scalar_identity_flag = 0;
if (fli) length_modifier
argument_parser::read_any_length_modifier ()
{
length_modifier result;
const format_length_info *fli = fki->length_char_specs;
if (!fli)
return result;
while (fli->name != 0
&& strncmp (fli->name, format_chars, strlen (fli->name)))
fli++;
if (fli->name != 0)
{
format_chars += strlen (fli->name);
if (fli->double_name != 0 && fli->name[0] == *format_chars)
{ {
while (fli->name != 0 format_chars++;
&& strncmp (fli->name, format_chars, strlen (fli->name))) result = length_modifier (fli->double_name, fli->double_index,
fli++; fli->double_std, 0);
if (fli->name != 0)
{
format_chars += strlen (fli->name);
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;
}
else
{
length_chars = fli->name;
length_chars_val = fli->index;
length_chars_std = fli->std;
scalar_identity_flag = fli->scalar_identity_flag;
}
i = strlen (flag_chars);
flag_chars[i++] = fki->length_code_char;
flag_chars[i] = 0;
}
if (pedantic)
{
/* Warn if the length modifier is non-standard. */
if (ADJ_STD (length_chars_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,
fki->name);
}
} }
else
/* Read any modifier (strftime E/O). */
if (fki->modifier_chars != NULL)
{ {
while (*format_chars != 0 result = length_modifier (fli->name, fli->index, fli->std,
&& strchr (fki->modifier_chars, *format_chars) != 0) fli->scalar_identity_flag);
{
if (strchr (flag_chars, *format_chars) != 0)
{
const format_flag_spec *s = get_flag_spec (flag_specs,
*format_chars, NULL);
warning_at (location_from_offset (format_string_loc,
format_chars
- orig_format_chars),
OPT_Wformat_,
"repeated %s in format", _(s->name));
}
else
{
i = strlen (flag_chars);
flag_chars[i++] = *format_chars;
flag_chars[i] = 0;
}
++format_chars;
}
} }
flag_chars.add_char (fki->length_code_char);
}
if (pedantic)
{
/* Warn if the length modifier is non-standard. */
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 (result.std), result.chars,
fki->name);
}
format_char = *format_chars; return result;
if (format_char == 0 }
|| (!(fki->flags & (int) FMT_FLAG_FANCY_PERCENT_OK)
&& format_char == '%')) /* 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 (flag_chars.has_char_p (*format_chars))
{ {
const format_flag_spec *s = get_flag_spec (flag_specs,
*format_chars, NULL);
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),
OPT_Wformat_, OPT_Wformat_,
"conversion lacks type at end of format"); "repeated %s in format", _(s->name));
continue;
} }
format_chars++; else
fci = fki->conversion_specs; flag_chars.add_char (*format_chars);
while (fci->format_chars != 0 ++format_chars;
&& strchr (fci->format_chars, format_char) == 0) }
++fci; }
if (fci->format_chars == 0)
/* 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;
while (fci->format_chars != 0
&& strchr (fci->format_chars, format_char) == 0)
++fci;
if (fci->format_chars == 0)
{
if (ISGRAPH (format_char))
warning_at (location_from_offset (format_string_loc,
format_chars - orig_format_chars),
OPT_Wformat_,
"unknown conversion type character %qc in format",
format_char);
else
warning_at (location_from_offset (format_string_loc,
format_chars - orig_format_chars),
OPT_Wformat_,
"unknown conversion type character 0x%x in format",
format_char);
return NULL;
}
if (pedantic)
{
if (ADJ_STD (fci->std) > C_STD_VER)
warning_at (location_from_offset (format_string_loc,
format_chars - orig_format_chars),
OPT_Wformat_,
"%s does not support the %<%%%c%> %s format",
C_STD_NAME (fci->std), format_char, fki->name);
}
return fci;
}
/* Validate the pairs of flags used.
Issue warnings about incompatible combinations of flags. */
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;
for (int i = 0; bad_flag_pairs[i].flag_char1 != 0; i++)
{
const format_flag_spec *s, *t;
if (!flag_chars.has_char_p (bad_flag_pairs[i].flag_char1))
continue;
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)
continue;
s = get_flag_spec (flag_specs, bad_flag_pairs[i].flag_char1, NULL);
t = get_flag_spec (flag_specs, bad_flag_pairs[i].flag_char2, NULL);
if (bad_flag_pairs[i].ignored)
{ {
if (ISGRAPH (format_char)) if (bad_flag_pairs[i].predicate != 0)
warning_at (location_from_offset (format_string_loc, warning_at (format_string_loc, OPT_Wformat_,
format_chars - orig_format_chars), "%s ignored with %s and %<%%%c%> %s format",
OPT_Wformat_, _(s->name), _(t->name), format_char,
"unknown conversion type character %qc in format", fki->name);
format_char);
else else
warning_at (location_from_offset (format_string_loc, warning_at (format_string_loc, OPT_Wformat_,
format_chars - orig_format_chars), "%s ignored with %s in %s format",
OPT_Wformat_, _(s->name), _(t->name), fki->name);
"unknown conversion type character 0x%x in format",
format_char);
continue;
} }
if (pedantic) else
{ {
if (ADJ_STD (fci->std) > C_STD_VER) if (bad_flag_pairs[i].predicate != 0)
warning_at (location_from_offset (format_string_loc, warning_at (format_string_loc, OPT_Wformat_,
format_chars - orig_format_chars), "use of %s and %s together with %<%%%c%> %s format",
OPT_Wformat_, _(s->name), _(t->name), format_char,
"%s does not support the %<%%%c%> %s format", fki->name);
C_STD_NAME (fci->std), format_char, fki->name); else
warning_at (format_string_loc, OPT_Wformat_,
"use of %s and %s together in %s format",
_(s->name), _(t->name), fki->name);
} }
}
}
/* Validate the individual flags used, removing any that are invalid. */ /* Give Y2K warnings. */
{
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)
&& strchr (flag_chars, 'a') != 0)
alloc_flag = 1;
if (fki->alloc_char && strchr (flag_chars, fki->alloc_char) != 0)
alloc_flag = 1;
if (fki->suppression_char
&& strchr (flag_chars, fki->suppression_char) != 0)
suppressed = 1;
/* Validate the pairs of flags used. */ void
for (i = 0; bad_flag_pairs[i].flag_char1 != 0; i++) argument_parser::give_y2k_warnings (const format_char_info *fci,
char format_char)
{
if (!warn_format_y2k)
return;
int y2k_level = 0;
if (strchr (fci->flags2, '4') != 0)
if (flag_chars.has_char_p ('E'))
y2k_level = 3;
else
y2k_level = 2;
else if (strchr (fci->flags2, '3') != 0)
y2k_level = 3;
else if (strchr (fci->flags2, '2') != 0)
y2k_level = 2;
if (y2k_level == 3)
warning_at (format_string_loc, OPT_Wformat_y2k,
"%<%%%c%> yields only last 2 digits of "
"year in some locales", format_char);
else if (y2k_level == 2)
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;
/* Skip over scan set, in case it happens to have '%' in it. */
if (*format_chars == '^')
++format_chars;
/* Find closing bracket; if one is hit immediately, then
it's part of the scan set rather than a terminator. */
if (*format_chars == ']')
++format_chars;
while (*format_chars && *format_chars != ']')
++format_chars;
if (*format_chars != ']')
/* The end of the format string was reached. */
warning_at (location_from_offset (format_string_loc,
format_chars - orig_format_chars),
OPT_Wformat_,
"no closing %<]%> for %<%%[%> format");
}
/* 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,
format_chars - orig_format_chars),
OPT_Wformat_,
"use of %qs length modifier with %qc type character"
" has either no effect or undefined behavior",
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);
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 (len_modifier.std)
&& ADJ_STD (wanted_type_std) > ADJ_STD (fci->std))
{
if (ADJ_STD (wanted_type_std) > C_STD_VER)
warning_at (location_from_offset (format_string_loc,
format_chars - orig_format_chars),
OPT_Wformat_,
"%s does not support the %<%%%s%c%> %s format",
C_STD_NAME (wanted_type_std), len_modifier.chars,
format_char, fki->name);
}
return true;
}
/* 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)
return true;
if ((fci->pointer_count == 0 && wanted_type == void_type_node)
|| suppressed)
{
if (main_arg_num != 0)
{ {
const format_flag_spec *s, *t; if (suppressed)
if (strchr (flag_chars, bad_flag_pairs[i].flag_char1) == 0) warning_at (format_string_loc, OPT_Wformat_,
continue; "operand number specified with "
if (strchr (flag_chars, bad_flag_pairs[i].flag_char2) == 0) "suppressed assignment");
continue;
if (bad_flag_pairs[i].predicate != 0
&& strchr (fci->flags2, bad_flag_pairs[i].predicate) == 0)
continue;
s = get_flag_spec (flag_specs, bad_flag_pairs[i].flag_char1, NULL);
t = get_flag_spec (flag_specs, bad_flag_pairs[i].flag_char2, NULL);
if (bad_flag_pairs[i].ignored)
{
if (bad_flag_pairs[i].predicate != 0)
warning_at (format_string_loc, OPT_Wformat_,
"%s ignored with %s and %<%%%c%> %s format",
_(s->name), _(t->name), format_char,
fki->name);
else
warning_at (format_string_loc, OPT_Wformat_,
"%s ignored with %s in %s format",
_(s->name), _(t->name), fki->name);
}
else else
{ warning_at (format_string_loc, OPT_Wformat_,
if (bad_flag_pairs[i].predicate != 0) "operand number specified for format "
warning_at (format_string_loc, OPT_Wformat_, "taking no argument");
"use of %s and %s together with %<%%%c%> %s format",
_(s->name), _(t->name), format_char,
fki->name);
else
warning_at (format_string_loc, OPT_Wformat_,
"use of %s and %s together in %s format",
_(s->name), _(t->name), fki->name);
}
} }
}
else
{
format_wanted_type *wanted_type_ptr;
/* Give Y2K warnings. */ if (main_arg_num != 0)
if (warn_format_y2k)
{ {
int y2k_level = 0; arg_num = main_arg_num;
if (strchr (fci->flags2, '4') != 0) params = main_arg_params;
if (strchr (flag_chars, 'E') != 0)
y2k_level = 3;
else
y2k_level = 2;
else if (strchr (fci->flags2, '3') != 0)
y2k_level = 3;
else if (strchr (fci->flags2, '2') != 0)
y2k_level = 2;
if (y2k_level == 3)
warning_at (format_string_loc, OPT_Wformat_y2k,
"%<%%%c%> yields only last 2 digits of "
"year in some locales", format_char);
else if (y2k_level == 2)
warning_at (format_string_loc, OPT_Wformat_y2k,
"%<%%%c%> yields only last 2 digits of year",
format_char);
} }
else
if (strchr (fci->flags2, '[') != 0)
{ {
/* Skip over scan set, in case it happens to have '%' in it. */ ++arg_num;
if (*format_chars == '^') if (has_operand_number > 0)
++format_chars; {
/* Find closing bracket; if one is hit immediately, then warning_at (format_string_loc, OPT_Wformat_,
it's part of the scan set rather than a terminator. */ "missing $ operand number in format");
if (*format_chars == ']') return false;
++format_chars; }
while (*format_chars && *format_chars != ']') else
++format_chars; has_operand_number = 0;
if (*format_chars != ']')
/* The end of the format string was reached. */
warning_at (location_from_offset (format_string_loc,
format_chars - orig_format_chars),
OPT_Wformat_,
"no closing %<]%> for %<%%[%> format");
} }
wanted_type = 0; wanted_type_ptr = &main_wanted_type;
wanted_type_name = 0; while (fci)
if (fki->flags & (int) FMT_FLAG_ARG_CONVERT)
{ {
wanted_type = (fci->types[length_chars_val].type tree cur_param;
? *fci->types[length_chars_val].type : 0); if (params == 0)
wanted_type_name = fci->types[length_chars_val].name; cur_param = NULL;
wanted_type_std = fci->types[length_chars_val].std; else
if (wanted_type == 0)
{ {
warning_at (location_from_offset (format_string_loc, cur_param = TREE_VALUE (params);
format_chars - orig_format_chars), params = TREE_CHAIN (params);
OPT_Wformat_,
"use of %qs length modifier with %qc type character"
" has either no effect or undefined behavior",
length_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;
} }
else if (pedantic
/* Warn if non-standard, provided it is more non-standard wanted_type_ptr->wanted_type = wanted_type;
than the length and type characters that may already wanted_type_ptr->wanted_type_name = wanted_type_name;
have been warned for. */ wanted_type_ptr->pointer_count = fci->pointer_count + alloc_flag;
&& ADJ_STD (wanted_type_std) > ADJ_STD (length_chars_std) wanted_type_ptr->char_lenient_flag = 0;
&& ADJ_STD (wanted_type_std) > ADJ_STD (fci->std)) if (strchr (fci->flags2, 'c') != 0)
wanted_type_ptr->char_lenient_flag = 1;
wanted_type_ptr->scalar_identity_flag = 0;
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;
if (alloc_flag)
wanted_type_ptr->writing_in_flag = 1;
else
{ {
if (ADJ_STD (wanted_type_std) > C_STD_VER) if (strchr (fci->flags2, 'W') != 0)
warning_at (location_from_offset (format_string_loc, wanted_type_ptr->writing_in_flag = 1;
format_chars - orig_format_chars), if (strchr (fci->flags2, 'R') != 0)
OPT_Wformat_, wanted_type_ptr->reading_from_flag = 1;
"%s does not support the %<%%%s%c%> %s format", }
C_STD_NAME (wanted_type_std), length_chars, wanted_type_ptr->kind = CF_KIND_FORMAT;
format_char, fki->name); wanted_type_ptr->param = cur_param;
wanted_type_ptr->arg_num = arg_num;
wanted_type_ptr->format_start = format_start;
wanted_type_ptr->format_length = format_chars - format_start;
wanted_type_ptr->offset_loc = format_chars - orig_format_chars;
wanted_type_ptr->next = NULL;
if (last_wanted_type != 0)
last_wanted_type->next = wanted_type_ptr;
if (first_wanted_type == 0)
first_wanted_type = wanted_type_ptr;
last_wanted_type = wanted_type_ptr;
fci = fci->chain;
if (fci)
{
wanted_type_ptr = fwt_pool.allocate ();
arg_num++;
wanted_type = *fci->types[len_modifier.val].type;
wanted_type_name = fci->types[len_modifier.val].name;
} }
} }
}
main_wanted_type.next = NULL; if (first_wanted_type != 0)
check_format_types (format_string_loc, first_wanted_type);
/* Finally. . .check type of argument against desired type! */ return true;
if (info->first_arg_num == 0) }
/* 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; continue;
if ((fci->pointer_count == 0 && wanted_type == void_type_node) if (*format_chars == 0)
|| suppressed)
{ {
if (main_arg_num != 0) warning_at (location_from_offset (format_string_loc,
{ format_chars - orig_format_chars),
if (suppressed) OPT_Wformat_,
warning_at (format_string_loc, OPT_Wformat_, "spurious trailing %<%%%> in format");
"operand number specified with " continue;
"suppressed assignment");
else
warning_at (format_string_loc, OPT_Wformat_,
"operand number specified for format "
"taking no argument");
}
} }
else if (*format_chars == '%')
{ {
format_wanted_type *wanted_type_ptr; ++format_chars;
continue;
}
if (main_arg_num != 0) flag_chars_t flag_chars;
{ argument_parser arg_parser (info, format_chars, orig_format_chars,
arg_num = main_arg_num; format_string_loc,
params = main_arg_params; flag_chars, has_operand_number,
} first_fillin_param, fwt_pool);
else
{
++arg_num;
if (has_operand_number > 0)
{
warning_at (format_string_loc, OPT_Wformat_,
"missing $ operand number in format");
return;
}
else
has_operand_number = 0;
}
wanted_type_ptr = &main_wanted_type; if (!arg_parser.read_any_dollar ())
while (fci) return;
{
if (params == 0) if (!arg_parser.read_format_flags ())
cur_param = NULL; return;
else
{ /* Read any format width, possibly * or *m$. */
cur_param = TREE_VALUE (params); if (!arg_parser.read_any_format_width (params, arg_num))
params = TREE_CHAIN (params); return;
}
/* Read any format left precision (must be a number, not *). */
wanted_type_ptr->wanted_type = wanted_type; arg_parser.read_any_format_left_precision ();
wanted_type_ptr->wanted_type_name = wanted_type_name;
wanted_type_ptr->pointer_count = fci->pointer_count + alloc_flag; /* Read any format precision, possibly * or *m$. */
wanted_type_ptr->char_lenient_flag = 0; if (!arg_parser.read_any_format_precision (params, arg_num))
if (strchr (fci->flags2, 'c') != 0) return;
wanted_type_ptr->char_lenient_flag = 1;
wanted_type_ptr->scalar_identity_flag = 0; const char *format_start = format_chars;
if (scalar_identity_flag)
wanted_type_ptr->scalar_identity_flag = 1; arg_parser.handle_alloc_chars ();
wanted_type_ptr->writing_in_flag = 0;
wanted_type_ptr->reading_from_flag = 0; /* Read any length modifier, if this kind of format has them. */
if (alloc_flag) const length_modifier len_modifier
wanted_type_ptr->writing_in_flag = 1; = arg_parser.read_any_length_modifier ();
else
{ /* Read any modifier (strftime E/O). */
if (strchr (fci->flags2, 'W') != 0) arg_parser.read_any_other_modifier ();
wanted_type_ptr->writing_in_flag = 1;
if (strchr (fci->flags2, 'R') != 0) char format_char = *format_chars;
wanted_type_ptr->reading_from_flag = 1; if (format_char == 0
} || (!(fki->flags & (int) FMT_FLAG_FANCY_PERCENT_OK)
wanted_type_ptr->kind = CF_KIND_FORMAT; && format_char == '%'))
wanted_type_ptr->param = cur_param; {
wanted_type_ptr->arg_num = arg_num; warning_at (location_from_offset (format_string_loc,
wanted_type_ptr->format_start = format_start; format_chars - orig_format_chars),
wanted_type_ptr->format_length = format_chars - format_start; OPT_Wformat_,
wanted_type_ptr->offset_loc = format_chars - orig_format_chars; "conversion lacks type at end of format");
wanted_type_ptr->next = NULL; continue;
if (last_wanted_type != 0)
last_wanted_type->next = wanted_type_ptr;
if (first_wanted_type == 0)
first_wanted_type = wanted_type_ptr;
last_wanted_type = wanted_type_ptr;
fci = fci->chain;
if (fci)
{
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;
}
}
} }
format_chars++;
if (first_wanted_type != 0) const format_char_info * const fci
check_format_types (format_string_loc, first_wanted_type); = 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