Commit e52ed3fe by David Malcolm Committed by David Malcolm

Use class substring_loc in c-format.c (PR c/52952)

gcc/c-family/ChangeLog:
	PR c/52952
	* c-format.c: Include "diagnostic.h".
	(location_column_from_byte_offset): Delete.
	(location_from_offset): Delete.
	(format_warning_va): New function.
	(format_warning_at_substring): New function.
	(format_warning_at_char): New function.
	(check_format_arg): Capture location of format_tree and pass to
	check_format_info_main.
	(argument_parser): Add fields "start_of_this_format" and
	"format_string_cst".
	(flag_chars_t::validate): Add param "format_string_cst".  Convert
	warning_at call using location_from_offset to call to
	format_warning_at_char.
	(argument_parser::argument_parser): Add param "format_string_cst_"
	and use use it to initialize field "format_string_cst".
	Initialize new field "start_of_this_format".
	(argument_parser::read_format_flags): Convert warning_at call
	using location_from_offset to a call to format_warning_at_char.
	(argument_parser::read_any_format_left_precision): Likewise.
	(argument_parser::read_any_format_precision): Likewise.
	(argument_parser::read_any_other_modifier): Likewise.
	(argument_parser::find_format_char_info): Likewise, in three places.
	(argument_parser::parse_any_scan_set): Likewise, in one place.
	(argument_parser::handle_conversions): Likewise, in two places.
	(argument_parser::check_argument_type): Add param "fmt_param_loc"
	and use it to make a substring_loc.  Pass the latter to
	check_format_types.
	(check_format_info_main): Add params "fmt_param_loc" and
	"format_string_cst".  Convert warning_at calls using
	location_from_offset to calls to format_warning_at_char.  Pass the
	new params to the arg_parser ctor.  Pass "format_string_cst" to
	flag_chars.validate.  Pass "fmt_param_loc" to
	arg_parser.check_argument_type.
	(check_format_types): Convert first param from a location_t
	to a const substring_loc & and rename to "fmt_loc".  Attempt
	to extract the range of the relevant parameter and pass it
	to format_type_warning.
	(format_type_warning): Convert first param from a location_t
	to a const substring_loc & and rename to "fmt_loc".  Add
	params "param_range" and "type".  Replace calls to warning_at
	with calls to format_warning_at_substring.

gcc/testsuite/ChangeLog:
	PR c/52952
	* gcc.dg/cpp/pr66415-1.c: Likewise.
	* gcc.dg/format/asm_fprintf-1.c: Update column numbers.
	* gcc.dg/format/c90-printf-1.c: Likewise.
	* gcc.dg/format/diagnostic-ranges.c: New test case.

