Commit d127ae9f by Martin Sebor Committed by Martin Sebor

PR tree-optimization/80523 - -Wformat-overflow doesn't consider -fexec-charset

gcc/ChangeLog:

	PR tree-optimization/80523
	* gimple-ssa-sprintf.c (target_to_host_charmap): New global variable.
	(init_target_to_host_charmap, target_to_host, target_strtol10): New
	functions.
	(maybe_warn, format_directive, parse_directive): Use new functions.
	(pass_sprintf_length::execute): Call init_target_to_host_charmap.

gcc/testsuite/ChangeLog:

	PR tree-optimization/80523
	* gcc.dg/tree-ssa/builtin-sprintf-warn-18.c: New test.

From-SVN: r247401
parent c9610933
2017-04-28 Martin Sebor <msebor@redhat.com>
PR tree-optimization/80523
* gimple-ssa-sprintf.c (target_to_host_charmap): New global variable.
(init_target_to_host_charmap, target_to_host, target_strtol10): New
functions.
(maybe_warn, format_directive, parse_directive): Use new functions.
(pass_sprintf_length::execute): Call init_target_to_host_charmap.
2017-04-28 Marc Glisse <marc.glisse@inria.fr>
* match.pd (X+Z OP Y+Z, X-Z OP Y-Z, Z-X OP Z-Y): New transformations.
......
......@@ -304,7 +304,7 @@ C ObjC C++ ObjC++ Var(warn_alloca) Warning
Warn on any use of alloca.
Walloc-size-larger-than=
C ObjC C++ ObjC++ Var(warn_alloc_size_limit) Warning Joined
C ObjC C++ ObjC++ Var(warn_alloc_size_limit) Warning Joined LangEnabledBy(C ObjC C++ ObjC++,Wall)
-Walloc-size-larger-than=<bytes> Warn for calls to allocation functions that
attempt to allocate objects larger than the specified number of bytes.
......@@ -716,7 +716,7 @@ Warn about buffer overflow in string manipulation functions like memcpy
and strcpy.
Wstringop-overflow=
C ObjC C++ ObjC++ Joined RejectNegative UInteger Var(warn_stringop_overflow) Init(2) Warning
C ObjC C++ ObjC++ Joined RejectNegative UInteger Var(warn_stringop_overflow) Init(2) Warning LangEnabledBy(C ObjC C++ ObjC++, Wall)
Under the control of Object Size type, warn about buffer overflow in string
manipulation functions like memcpy and strcpy.
......
......@@ -66,6 +66,7 @@ along with GCC; see the file COPYING3. If not see
#include "calls.h"
#include "cfgloop.h"
#include "intl.h"
#include "langhooks.h"
#include "builtins.h"
#include "stor-layout.h"
......@@ -273,6 +274,158 @@ target_size_max ()
return tree_to_uhwi (TYPE_MAX_VALUE (size_type_node));
}
/* A straightforward mapping from the execution character set to the host
character set indexed by execution character. */
static char target_to_host_charmap[256];
/* Initialize a mapping from the execution character set to the host
character set. */
static bool
init_target_to_host_charmap ()
{
/* If the percent sign is non-zero the mapping has already been
initialized. */
if (target_to_host_charmap['%'])
return true;
/* Initialize the target_percent character (done elsewhere). */
if (!init_target_chars ())
return false;
/* The subset of the source character set used by printf conversion
specifications (strictly speaking, not all letters are used but
they are included here for the sake of simplicity). The dollar
sign must be included even though it's not in the basic source
character set. */
const char srcset[] = " 0123456789!\"#%&'()*+,-./:;<=>?[\\]^_{|}~$"
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
/* Set the mapping for all characters to some ordinary value (i,e.,
not none used in printf conversion specifications) and overwrite
those that are used by conversion specifications with their
corresponding values. */
memset (target_to_host_charmap + 1, '?', sizeof target_to_host_charmap - 1);
/* Are the two sets of characters the same? */
bool all_same_p = true;
for (const char *pc = srcset; *pc; ++pc)
{
/* Slice off the high end bits in case target characters are
signed. All values are expected to be non-nul, otherwise
there's a problem. */
if (unsigned char tc = lang_hooks.to_target_charset (*pc))
{
target_to_host_charmap[tc] = *pc;
if (tc != *pc)
all_same_p = false;
}
else
return false;
}
/* Set the first element to a non-zero value if the mapping
is 1-to-1, otherwise leave it clear (NUL is assumed to be
the same in both character sets). */
target_to_host_charmap[0] = all_same_p;
return true;
}
/* Return the host source character corresponding to the character
CH in the execution character set if one exists, or some innocuous
(non-special, non-nul) source character otherwise. */
static inline unsigned char
target_to_host (unsigned char ch)
{
return target_to_host_charmap[ch];
}
/* Convert an initial substring of the string TARGSTR consisting of
characters in the execution character set into a string in the
source character set on the host and store up to HOSTSZ characters
in the buffer pointed to by HOSTR. Return HOSTR. */
static const char*
target_to_host (char *hostr, size_t hostsz, const char *targstr)
{
/* Make sure the buffer is reasonably big. */
gcc_assert (hostsz > 4);
/* The interesting subset of source and execution characters are
the same so no conversion is necessary. However, truncate
overlong strings just like the translated strings are. */
if (target_to_host_charmap['\0'] == 1)
{
strncpy (hostr, targstr, hostsz - 4);
if (strlen (targstr) >= hostsz)
strcpy (hostr + hostsz - 4, "...");
return hostr;
}
/* Convert the initial substring of TARGSTR to the corresponding
characters in the host set, appending "..." if TARGSTR is too
long to fit. Using the static buffer assumes the function is
not called in between sequence points (which it isn't). */
for (char *ph = hostr; ; ++targstr)
{
*ph++ = target_to_host (*targstr);
if (!*targstr)
break;
if (size_t (ph - hostr) == hostsz - 4)
{
*ph = '\0';
strcat (ph, "...");
break;
}
}
return hostr;
}
/* Convert the sequence of decimal digits in the execution character
starting at S to a long, just like strtol does. Return the result
and set *END to one past the last converted character. On range
error set ERANGE to the digit that caused it. */
static inline long
target_strtol10 (const char **ps, const char **erange)
{
unsigned HOST_WIDE_INT val = 0;
for ( ; ; ++*ps)
{
unsigned char c = target_to_host (**ps);
if (ISDIGIT (c))
{
c -= '0';
/* Check for overflow. */
if (val > (LONG_MAX - c) / 10LU)
{
val = LONG_MAX;
*erange = *ps;
/* Skip the remaining digits. */
do
c = target_to_host (*++*ps);
while (ISDIGIT (c));
break;
}
else
val = val * 10 + c;
}
else
break;
}
return val;
}
/* Return the constant initial value of DECL if available or DECL
otherwise. Same as the synonymous function in c/c-typeck.c. */
......@@ -2284,12 +2437,16 @@ maybe_warn (substring_loc &dirloc, source_range *pargrange,
|| (res.max < HOST_WIDE_INT_MAX
&& avail_range.min < res.max)));
/* Buffer for the directive in the host character set (used when
the source character set is different). */
char hostdir[32];
if (avail_range.min == avail_range.max)
{
/* The size of the destination region is exact. */
unsigned HOST_WIDE_INT navail = avail_range.max;
if (*dir.beg != '%')
if (target_to_host (*dir.beg) != '%')
{
/* For plain character directives (i.e., the format string itself)
but not others, point the caret at the first character that's
......@@ -2339,9 +2496,9 @@ maybe_warn (substring_loc &dirloc, source_range *pargrange,
: G_("%<%.*s%> directive writing %wu bytes "
"into a region of size %wu")));
return fmtwarn (dirloc, pargrange, NULL,
info.warnopt (), fmtstr,
dir.len, dir.beg, res.min,
navail);
info.warnopt (), fmtstr, dir.len,
target_to_host (hostdir, sizeof hostdir, dir.beg),
res.min, navail);
}
if (res.min == 0 && res.max < maxbytes)
......@@ -2356,8 +2513,8 @@ maybe_warn (substring_loc &dirloc, source_range *pargrange,
: G_("%<%.*s%> directive writing up to %wu bytes "
"into a region of size %wu"));
return fmtwarn (dirloc, pargrange, NULL,
info.warnopt (), fmtstr,
dir.len, dir.beg,
info.warnopt (), fmtstr, dir.len,
target_to_host (hostdir, sizeof hostdir, dir.beg),
res.max, navail);
}
......@@ -2376,8 +2533,8 @@ maybe_warn (substring_loc &dirloc, source_range *pargrange,
: G_("%<%.*s%> directive writing likely %wu or more bytes "
"into a region of size %wu"));
return fmtwarn (dirloc, pargrange, NULL,
info.warnopt (), fmtstr,
dir.len, dir.beg,
info.warnopt (), fmtstr, dir.len,
target_to_host (hostdir, sizeof hostdir, dir.beg),
res.likely, navail);
}
......@@ -2393,10 +2550,9 @@ maybe_warn (substring_loc &dirloc, source_range *pargrange,
: G_("%<%.*s%> directive writing between %wu and "
"%wu bytes into a region of size %wu"));
return fmtwarn (dirloc, pargrange, NULL,
info.warnopt (), fmtstr,
dir.len, dir.beg,
res.min, res.max,
navail);
info.warnopt (), fmtstr, dir.len,
target_to_host (hostdir, sizeof hostdir, dir.beg),
res.min, res.max, navail);
}
const char* fmtstr
......@@ -2409,14 +2565,14 @@ maybe_warn (substring_loc &dirloc, source_range *pargrange,
: G_("%<%.*s%> directive writing %wu or more bytes "
"into a region of size %wu"));
return fmtwarn (dirloc, pargrange, NULL,
info.warnopt (), fmtstr,
dir.len, dir.beg,
info.warnopt (), fmtstr, dir.len,
target_to_host (hostdir, sizeof hostdir, dir.beg),
res.min, navail);
}
/* The size of the destination region is a range. */
if (*dir.beg != '%')
if (target_to_host (*dir.beg) != '%')
{
unsigned HOST_WIDE_INT navail = avail_range.max;
......@@ -2468,9 +2624,9 @@ maybe_warn (substring_loc &dirloc, source_range *pargrange,
"into a region of size between %wu and %wu")));
return fmtwarn (dirloc, pargrange, NULL,
info.warnopt (), fmtstr,
dir.len, dir.beg, res.min,
avail_range.min, avail_range.max);
info.warnopt (), fmtstr, dir.len,
target_to_host (hostdir, sizeof hostdir, dir.beg),
res.min, avail_range.min, avail_range.max);
}
if (res.min == 0 && res.max < maxbytes)
......@@ -2487,9 +2643,9 @@ maybe_warn (substring_loc &dirloc, source_range *pargrange,
: G_("%<%.*s%> directive writing up to %wu bytes "
"into a region of size between %wu and %wu"));
return fmtwarn (dirloc, pargrange, NULL,
info.warnopt (), fmtstr,
dir.len, dir.beg, res.max,
avail_range.min, avail_range.max);
info.warnopt (), fmtstr, dir.len,
target_to_host (hostdir, sizeof hostdir, dir.beg),
res.max, avail_range.min, avail_range.max);
}
if (res.min == 0 && maxbytes <= res.max)
......@@ -2509,9 +2665,9 @@ maybe_warn (substring_loc &dirloc, source_range *pargrange,
: G_("%<%.*s%> directive writing likely %wu or more bytes "
"into a region of size between %wu and %wu"));
return fmtwarn (dirloc, pargrange, NULL,
info.warnopt (), fmtstr,
dir.len, dir.beg, res.likely,
avail_range.min, avail_range.max);
info.warnopt (), fmtstr, dir.len,
target_to_host (hostdir, sizeof hostdir, dir.beg),
res.likely, avail_range.min, avail_range.max);
}
if (res.max < maxbytes)
......@@ -2528,10 +2684,9 @@ maybe_warn (substring_loc &dirloc, source_range *pargrange,
: G_("%<%.*s%> directive writing between %wu and "
"%wu bytes into a region of size between %wu and %wu"));
return fmtwarn (dirloc, pargrange, NULL,
info.warnopt (), fmtstr,
dir.len, dir.beg,
res.min, res.max,
avail_range.min, avail_range.max);
info.warnopt (), fmtstr, dir.len,
target_to_host (hostdir, sizeof hostdir, dir.beg),
res.min, res.max, avail_range.min, avail_range.max);
}
const char* fmtstr
......@@ -2546,10 +2701,9 @@ maybe_warn (substring_loc &dirloc, source_range *pargrange,
: G_("%<%.*s%> directive writing %wu or more bytes "
"into a region of size between %wu and %wu"));
return fmtwarn (dirloc, pargrange, NULL,
info.warnopt (), fmtstr,
dir.len, dir.beg,
res.min,
avail_range.min, avail_range.max);
info.warnopt (), fmtstr, dir.len,
target_to_host (hostdir, sizeof hostdir, dir.beg),
res.min, avail_range.min, avail_range.max);
}
/* Compute the length of the output resulting from the directive DIR
......@@ -2630,13 +2784,17 @@ format_directive (const pass_sprintf_length::call_info &info,
}
}
/* Buffer for the directive in the host character set (used when
the source character set is different). */
char hostdir[32];
int dirlen = dir.len;
if (fmtres.nullp)
{
fmtwarn (dirloc, pargrange, NULL, info.warnopt (),
"%<%.*s%> directive argument is null",
dirlen, dir.beg);
dirlen, target_to_host (hostdir, sizeof hostdir, dir.beg));
/* Don't bother processing the rest of the format string. */
res->warned = true;
......@@ -2703,7 +2861,9 @@ format_directive (const pass_sprintf_length::call_info &info,
info.warnopt (),
"%<%.*s%> directive output of %wu bytes exceeds "
"minimum required size of 4095",
dirlen, dir.beg, fmtres.range.min);
dirlen,
target_to_host (hostdir, sizeof hostdir, dir.beg),
fmtres.range.min);
else
{
const char *fmtstr
......@@ -2714,8 +2874,8 @@ format_directive (const pass_sprintf_length::call_info &info,
"bytes exceeds minimum required size of 4095"));
warned = fmtwarn (dirloc, pargrange, NULL,
info.warnopt (), fmtstr,
dirlen, dir.beg,
info.warnopt (), fmtstr, dirlen,
target_to_host (hostdir, sizeof hostdir, dir.beg),
fmtres.range.min, fmtres.range.max);
}
}
......@@ -2744,7 +2904,9 @@ format_directive (const pass_sprintf_length::call_info &info,
warned = fmtwarn (dirloc, pargrange, NULL, info.warnopt (),
"%<%.*s%> directive output of %wu bytes causes "
"result to exceed %<INT_MAX%>",
dirlen, dir.beg, fmtres.range.min);
dirlen,
target_to_host (hostdir, sizeof hostdir, dir.beg),
fmtres.range.min);
else
{
const char *fmtstr
......@@ -2754,8 +2916,8 @@ format_directive (const pass_sprintf_length::call_info &info,
: G_ ("%<%.*s%> directive output between %wu and %wu "
"bytes may cause result to exceed %<INT_MAX%>"));
warned = fmtwarn (dirloc, pargrange, NULL,
info.warnopt (), fmtstr,
dirlen, dir.beg,
info.warnopt (), fmtstr, dirlen,
target_to_host (hostdir, sizeof hostdir, dir.beg),
fmtres.range.min, fmtres.range.max);
}
}
......@@ -2847,7 +3009,7 @@ parse_directive (pass_sprintf_length::call_info &info,
directive &dir, format_result *res,
const char *str, unsigned *argno)
{
const char *pcnt = strchr (str, '%');
const char *pcnt = strchr (str, target_percent);
dir.beg = str;
if (size_t len = pcnt ? pcnt - str : *str ? strlen (str) : 1)
......@@ -2873,7 +3035,7 @@ parse_directive (pass_sprintf_length::call_info &info,
const char *pf = pcnt + 1;
/* POSIX numbered argument index or zero when none. */
unsigned dollar = 0;
HOST_WIDE_INT dollar = 0;
/* With and precision. -1 when not specified, HOST_WIDE_INT_MIN
when given by a va_list argument, and a non-negative value
......@@ -2881,6 +3043,17 @@ parse_directive (pass_sprintf_length::call_info &info,
HOST_WIDE_INT width = -1;
HOST_WIDE_INT precision = -1;
/* Pointers to the beginning of the width and precision decimal
string (if any) within the directive. */
const char *pwidth = 0;
const char *pprec = 0;
/* When the value of the decimal string that specifies width or
precision is out of range, points to the digit that causes
the value to exceed the limit. */
const char *werange = NULL;
const char *perange = NULL;
/* Width specified via the asterisk. Need not be INTEGER_CST.
For vararg functions set to void_node. */
tree star_width = NULL_TREE;
......@@ -2889,17 +3062,16 @@ parse_directive (pass_sprintf_length::call_info &info,
For vararg functions set to void_node. */
tree star_precision = NULL_TREE;
if (ISDIGIT (*pf))
if (ISDIGIT (target_to_host (*pf)))
{
/* This could be either a POSIX positional argument, the '0'
flag, or a width, depending on what follows. Store it as
width and sort it out later after the next character has
been seen. */
char *end;
width = strtol (pf, &end, 10);
pf = end;
pwidth = pf;
width = target_strtol10 (&pf, &werange);
}
else if ('*' == *pf)
else if (target_to_host (*pf) == '*')
{
/* Similarly to the block above, this could be either a POSIX
positional argument or a width, depending on what follows. */
......@@ -2910,7 +3082,7 @@ parse_directive (pass_sprintf_length::call_info &info,
++pf;
}
if (*pf == '$')
if (target_to_host (*pf) == '$')
{
/* Handle the POSIX dollar sign which references the 1-based
positional argument number. */
......@@ -2925,7 +3097,7 @@ parse_directive (pass_sprintf_length::call_info &info,
/* Bail when the numbered argument is out of range (it will
have already been diagnosed by -Wformat). */
if (dollar == 0
|| dollar == info.argidx
|| dollar == (int)info.argidx
|| dollar > gimple_call_num_args (info.callstmt))
return false;
......@@ -2959,14 +3131,14 @@ parse_directive (pass_sprintf_length::call_info &info,
the next field is the optional flags followed by an optional
width. */
for ( ; ; ) {
switch (*pf)
switch (target_to_host (*pf))
{
case ' ':
case '0':
case '+':
case '-':
case '#':
dir.set_flag (*pf++);
dir.set_flag (target_to_host (*pf++));
break;
default:
......@@ -2975,13 +3147,13 @@ parse_directive (pass_sprintf_length::call_info &info,
}
start_width:
if (ISDIGIT (*pf))
if (ISDIGIT (target_to_host (*pf)))
{
char *end;
width = strtol (pf, &end, 10);
pf = end;
werange = 0;
pwidth = pf;
width = target_strtol10 (&pf, &werange);
}
else if ('*' == *pf)
else if (target_to_host (*pf) == '*')
{
if (*argno < gimple_call_num_args (info.callstmt))
star_width = gimple_call_arg (info.callstmt, (*argno)++);
......@@ -2993,7 +3165,7 @@ parse_directive (pass_sprintf_length::call_info &info,
}
++pf;
}
else if ('\'' == *pf)
else if (target_to_host (*pf) == '\'')
{
/* The POSIX apostrophe indicating a numeric grouping
in the current locale. Even though it's possible to
......@@ -3005,17 +3177,16 @@ parse_directive (pass_sprintf_length::call_info &info,
}
start_precision:
if ('.' == *pf)
if (target_to_host (*pf) == '.')
{
++pf;
if (ISDIGIT (*pf))
if (ISDIGIT (target_to_host (*pf)))
{
char *end;
precision = strtol (pf, &end, 10);
pf = end;
pprec = pf;
precision = target_strtol10 (&pf, &perange);
}
else if ('*' == *pf)
else if (target_to_host (*pf) == '*')
{
if (*argno < gimple_call_num_args (info.callstmt))
star_precision = gimple_call_arg (info.callstmt, (*argno)++);
......@@ -3035,10 +3206,10 @@ parse_directive (pass_sprintf_length::call_info &info,
}
}
switch (*pf)
switch (target_to_host (*pf))
{
case 'h':
if (pf[1] == 'h')
if (target_to_host (pf[1]) == 'h')
{
++pf;
dir.modifier = FMT_LEN_hh;
......@@ -3059,7 +3230,7 @@ parse_directive (pass_sprintf_length::call_info &info,
break;
case 'l':
if (pf[1] == 'l')
if (target_to_host (pf[1]) == 'l')
{
++pf;
dir.modifier = FMT_LEN_ll;
......@@ -3080,7 +3251,7 @@ parse_directive (pass_sprintf_length::call_info &info,
break;
}
switch (*pf)
switch (target_to_host (*pf))
{
/* Handle a sole '%' character the same as "%%" but since it's
undefined prevent the result from being folded. */
......@@ -3141,7 +3312,14 @@ parse_directive (pass_sprintf_length::call_info &info,
return 0;
}
dir.specifier = *pf++;
dir.specifier = target_to_host (*pf++);
/* Store the length of the format directive. */
dir.len = pf - pcnt;
/* Buffer for the directive in the host character set (used when
the source character set is different). */
char hostdir[32];
if (star_width)
{
......@@ -3156,7 +3334,25 @@ parse_directive (pass_sprintf_length::call_info &info,
}
}
else
dir.set_width (width);
{
if (width == LONG_MAX && werange)
{
size_t begin = dir.beg - info.fmtstr + (pwidth - pcnt);
size_t caret = begin + (werange - pcnt);
size_t end = pf - info.fmtstr - 1;
/* Create a location for the width part of the directive,
pointing the caret at the first out-of-range digit. */
substring_loc dirloc (info.fmtloc, TREE_TYPE (info.format),
caret, begin, end);
fmtwarn (dirloc, NULL, NULL,
info.warnopt (), "%<%.*s%> directive width out of range",
dir.len, target_to_host (hostdir, sizeof hostdir, dir.beg));
}
dir.set_width (width);
}
if (star_precision)
{
......@@ -3171,7 +3367,26 @@ parse_directive (pass_sprintf_length::call_info &info,
}
}
else
dir.set_precision (precision);
{
if (precision == LONG_MAX && perange)
{
size_t begin = dir.beg - info.fmtstr + (pprec - pcnt) - 1;
size_t caret = dir.beg - info.fmtstr + (perange - pcnt) - 1;
size_t end = pf - info.fmtstr - 2;
/* Create a location for the precision part of the directive,
including the leading period, pointing the caret at the first
out-of-range digit . */
substring_loc dirloc (info.fmtloc, TREE_TYPE (info.format),
caret, begin, end);
fmtwarn (dirloc, NULL, NULL,
info.warnopt (), "%<%.*s%> directive precision out of range",
dir.len, target_to_host (hostdir, sizeof hostdir, dir.beg));
}
dir.set_precision (precision);
}
/* Extract the argument if the directive takes one and if it's
available (e.g., the function doesn't take a va_list). Treat
......@@ -3181,9 +3396,6 @@ parse_directive (pass_sprintf_length::call_info &info,
&& *argno < gimple_call_num_args (info.callstmt))
dir.arg = gimple_call_arg (info.callstmt, dollar ? dollar : (*argno)++);
/* Return the length of the format directive. */
dir.len = pf - pcnt;
if (dump_file)
{
fprintf (dump_file, " Directive %u at offset %llu: \"%.*s\"",
......@@ -3708,6 +3920,8 @@ pass_sprintf_length::handle_gimple_call (gimple_stmt_iterator *gsi)
unsigned int
pass_sprintf_length::execute (function *fun)
{
init_target_to_host_charmap ();
basic_block bb;
FOR_EACH_BB_FN (bb, fun)
{
......
2017-04-28 Martin Sebor <msebor@redhat.com>
PR tree-optimization/80523
* gcc.dg/tree-ssa/builtin-sprintf-warn-18.c: New test.
2017-04-28 Tom de Vries <tom@codesourcery.com>
PR testsuite/80557
......
/* PR tree-optimization/80523 - -Wformat-overflow doesn't consider
-fexec-charset
{ dg-do compile }
{ dg-require-iconv "IBM1047" }
{ dg-options "-O2 -Wall -Wno-format -Wformat-overflow -fexec-charset=IBM1047 -ftrack-macro-expansion=0" } */
char buf[1];
void sink (void*);
#define T(...) (__builtin_sprintf (buf + 1, __VA_ARGS__), sink (buf))
/* Exercise all special C and POSIX characters. */
void test_characters ()
{
T ("%%"); /* { dg-warning ".%%. directive writing 1 byte" } */
T ("%A", 0.0); /* { dg-warning ".%A. directive writing between 6 and 20 " } */
T ("%a", 0.0); /* { dg-warning ".%a. directive writing between 6 and 20 " } */
T ("%C", 'a'); /* { dg-warning ".%C. directive writing 1 byte" "bug 80537" { xfail *-*-* } } */
T ("%c", 'a'); /* { dg-warning ".%c. directive writing 1 byte" } */
T ("%d", 12); /* { dg-warning ".%d. directive writing 2 bytes" } */
T ("% d", 12); /* { dg-warning ".% d. directive writing 3 bytes" } */
T ("%-d", 123); /* { dg-warning ".%-d. directive writing 3 bytes" } */
T ("%+d", 1234); /* { dg-warning ".%\\+d. directive writing 5 bytes" } */
T ("%'d", 1234); /* { dg-warning ".%'d. directive writing 5 bytes" "bug 80535" { xfail *-*-* } } */
T ("%1$d", 2345); /* { dg-warning ".%1\\\$d. directive writing 4 bytes" } */
/* Verify that digits are correctly interpreted as width and precision. */
T ("%0d", 12345); /* { dg-warning ".%0d. directive writing 5 bytes" } */
T ("%1d", 12345); /* { dg-warning ".%1d. directive writing 5 bytes" } */
T ("%2d", 12345); /* { dg-warning ".%2d. directive writing 5 bytes" } */
T ("%3d", 12345); /* { dg-warning ".%3d. directive writing 5 bytes" } */
T ("%4d", 12345); /* { dg-warning ".%4d. directive writing 5 bytes" } */
T ("%5d", 12345); /* { dg-warning ".%5d. directive writing 5 bytes" } */
T ("%6d", 12345); /* { dg-warning ".%6d. directive writing 6 bytes" } */
T ("%7d", 12345); /* { dg-warning ".%7d. directive writing 7 bytes" } */
T ("%8d", 12345); /* { dg-warning ".%8d. directive writing 8 bytes" } */
T ("%9d", 12345); /* { dg-warning ".%9d. directive writing 9 bytes" } */
T ("%.0d", 12345); /* { dg-warning ".%.0d. directive writing 5 bytes" } */
T ("%.1d", 12345); /* { dg-warning ".%.1d. directive writing 5 bytes" } */
T ("%.2d", 12345); /* { dg-warning ".%.2d. directive writing 5 bytes" } */
T ("%.3d", 12345); /* { dg-warning ".%.3d. directive writing 5 bytes" } */
T ("%.4d", 12345); /* { dg-warning ".%.4d. directive writing 5 bytes" } */
T ("%.5d", 12345); /* { dg-warning ".%.5d. directive writing 5 bytes" } */
T ("%.6d", 12345); /* { dg-warning ".%.6d. directive writing 6 bytes" } */
T ("%.7d", 12345); /* { dg-warning ".%.7d. directive writing 7 bytes" } */
T ("%.8d", 12345); /* { dg-warning ".%.8d. directive writing 8 bytes" } */
T ("%.9d", 12345); /* { dg-warning ".%.9d. directive writing 9 bytes" } */
T ("%hhd", 12); /* { dg-warning ".%hhd. directive writing 2 bytes" } */
T ("%hd", 234); /* { dg-warning ".%hd. directive writing 3 bytes" } */
{
const __PTRDIFF_TYPE__ i = 3456;
T ("%jd", i); /* { dg-warning ".%jd. directive writing 4 bytes" } */
}
T ("%ld", 45678L); /* { dg-warning ".%ld. directive writing 5 bytes" } */
{
const __PTRDIFF_TYPE__ i = 56789;
T ("%td", i); /* { dg-warning ".%td. directive writing 5 bytes" } */
}
{
const __SIZE_TYPE__ i = 67890;
T ("%zd", i); /* { dg-warning ".%zd. directive writing 5 bytes" } */
}
T ("%E", 0.0); /* { dg-warning ".%E. directive writing 12 bytes" } */
T ("%e", 0.0); /* { dg-warning ".%e. directive writing 12 bytes" } */
T ("%F", 0.0); /* { dg-warning ".%F. directive writing 8 bytes" } */
T ("%f", 0.0); /* { dg-warning ".%f. directive writing 8 bytes" } */
T ("%G", 0.0); /* { dg-warning ".%G. directive writing 1 byte" } */
T ("%g", 0.0); /* { dg-warning ".%g. directive writing 1 byte" } */
T ("%i", 123); /* { dg-warning ".%i. directive writing 3 bytes" } */
{
int n;
T ("%n", &n); /* { dg-warning "writing a terminating nul" } */
T ("%nH", &n); /* { dg-warning ".H. directive writing 1 byte" } */
}
T ("%o", 999); /* { dg-warning ".%o. directive writing 4 bytes" } */
T ("%#o", 999); /* { dg-warning ".%#o. directive writing 5 bytes" } */
T ("%x", 1234); /* { dg-warning ".%x. directive writing 3 bytes" } */
T ("%#X", 1235); /* { dg-warning ".%#X. directive writing 5 bytes" } */
T ("%S", L"1"); /* { dg-warning ".%S. directive writing 1 byte" } */
T ("%-s", "1"); /* { dg-warning ".%-s. directive writing 1 byte" } */
/* Verify that characters in the source character set appear in
the text of the warning unchanged (i.e., not as their equivalents
in the execution character set on the target). The trailing %%
disables sprintf->strcpy optimization. */
T ("ABCDEFGHIJ%%"); /* { dg-warning ".ABCDEFGHIJ. directive writing 10 bytes" } */
T ("KLMNOPQRST%%"); /* { dg-warning ".KLMNOPQRST. directive writing 10 bytes" } */
T ("UVWXYZ%%"); /* { dg-warning ".UVWXYZ. directive writing 6 bytes" } */
T ("abcdefghij%%"); /* { dg-warning ".abcdefghij. directive writing 10 bytes" } */
T ("klmnopqrst%%"); /* { dg-warning ".klmnopqrst. directive writing 10 bytes" } */
T ("uvwxyz%%"); /* { dg-warning ".uvwxyz. directive writing 6 bytes" } */
}
#undef T
#define T(...) (__builtin_sprintf (d, __VA_ARGS__), sink (d))
void test_width_and_precision_out_of_range (char *d)
{
#if __LONG_MAX__ == 2147483647
# define MAX_P1_STR "2147483648"
#elif __LONG_MAX__ == 9223372036854775807
# define MAX_P1_STR "9223372036854775808"
#endif
T ("%" MAX_P1_STR "i", 0); /* { dg-warning "width out of range" } */
/* { dg-warning "result to exceed .INT_MAX. " "" { target *-*-* } .-1 } */
T ("%." MAX_P1_STR "i", 0); /* { dg-warning "precision out of range" } */
/* The following is diagnosed by -Wformat (disabled here). */
/* T ("%" MAX_P1_STR "$i", 0); */
}
/* Verify that an excessively long directive is truncated and the truncation
is indicated by three trailing dots in the text of the warning. */
void test_overlong_plain_string ()
{
static const char longfmtstr[] =
"0123456789012345678901234567890123456789012345678901234567890123456789%%";
char d[1];
T (longfmtstr); /* { dg-warning ".0123\[0-9\]\*\.\.\.. directive writing 70 bytes" } */
}
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