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,740 +1688,1123 @@ 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;
format_wanted_type width_wanted_type;
format_wanted_type precision_wanted_type;
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;
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;
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");
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 (*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;
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;
opnum = maybe_read_dollar_number (&format_chars, 0,
opnum = maybe_read_dollar_number (&format_chars,
has_operand_number == 1,
first_fillin_param,
&main_arg_params, fki);
&params, fki);
if (opnum == -1)
return;
return false;
else if (opnum > 0)
{
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))
return;
return false;
}
/* 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)
if (info->first_arg_num != 0)
{
const format_flag_spec *s = get_flag_spec (flag_specs,
*format_chars, NULL);
if (strchr (flag_chars, *format_chars) != 0)
{
warning_at (location_from_offset (format_string_loc,
format_chars + 1
- orig_format_chars),
OPT_Wformat_,
"repeated %s in format", _(s->name));
}
tree cur_param;
if (params == 0)
cur_param = NULL;
else
{
i = strlen (flag_chars);
flag_chars[i++] = *format_chars;
flag_chars[i] = 0;
}
if (s->skip_next_char)
{
++format_chars;
if (*format_chars == 0)
cur_param = TREE_VALUE (params);
if (has_operand_number <= 0)
{
warning_at (format_string_loc, OPT_Wformat_,
"missing fill character at end of strfmon format");
return;
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
{
/* 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)
flag_chars.add_char (fki->width_char);
}
/* Read any format width, possibly * or *m$. */
if (fki->width_char != 0)
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;
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);
flag_chars[i++] = fki->width_char;
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;
}
has_operand_number = 1;
arg_num = opnum + info->first_arg_num - 1;
}
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;
}
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;
}
}
has_operand_number = 0;
}
/* Read any format left precision (must be a number, not *). */
if (fki->left_precision_char != 0 && *format_chars == '#')
else
{
++format_chars;
i = strlen (flag_chars);
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 (avoid_dollar_number (format_chars))
return false;
}
/* Read any format precision, possibly * or *m$. */
if (fki->precision_char != 0 && *format_chars == '.')
if (info->first_arg_num != 0)
{
++format_chars;
i = strlen (flag_chars);
flag_chars[i++] = fki->precision_char;
flag_chars[i] = 0;
if (fki->precision_type != NULL && *format_chars == '*')
tree cur_param;
if (params == 0)
cur_param = NULL;
else
{
/* "...a...precision...may be indicated by an asterisk.
In this case, an int argument supplies the...precision." */
++format_chars;
if (has_operand_number != 0)
cur_param = TREE_VALUE (params);
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;
}
}
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;
params = TREE_CHAIN (params);
++arg_num;
}
}
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;
}
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
{
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;
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++;
}
return true;
}
/* Handle the scanf allocation kludge. */
if (fki->flags & (int) FMT_FLAG_SCANF_A_KLUDGE)
/* 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)
{
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'
|| format_chars[1] == '[')
{
/* 'a' is used as a flag. */
i = strlen (flag_chars);
flag_chars[i++] = 'a';
flag_chars[i] = 0;
format_chars++;
}
/* 'a' is used as a flag. */
flag_chars.add_char ('a');
format_chars++;
}
}
}
}
/* 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)
/* 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;
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
&& 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)
{
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);
}
format_chars++;
result = length_modifier (fli->double_name, fli->double_index,
fli->double_std, 0);
}
/* Read any modifier (strftime E/O). */
if (fki->modifier_chars != NULL)
else
{
while (*format_chars != 0
&& strchr (fki->modifier_chars, *format_chars) != 0)
{
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;
}
result = length_modifier (fli->name, fli->index, fli->std,
fli->scalar_identity_flag);
}
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;
if (format_char == 0
|| (!(fki->flags & (int) FMT_FLAG_FANCY_PERCENT_OK)
&& format_char == '%'))
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 (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,
format_chars - orig_format_chars),
format_chars
- orig_format_chars),
OPT_Wformat_,
"conversion lacks type at end of format");
continue;
"repeated %s in format", _(s->name));
}
format_chars++;
fci = fki->conversion_specs;
while (fci->format_chars != 0
&& strchr (fci->format_chars, format_char) == 0)
++fci;
if (fci->format_chars == 0)
else
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;
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))
warning_at (location_from_offset (format_string_loc,
format_chars - orig_format_chars),
OPT_Wformat_,
"unknown conversion type character %qc in format",
format_char);
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 (location_from_offset (format_string_loc,
format_chars - orig_format_chars),
OPT_Wformat_,
"unknown conversion type character 0x%x in format",
format_char);
continue;
warning_at (format_string_loc, OPT_Wformat_,
"%s ignored with %s in %s format",
_(s->name), _(t->name), fki->name);
}
if (pedantic)
else
{
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);
if (bad_flag_pairs[i].predicate != 0)
warning_at (format_string_loc, OPT_Wformat_,
"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);
}
}
}
/* 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;
}
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;
/* Give Y2K warnings. */
/* Validate the pairs of flags used. */
for (i = 0; bad_flag_pairs[i].flag_char1 != 0; i++)
void
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 (strchr (flag_chars, bad_flag_pairs[i].flag_char1) == 0)
continue;
if (strchr (flag_chars, bad_flag_pairs[i].flag_char2) == 0)
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);
}
if (suppressed)
warning_at (format_string_loc, OPT_Wformat_,
"operand number specified with "
"suppressed assignment");
else
{
if (bad_flag_pairs[i].predicate != 0)
warning_at (format_string_loc, OPT_Wformat_,
"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);
}
warning_at (format_string_loc, OPT_Wformat_,
"operand number specified for format "
"taking no argument");
}
}
else
{
format_wanted_type *wanted_type_ptr;
/* Give Y2K warnings. */
if (warn_format_y2k)
if (main_arg_num != 0)
{
int y2k_level = 0;
if (strchr (fci->flags2, '4') != 0)
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);
arg_num = main_arg_num;
params = main_arg_params;
}
if (strchr (fci->flags2, '[') != 0)
else
{
/* 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");
++arg_num;
if (has_operand_number > 0)
{
warning_at (format_string_loc, OPT_Wformat_,
"missing $ operand number in format");
return false;
}
else
has_operand_number = 0;
}
wanted_type = 0;
wanted_type_name = 0;
if (fki->flags & (int) FMT_FLAG_ARG_CONVERT)
wanted_type_ptr = &main_wanted_type;
while (fci)
{
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;
if (wanted_type == 0)
tree cur_param;
if (params == 0)
cur_param = NULL;
else
{
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",
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;
cur_param = TREE_VALUE (params);
params = TREE_CHAIN (params);
}
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 (fci->std))
wanted_type_ptr->wanted_type = wanted_type;
wanted_type_ptr->wanted_type_name = wanted_type_name;
wanted_type_ptr->pointer_count = fci->pointer_count + alloc_flag;
wanted_type_ptr->char_lenient_flag = 0;
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)
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), length_chars,
format_char, fki->name);
if (strchr (fci->flags2, 'W') != 0)
wanted_type_ptr->writing_in_flag = 1;
if (strchr (fci->flags2, 'R') != 0)
wanted_type_ptr->reading_from_flag = 1;
}
wanted_type_ptr->kind = CF_KIND_FORMAT;
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! */
if (info->first_arg_num == 0)
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 ((fci->pointer_count == 0 && wanted_type == void_type_node)
|| suppressed)
if (*format_chars == 0)
{
if (main_arg_num != 0)
{
if (suppressed)
warning_at (format_string_loc, OPT_Wformat_,
"operand number specified with "
"suppressed assignment");
else
warning_at (format_string_loc, OPT_Wformat_,
"operand number specified for format "
"taking no argument");
}
warning_at (location_from_offset (format_string_loc,
format_chars - orig_format_chars),
OPT_Wformat_,
"spurious trailing %<%%%> in format");
continue;
}
else
if (*format_chars == '%')
{
format_wanted_type *wanted_type_ptr;
++format_chars;
continue;
}
if (main_arg_num != 0)
{
arg_num = main_arg_num;
params = main_arg_params;
}
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;
}
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);
wanted_type_ptr = &main_wanted_type;
while (fci)
{
if (params == 0)
cur_param = NULL;
else
{
cur_param = TREE_VALUE (params);
params = TREE_CHAIN (params);
}
wanted_type_ptr->wanted_type = wanted_type;
wanted_type_ptr->wanted_type_name = wanted_type_name;
wanted_type_ptr->pointer_count = fci->pointer_count + alloc_flag;
wanted_type_ptr->char_lenient_flag = 0;
if (strchr (fci->flags2, 'c') != 0)
wanted_type_ptr->char_lenient_flag = 1;
wanted_type_ptr->scalar_identity_flag = 0;
if (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 (strchr (fci->flags2, 'W') != 0)
wanted_type_ptr->writing_in_flag = 1;
if (strchr (fci->flags2, 'R') != 0)
wanted_type_ptr->reading_from_flag = 1;
}
wanted_type_ptr->kind = CF_KIND_FORMAT;
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[length_chars_val].type;
wanted_type_name = fci->types[length_chars_val].name;
}
}
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++;
if (first_wanted_type != 0)
check_format_types (format_string_loc, first_wanted_type);
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