Fix suggestions for non-trivial Wformat type cases (PR c/72858)
gcc/c-family/ChangeLog: PR c/72858 * c-format.c (argument_parser::check_argument_type): Add params "type_start" and "conversion_char". Use the former to generate offset_to_type_start and pass it and conversion_char to check_format_types. (check_format_info_main): Capture the start of the type information as "type_start", and pass it an format_char to arg_parser.check_argument_type. (check_format_types): Provide an example in the leading comment. Add params "offset_to_type_start" and "conversion_char"; pass them to format_type_warning calls. (test_get_modifier_for_format_len): Likewise. (matching_type_p): New function. (get_format_for_type): Add param "conversion_char" and move implementation into... (get_format_for_type_1): ...new function, called twice. Use new function matching_type_p rather than checking for TYPE_CANONICAL equality. (get_corrected_substring): New function. (format_type_warning): Provide an example in the leading comment. Add params "offset_to_type_start" and "conversion_char". Replace call to get_format_for_type with call to get_corrected_substring and move rejection of hints for widths/precisions there. (assert_format_for_type_streq): Add param "conversion_char". (ASSERT_FORMAT_FOR_TYPE_STREQ): Add param CONVERSION_CHAR. (test_get_format_for_type_printf): Add conversion chars to the tests, adding coverage for various combinations of integer vs double conversions, and for preserving octal and hexadecimal conversions. (test_get_format_for_type_scanf): Add conversion chars to the tests. gcc/testsuite/ChangeLog: PR c/72858 * gcc.dg/format/diagnostic-ranges.c: Update expected suggestions to preserve conversion chars, and to preserve prefix information. * gcc.dg/format/pr72858.c: New test case. From-SVN: r240095
This commit is contained in:
parent
5e8037ba97
commit
d1463eb947
5 changed files with 816 additions and 63 deletions
|
@ -1,3 +1,37 @@
|
|||
2016-09-12 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
PR c/72858
|
||||
* c-format.c (argument_parser::check_argument_type): Add params
|
||||
"type_start" and "conversion_char". Use the former to generate
|
||||
offset_to_type_start and pass it and conversion_char to
|
||||
check_format_types.
|
||||
(check_format_info_main): Capture the start of the type
|
||||
information as "type_start", and pass it an format_char
|
||||
to arg_parser.check_argument_type.
|
||||
(check_format_types): Provide an example in the leading comment.
|
||||
Add params "offset_to_type_start" and "conversion_char"; pass
|
||||
them to format_type_warning calls.
|
||||
(test_get_modifier_for_format_len): Likewise.
|
||||
(matching_type_p): New function.
|
||||
(get_format_for_type): Add param "conversion_char" and move
|
||||
implementation into...
|
||||
(get_format_for_type_1): ...new function, called twice.
|
||||
Use new function matching_type_p rather than checking for
|
||||
TYPE_CANONICAL equality.
|
||||
(get_corrected_substring): New function.
|
||||
(format_type_warning): Provide an example in the leading comment.
|
||||
Add params "offset_to_type_start" and "conversion_char". Replace
|
||||
call to get_format_for_type with call to get_corrected_substring
|
||||
and move rejection of hints for widths/precisions there.
|
||||
(assert_format_for_type_streq): Add param "conversion_char".
|
||||
(ASSERT_FORMAT_FOR_TYPE_STREQ): Add param CONVERSION_CHAR.
|
||||
(test_get_format_for_type_printf): Add conversion chars to the
|
||||
tests, adding coverage for various combinations of integer
|
||||
vs double conversions, and for preserving octal and hexadecimal
|
||||
conversions.
|
||||
(test_get_format_for_type_scanf): Add conversion chars to the
|
||||
tests.
|
||||
|
||||
2016-09-10 Tom de Vries <tom@codesourcery.com>
|
||||
|
||||
PR C/71602
|
||||
|
|
|
@ -994,12 +994,16 @@ static const format_flag_spec *get_flag_spec (const format_flag_spec *,
|
|||
|
||||
static void check_format_types (const substring_loc &fmt_loc,
|
||||
format_wanted_type *,
|
||||
const format_kind_info *fki);
|
||||
const format_kind_info *fki,
|
||||
int offset_to_type_start,
|
||||
char conversion_char);
|
||||
static void format_type_warning (const substring_loc &fmt_loc,
|
||||
source_range *param_range,
|
||||
format_wanted_type *, tree,
|
||||
tree,
|
||||
const format_kind_info *fki);
|
||||
const format_kind_info *fki,
|
||||
int offset_to_type_start,
|
||||
char conversion_char);
|
||||
|
||||
/* Decode a format type from a string, returning the type, or
|
||||
format_type_error if not valid, in which case the caller should print an
|
||||
|
@ -1779,7 +1783,9 @@ class argument_parser
|
|||
tree ¶ms,
|
||||
const int alloc_flag,
|
||||
const char * const format_start,
|
||||
location_t fmt_param_loc);
|
||||
const char * const type_start,
|
||||
location_t fmt_param_loc,
|
||||
char conversion_char);
|
||||
|
||||
private:
|
||||
const function_format_info *const info;
|
||||
|
@ -2552,7 +2558,9 @@ check_argument_type (const format_char_info *fci,
|
|||
tree ¶ms,
|
||||
const int alloc_flag,
|
||||
const char * const format_start,
|
||||
location_t fmt_param_loc)
|
||||
const char * const type_start,
|
||||
location_t fmt_param_loc,
|
||||
char conversion_char)
|
||||
{
|
||||
if (info->first_arg_num == 0)
|
||||
return true;
|
||||
|
@ -2658,7 +2666,10 @@ check_argument_type (const format_char_info *fci,
|
|||
substring_loc fmt_loc (fmt_param_loc, TREE_TYPE (format_string_cst),
|
||||
offset_to_format_end,
|
||||
offset_to_format_start, offset_to_format_end);
|
||||
check_format_types (fmt_loc, first_wanted_type, fki);
|
||||
ptrdiff_t offset_to_type_start = type_start - orig_format_chars;
|
||||
check_format_types (fmt_loc, first_wanted_type, fki,
|
||||
offset_to_type_start,
|
||||
conversion_char);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -2737,6 +2748,13 @@ check_format_info_main (format_check_results *res,
|
|||
|
||||
arg_parser.handle_alloc_chars ();
|
||||
|
||||
/* The rest of the conversion specification is the length modifier
|
||||
(if any), and the conversion specifier, so this is where the
|
||||
type information starts. If we need to issue a suggestion
|
||||
about a type mismatch, then we should preserve everything up
|
||||
to here. */
|
||||
const char *type_start = format_chars;
|
||||
|
||||
/* Read any length modifier, if this kind of format has them. */
|
||||
const length_modifier len_modifier
|
||||
= arg_parser.read_any_length_modifier ();
|
||||
|
@ -2794,7 +2812,9 @@ check_format_info_main (format_check_results *res,
|
|||
suppressed,
|
||||
arg_num, params,
|
||||
alloc_flag,
|
||||
format_start, fmt_param_loc))
|
||||
format_start, type_start,
|
||||
fmt_param_loc,
|
||||
format_char))
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2814,11 +2834,58 @@ check_format_info_main (format_check_results *res,
|
|||
}
|
||||
|
||||
/* Check the argument types from a single format conversion (possibly
|
||||
including width and precision arguments). FMT_LOC is the
|
||||
location of the format conversion. */
|
||||
including width and precision arguments).
|
||||
|
||||
FMT_LOC is the location of the format conversion.
|
||||
|
||||
TYPES is a singly-linked list expressing the parts of the format
|
||||
conversion that expect argument types, and the arguments they
|
||||
correspond to.
|
||||
|
||||
OFFSET_TO_TYPE_START is the offset within the execution-charset encoded
|
||||
format string to where type information begins for the conversion
|
||||
(the length modifier and conversion specifier).
|
||||
|
||||
CONVERSION_CHAR is the user-provided conversion specifier.
|
||||
|
||||
For example, given:
|
||||
|
||||
sprintf (d, "before %-+*.*lld after", arg3, arg4, arg5);
|
||||
|
||||
then FMT_LOC covers this range:
|
||||
|
||||
sprintf (d, "before %-+*.*lld after", arg3, arg4, arg5);
|
||||
^^^^^^^^^
|
||||
|
||||
and TYPES in this case is a three-entry singly-linked list consisting of:
|
||||
(1) the check for the field width here:
|
||||
sprintf (d, "before %-+*.*lld after", arg3, arg4, arg5);
|
||||
^ ^^^^
|
||||
against arg3, and
|
||||
(2) the check for the field precision here:
|
||||
sprintf (d, "before %-+*.*lld after", arg3, arg4, arg5);
|
||||
^^ ^^^^
|
||||
against arg4, and
|
||||
(3) the check for the length modifier and conversion char here:
|
||||
sprintf (d, "before %-+*.*lld after", arg3, arg4, arg5);
|
||||
^^^ ^^^^
|
||||
against arg5.
|
||||
|
||||
OFFSET_TO_TYPE_START is 13, the offset to the "lld" within the
|
||||
STRING_CST:
|
||||
|
||||
0000000000111111111122
|
||||
0123456789012345678901
|
||||
sprintf (d, "before %-+*.*lld after", arg3, arg4, arg5);
|
||||
^ ^
|
||||
| ` CONVERSION_CHAR: 'd'
|
||||
type starts here. */
|
||||
|
||||
static void
|
||||
check_format_types (const substring_loc &fmt_loc,
|
||||
format_wanted_type *types, const format_kind_info *fki)
|
||||
format_wanted_type *types, const format_kind_info *fki,
|
||||
int offset_to_type_start,
|
||||
char conversion_char)
|
||||
{
|
||||
for (; types != 0; types = types->next)
|
||||
{
|
||||
|
@ -2845,7 +2912,8 @@ check_format_types (const substring_loc &fmt_loc,
|
|||
cur_param = types->param;
|
||||
if (!cur_param)
|
||||
{
|
||||
format_type_warning (fmt_loc, NULL, types, wanted_type, NULL, fki);
|
||||
format_type_warning (fmt_loc, NULL, types, wanted_type, NULL, fki,
|
||||
offset_to_type_start, conversion_char);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -2930,7 +2998,8 @@ check_format_types (const substring_loc &fmt_loc,
|
|||
else
|
||||
{
|
||||
format_type_warning (fmt_loc, param_range_ptr,
|
||||
types, wanted_type, orig_cur_type, fki);
|
||||
types, wanted_type, orig_cur_type, fki,
|
||||
offset_to_type_start, conversion_char);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -2999,7 +3068,8 @@ check_format_types (const substring_loc &fmt_loc,
|
|||
continue;
|
||||
/* Now we have a type mismatch. */
|
||||
format_type_warning (fmt_loc, param_range_ptr, types,
|
||||
wanted_type, orig_cur_type, fki);
|
||||
wanted_type, orig_cur_type, fki,
|
||||
offset_to_type_start, conversion_char);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3064,16 +3134,47 @@ test_get_modifier_for_format_len ()
|
|||
|
||||
#endif /* CHECKING_P */
|
||||
|
||||
/* Generate a string containing the format string that should be
|
||||
used to format arguments of type ARG_TYPE within FKI (effectively
|
||||
the inverse of the checking code).
|
||||
/* Determine if SPEC_TYPE and ARG_TYPE are sufficiently similar for a
|
||||
format_type_detail using SPEC_TYPE to be offered as a suggestion for
|
||||
Wformat type errors where the argument has type ARG_TYPE. */
|
||||
|
||||
static bool
|
||||
matching_type_p (tree spec_type, tree arg_type)
|
||||
{
|
||||
gcc_assert (spec_type);
|
||||
gcc_assert (arg_type);
|
||||
|
||||
spec_type = TYPE_CANONICAL (spec_type);
|
||||
arg_type = TYPE_CANONICAL (arg_type);
|
||||
|
||||
if (TREE_CODE (spec_type) == INTEGER_TYPE
|
||||
&& TREE_CODE (arg_type) == INTEGER_TYPE
|
||||
&& (TYPE_UNSIGNED (spec_type)
|
||||
? spec_type == c_common_unsigned_type (arg_type)
|
||||
: spec_type == c_common_signed_type (arg_type)))
|
||||
return true;
|
||||
|
||||
return spec_type == arg_type;
|
||||
}
|
||||
|
||||
/* Subroutine of get_format_for_type.
|
||||
|
||||
Generate a string containing the length modifier and conversion specifier
|
||||
that should be used to format arguments of type ARG_TYPE within FKI
|
||||
(effectively the inverse of the checking code).
|
||||
|
||||
If CONVERSION_CHAR is not zero (the first pass), the resulting suggestion
|
||||
is required to use it, for correcting bogus length modifiers.
|
||||
If CONVERSION_CHAR is zero (the second pass), then allow any suggestion
|
||||
that matches ARG_TYPE.
|
||||
|
||||
If successful, returns a non-NULL string which should be freed
|
||||
by the called.
|
||||
by the caller.
|
||||
Otherwise, returns NULL. */
|
||||
|
||||
static char *
|
||||
get_format_for_type (const format_kind_info *fki, tree arg_type)
|
||||
get_format_for_type_1 (const format_kind_info *fki, tree arg_type,
|
||||
char conversion_char)
|
||||
{
|
||||
gcc_assert (arg_type);
|
||||
|
||||
|
@ -3082,6 +3183,10 @@ get_format_for_type (const format_kind_info *fki, tree arg_type)
|
|||
spec->format_chars;
|
||||
spec++)
|
||||
{
|
||||
if (conversion_char)
|
||||
if (!strchr (spec->format_chars, conversion_char))
|
||||
continue;
|
||||
|
||||
tree effective_arg_type = deref_n_times (arg_type,
|
||||
spec->pointer_count);
|
||||
if (!effective_arg_type)
|
||||
|
@ -3091,8 +3196,7 @@ get_format_for_type (const format_kind_info *fki, tree arg_type)
|
|||
const format_type_detail *ftd = &spec->types[i];
|
||||
if (!ftd->type)
|
||||
continue;
|
||||
if (TYPE_CANONICAL (*ftd->type)
|
||||
== TYPE_CANONICAL (effective_arg_type))
|
||||
if (matching_type_p (*ftd->type, effective_arg_type))
|
||||
{
|
||||
const char *len_modifier
|
||||
= get_modifier_for_format_len (fki->length_char_specs,
|
||||
|
@ -3100,15 +3204,168 @@ get_format_for_type (const format_kind_info *fki, tree arg_type)
|
|||
if (!len_modifier)
|
||||
len_modifier = "";
|
||||
|
||||
return xasprintf ("%%%s%c",
|
||||
len_modifier,
|
||||
spec->format_chars[0]);
|
||||
if (conversion_char)
|
||||
/* We found a match, using the given conversion char - the
|
||||
length modifier was incorrect (or absent).
|
||||
Provide a suggestion using the conversion char with the
|
||||
correct length modifier for the type. */
|
||||
return xasprintf ("%s%c", len_modifier, conversion_char);
|
||||
else
|
||||
/* 2nd pass: no match was possible using the user-provided
|
||||
conversion char, but we do have a match without using it.
|
||||
Provide a suggestion using the first conversion char
|
||||
listed for the given type. */
|
||||
return xasprintf ("%s%c", len_modifier, spec->format_chars[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Generate a string containing the length modifier and conversion specifier
|
||||
that should be used to format arguments of type ARG_TYPE within FKI
|
||||
(effectively the inverse of the checking code).
|
||||
|
||||
If successful, returns a non-NULL string which should be freed
|
||||
by the caller.
|
||||
Otherwise, returns NULL. */
|
||||
|
||||
static char *
|
||||
get_format_for_type (const format_kind_info *fki, tree arg_type,
|
||||
char conversion_char)
|
||||
{
|
||||
gcc_assert (arg_type);
|
||||
gcc_assert (conversion_char);
|
||||
|
||||
/* First pass: look for a format_char_info containing CONVERSION_CHAR
|
||||
If we find one, then presumably the length modifier was incorrect
|
||||
(or absent). */
|
||||
char *result = get_format_for_type_1 (fki, arg_type, conversion_char);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
/* Second pass: we didn't find a match for CONVERSION_CHAR, so try
|
||||
matching just on the type. */
|
||||
return get_format_for_type_1 (fki, arg_type, '\0');
|
||||
}
|
||||
|
||||
/* Attempt to get a string for use as a replacement fix-it hint for the
|
||||
source range in FMT_LOC.
|
||||
|
||||
Preserve all of the text within the range of FMT_LOC up to
|
||||
OFFSET_TO_TYPE_START, replacing the rest with an appropriate
|
||||
length modifier and conversion specifier for ARG_TYPE, attempting
|
||||
to keep the user-provided CONVERSION_CHAR if possible.
|
||||
|
||||
For example, given a long vs long long mismatch for arg5 here:
|
||||
|
||||
000000000111111111122222222223333333333|
|
||||
123456789012345678901234567890123456789` column numbers
|
||||
0000000000111111111122|
|
||||
0123456789012345678901` string offsets
|
||||
V~~~~~~~~ : range of FMT_LOC, from cols 23-31
|
||||
sprintf (d, "before %-+*.*lld after", arg3, arg4, arg5);
|
||||
^ ^
|
||||
| ` CONVERSION_CHAR: 'd'
|
||||
type starts here
|
||||
|
||||
where OFFSET_TO_TYPE_START is 13 (the offset to the "lld" within the
|
||||
STRING_CST), where the user provided:
|
||||
%-+*.*lld
|
||||
the result (assuming "long" argument 5) should be:
|
||||
%-+*.*ld
|
||||
|
||||
If successful, returns a non-NULL string which should be freed
|
||||
by the caller.
|
||||
Otherwise, returns NULL. */
|
||||
|
||||
static char *
|
||||
get_corrected_substring (const substring_loc &fmt_loc,
|
||||
format_wanted_type *type, tree arg_type,
|
||||
const format_kind_info *fki,
|
||||
int offset_to_type_start, char conversion_char)
|
||||
{
|
||||
/* Attempt to provide hints for argument types, but not for field widths
|
||||
and precisions. */
|
||||
if (!arg_type)
|
||||
return NULL;
|
||||
if (type->kind != CF_KIND_FORMAT)
|
||||
return NULL;
|
||||
|
||||
/* Locate the current code within the source range, rejecting
|
||||
any awkward cases where the format string occupies more than
|
||||
one line.
|
||||
Lookup the place where the type starts (including any length
|
||||
modifiers), getting it as the caret location. */
|
||||
substring_loc type_loc (fmt_loc);
|
||||
type_loc.set_caret_index (offset_to_type_start);
|
||||
|
||||
location_t fmt_substring_loc;
|
||||
const char *err = type_loc.get_location (&fmt_substring_loc);
|
||||
if (err)
|
||||
return NULL;
|
||||
|
||||
source_range fmt_substring_range
|
||||
= get_range_from_loc (line_table, fmt_substring_loc);
|
||||
|
||||
expanded_location caret
|
||||
= expand_location_to_spelling_point (fmt_substring_loc);
|
||||
expanded_location start
|
||||
= expand_location_to_spelling_point (fmt_substring_range.m_start);
|
||||
expanded_location finish
|
||||
= expand_location_to_spelling_point (fmt_substring_range.m_finish);
|
||||
if (caret.file != start.file)
|
||||
return NULL;
|
||||
if (start.file != finish.file)
|
||||
return NULL;
|
||||
if (caret.line != start.line)
|
||||
return NULL;
|
||||
if (start.line != finish.line)
|
||||
return NULL;
|
||||
if (start.column > caret.column)
|
||||
return NULL;
|
||||
if (start.column > finish.column)
|
||||
return NULL;
|
||||
if (caret.column > finish.column)
|
||||
return NULL;
|
||||
|
||||
int line_width;
|
||||
const char *line = location_get_source_line (start.file, start.line,
|
||||
&line_width);
|
||||
if (line == NULL)
|
||||
return NULL;
|
||||
|
||||
/* If we got this far, then we have the line containing the
|
||||
existing conversion specification.
|
||||
|
||||
Generate a trimmed copy, containing the prefix part of the conversion
|
||||
specification, up to the (but not including) the length modifier.
|
||||
In the above example, this would be "%-+*.*". */
|
||||
const char *current_content = line + start.column - 1;
|
||||
int length_up_to_type = caret.column - start.column;
|
||||
char *prefix = xstrndup (current_content, length_up_to_type);
|
||||
|
||||
/* Now attempt to generate a suggestion for the rest of the specification
|
||||
(length modifier and conversion char), based on ARG_TYPE and
|
||||
CONVERSION_CHAR.
|
||||
In the above example, this would be "ld". */
|
||||
char *format_for_type = get_format_for_type (fki, arg_type, conversion_char);
|
||||
if (!format_for_type)
|
||||
{
|
||||
free (prefix);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Success. Generate the resulting suggestion for the whole range of
|
||||
FMT_LOC by concatenating the two strings.
|
||||
In the above example, this would be "%-+*.*ld". */
|
||||
char *result = concat (prefix, format_for_type, NULL);
|
||||
free (format_for_type);
|
||||
free (prefix);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Give a warning about a format argument of different type from that expected.
|
||||
The range of the diagnostic is taken from WHOLE_FMT_LOC; the caret location
|
||||
is based on the location of the char at TYPE->offset_loc.
|
||||
|
@ -3118,13 +3375,36 @@ get_format_for_type (const format_kind_info *fki, tree arg_type)
|
|||
precision"), the placement in the format string, a possibly more
|
||||
friendly name of WANTED_TYPE, and the number of pointer dereferences
|
||||
are taken from TYPE. ARG_TYPE is the type of the actual argument,
|
||||
or NULL if it is missing. */
|
||||
or NULL if it is missing.
|
||||
|
||||
OFFSET_TO_TYPE_START is the offset within the execution-charset encoded
|
||||
format string to where type information begins for the conversion
|
||||
(the length modifier and conversion specifier).
|
||||
CONVERSION_CHAR is the user-provided conversion specifier.
|
||||
|
||||
For example, given a type mismatch for argument 5 here:
|
||||
|
||||
00000000011111111112222222222333333333344444444445555555555|
|
||||
12345678901234567890123456789012345678901234567890123456789` column numbers
|
||||
0000000000111111111122|
|
||||
0123456789012345678901` offsets within STRING_CST
|
||||
V~~~~~~~~ : range of WHOLE_FMT_LOC, from cols 23-31
|
||||
sprintf (d, "before %-+*.*lld after", int_expr, int_expr, long_expr);
|
||||
^ ^ ^~~~~~~~~
|
||||
| ` CONVERSION_CHAR: 'd' *PARAM_RANGE
|
||||
type starts here
|
||||
|
||||
OFFSET_TO_TYPE_START is 13, the offset to the "lld" within the
|
||||
STRING_CST. */
|
||||
|
||||
static void
|
||||
format_type_warning (const substring_loc &whole_fmt_loc,
|
||||
source_range *param_range,
|
||||
format_wanted_type *type,
|
||||
tree wanted_type, tree arg_type,
|
||||
const format_kind_info *fki)
|
||||
const format_kind_info *fki,
|
||||
int offset_to_type_start,
|
||||
char conversion_char)
|
||||
{
|
||||
enum format_specifier_kind kind = type->kind;
|
||||
const char *wanted_type_name = type->wanted_type_name;
|
||||
|
@ -3171,18 +3451,18 @@ format_type_warning (const substring_loc &whole_fmt_loc,
|
|||
substring_loc fmt_loc (whole_fmt_loc);
|
||||
fmt_loc.set_caret_index (type->offset_loc - 1);
|
||||
|
||||
/* Attempt to provide hints for argument types, but not for field widths
|
||||
and precisions. */
|
||||
char *format_for_type = NULL;
|
||||
if (arg_type && kind == CF_KIND_FORMAT)
|
||||
format_for_type = get_format_for_type (fki, arg_type);
|
||||
/* Get a string for use as a replacement fix-it hint for the range in
|
||||
fmt_loc, or NULL. */
|
||||
char *corrected_substring
|
||||
= get_corrected_substring (fmt_loc, type, arg_type, fki,
|
||||
offset_to_type_start, conversion_char);
|
||||
|
||||
if (wanted_type_name)
|
||||
{
|
||||
if (arg_type)
|
||||
format_warning_at_substring
|
||||
(fmt_loc, param_range,
|
||||
format_for_type, OPT_Wformat_,
|
||||
corrected_substring, OPT_Wformat_,
|
||||
"%s %<%s%.*s%> expects argument of type %<%s%s%>, "
|
||||
"but argument %d has type %qT",
|
||||
gettext (kind_descriptions[kind]),
|
||||
|
@ -3192,7 +3472,7 @@ format_type_warning (const substring_loc &whole_fmt_loc,
|
|||
else
|
||||
format_warning_at_substring
|
||||
(fmt_loc, param_range,
|
||||
format_for_type, OPT_Wformat_,
|
||||
corrected_substring, OPT_Wformat_,
|
||||
"%s %<%s%.*s%> expects a matching %<%s%s%> argument",
|
||||
gettext (kind_descriptions[kind]),
|
||||
(kind == CF_KIND_FORMAT ? "%" : ""),
|
||||
|
@ -3203,7 +3483,7 @@ format_type_warning (const substring_loc &whole_fmt_loc,
|
|||
if (arg_type)
|
||||
format_warning_at_substring
|
||||
(fmt_loc, param_range,
|
||||
format_for_type, OPT_Wformat_,
|
||||
corrected_substring, OPT_Wformat_,
|
||||
"%s %<%s%.*s%> expects argument of type %<%T%s%>, "
|
||||
"but argument %d has type %qT",
|
||||
gettext (kind_descriptions[kind]),
|
||||
|
@ -3213,14 +3493,14 @@ format_type_warning (const substring_loc &whole_fmt_loc,
|
|||
else
|
||||
format_warning_at_substring
|
||||
(fmt_loc, param_range,
|
||||
format_for_type, OPT_Wformat_,
|
||||
corrected_substring, OPT_Wformat_,
|
||||
"%s %<%s%.*s%> expects a matching %<%T%s%> argument",
|
||||
gettext (kind_descriptions[kind]),
|
||||
(kind == CF_KIND_FORMAT ? "%" : ""),
|
||||
format_length, format_start, wanted_type, p);
|
||||
}
|
||||
|
||||
free (format_for_type);
|
||||
free (corrected_substring);
|
||||
}
|
||||
|
||||
|
||||
|
@ -3759,25 +4039,28 @@ get_info (const char *name)
|
|||
return fki;
|
||||
}
|
||||
|
||||
/* Verify that get_format_for_type (FKI, TYPE) is EXPECTED_FORMAT. */
|
||||
/* Verify that get_format_for_type (FKI, TYPE, CONVERSION_CHAR)
|
||||
is EXPECTED_FORMAT. */
|
||||
|
||||
static void
|
||||
assert_format_for_type_streq (const location &loc, const format_kind_info *fki,
|
||||
const char *expected_format, tree type)
|
||||
const char *expected_format, tree type,
|
||||
char conversion_char)
|
||||
{
|
||||
gcc_assert (fki);
|
||||
gcc_assert (expected_format);
|
||||
gcc_assert (type);
|
||||
|
||||
char *actual_format = get_format_for_type (fki, type);
|
||||
char *actual_format = get_format_for_type (fki, type, conversion_char);
|
||||
ASSERT_STREQ_AT (loc, expected_format, actual_format);
|
||||
free (actual_format);
|
||||
}
|
||||
|
||||
/* Selftests for get_format_for_type. */
|
||||
|
||||
#define ASSERT_FORMAT_FOR_TYPE_STREQ(EXPECTED_FORMAT, TYPE) \
|
||||
assert_format_for_type_streq (SELFTEST_LOCATION, (fki), (EXPECTED_FORMAT), (TYPE))
|
||||
#define ASSERT_FORMAT_FOR_TYPE_STREQ(EXPECTED_FORMAT, TYPE, CONVERSION_CHAR) \
|
||||
assert_format_for_type_streq (SELFTEST_LOCATION, (fki), (EXPECTED_FORMAT), \
|
||||
(TYPE), (CONVERSION_CHAR))
|
||||
|
||||
/* Selftest for get_format_for_type for "printf"-style functions. */
|
||||
|
||||
|
@ -3787,15 +4070,34 @@ test_get_format_for_type_printf ()
|
|||
const format_kind_info *fki = get_info ("gnu_printf");
|
||||
ASSERT_NE (fki, NULL);
|
||||
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("%f", double_type_node);
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("%Lf", long_double_type_node);
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("%d", integer_type_node);
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("%o", unsigned_type_node);
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("%ld", long_integer_type_node);
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("%lo", long_unsigned_type_node);
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("%lld", long_long_integer_type_node);
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("%llo", long_long_unsigned_type_node);
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("%s", build_pointer_type (char_type_node));
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("f", double_type_node, 'i');
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("Lf", long_double_type_node, 'i');
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("f", double_type_node, 'o');
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("Lf", long_double_type_node, 'o');
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("f", double_type_node, 'x');
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("Lf", long_double_type_node, 'x');
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("f", double_type_node, 'X');
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("Lf", long_double_type_node, 'X');
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("d", integer_type_node, 'd');
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("i", integer_type_node, 'i');
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("o", integer_type_node, 'o');
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("x", integer_type_node, 'x');
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("X", integer_type_node, 'X');
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("d", unsigned_type_node, 'd');
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("i", unsigned_type_node, 'i');
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("o", unsigned_type_node, 'o');
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("x", unsigned_type_node, 'x');
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("X", unsigned_type_node, 'X');
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("ld", long_integer_type_node, 'd');
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("li", long_integer_type_node, 'i');
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("lx", long_integer_type_node, 'x');
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("lo", long_unsigned_type_node, 'o');
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("lx", long_unsigned_type_node, 'x');
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("lld", long_long_integer_type_node, 'd');
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("lli", long_long_integer_type_node, 'i');
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("llo", long_long_unsigned_type_node, 'o');
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("llx", long_long_unsigned_type_node, 'x');
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("s", build_pointer_type (char_type_node), 'i');
|
||||
}
|
||||
|
||||
/* Selftest for get_format_for_type for "scanf"-style functions. */
|
||||
|
@ -3805,18 +4107,18 @@ test_get_format_for_type_scanf ()
|
|||
{
|
||||
const format_kind_info *fki = get_info ("gnu_scanf");
|
||||
ASSERT_NE (fki, NULL);
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("%d", build_pointer_type (integer_type_node));
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("%u", build_pointer_type (unsigned_type_node));
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("%ld",
|
||||
build_pointer_type (long_integer_type_node));
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("%lu",
|
||||
build_pointer_type (long_unsigned_type_node));
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("d", build_pointer_type (integer_type_node), 'd');
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("u", build_pointer_type (unsigned_type_node), 'u');
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("ld",
|
||||
build_pointer_type (long_integer_type_node), 'd');
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("lu",
|
||||
build_pointer_type (long_unsigned_type_node), 'u');
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ
|
||||
("%lld", build_pointer_type (long_long_integer_type_node));
|
||||
("lld", build_pointer_type (long_long_integer_type_node), 'd');
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ
|
||||
("%llu", build_pointer_type (long_long_unsigned_type_node));
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("%e", build_pointer_type (float_type_node));
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("%le", build_pointer_type (double_type_node));
|
||||
("llu", build_pointer_type (long_long_unsigned_type_node), 'u');
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("e", build_pointer_type (float_type_node), 'e');
|
||||
ASSERT_FORMAT_FOR_TYPE_STREQ ("le", build_pointer_type (double_type_node), 'e');
|
||||
}
|
||||
|
||||
#undef ASSERT_FORMAT_FOR_TYPE_STREQ
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
2016-09-12 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
PR c/72858
|
||||
* gcc.dg/format/diagnostic-ranges.c: Update expected suggestions
|
||||
to preserve conversion chars, and to preserve prefix information.
|
||||
* gcc.dg/format/pr72858.c: New test case.
|
||||
|
||||
2016-09-11 Steven G. Kargl <kargl@gcc.gnu.org>
|
||||
|
||||
* gfortran.dg/pr77507.f90: Move to ...
|
||||
|
|
|
@ -30,7 +30,7 @@ void test_mismatching_types (const char *msg)
|
|||
/* { dg-begin-multiline-output "" }
|
||||
printf("hello %i", (long)0);
|
||||
~^
|
||||
%ld
|
||||
%li
|
||||
{ dg-end-multiline-output "" } */
|
||||
}
|
||||
|
||||
|
@ -88,7 +88,7 @@ void test_hex (const char *msg)
|
|||
/* { dg-begin-multiline-output "" }
|
||||
printf("hello \x25\x69", msg);
|
||||
~~~~^~~~
|
||||
%s
|
||||
\x25s
|
||||
{ dg-end-multiline-output "" } */
|
||||
}
|
||||
|
||||
|
@ -102,7 +102,7 @@ void test_oct (const char *msg)
|
|||
/* { dg-begin-multiline-output "" }
|
||||
printf("hello \045\151", msg);
|
||||
~~~~^~~~
|
||||
%s
|
||||
\045s
|
||||
{ dg-end-multiline-output "" } */
|
||||
}
|
||||
|
||||
|
@ -121,7 +121,7 @@ void test_multiple (const char *msg)
|
|||
/* { dg-begin-multiline-output "" }
|
||||
printf("prefix" "\x25" "\151" "suffix",
|
||||
~~~~~~~~^~~~
|
||||
%s
|
||||
\x25" "s
|
||||
{ dg-end-multiline-output "" } */
|
||||
}
|
||||
|
||||
|
@ -267,7 +267,7 @@ void test_non_contiguous_strings (void)
|
|||
/* { dg-begin-multiline-output "" }
|
||||
__builtin_printf(" %" "d ", 0.5);
|
||||
~~~~^
|
||||
%f
|
||||
%" "f
|
||||
{ dg-end-multiline-output "" } */
|
||||
}
|
||||
|
||||
|
|
410
gcc/testsuite/gcc.dg/format/pr72858.c
Normal file
410
gcc/testsuite/gcc.dg/format/pr72858.c
Normal file
|
@ -0,0 +1,410 @@
|
|||
/* { dg-options "-Wformat -fdiagnostics-show-caret" } */
|
||||
|
||||
#include "format.h"
|
||||
|
||||
/* Various format tests, some containing type mismatches. Verify that for
|
||||
the type mismatch cases that we offer "good" suggestions. Specifically,
|
||||
any suggestions should preserve flags characters, field width and precision,
|
||||
and, if possible, the conversion specifier character, whilst giving a
|
||||
corrected length modifier appropriate to the argument type. */
|
||||
|
||||
/* Tests of "x" without a length modifier, with various param types.
|
||||
Suggestions should preserve the "x" for integer arguments. */
|
||||
|
||||
void
|
||||
test_x (char *d,
|
||||
int iexpr, unsigned int uiexpr,
|
||||
long lexpr, unsigned long ulexpr,
|
||||
long long llexpr, unsigned long long ullexpr,
|
||||
float fexpr, double dexpr, long double ldexpr,
|
||||
void *ptr)
|
||||
{
|
||||
/* Integer arguments. */
|
||||
|
||||
sprintf (d, " %-8x ", iexpr);
|
||||
sprintf (d, " %-8x ", uiexpr);
|
||||
|
||||
sprintf (d, " %-8x ", lexpr); /* { dg-warning "20: format '%x' expects argument of type 'unsigned int', but argument 3 has type 'long int'" } */
|
||||
/* TODO: ideally would also underline "lexpr". */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
sprintf (d, " %-8x ", lexpr);
|
||||
~~~^
|
||||
%-8lx
|
||||
{ dg-end-multiline-output "" } */
|
||||
sprintf (d, " %-8x ", ulexpr); /* { dg-warning "20: format '%x' expects argument of type 'unsigned int', but argument 3 has type 'long unsigned int'" } */
|
||||
/* TODO: ideally would also underline "lexpr". */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
sprintf (d, " %-8x ", ulexpr);
|
||||
~~~^
|
||||
%-8lx
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
sprintf (d, " %-8x ", llexpr); /* { dg-warning "20: format '%x' expects argument of type 'unsigned int', but argument 3 has type 'long long int'" } */
|
||||
/* TODO: ideally would also underline "lexpr". */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
sprintf (d, " %-8x ", llexpr);
|
||||
~~~^
|
||||
%-8llx
|
||||
{ dg-end-multiline-output "" } */
|
||||
sprintf (d, " %-8x ", ullexpr); /* { dg-warning "20: format '%x' expects argument of type 'unsigned int', but argument 3 has type 'long long unsigned int'" } */
|
||||
/* TODO: ideally would also underline "lexpr". */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
sprintf (d, " %-8x ", ullexpr);
|
||||
~~~^
|
||||
%-8llx
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
/* Floating-point arguments. */
|
||||
|
||||
sprintf (d, " %-8x ", fexpr); /* { dg-warning "20: format '%x' expects argument of type 'unsigned int', but argument 3 has type 'double'" } */
|
||||
/* TODO: ideally would also underline "fexpr". */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
sprintf (d, " %-8x ", fexpr);
|
||||
~~~^
|
||||
%-8f
|
||||
{ dg-end-multiline-output "" } */
|
||||
sprintf (d, " %-8x ", dexpr); /* { dg-warning "20: format '%x' expects argument of type 'unsigned int', but argument 3 has type 'double'" } */
|
||||
/* TODO: ideally would also underline "dexpr". */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
sprintf (d, " %-8x ", dexpr);
|
||||
~~~^
|
||||
%-8f
|
||||
{ dg-end-multiline-output "" } */
|
||||
sprintf (d, " %-8x ", ldexpr); /* { dg-warning "20: format '%x' expects argument of type 'unsigned int', but argument 3 has type 'long double'" } */
|
||||
/* TODO: ideally would also underline "ldexpr". */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
sprintf (d, " %-8x ", ldexpr);
|
||||
~~~^
|
||||
%-8Lf
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
/* Pointer. */
|
||||
sprintf (d, " %-8x ", ptr); /* { dg-warning "20: format '%x' expects argument of type 'unsigned int', but argument 3 has type 'void \\*'" } */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
sprintf (d, " %-8x ", ptr);
|
||||
~~~^
|
||||
%-8p
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
/* Something unrecognized. */
|
||||
struct s { int i; };
|
||||
struct s s;
|
||||
sprintf (d, " %-8x ", s); /* { dg-warning "20: format '%x' expects argument of type 'unsigned int', but argument 3 has type 'struct s'" } */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
sprintf (d, " %-8x ", s);
|
||||
~~~^
|
||||
{ dg-end-multiline-output "" } */
|
||||
}
|
||||
|
||||
/* Tests of "x" with "l", with various param types.
|
||||
Suggestions should preserve the "x" for integer arguments. */
|
||||
|
||||
void
|
||||
test_lx (char *d,
|
||||
int iexpr, unsigned int uiexpr,
|
||||
long lexpr, unsigned long ulexpr,
|
||||
long long llexpr, unsigned long long ullexpr,
|
||||
float fexpr, double dexpr, long double ldexpr)
|
||||
{
|
||||
/* Integer arguments. */
|
||||
|
||||
sprintf (d, " %-8lx ", iexpr); /* { dg-warning "21: format '%lx' expects argument of type 'long unsigned int', but argument 3 has type 'int'" } */
|
||||
/* TODO: ideally would also underline "iexpr". */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
sprintf (d, " %-8lx ", iexpr);
|
||||
~~~~^
|
||||
%-8x
|
||||
{ dg-end-multiline-output "" } */
|
||||
sprintf (d, " %-8lx ", uiexpr); /* { dg-warning "21: format '%lx' expects argument of type 'long unsigned int', but argument 3 has type 'unsigned int'" } */
|
||||
/* TODO: ideally would also underline "uiexpr". */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
sprintf (d, " %-8lx ", uiexpr);
|
||||
~~~~^
|
||||
%-8x
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
sprintf (d, " %-8lx ", lexpr);
|
||||
sprintf (d, " %-8lx ", ulexpr);
|
||||
|
||||
sprintf (d, " %-8lx ", llexpr); /* { dg-warning "21: format '%lx' expects argument of type 'long unsigned int', but argument 3 has type 'long long int'" } */
|
||||
/* TODO: ideally would also underline "llexpr". */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
sprintf (d, " %-8lx ", llexpr);
|
||||
~~~~^
|
||||
%-8llx
|
||||
{ dg-end-multiline-output "" } */
|
||||
sprintf (d, " %-8lx ", ullexpr); /* { dg-warning "21: format '%lx' expects argument of type 'long unsigned int', but argument 3 has type 'long long unsigned int'" } */
|
||||
/* TODO: ideally would also underline "ullexpr". */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
sprintf (d, " %-8lx ", ullexpr);
|
||||
~~~~^
|
||||
%-8llx
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
/* Floating-point arguments. */
|
||||
|
||||
sprintf (d, " %-8lx ", fexpr); /* { dg-warning "21: format '%lx' expects argument of type 'long unsigned int', but argument 3 has type 'double'" } */
|
||||
/* TODO: ideally would also underline "fexpr". */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
sprintf (d, " %-8lx ", fexpr);
|
||||
~~~~^
|
||||
%-8f
|
||||
{ dg-end-multiline-output "" } */
|
||||
sprintf (d, " %-8lx ", dexpr); /* { dg-warning "21: format '%lx' expects argument of type 'long unsigned int', but argument 3 has type 'double'" } */
|
||||
/* TODO: ideally would also underline "dexpr". */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
sprintf (d, " %-8lx ", dexpr);
|
||||
~~~~^
|
||||
%-8f
|
||||
{ dg-end-multiline-output "" } */
|
||||
sprintf (d, " %-8lx ", ldexpr); /* { dg-warning "21: format '%lx' expects argument of type 'long unsigned int', but argument 3 has type 'long double'" } */
|
||||
/* TODO: ideally would also underline "ldexpr". */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
sprintf (d, " %-8lx ", ldexpr);
|
||||
~~~~^
|
||||
%-8Lf
|
||||
{ dg-end-multiline-output "" } */
|
||||
}
|
||||
|
||||
/* Tests of "o" without a length modifier, with various param types.
|
||||
Suggestions should preserve the "o" for integer arguments. */
|
||||
|
||||
void
|
||||
test_o (char *d,
|
||||
int iexpr, unsigned int uiexpr,
|
||||
long lexpr, unsigned long ulexpr,
|
||||
long long llexpr, unsigned long long ullexpr)
|
||||
{
|
||||
/* Integer arguments. */
|
||||
|
||||
sprintf (d, " %-8o ", iexpr);
|
||||
sprintf (d, " %-8o ", uiexpr);
|
||||
|
||||
sprintf (d, " %-8o ", lexpr); /* { dg-warning "20: format '%o' expects argument of type 'unsigned int', but argument 3 has type 'long int'" } */
|
||||
/* TODO: ideally would also underline "lexpr". */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
sprintf (d, " %-8o ", lexpr);
|
||||
~~~^
|
||||
%-8lo
|
||||
{ dg-end-multiline-output "" } */
|
||||
sprintf (d, " %-8o ", ulexpr); /* { dg-warning "20: format '%o' expects argument of type 'unsigned int', but argument 3 has type 'long unsigned int'" } */
|
||||
/* TODO: ideally would also underline "lexpr". */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
sprintf (d, " %-8o ", ulexpr);
|
||||
~~~^
|
||||
%-8lo
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
sprintf (d, " %-8o ", llexpr); /* { dg-warning "20: format '%o' expects argument of type 'unsigned int', but argument 3 has type 'long long int'" } */
|
||||
/* TODO: ideally would also underline "lexpr". */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
sprintf (d, " %-8o ", llexpr);
|
||||
~~~^
|
||||
%-8llo
|
||||
{ dg-end-multiline-output "" } */
|
||||
sprintf (d, " %-8o ", ullexpr); /* { dg-warning "20: format '%o' expects argument of type 'unsigned int', but argument 3 has type 'long long unsigned int'" } */
|
||||
/* TODO: ideally would also underline "lexpr". */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
sprintf (d, " %-8o ", ullexpr);
|
||||
~~~^
|
||||
%-8llo
|
||||
{ dg-end-multiline-output "" } */
|
||||
}
|
||||
|
||||
/* Tests of "o" with "l", with various param types.
|
||||
Suggestions should preserve the "o" for integer arguments. */
|
||||
|
||||
void
|
||||
test_lo (char *d,
|
||||
int iexpr, unsigned int uiexpr,
|
||||
long lexpr, unsigned long ulexpr,
|
||||
long long llexpr, unsigned long long ullexpr)
|
||||
{
|
||||
/* Integer arguments. */
|
||||
|
||||
sprintf (d, " %-8lo ", iexpr); /* { dg-warning "21: format '%lo' expects argument of type 'long unsigned int', but argument 3 has type 'int'" } */
|
||||
/* TODO: ideally would also underline "iexpr". */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
sprintf (d, " %-8lo ", iexpr);
|
||||
~~~~^
|
||||
%-8o
|
||||
{ dg-end-multiline-output "" } */
|
||||
sprintf (d, " %-8lo ", uiexpr); /* { dg-warning "21: format '%lo' expects argument of type 'long unsigned int', but argument 3 has type 'unsigned int'" } */
|
||||
/* TODO: ideally would also underline "uiexpr". */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
sprintf (d, " %-8lo ", uiexpr);
|
||||
~~~~^
|
||||
%-8o
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
sprintf (d, " %-8lo ", lexpr);
|
||||
sprintf (d, " %-8lo ", ulexpr);
|
||||
|
||||
sprintf (d, " %-8lo ", llexpr); /* { dg-warning "21: format '%lo' expects argument of type 'long unsigned int', but argument 3 has type 'long long int'" } */
|
||||
/* TODO: ideally would also underline "llexpr". */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
sprintf (d, " %-8lo ", llexpr);
|
||||
~~~~^
|
||||
%-8llo
|
||||
{ dg-end-multiline-output "" } */
|
||||
sprintf (d, " %-8lo ", ullexpr); /* { dg-warning "21: format '%lo' expects argument of type 'long unsigned int', but argument 3 has type 'long long unsigned int'" } */
|
||||
/* TODO: ideally would also underline "ullexpr". */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
sprintf (d, " %-8lo ", ullexpr);
|
||||
~~~~^
|
||||
%-8llo
|
||||
{ dg-end-multiline-output "" } */
|
||||
}
|
||||
|
||||
/* Tests of "e" without a length modifier, with various param types.
|
||||
Suggestions should preserve the "e" for float arguments. */
|
||||
|
||||
void
|
||||
test_e (char *d, int iexpr, float fexpr, double dexpr, long double ldexpr)
|
||||
{
|
||||
/* Integer arguments. */
|
||||
|
||||
sprintf (d, " %-8e ", iexpr); /* { dg-warning "20: format '%e' expects argument of type 'double', but argument 3 has type 'int'" } */
|
||||
/* TODO: ideally would also underline "iexpr". */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
sprintf (d, " %-8e ", iexpr);
|
||||
~~~^
|
||||
%-8d
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
/* Floating-point arguments. */
|
||||
|
||||
sprintf (d, " %-8e ", fexpr);
|
||||
sprintf (d, " %-8e ", dexpr);
|
||||
sprintf (d, " %-8e ", ldexpr); /* { dg-warning "20: format '%e' expects argument of type 'double', but argument 3 has type 'long double'" } */
|
||||
/* TODO: ideally would also underline "ldexpr". */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
sprintf (d, " %-8e ", ldexpr);
|
||||
~~~^
|
||||
%-8Le
|
||||
{ dg-end-multiline-output "" } */
|
||||
}
|
||||
|
||||
/* Tests of "e" with "L", with various param types.
|
||||
Suggestions should preserve the "e" for float arguments. */
|
||||
|
||||
void
|
||||
test_Le (char *d, int iexpr, float fexpr, double dexpr, long double ldexpr)
|
||||
{
|
||||
/* Integer arguments. */
|
||||
|
||||
sprintf (d, " %-8Le ", iexpr); /* { dg-warning "21: format '%Le' expects argument of type 'long double', but argument 3 has type 'int'" } */
|
||||
/* TODO: ideally would also underline "iexpr". */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
sprintf (d, " %-8Le ", iexpr);
|
||||
~~~~^
|
||||
%-8d
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
/* Floating-point arguments. */
|
||||
|
||||
sprintf (d, " %-8Le ", fexpr); /* { dg-warning "21: format '%Le' expects argument of type 'long double', but argument 3 has type 'double'" } */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
sprintf (d, " %-8Le ", fexpr);
|
||||
~~~~^
|
||||
%-8e
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
sprintf (d, " %-8Le ", dexpr); /* { dg-warning "21: format '%Le' expects argument of type 'long double', but argument 3 has type 'double'" } */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
sprintf (d, " %-8Le ", dexpr);
|
||||
~~~~^
|
||||
%-8e
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
sprintf (d, " %-8Le ", ldexpr);
|
||||
}
|
||||
|
||||
/* Tests of "E" without a length modifier, with various param types.
|
||||
Suggestions should preserve the "E" for floating-point arguments. */
|
||||
|
||||
void
|
||||
test_E (char *d, int iexpr, float fexpr, double dexpr, long double ldexpr)
|
||||
{
|
||||
/* Integer arguments. */
|
||||
|
||||
sprintf (d, " %-8E ", iexpr); /* { dg-warning "20: format '%E' expects argument of type 'double', but argument 3 has type 'int'" } */
|
||||
/* TODO: ideally would also underline "iexpr". */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
sprintf (d, " %-8E ", iexpr);
|
||||
~~~^
|
||||
%-8d
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
/* Floating-point arguments. */
|
||||
|
||||
sprintf (d, " %-8E ", fexpr);
|
||||
sprintf (d, " %-8E ", dexpr);
|
||||
sprintf (d, " %-8E ", ldexpr); /* { dg-warning "20: format '%E' expects argument of type 'double', but argument 3 has type 'long double'" } */
|
||||
/* TODO: ideally would also underline "ldexpr". */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
sprintf (d, " %-8E ", ldexpr);
|
||||
~~~^
|
||||
%-8LE
|
||||
{ dg-end-multiline-output "" } */
|
||||
}
|
||||
|
||||
/* Tests of "E" with "L", with various param types.
|
||||
Suggestions should preserve the "E" for floating-point arguments. */
|
||||
|
||||
void
|
||||
test_LE (char *d, int iexpr, float fexpr, double dexpr, long double ldexpr)
|
||||
{
|
||||
/* Integer arguments. */
|
||||
|
||||
sprintf (d, " %-8LE ", iexpr); /* { dg-warning "21: format '%LE' expects argument of type 'long double', but argument 3 has type 'int'" } */
|
||||
/* TODO: ideally would also underline "iexpr". */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
sprintf (d, " %-8LE ", iexpr);
|
||||
~~~~^
|
||||
%-8d
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
sprintf (d, " %-8LE ", fexpr); /* { dg-warning "21: format '%LE' expects argument of type 'long double', but argument 3 has type 'double'" } */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
sprintf (d, " %-8LE ", fexpr);
|
||||
~~~~^
|
||||
%-8E
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
sprintf (d, " %-8LE ", dexpr); /* { dg-warning "21: format '%LE' expects argument of type 'long double', but argument 3 has type 'double'" } */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
sprintf (d, " %-8LE ", dexpr);
|
||||
~~~~^
|
||||
%-8E
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
sprintf (d, " %-8LE ", ldexpr);
|
||||
}
|
||||
|
||||
/* Test of a suggestion for a conversion specification containing
|
||||
all features (flags, width, precision, length modifier), where
|
||||
all the other arguments have mismatching types. */
|
||||
|
||||
void
|
||||
test_everything (char *d, long lexpr)
|
||||
{
|
||||
sprintf (d, "before %-+*.*lld after", lexpr, lexpr, lexpr); /* { dg-warning "26: field width specifier '\\*' expects argument of type 'int', but argument 3 has type 'long int'" } */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
sprintf (d, "before %-+*.*lld after", lexpr, lexpr, lexpr);
|
||||
~~~^~~~~~
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
/* { dg-warning "28: field precision specifier '\\.\\*' expects argument of type 'int', but argument 4 has type 'long int'" "" { target *-*-* } 392 } */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
sprintf (d, "before %-+*.*lld after", lexpr, lexpr, lexpr);
|
||||
~~~~~^~~~
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
/* { dg-warning "31: format '%lld' expects argument of type 'long long int', but argument 5 has type 'long int'" "" { target *-*-* } 392 } */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
sprintf (d, "before %-+*.*lld after", lexpr, lexpr, lexpr);
|
||||
~~~~~~~~^
|
||||
%-+*.*ld
|
||||
{ dg-end-multiline-output "" } */
|
||||
}
|
Loading…
Add table
Reference in a new issue