From-SVN: r239253
parent f432d128
2016-08-08 David Malcolm <dmalcolm@redhat.com> 2016-08-08 David Malcolm <dmalcolm@redhat.com>
PR c/52952
* c-format.c: Include "diagnostic.h".
(location_column_from_byte_offset): Delete.
(location_from_offset): Delete.
(format_warning_va): New function.
(format_warning_at_substring): New function.
(format_warning_at_char): New function.
(check_format_arg): Capture location of format_tree and pass to
check_format_info_main.
(argument_parser): Add fields "start_of_this_format" and
"format_string_cst".
(flag_chars_t::validate): Add param "format_string_cst". Convert
warning_at call using location_from_offset to call to
format_warning_at_char.
(argument_parser::argument_parser): Add param "format_string_cst_"
and use use it to initialize field "format_string_cst".
Initialize new field "start_of_this_format".
(argument_parser::read_format_flags): Convert warning_at call
using location_from_offset to a call to format_warning_at_char.
(argument_parser::read_any_format_left_precision): Likewise.
(argument_parser::read_any_format_precision): Likewise.
(argument_parser::read_any_other_modifier): Likewise.
(argument_parser::find_format_char_info): Likewise, in three places.
(argument_parser::parse_any_scan_set): Likewise, in one place.
(argument_parser::handle_conversions): Likewise, in two places.
(argument_parser::check_argument_type): Add param "fmt_param_loc"
and use it to make a substring_loc. Pass the latter to
check_format_types.
(check_format_info_main): Add params "fmt_param_loc" and
"format_string_cst". Convert warning_at calls using
location_from_offset to calls to format_warning_at_char. Pass the
new params to the arg_parser ctor. Pass "format_string_cst" to
flag_chars.validate. Pass "fmt_param_loc" to
arg_parser.check_argument_type.
(check_format_types): Convert first param from a location_t
to a const substring_loc & and rename to "fmt_loc". Attempt
to extract the range of the relevant parameter and pass it
to format_type_warning.
(format_type_warning): Convert first param from a location_t
to a const substring_loc & and rename to "fmt_loc". Add
params "param_range" and "type". Replace calls to warning_at
with calls to format_warning_at_substring.
2016-08-08 David Malcolm <dmalcolm@redhat.com>
* c-format.c (class flag_chars_t): New class. * c-format.c (class flag_chars_t): New class.
(struct length_modifier): New struct. (struct length_modifier): New struct.
(class argument_parser): New class. (class argument_parser): New class.
......
...@@ -29,6 +29,7 @@ along with GCC; see the file COPYING3. If not see ...@@ -29,6 +29,7 @@ along with GCC; see the file COPYING3. If not see
#include "intl.h" #include "intl.h"
#include "langhooks.h" #include "langhooks.h"
#include "c-format.h" #include "c-format.h"
#include "diagnostic.h"
/* Handle attributes associated with format checking. */ /* Handle attributes associated with format checking. */
...@@ -65,78 +66,169 @@ static int first_target_format_type; ...@@ -65,78 +66,169 @@ static int first_target_format_type;
static const char *format_name (int format_num); static const char *format_name (int format_num);
static int format_flags (int format_num); static int format_flags (int format_num);
/* Given a string S of length LINE_WIDTH, find the visual column /* Emit a warning governed by option OPT, using GMSGID as the format
corresponding to OFFSET bytes. */ string and AP as its arguments.
static unsigned int Attempt to obtain precise location information within a string
location_column_from_byte_offset (const char *s, int line_width, literal from FMT_LOC.
unsigned int offset)
{ Case 1: if substring location is available, and is within the range of
const char * c = s; the format string itself, the primary location of the
if (*c != '"') diagnostic is the substring range obtained from FMT_LOC, with the
return 0; caret at the *end* of the substring range.
For example:
test.c:90:10: warning: problem with '%i' here [-Wformat=]
printf ("hello %i", msg);
~^
Case 2: if the substring location is available, but is not within
the range of the format string, the primary location is that of the
format string, and an note is emitted showing the substring location.
For example:
test.c:90:10: warning: problem with '%i' here [-Wformat=]
printf("hello " INT_FMT " world", msg);
^~~~~~~~~~~~~~~~~~~~~~~~~
test.c:19: note: format string is defined here
#define INT_FMT "%i"
~^
Case 3: if precise substring information is unavailable, the primary
location is that of the whole string passed to FMT_LOC's constructor.
For example:
test.c:90:10: warning: problem with '%i' here [-Wformat=]
printf(fmt, msg);
^~~
For each of cases 1-3, if param_range is non-NULL, then it is used
as a secondary range within the warning. For example, here it
is used with case 1:
test.c:90:16: warning: '%s' here but arg 2 has 'long' type [-Wformat=]
printf ("foo %s bar", long_i + long_j);
~^ ~~~~~~~~~~~~~~~
c++, offset--; and here with case 2:
while (offset > 0)
test.c:90:16: warning: '%s' here but arg 2 has 'long' type [-Wformat=]
printf ("foo " STR_FMT " bar", long_i + long_j);
^~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~
test.c:89:16: note: format string is defined here
#define STR_FMT "%s"
~^
and with case 3:
test.c:90:10: warning: '%i' here, but arg 2 is "const char *' [-Wformat=]
printf(fmt, msg);
^~~ ~~~
Return true if a warning was emitted, false otherwise. */
ATTRIBUTE_GCC_DIAG (4,0)
static bool
format_warning_va (const substring_loc &fmt_loc, source_range *param_range,
int opt, const char *gmsgid, va_list *ap)
{
bool substring_within_range = false;
location_t primary_loc;
location_t substring_loc = UNKNOWN_LOCATION;
source_range fmt_loc_range
= get_range_from_loc (line_table, fmt_loc.get_fmt_string_loc ());
source_range fmt_substring_range;
const char *err = fmt_loc.get_range (&fmt_substring_range);
if (err)
/* Case 3: unable to get substring location. */
primary_loc = fmt_loc.get_fmt_string_loc ();
else
{ {
if (c - s >= line_width) substring_loc = make_location (fmt_substring_range.m_finish,
return 0; fmt_substring_range.m_start,
fmt_substring_range.m_finish);
switch (*c) if (fmt_substring_range.m_start >= fmt_loc_range.m_start
&& fmt_substring_range.m_finish <= fmt_loc_range.m_finish)
/* Case 1. */
{ {
case '\\': substring_within_range = true;
c++; primary_loc = substring_loc;
if (c - s >= line_width) }
return 0; else
switch (*c) /* Case 2. */
{ {
case '\\': case '\'': case '"': case '?': substring_within_range = false;
case '(': case '{': case '[': case '%': primary_loc = fmt_loc.get_fmt_string_loc ();
case 'a': case 'b': case 'f': case 'n': }
case 'r': case 't': case 'v': }
case 'e': case 'E':
c++, offset--;
break;
default: rich_location richloc (line_table, primary_loc);
return 0;
}
break;
case '"': if (param_range)
/* We found the end of the string too early. */ {
return 0; location_t param_loc = make_location (param_range->m_start,
param_range->m_start,
default: param_range->m_finish);
c++, offset--; richloc.add_range (param_loc, false);
break;
}
} }
return c - s;
diagnostic_info diagnostic;
diagnostic_set_info (&diagnostic, gmsgid, ap, &richloc, DK_WARNING);
diagnostic.option_index = opt;
bool warned = report_diagnostic (&diagnostic);
if (!err && substring_loc && !substring_within_range)
/* Case 2. */
if (warned)
inform (substring_loc, "format string is defined here");
return warned;
} }
/* Return a location that encodes the same location as LOC but shifted /* Variadic call to format_warning_va. */
by OFFSET bytes. */
static location_t ATTRIBUTE_GCC_DIAG (4,0)
location_from_offset (location_t loc, int offset) static bool
format_warning_at_substring (const substring_loc &fmt_loc,
source_range *param_range,
int opt, const char *gmsgid, ...)
{ {
gcc_checking_assert (offset >= 0); va_list ap;
if (linemap_location_from_macro_expansion_p (line_table, loc) va_start (ap, gmsgid);
|| offset < 0) bool warned = format_warning_va (fmt_loc, param_range, opt, gmsgid, &ap);
return loc; va_end (ap);
expanded_location s = expand_location_to_spelling_point (loc); return warned;
int line_width; }
const char *line = location_get_source_line (s.file, s.line, &line_width);
if (line == NULL) /* Emit a warning as per format_warning_va, but construct the substring_loc
return loc; for the character at offset (CHAR_IDX - 1) within a string constant
line += s.column - 1 ; FORMAT_STRING_CST at FMT_STRING_LOC. */
line_width -= s.column - 1;
unsigned int column =
location_column_from_byte_offset (line, line_width, (unsigned) offset);
return linemap_position_for_loc_and_offset (line_table, loc, column); ATTRIBUTE_GCC_DIAG (5,6)
static bool
format_warning_at_char (location_t fmt_string_loc, tree format_string_cst,
int char_idx, int opt, const char *gmsgid, ...)
{
va_list ap;
va_start (ap, gmsgid);
tree string_type = TREE_TYPE (format_string_cst);
/* The callers are of the form:
format_warning (format_string_loc, format_string_cst,
format_chars - orig_format_chars,
where format_chars has already been incremented, so that
CHAR_IDX is one character beyond where the warning should
be emitted. Fix it. */
char_idx -= 1;
substring_loc fmt_loc (fmt_string_loc, string_type, char_idx, char_idx);
bool warned = format_warning_va (fmt_loc, NULL, opt, gmsgid, &ap);
va_end (ap);
return warned;
} }
/* Check that we have a pointer to a string suitable for use as a format. /* Check that we have a pointer to a string suitable for use as a format.
...@@ -1018,8 +1110,9 @@ format_flags (int format_num) ...@@ -1018,8 +1110,9 @@ format_flags (int format_num)
static void check_format_info (function_format_info *, tree); static void check_format_info (function_format_info *, tree);
static void check_format_arg (void *, tree, unsigned HOST_WIDE_INT); static void check_format_arg (void *, tree, unsigned HOST_WIDE_INT);
static void check_format_info_main (format_check_results *, static void check_format_info_main (format_check_results *,
function_format_info *, function_format_info *, const char *,
const char *, int, tree, location_t, tree,
int, tree,
unsigned HOST_WIDE_INT, unsigned HOST_WIDE_INT,
object_allocator<format_wanted_type> &); object_allocator<format_wanted_type> &);
...@@ -1032,8 +1125,12 @@ static void finish_dollar_format_checking (format_check_results *, int); ...@@ -1032,8 +1125,12 @@ static void finish_dollar_format_checking (format_check_results *, int);
static const format_flag_spec *get_flag_spec (const format_flag_spec *, static const format_flag_spec *get_flag_spec (const format_flag_spec *,
int, const char *); int, const char *);
static void check_format_types (location_t, format_wanted_type *); static void check_format_types (const substring_loc &fmt_loc,
static void format_type_warning (location_t, format_wanted_type *, tree, tree); format_wanted_type *);
static void format_type_warning (const substring_loc &fmt_loc,
source_range *param_range,
format_wanted_type *, tree,
tree);
/* Decode a format type from a string, returning the type, or /* 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 format_type_error if not valid, in which case the caller should print an
...@@ -1509,6 +1606,8 @@ check_format_arg (void *ctx, tree format_tree, ...@@ -1509,6 +1606,8 @@ check_format_arg (void *ctx, tree format_tree,
tree array_size = 0; tree array_size = 0;
tree array_init; tree array_init;
location_t fmt_param_loc = EXPR_LOC_OR_LOC (format_tree, input_location);
if (VAR_P (format_tree)) if (VAR_P (format_tree))
{ {
/* Pull out a constant value if the front end didn't. */ /* Pull out a constant value if the front end didn't. */
...@@ -1684,8 +1783,8 @@ check_format_arg (void *ctx, tree format_tree, ...@@ -1684,8 +1783,8 @@ check_format_arg (void *ctx, tree format_tree,
need not adjust it for every return. */ need not adjust it for every return. */
res->number_other++; res->number_other++;
object_allocator <format_wanted_type> fwt_pool ("format_wanted_type pool"); object_allocator <format_wanted_type> fwt_pool ("format_wanted_type pool");
check_format_info_main (res, info, format_chars, format_length, check_format_info_main (res, info, format_chars, fmt_param_loc, format_tree,
params, arg_num, fwt_pool); format_length, params, arg_num, fwt_pool);
} }
/* Support class for argument_parser and check_format_info_main. /* Support class for argument_parser and check_format_info_main.
...@@ -1702,6 +1801,7 @@ class flag_chars_t ...@@ -1702,6 +1801,7 @@ class flag_chars_t
const format_char_info *fci, const format_char_info *fci,
const format_flag_spec *flag_specs, const format_flag_spec *flag_specs,
const char * const format_chars, const char * const format_chars,
tree format_string_cst,
location_t format_string_loc, location_t format_string_loc,
const char * const orig_format_chars, const char * const orig_format_chars,
char format_char); char format_char);
...@@ -1744,6 +1844,7 @@ class argument_parser ...@@ -1744,6 +1844,7 @@ class argument_parser
{ {
public: public:
argument_parser (function_format_info *info, const char *&format_chars, argument_parser (function_format_info *info, const char *&format_chars,
tree format_string_cst,
const char * const orig_format_chars, const char * const orig_format_chars,
location_t format_string_loc, flag_chars_t &flag_chars, location_t format_string_loc, flag_chars_t &flag_chars,
int &has_operand_number, tree first_fillin_param, int &has_operand_number, tree first_fillin_param,
...@@ -1799,13 +1900,16 @@ class argument_parser ...@@ -1799,13 +1900,16 @@ class argument_parser
unsigned HOST_WIDE_INT &arg_num, unsigned HOST_WIDE_INT &arg_num,
tree &params, tree &params,
const int alloc_flag, const int alloc_flag,
const char * const format_start); const char * const format_start,
location_t fmt_param_loc);
private: private:
const function_format_info *const info; const function_format_info *const info;
const format_kind_info * const fki; const format_kind_info * const fki;
const format_flag_spec * const flag_specs; const format_flag_spec * const flag_specs;
const char *start_of_this_format;
const char *&format_chars; const char *&format_chars;
const tree format_string_cst;
const char * const orig_format_chars; const char * const orig_format_chars;
const location_t format_string_loc; const location_t format_string_loc;
object_allocator <format_wanted_type> &fwt_pool; object_allocator <format_wanted_type> &fwt_pool;
...@@ -1855,6 +1959,7 @@ flag_chars_t::validate (const format_kind_info *fki, ...@@ -1855,6 +1959,7 @@ flag_chars_t::validate (const format_kind_info *fki,
const format_char_info *fci, const format_char_info *fci,
const format_flag_spec *flag_specs, const format_flag_spec *flag_specs,
const char * const format_chars, const char * const format_chars,
tree format_string_cst,
location_t format_string_loc, location_t format_string_loc,
const char * const orig_format_chars, const char * const orig_format_chars,
char format_char) char format_char)
...@@ -1870,11 +1975,11 @@ flag_chars_t::validate (const format_kind_info *fki, ...@@ -1870,11 +1975,11 @@ flag_chars_t::validate (const format_kind_info *fki,
continue; continue;
if (strchr (fci->flag_chars, m_flag_chars[i]) == 0) if (strchr (fci->flag_chars, m_flag_chars[i]) == 0)
{ {
warning_at (location_from_offset (format_string_loc, format_warning_at_char (format_string_loc, format_string_cst,
format_chars format_chars - orig_format_chars,
- orig_format_chars), OPT_Wformat_,
OPT_Wformat_, "%s used with %<%%%c%> %s format", "%s used with %<%%%c%> %s format",
_(s->name), format_char, fki->name); _(s->name), format_char, fki->name);
d++; d++;
continue; continue;
} }
...@@ -1935,6 +2040,7 @@ flag_chars_t::assignment_suppression_p (const format_kind_info *fki) ...@@ -1935,6 +2040,7 @@ flag_chars_t::assignment_suppression_p (const format_kind_info *fki)
argument_parser:: argument_parser::
argument_parser (function_format_info *info_, const char *&format_chars_, argument_parser (function_format_info *info_, const char *&format_chars_,
tree format_string_cst_,
const char * const orig_format_chars_, const char * const orig_format_chars_,
location_t format_string_loc_, location_t format_string_loc_,
flag_chars_t &flag_chars_, flag_chars_t &flag_chars_,
...@@ -1944,7 +2050,9 @@ argument_parser (function_format_info *info_, const char *&format_chars_, ...@@ -1944,7 +2050,9 @@ argument_parser (function_format_info *info_, const char *&format_chars_,
: info (info_), : info (info_),
fki (&format_types[info->format_type]), fki (&format_types[info->format_type]),
flag_specs (fki->flag_specs), flag_specs (fki->flag_specs),
start_of_this_format (format_chars_),
format_chars (format_chars_), format_chars (format_chars_),
format_string_cst (format_string_cst_),
orig_format_chars (orig_format_chars_), orig_format_chars (orig_format_chars_),
format_string_loc (format_string_loc_), format_string_loc (format_string_loc_),
fwt_pool (fwt_pool_), fwt_pool (fwt_pool_),
...@@ -2008,11 +2116,10 @@ argument_parser::read_format_flags () ...@@ -2008,11 +2116,10 @@ argument_parser::read_format_flags ()
*format_chars, NULL); *format_chars, NULL);
if (flag_chars.has_char_p (*format_chars)) if (flag_chars.has_char_p (*format_chars))
{ {
warning_at (location_from_offset (format_string_loc, format_warning_at_char (format_string_loc, format_string_cst,
format_chars + 1 format_chars + 1 - orig_format_chars,
- orig_format_chars), OPT_Wformat_,
OPT_Wformat_, "repeated %s in format", _(s->name));
"repeated %s in format", _(s->name));
} }
else else
flag_chars.add_char (*format_chars); flag_chars.add_char (*format_chars);
...@@ -2145,10 +2252,10 @@ argument_parser::read_any_format_left_precision () ...@@ -2145,10 +2252,10 @@ argument_parser::read_any_format_left_precision ()
++format_chars; ++format_chars;
flag_chars.add_char (fki->left_precision_char); flag_chars.add_char (fki->left_precision_char);
if (!ISDIGIT (*format_chars)) if (!ISDIGIT (*format_chars))
warning_at (location_from_offset (format_string_loc, format_warning_at_char (format_string_loc, format_string_cst,
format_chars - orig_format_chars), format_chars - orig_format_chars,
OPT_Wformat_, OPT_Wformat_,
"empty left precision in %s format", fki->name); "empty left precision in %s format", fki->name);
while (ISDIGIT (*format_chars)) while (ISDIGIT (*format_chars))
++format_chars; ++format_chars;
} }
...@@ -2236,10 +2343,10 @@ read_any_format_precision (tree &params, ...@@ -2236,10 +2343,10 @@ read_any_format_precision (tree &params,
{ {
if (!(fki->flags & (int) FMT_FLAG_EMPTY_PREC_OK) if (!(fki->flags & (int) FMT_FLAG_EMPTY_PREC_OK)
&& !ISDIGIT (*format_chars)) && !ISDIGIT (*format_chars))
warning_at (location_from_offset (format_string_loc, format_warning_at_char (format_string_loc, format_string_cst,
format_chars - orig_format_chars), format_chars - orig_format_chars,
OPT_Wformat_, OPT_Wformat_,
"empty precision in %s format", fki->name); "empty precision in %s format", fki->name);
while (ISDIGIT (*format_chars)) while (ISDIGIT (*format_chars))
++format_chars; ++format_chars;
} }
...@@ -2340,11 +2447,10 @@ argument_parser::read_any_other_modifier () ...@@ -2340,11 +2447,10 @@ argument_parser::read_any_other_modifier ()
{ {
const format_flag_spec *s = get_flag_spec (flag_specs, const format_flag_spec *s = get_flag_spec (flag_specs,
*format_chars, NULL); *format_chars, NULL);
warning_at (location_from_offset (format_string_loc, format_warning_at_char (format_string_loc, format_string_cst,
format_chars format_chars - orig_format_chars,
- orig_format_chars), OPT_Wformat_,
OPT_Wformat_, "repeated %s in format", _(s->name));
"repeated %s in format", _(s->name));
} }
else else
flag_chars.add_char (*format_chars); flag_chars.add_char (*format_chars);
...@@ -2372,28 +2478,30 @@ argument_parser::find_format_char_info (char format_char) ...@@ -2372,28 +2478,30 @@ argument_parser::find_format_char_info (char format_char)
if (fci->format_chars == 0) if (fci->format_chars == 0)
{ {
if (ISGRAPH (format_char)) if (ISGRAPH (format_char))
warning_at (location_from_offset (format_string_loc, format_warning_at_char (format_string_loc, format_string_cst,
format_chars - orig_format_chars), format_chars - orig_format_chars,
OPT_Wformat_, OPT_Wformat_,
"unknown conversion type character %qc in format", "unknown conversion type character"
format_char); " %qc in format",
format_char);
else else
warning_at (location_from_offset (format_string_loc, format_warning_at_char (format_string_loc, format_string_cst,
format_chars - orig_format_chars), format_chars - orig_format_chars,
OPT_Wformat_, OPT_Wformat_,
"unknown conversion type character 0x%x in format", "unknown conversion type character"
format_char); " 0x%x in format",
format_char);
return NULL; return NULL;
} }
if (pedantic) if (pedantic)
{ {
if (ADJ_STD (fci->std) > C_STD_VER) if (ADJ_STD (fci->std) > C_STD_VER)
warning_at (location_from_offset (format_string_loc, format_warning_at_char (format_string_loc, format_string_cst,
format_chars - orig_format_chars), format_chars - orig_format_chars,
OPT_Wformat_, OPT_Wformat_,
"%s does not support the %<%%%c%> %s format", "%s does not support the %<%%%c%> %s format",
C_STD_NAME (fci->std), format_char, fki->name); C_STD_NAME (fci->std), format_char, fki->name);
} }
return fci; return fci;
...@@ -2496,10 +2604,10 @@ argument_parser::parse_any_scan_set (const format_char_info *fci) ...@@ -2496,10 +2604,10 @@ argument_parser::parse_any_scan_set (const format_char_info *fci)
++format_chars; ++format_chars;
if (*format_chars != ']') if (*format_chars != ']')
/* The end of the format string was reached. */ /* The end of the format string was reached. */
warning_at (location_from_offset (format_string_loc, format_warning_at_char (format_string_loc, format_string_cst,
format_chars - orig_format_chars), format_chars - orig_format_chars,
OPT_Wformat_, OPT_Wformat_,
"no closing %<]%> for %<%%[%> format"); "no closing %<]%> for %<%%[%> format");
} }
/* Return true if this argument is to be continued to be parsed, /* Return true if this argument is to be continued to be parsed,
...@@ -2525,12 +2633,13 @@ argument_parser::handle_conversions (const format_char_info *fci, ...@@ -2525,12 +2633,13 @@ argument_parser::handle_conversions (const format_char_info *fci,
wanted_type_std = fci->types[len_modifier.val].std; wanted_type_std = fci->types[len_modifier.val].std;
if (wanted_type == 0) if (wanted_type == 0)
{ {
warning_at (location_from_offset (format_string_loc, format_warning_at_char (format_string_loc, format_string_cst,
format_chars - orig_format_chars), format_chars - orig_format_chars,
OPT_Wformat_, OPT_Wformat_,
"use of %qs length modifier with %qc type character" "use of %qs length modifier with %qc type"
" has either no effect or undefined behavior", " character has either no effect"
len_modifier.chars, format_char); " or undefined behavior",
len_modifier.chars, format_char);
/* Heuristic: skip one argument when an invalid length/type /* Heuristic: skip one argument when an invalid length/type
combination is encountered. */ combination is encountered. */
arg_num++; arg_num++;
...@@ -2546,12 +2655,13 @@ argument_parser::handle_conversions (const format_char_info *fci, ...@@ -2546,12 +2655,13 @@ argument_parser::handle_conversions (const format_char_info *fci,
&& ADJ_STD (wanted_type_std) > ADJ_STD (fci->std)) && ADJ_STD (wanted_type_std) > ADJ_STD (fci->std))
{ {
if (ADJ_STD (wanted_type_std) > C_STD_VER) if (ADJ_STD (wanted_type_std) > C_STD_VER)
warning_at (location_from_offset (format_string_loc, format_warning_at_char (format_string_loc, format_string_cst,
format_chars - orig_format_chars), format_chars - orig_format_chars,
OPT_Wformat_, OPT_Wformat_,
"%s does not support the %<%%%s%c%> %s format", "%s does not support the %<%%%s%c%> %s format",
C_STD_NAME (wanted_type_std), len_modifier.chars, C_STD_NAME (wanted_type_std),
format_char, fki->name); len_modifier.chars,
format_char, fki->name);
} }
return true; return true;
...@@ -2571,7 +2681,8 @@ check_argument_type (const format_char_info *fci, ...@@ -2571,7 +2681,8 @@ check_argument_type (const format_char_info *fci,
unsigned HOST_WIDE_INT &arg_num, unsigned HOST_WIDE_INT &arg_num,
tree &params, tree &params,
const int alloc_flag, const int alloc_flag,
const char * const format_start) const char * const format_start,
location_t fmt_param_loc)
{ {
if (info->first_arg_num == 0) if (info->first_arg_num == 0)
return true; return true;
...@@ -2670,7 +2781,13 @@ check_argument_type (const format_char_info *fci, ...@@ -2670,7 +2781,13 @@ check_argument_type (const format_char_info *fci,
} }
if (first_wanted_type != 0) if (first_wanted_type != 0)
check_format_types (format_string_loc, first_wanted_type); {
ptrdiff_t offset_to_format_start = (start_of_this_format - 1) - orig_format_chars;
ptrdiff_t offset_to_format_end = (format_chars - 1) - orig_format_chars;
substring_loc fmt_loc (fmt_param_loc, TREE_TYPE (format_string_cst),
offset_to_format_start, offset_to_format_end);
check_format_types (fmt_loc, first_wanted_type);
}
return true; return true;
} }
...@@ -2685,6 +2802,7 @@ check_argument_type (const format_char_info *fci, ...@@ -2685,6 +2802,7 @@ check_argument_type (const format_char_info *fci,
static void static void
check_format_info_main (format_check_results *res, check_format_info_main (format_check_results *res,
function_format_info *info, const char *format_chars, function_format_info *info, const char *format_chars,
location_t fmt_param_loc, tree format_string_cst,
int format_length, tree params, int format_length, tree params,
unsigned HOST_WIDE_INT arg_num, unsigned HOST_WIDE_INT arg_num,
object_allocator <format_wanted_type> &fwt_pool) object_allocator <format_wanted_type> &fwt_pool)
...@@ -2708,10 +2826,10 @@ check_format_info_main (format_check_results *res, ...@@ -2708,10 +2826,10 @@ check_format_info_main (format_check_results *res,
continue; continue;
if (*format_chars == 0) if (*format_chars == 0)
{ {
warning_at (location_from_offset (format_string_loc, format_warning_at_char (format_string_loc, format_string_cst,
format_chars - orig_format_chars), format_chars - orig_format_chars,
OPT_Wformat_, OPT_Wformat_,
"spurious trailing %<%%%> in format"); "spurious trailing %<%%%> in format");
continue; continue;
} }
if (*format_chars == '%') if (*format_chars == '%')
...@@ -2721,8 +2839,8 @@ check_format_info_main (format_check_results *res, ...@@ -2721,8 +2839,8 @@ check_format_info_main (format_check_results *res,
} }
flag_chars_t flag_chars; flag_chars_t flag_chars;
argument_parser arg_parser (info, format_chars, orig_format_chars, argument_parser arg_parser (info, format_chars, format_string_cst,
format_string_loc, orig_format_chars, format_string_loc,
flag_chars, has_operand_number, flag_chars, has_operand_number,
first_fillin_param, fwt_pool); first_fillin_param, fwt_pool);
...@@ -2759,10 +2877,10 @@ check_format_info_main (format_check_results *res, ...@@ -2759,10 +2877,10 @@ check_format_info_main (format_check_results *res,
|| (!(fki->flags & (int) FMT_FLAG_FANCY_PERCENT_OK) || (!(fki->flags & (int) FMT_FLAG_FANCY_PERCENT_OK)
&& format_char == '%')) && format_char == '%'))
{ {
warning_at (location_from_offset (format_string_loc, format_warning_at_char (format_string_loc, format_string_cst,
format_chars - orig_format_chars), format_chars - orig_format_chars,
OPT_Wformat_, OPT_Wformat_,
"conversion lacks type at end of format"); "conversion lacks type at end of format");
continue; continue;
} }
format_chars++; format_chars++;
...@@ -2773,6 +2891,7 @@ check_format_info_main (format_check_results *res, ...@@ -2773,6 +2891,7 @@ check_format_info_main (format_check_results *res,
continue; continue;
flag_chars.validate (fki, fci, flag_specs, format_chars, flag_chars.validate (fki, fci, flag_specs, format_chars,
format_string_cst,
format_string_loc, orig_format_chars, format_char); format_string_loc, orig_format_chars, format_char);
const int alloc_flag = flag_chars.get_alloc_flag (fki); const int alloc_flag = flag_chars.get_alloc_flag (fki);
...@@ -2803,15 +2922,15 @@ check_format_info_main (format_check_results *res, ...@@ -2803,15 +2922,15 @@ check_format_info_main (format_check_results *res,
suppressed, suppressed,
arg_num, params, arg_num, params,
alloc_flag, alloc_flag,
format_start)) format_start, fmt_param_loc))
return; return;
} }
if (format_chars - orig_format_chars != format_length) if (format_chars - orig_format_chars != format_length)
warning_at (location_from_offset (format_string_loc, format_warning_at_char (format_string_loc, format_string_cst,
format_chars + 1 - orig_format_chars), format_chars + 1 - orig_format_chars,
OPT_Wformat_contains_nul, OPT_Wformat_contains_nul,
"embedded %<\\0%> in format"); "embedded %<\\0%> in format");
if (info->first_arg_num != 0 && params != 0 if (info->first_arg_num != 0 && params != 0
&& has_operand_number <= 0) && has_operand_number <= 0)
{ {
...@@ -2822,12 +2941,12 @@ check_format_info_main (format_check_results *res, ...@@ -2822,12 +2941,12 @@ check_format_info_main (format_check_results *res,
finish_dollar_format_checking (res, fki->flags & (int) FMT_FLAG_DOLLAR_GAP_POINTER_OK); finish_dollar_format_checking (res, fki->flags & (int) FMT_FLAG_DOLLAR_GAP_POINTER_OK);
} }
/* Check the argument types from a single format conversion (possibly /* Check the argument types from a single format conversion (possibly
including width and precision arguments). LOC is the location of including width and precision arguments). FMT_LOC is the
the format string. */ location of the format conversion. */
static void static void
check_format_types (location_t loc, format_wanted_type *types) check_format_types (const substring_loc &fmt_loc,
format_wanted_type *types)
{ {
for (; types != 0; types = types->next) for (; types != 0; types = types->next)
{ {
...@@ -2854,7 +2973,7 @@ check_format_types (location_t loc, format_wanted_type *types) ...@@ -2854,7 +2973,7 @@ check_format_types (location_t loc, format_wanted_type *types)
cur_param = types->param; cur_param = types->param;
if (!cur_param) if (!cur_param)
{ {
format_type_warning (loc, types, wanted_type, NULL); format_type_warning (fmt_loc, NULL, types, wanted_type, NULL);
continue; continue;
} }
...@@ -2864,6 +2983,16 @@ check_format_types (location_t loc, format_wanted_type *types) ...@@ -2864,6 +2983,16 @@ check_format_types (location_t loc, format_wanted_type *types)
orig_cur_type = cur_type; orig_cur_type = cur_type;
char_type_flag = 0; char_type_flag = 0;
source_range param_range;
source_range *param_range_ptr;
if (CAN_HAVE_LOCATION_P (cur_param))
{
param_range = EXPR_LOCATION_RANGE (cur_param);
param_range_ptr = &param_range;
}
else
param_range_ptr = NULL;
STRIP_NOPS (cur_param); STRIP_NOPS (cur_param);
/* Check the types of any additional pointer arguments /* Check the types of any additional pointer arguments
...@@ -2928,7 +3057,8 @@ check_format_types (location_t loc, format_wanted_type *types) ...@@ -2928,7 +3057,8 @@ check_format_types (location_t loc, format_wanted_type *types)
} }
else else
{ {
format_type_warning (loc, types, wanted_type, orig_cur_type); format_type_warning (fmt_loc, param_range_ptr,
types, wanted_type, orig_cur_type);
break; break;
} }
} }
...@@ -2996,20 +3126,24 @@ check_format_types (location_t loc, format_wanted_type *types) ...@@ -2996,20 +3126,24 @@ check_format_types (location_t loc, format_wanted_type *types)
&& TYPE_PRECISION (cur_type) == TYPE_PRECISION (wanted_type)) && TYPE_PRECISION (cur_type) == TYPE_PRECISION (wanted_type))
continue; continue;
/* Now we have a type mismatch. */ /* Now we have a type mismatch. */
format_type_warning (loc, types, wanted_type, orig_cur_type); format_type_warning (fmt_loc, param_range_ptr, types,
wanted_type, orig_cur_type);
} }
} }
/* Give a warning at LOC about a format argument of different type from that /* Give a warning at FMT_LOC about a format argument of different type
expected. WANTED_TYPE is the type the argument should have, possibly from that expected. If non-NULL, PARAM_RANGE is the source range of the
stripped of pointer dereferences. The description (such as "field relevant argument. WANTED_TYPE is the type the argument should have,
possibly stripped of pointer dereferences. The description (such as "field
precision"), the placement in the format string, a possibly more precision"), the placement in the format string, a possibly more
friendly name of WANTED_TYPE, and the number of pointer dereferences friendly name of WANTED_TYPE, and the number of pointer dereferences
are taken from TYPE. ARG_TYPE is the type of the actual argument, 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. */
static void static void
format_type_warning (location_t loc, format_wanted_type *type, format_type_warning (const substring_loc &fmt_loc,
source_range *param_range,
format_wanted_type *type,
tree wanted_type, tree arg_type) tree wanted_type, tree arg_type)
{ {
int kind = type->kind; int kind = type->kind;
...@@ -3018,7 +3152,6 @@ format_type_warning (location_t loc, format_wanted_type *type, ...@@ -3018,7 +3152,6 @@ format_type_warning (location_t loc, format_wanted_type *type,
int format_length = type->format_length; int format_length = type->format_length;
int pointer_count = type->pointer_count; int pointer_count = type->pointer_count;
int arg_num = type->arg_num; int arg_num = type->arg_num;
unsigned int offset_loc = type->offset_loc;
char *p; char *p;
/* If ARG_TYPE is a typedef with a misleading name (for example, /* If ARG_TYPE is a typedef with a misleading name (for example,
...@@ -3052,41 +3185,47 @@ format_type_warning (location_t loc, format_wanted_type *type, ...@@ -3052,41 +3185,47 @@ format_type_warning (location_t loc, format_wanted_type *type,
p[pointer_count + 1] = 0; p[pointer_count + 1] = 0;
} }
loc = location_from_offset (loc, offset_loc);
if (wanted_type_name) if (wanted_type_name)
{ {
if (arg_type) if (arg_type)
warning_at (loc, OPT_Wformat_, format_warning_at_substring
"%s %<%s%.*s%> expects argument of type %<%s%s%>, " (fmt_loc, param_range,
"but argument %d has type %qT", OPT_Wformat_,
gettext (kind_descriptions[kind]), "%s %<%s%.*s%> expects argument of type %<%s%s%>, "
(kind == CF_KIND_FORMAT ? "%" : ""), "but argument %d has type %qT",
format_length, format_start, gettext (kind_descriptions[kind]),
wanted_type_name, p, arg_num, arg_type); (kind == CF_KIND_FORMAT ? "%" : ""),
format_length, format_start,
wanted_type_name, p, arg_num, arg_type);
else else
warning_at (loc, OPT_Wformat_, format_warning_at_substring
"%s %<%s%.*s%> expects a matching %<%s%s%> argument", (fmt_loc, param_range,
gettext (kind_descriptions[kind]), OPT_Wformat_,
(kind == CF_KIND_FORMAT ? "%" : ""), "%s %<%s%.*s%> expects a matching %<%s%s%> argument",
format_length, format_start, wanted_type_name, p); gettext (kind_descriptions[kind]),
(kind == CF_KIND_FORMAT ? "%" : ""),
format_length, format_start, wanted_type_name, p);
} }
else else
{ {
if (arg_type) if (arg_type)
warning_at (loc, OPT_Wformat_, format_warning_at_substring
"%s %<%s%.*s%> expects argument of type %<%T%s%>, " (fmt_loc, param_range,
"but argument %d has type %qT", OPT_Wformat_,
gettext (kind_descriptions[kind]), "%s %<%s%.*s%> expects argument of type %<%T%s%>, "
(kind == CF_KIND_FORMAT ? "%" : ""), "but argument %d has type %qT",
format_length, format_start, gettext (kind_descriptions[kind]),
wanted_type, p, arg_num, arg_type); (kind == CF_KIND_FORMAT ? "%" : ""),
format_length, format_start,
wanted_type, p, arg_num, arg_type);
else else
warning_at (loc, OPT_Wformat_, format_warning_at_substring
"%s %<%s%.*s%> expects a matching %<%T%s%> argument", (fmt_loc, param_range,
gettext (kind_descriptions[kind]), OPT_Wformat_,
(kind == CF_KIND_FORMAT ? "%" : ""), "%s %<%s%.*s%> expects a matching %<%T%s%> argument",
format_length, format_start, wanted_type, p); gettext (kind_descriptions[kind]),
(kind == CF_KIND_FORMAT ? "%" : ""),
format_length, format_start, wanted_type, p);
} }
} }
......
2016-08-08 David Malcolm <dmalcolm@redhat.com>
PR c/52952
* gcc.dg/cpp/pr66415-1.c: Likewise.
* gcc.dg/format/asm_fprintf-1.c: Update column numbers.
* gcc.dg/format/c90-printf-1.c: Likewise.
* gcc.dg/format/diagnostic-ranges.c: New test case.
2016-08-08 Jakub Jelinek <jakub@redhat.com> 2016-08-08 Jakub Jelinek <jakub@redhat.com>
PR fortran/72716 PR fortran/72716
......
/* PR c/66415 */ /* PR c/66415 */
/* { dg-do compile } */ /* { dg-do compile } */
/* { dg-options "-Wformat" } */ /* { dg-options "-Wformat -fdiagnostics-show-caret" } */
void void
fn1 (void) fn1 (void)
{ {
__builtin_printf ("xxxxxxxxxxxxxxxxx%dxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); /* { dg-warning "71:format" } */ __builtin_printf ("xxxxxxxxxxxxxxxxx%dxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); /* { dg-warning "71:format" } */
/* { dg-begin-multiline-output "" }
__builtin_printf ("xxxxxxxxxxxxxxxxx%dxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
~^
{ dg-end-multiline-output "" } */
} }
...@@ -66,9 +66,9 @@ foo (int i, int i1, int i2, unsigned int u, double d, char *s, void *p, ...@@ -66,9 +66,9 @@ foo (int i, int i1, int i2, unsigned int u, double d, char *s, void *p,
asm_fprintf ("%d", i, i); /* { dg-warning "16:arguments" "wrong number of args" } */ asm_fprintf ("%d", i, i); /* { dg-warning "16:arguments" "wrong number of args" } */
/* Miscellaneous bogus constructions. */ /* Miscellaneous bogus constructions. */
asm_fprintf (""); /* { dg-warning "16:zero-length" "warning for empty format" } */ asm_fprintf (""); /* { dg-warning "16:zero-length" "warning for empty format" } */
asm_fprintf ("\0"); /* { dg-warning "17:embedded" "warning for embedded NUL" } */ asm_fprintf ("\0"); /* { dg-warning "18:embedded" "warning for embedded NUL" } */
asm_fprintf ("%d\0", i); /* { dg-warning "19:embedded" "warning for embedded NUL" } */ asm_fprintf ("%d\0", i); /* { dg-warning "20:embedded" "warning for embedded NUL" } */
asm_fprintf ("%d\0%d", i, i); /* { dg-warning "19:embedded|too many" "warning for embedded NUL" } */ asm_fprintf ("%d\0%d", i, i); /* { dg-warning "20:embedded|too many" "warning for embedded NUL" } */
asm_fprintf (NULL); /* { dg-warning "null" "null format string warning" } */ asm_fprintf (NULL); /* { dg-warning "null" "null format string warning" } */
asm_fprintf ("%"); /* { dg-warning "17:trailing" "trailing % warning" } */ asm_fprintf ("%"); /* { dg-warning "17:trailing" "trailing % warning" } */
asm_fprintf ("%++d", i); /* { dg-warning "19:repeated" "repeated flag warning" } */ asm_fprintf ("%++d", i); /* { dg-warning "19:repeated" "repeated flag warning" } */
......
...@@ -58,11 +58,11 @@ foo (int i, int i1, int i2, unsigned int u, double d, char *s, void *p, ...@@ -58,11 +58,11 @@ foo (int i, int i1, int i2, unsigned int u, double d, char *s, void *p,
printf ("%-%"); /* { dg-warning "13:type" "missing type" } */ printf ("%-%"); /* { dg-warning "13:type" "missing type" } */
/* { dg-warning "14:trailing" "bogus %%" { target *-*-* } 58 } */ /* { dg-warning "14:trailing" "bogus %%" { target *-*-* } 58 } */
printf ("%-%\n"); /* { dg-warning "13:format" "bogus %%" } */ printf ("%-%\n"); /* { dg-warning "13:format" "bogus %%" } */
/* { dg-warning "15:format" "bogus %%" { target *-*-* } 60 } */ /* { dg-warning "16:format" "bogus %%" { target *-*-* } 60 } */
printf ("%5%\n"); /* { dg-warning "13:format" "bogus %%" } */ printf ("%5%\n"); /* { dg-warning "13:format" "bogus %%" } */
/* { dg-warning "15:format" "bogus %%" { target *-*-* } 62 } */ /* { dg-warning "16:format" "bogus %%" { target *-*-* } 62 } */
printf ("%h%\n"); /* { dg-warning "13:format" "bogus %%" } */ printf ("%h%\n"); /* { dg-warning "13:format" "bogus %%" } */
/* { dg-warning "15:format" "bogus %%" { target *-*-* } 64 } */ /* { dg-warning "16:format" "bogus %%" { target *-*-* } 64 } */
/* Valid and invalid %h, %l, %L constructions. */ /* Valid and invalid %h, %l, %L constructions. */
printf ("%hd", i); printf ("%hd", i);
printf ("%hi", i); printf ("%hi", i);
...@@ -184,8 +184,8 @@ foo (int i, int i1, int i2, unsigned int u, double d, char *s, void *p, ...@@ -184,8 +184,8 @@ foo (int i, int i1, int i2, unsigned int u, double d, char *s, void *p,
printf ("%-08G", d); /* { dg-warning "11:flags|ignored" "0 flag ignored with - flag" } */ printf ("%-08G", d); /* { dg-warning "11:flags|ignored" "0 flag ignored with - flag" } */
/* Various tests of bad argument types. */ /* Various tests of bad argument types. */
printf ("%d", l); /* { dg-warning "13:format" "bad argument types" } */ printf ("%d", l); /* { dg-warning "13:format" "bad argument types" } */
printf ("%*.*d", l, i2, i); /* { dg-warning "13:field" "bad * argument types" } */ printf ("%*.*d", l, i2, i); /* { dg-warning "16:field" "bad * argument types" } */
printf ("%*.*d", i1, l, i); /* { dg-warning "15:field" "bad * argument types" } */ printf ("%*.*d", i1, l, i); /* { dg-warning "16:field" "bad * argument types" } */
printf ("%ld", i); /* { dg-warning "14:format" "bad argument types" } */ printf ("%ld", i); /* { dg-warning "14:format" "bad argument types" } */
printf ("%s", n); /* { dg-warning "13:format" "bad argument types" } */ printf ("%s", n); /* { dg-warning "13:format" "bad argument types" } */
printf ("%p", i); /* { dg-warning "13:format" "bad argument types" } */ printf ("%p", i); /* { dg-warning "13:format" "bad argument types" } */
...@@ -231,8 +231,8 @@ foo (int i, int i1, int i2, unsigned int u, double d, char *s, void *p, ...@@ -231,8 +231,8 @@ foo (int i, int i1, int i2, unsigned int u, double d, char *s, void *p,
printf ("%d", i, i); /* { dg-warning "11:arguments" "wrong number of args" } */ printf ("%d", i, i); /* { dg-warning "11:arguments" "wrong number of args" } */
/* Miscellaneous bogus constructions. */ /* Miscellaneous bogus constructions. */
printf (""); /* { dg-warning "11:zero-length" "warning for empty format" } */ printf (""); /* { dg-warning "11:zero-length" "warning for empty format" } */
printf ("\0"); /* { dg-warning "12:embedded" "warning for embedded NUL" } */ printf ("\0"); /* { dg-warning "13:embedded" "warning for embedded NUL" } */
printf ("%d\0", i); /* { dg-warning "14:embedded" "warning for embedded NUL" } */ printf ("%d\0", i); /* { dg-warning "15:embedded" "warning for embedded NUL" } */
printf ("%d\0%d", i, i); /* { dg-warning "embedded|too many" "warning for embedded NUL" } */ printf ("%d\0%d", i, i); /* { dg-warning "embedded|too many" "warning for embedded NUL" } */
printf (NULL); /* { dg-warning "3:null" "null format string warning" } */ printf (NULL); /* { dg-warning "3:null" "null format string warning" } */
printf ("%"); /* { dg-warning "12:trailing" "trailing % warning" } */ printf ("%"); /* { dg-warning "12:trailing" "trailing % warning" } */
......
/* { dg-options "-Wformat -fdiagnostics-show-caret" } */
/* See PR 52952. */
#include "format.h"
void test_mismatching_types (const char *msg)
{
printf("hello %i", msg); /* { dg-warning "format '%i' expects argument of type 'int', but argument 2 has type 'const char \\*' " } */
/* TODO: ideally would also underline "msg". */
/* { dg-begin-multiline-output "" }
printf("hello %i", msg);
~^
{ dg-end-multiline-output "" } */
}
void test_multiple_arguments (void)
{
printf ("arg0: %i arg1: %s arg 2: %i", /* { dg-warning "29: format '%s'" } */
100, 101, 102);
/* TODO: ideally would also underline "101". */
/* { dg-begin-multiline-output "" }
printf ("arg0: %i arg1: %s arg 2: %i",
~^
{ dg-end-multiline-output "" } */
}
void test_multiple_arguments_2 (int i, int j)
{
printf ("arg0: %i arg1: %s arg 2: %i", /* { dg-warning "29: format '%s'" } */
100, i + j, 102);
/* { dg-begin-multiline-output "" }
printf ("arg0: %i arg1: %s arg 2: %i",
~^
100, i + j, 102);
~~~~~
{ dg-end-multiline-output "" } */
}
void multiline_format_string (void) {
printf ("before the fmt specifier" /* { dg-warning "11: format '%d' expects a matching 'int' argument" } */
/* { dg-begin-multiline-output "" }
printf ("before the fmt specifier"
^~~~~~~~~~~~~~~~~~~~~~~~~~
{ dg-end-multiline-output "" } */
"%"
"d" /* { dg-message "12: format string is defined here" } */
"after the fmt specifier");
/* { dg-begin-multiline-output "" }
"%"
~~
"d"
~^
{ dg-end-multiline-output "" } */
}
void test_hex (const char *msg)
{
/* "%" is \x25
"i" is \x69 */
printf("hello \x25\x69", msg); /* { dg-warning "format '%i' expects argument of type 'int', but argument 2 has type 'const char \\*' " } */
/* TODO: ideally would also underline "msg". */
/* { dg-begin-multiline-output "" }
printf("hello \x25\x69", msg);
~~~~~~~^
{ dg-end-multiline-output "" } */
}
void test_oct (const char *msg)
{
/* "%" is octal 045
"i" is octal 151. */
printf("hello \045\151", msg); /* { dg-warning "format '%i' expects argument of type 'int', but argument 2 has type 'const char \\*' " } */
/* TODO: ideally would also underline "msg". */
/* { dg-begin-multiline-output "" }
printf("hello \045\151", msg);
~~~~~~~^
{ dg-end-multiline-output "" } */
}
void test_multiple (const char *msg)
{
/* "%" is \x25 in hex
"i" is \151 in octal. */
printf("prefix" "\x25" "\151" "suffix", /* { dg-warning "format '%i'" } */
msg);
/* { dg-begin-multiline-output "" }
printf("prefix" "\x25" "\151" "suffix",
^~~~~~~~
{ dg-end-multiline-output "" } */
/* TODO: ideally would also underline "msg". */
/* { dg-begin-multiline-output "" }
printf("prefix" "\x25" "\151" "suffix",
~~~~~~~~~~~^
{ dg-end-multiline-output "" } */
}
void test_u8 (const char *msg)
{
printf(u8"hello %i", msg);/* { dg-warning "format '%i' expects argument of type 'int', but argument 2 has type 'const char \\*' " } */
/* TODO: ideally would also underline "msg". */
/* { dg-begin-multiline-output "" }
printf(u8"hello %i", msg);
~^
{ dg-end-multiline-output "" } */
}
void test_param (long long_i, long long_j)
{
printf ("foo %s bar", long_i + long_j); /* { dg-warning "17: format '%s' expects argument of type 'char \\*', but argument 2 has type 'long int'" } */
/* { dg-begin-multiline-output "" }
printf ("foo %s bar", long_i + long_j);
~^ ~~~~~~~~~~~~~~~
{ dg-end-multiline-output "" } */
}
void test_field_width_specifier (long l, int i1, int i2)
{
printf (" %*.*d ", l, i1, i2); /* { dg-warning "17: field width specifier '\\*' expects argument of type 'int', but argument 2 has type 'long int'" } */
/* { dg-begin-multiline-output "" }
printf (" %*.*d ", l, i1, i2);
~~~~^
{ dg-end-multiline-output "" } */
}
void test_spurious_percent (void)
{
printf("hello world %"); /* { dg-warning "23: spurious trailing" } */
/* { dg-begin-multiline-output "" }
printf("hello world %");
^
{ dg-end-multiline-output "" } */
}
void test_empty_precision (char *s, size_t m, double d)
{
strfmon (s, m, "%#.5n", d); /* { dg-warning "20: empty left precision in gnu_strfmon format" } */
/* { dg-begin-multiline-output "" }
strfmon (s, m, "%#.5n", d);
^
{ dg-end-multiline-output "" } */
strfmon (s, m, "%#5.n", d); /* { dg-warning "22: empty precision in gnu_strfmon format" } */
/* { dg-begin-multiline-output "" }
strfmon (s, m, "%#5.n", d);
^
{ dg-end-multiline-output "" } */
}
void test_repeated (int i)
{
printf ("%++d", i); /* { dg-warning "14: repeated '\\+' flag in format" } */
/* { dg-begin-multiline-output "" }
printf ("%++d", i);
^
{ dg-end-multiline-output "" } */
}
void test_conversion_lacks_type (void)
{
printf (" %h"); /* { dg-warning "14:conversion lacks type at end of format" } */
/* { dg-begin-multiline-output "" }
printf (" %h");
^
{ dg-end-multiline-output "" } */
}
void test_embedded_nul (void)
{
printf (" \0 "); /* { dg-warning "14:embedded" "warning for embedded NUL" } */
/* { dg-begin-multiline-output "" }
printf (" \0 ");
~^
{ dg-end-multiline-output "" } */
}
void test_macro (const char *msg)
{
#define INT_FMT "%i" /* { dg-message "19: format string is defined here" } */
printf("hello " INT_FMT " world", msg); /* { dg-warning "10: format '%i' expects argument of type 'int', but argument 2 has type 'const char \\*' " } */
/* { dg-begin-multiline-output "" }
printf("hello " INT_FMT " world", msg);
^~~~~~~~
{ dg-end-multiline-output "" } */
/* { dg-begin-multiline-output "" }
#define INT_FMT "%i"
~^
{ dg-end-multiline-output "" } */
}
void test_non_contiguous_strings (void)
{
__builtin_printf(" %" "d ", 0.5); /* { dg-warning "20: format .%d. expects argument of type .int., but argument 2 has type .double." } */
/* { dg-message "26: format string is defined here" "" { target *-*-* } 200 } */
/* { dg-begin-multiline-output "" }
__builtin_printf(" %" "d ", 0.5);
^~~~
{ dg-end-multiline-output "" } */
/* { dg-begin-multiline-output "" }
__builtin_printf(" %" "d ", 0.5);
~~~~^
{ dg-end-multiline-output "" } */
}
void test_const_arrays (void)
{
/* TODO: ideally we'd highlight both the format string *and* the use of
it here. For now, just verify that we gracefully handle this case. */
const char a[] = " %d ";
__builtin_printf(a, 0.5); /* { dg-warning "20: format .%d. expects argument of type .int., but argument 2 has type .double." } */
/* { dg-begin-multiline-output "" }
__builtin_printf(a, 0.5);
^
{ dg-end-multiline-output "" } */
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment