summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordmalcolm <dmalcolm@138bc75d-0d04-0410-961f-82ee72b054a4>2016-08-08 18:10:54 +0000
committerdmalcolm <dmalcolm@138bc75d-0d04-0410-961f-82ee72b054a4>2016-08-08 18:10:54 +0000
commit0dae5fef8bf7aa35595fec5126beacd3b0026e5e (patch)
tree663ab8e6b537e7e88f55ea4ae185dac2a4da75c8
parentd10ec4052c2242ea12bc962fe9b693f5b52fea98 (diff)
downloadgcc-0dae5fef8bf7aa35595fec5126beacd3b0026e5e.tar.gz
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". git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@239247 138bc75d-0d04-0410-961f-82ee72b054a4
-rw-r--r--gcc/c-family/ChangeLog30
-rw-r--r--gcc/c-family/c-format.c1655
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 <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".
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 <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)