From 1c4d457e82695db889b52f62e4d45dec83e1d7f1 Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Mon, 8 Aug 2016 18:10:54 +0000 Subject: [PATCH] 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 --- gcc/c-family/ChangeLog | 30 + gcc/c-family/c-format.c | 1655 ++++++++++++++++++++++++--------------- 2 files changed, 1049 insertions(+), 636 deletions(-) diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog index d5cdfed537a..4374dbfb1b4 100644 --- a/gcc/c-family/ChangeLog +++ b/gcc/c-family/ChangeLog @@ -1,3 +1,33 @@ +2016-08-08 David Malcolm + + * 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 * c-common.c: Include "substring-locations.h". diff --git a/gcc/c-family/c-format.c b/gcc/c-family/c-format.c index c19c411d61a..92d2c1cf5be 100644 --- a/gcc/c-family/c-format.c +++ b/gcc/c-family/c-format.c @@ -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 &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 &fwt_pool_); + + bool read_any_dollar (); + + bool read_format_flags (); + + bool + read_any_format_width (tree ¶ms, + unsigned HOST_WIDE_INT &arg_num); + + void + read_any_format_left_precision (); + + bool + read_any_format_precision (tree ¶ms, + 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 ¶ms, + 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 ¶ms, + 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 &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 &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 ¶ms, + 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); + ¶ms, 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 ¶ms, + 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, + ¶ms, 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, - ¶ms, 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, - ¶ms, 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 ¶ms, + 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 ¶ms, + 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 &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) -- 2.30.2