+2017-01-26 Martin Sebor <msebor@redhat.com>
+
+ PR middle-end/78703
+ * gimple-ssa-sprintf.c (adjust_for_width_or_precision): Change
+ to accept adjustment as an array.
+ (get_int_range): New function.
+ (struct directive): Make width and prec arrays.
+ (directive::set_width, directive::set_precision): Call get_int_range.
+ (format_integer, format_floating): Handle width and precision ranges.
+ (format_string, parse_directive): Same.
+
2017-01-26 Jakub Jelinek <jakub@redhat.com>
PR debug/79129
range.unlikely = max;
}
- /* Adjust result upward to reflect the value the specified width
- or precision is known to have. */
- fmtresult& adjust_for_width_or_precision (HOST_WIDE_INT,
+ /* Adjust result upward to reflect the RANGE of values the specified
+ width or precision is known to be in. */
+ fmtresult& adjust_for_width_or_precision (const HOST_WIDE_INT[2],
tree = NULL_TREE,
unsigned = 0, unsigned = 0);
/* Return the maximum number of decimal digits a value of TYPE
formats as on output. */
- static unsigned type_max_digits (tree type, int base);
+ static unsigned type_max_digits (tree, int);
/* The range a directive's argument is in. */
tree argmin, argmax;
bool nullp;
};
-/* Adjust result upward to reflect the value SCALAR_ADJUST the specified
- width or precision is known to have. When non-null, TYPE denotes the
- type of the directive whose result is being adjusted, BASE gives the
- base of the directive (octal, decimal, or hex), and ADJ denotes
- the additional adjustment to the LIKELY counter that may need to be
- added when SCALAR_ADJUST represents a range. */
+/* Adjust result upward to reflect the range ADJUST of values the
+ specified width or precision is known to be in. When non-null,
+ TYPE denotes the type of the directive whose result is being
+ adjusted, BASE gives the base of the directive (octal, decimal,
+ or hex), and ADJ denotes the additional adjustment to the LIKELY
+ counter that may need to be added when ADJUST is a range. */
fmtresult&
-fmtresult::adjust_for_width_or_precision (HOST_WIDE_INT scalar_adjust,
+fmtresult::adjust_for_width_or_precision (const HOST_WIDE_INT adjust[2],
tree type /* = NULL_TREE */,
unsigned base /* = 0 */,
unsigned adj /* = 0 */)
{
bool minadjusted = false;
- /* Translate SCALAR_ADJUST to a "fake" range until width and precision
- ranges are handled. */
- HOST_WIDE_INT adjust[2];
- if (scalar_adjust == HOST_WIDE_INT_MIN)
- {
- adjust[0] = -1;
- adjust[1] = target_int_max () + 1;
- }
- else
- adjust[0] = adjust[1] = scalar_adjust;
-
/* Adjust the minimum and likely counters. */
if (0 <= adjust[0])
{
return prec * 301 / 1000 + 1;
}
-/* Description of a format directive. */
+static bool
+get_int_range (tree, tree, HOST_WIDE_INT *, HOST_WIDE_INT *,
+ bool, HOST_WIDE_INT);
+
+/* Description of a format directive. A directive is either a plain
+ string or a conversion specification that starts with '%'. */
struct directive
{
/* A bitmap of flags, one for each character. */
unsigned flags[256 / sizeof (int)];
- /* The specified width, or -1 if not specified. */
- HOST_WIDE_INT width;
- /* The specified precision, or -1 if not specified. */
- HOST_WIDE_INT prec;
+ /* The range of values of the specified width, or -1 if not specified. */
+ HOST_WIDE_INT width[2];
+ /* The range of values of the specified precision, or -1 if not
+ specified. */
+ HOST_WIDE_INT prec[2];
/* Length modifier. */
format_lengths modifier;
&= ~(1U << (c % (CHAR_BIT * sizeof *flags)));
}
- /* Set the width to VAL. */
+ /* Set both bounds of the width range to VAL. */
void set_width (HOST_WIDE_INT val)
{
- width = val;
+ width[0] = width[1] = val;
}
- /* Set the width to ARG. */
+ /* Set the width range according to ARG, with both bounds being
+ no less than 0. For a constant ARG set both bounds to its value
+ or 0, whichever is greater. For a non-constant ARG in some range
+ set width to its range adjusting each bound to -1 if it's less.
+ For an indeterminate ARG set width to [0, INT_MAX]. */
void set_width (tree arg)
{
- if (tree_fits_shwi_p (arg))
- {
- width = tree_to_shwi (arg);
- if (width < 0)
- {
- if (width == HOST_WIDE_INT_MIN)
- {
- /* Avoid undefined behavior due to negating a minimum.
- This case will be diagnosed since it will result in
- more than INT_MAX bytes on output, either by the
- directive itself (when INT_MAX < HOST_WIDE_INT_MAX)
- or by the format function itself. */
- width = HOST_WIDE_INT_MAX;
- }
- else
- width = -width;
- }
- }
- else
- width = HOST_WIDE_INT_MIN;
+ get_int_range (arg, integer_type_node, width, width + 1, true, 0);
}
- /* Set the precision to val. */
+ /* Set both bounds of the precision range to VAL. */
void set_precision (HOST_WIDE_INT val)
{
- prec = val;
+ prec[0] = prec[1] = val;
}
- /* Set the precision to ARG. */
+ /* Set the precision range according to ARG, with both bounds being
+ no less than -1. For a constant ARG set both bounds to its value
+ or -1 whichever is greater. For a non-constant ARG in some range
+ set precision to its range adjusting each bound to -1 if it's less.
+ For an indeterminate ARG set precision to [-1, INT_MAX]. */
void set_precision (tree arg)
{
- if (tree_fits_shwi_p (arg))
- {
- prec = tree_to_shwi (arg);
- if (prec < 0)
- prec = -1;
- }
- else
- prec = HOST_WIDE_INT_MIN;
+ get_int_range (arg, integer_type_node, prec, prec + 1, false, -1);
}
};
}
/* Given the formatting result described by RES and NAVAIL, the number
- of available in the destination, return the number of bytes remaining
+ of available in the destination, return the range of bytes remaining
in the destination. */
static inline result_range
minus the minimum. */
range.max = res.range.min < navail ? navail - res.range.min : 0;
- /* Given the formatting result described by RES and NAVAIL, the number
- of available in the destination, return the minimum number of bytes
- remaining in the destination. */
range.likely = res.range.likely < navail ? navail - res.range.likely : 0;
if (res.range.max < HOST_WIDE_INT_MAX)
}
}
+/* Determine the range [*PMIN, *PMAX] that the expression ARG of TYPE
+ is in. Return true when the range is a subrange of that of TYPE.
+ Whn ARG is null it is as if it had the full range of TYPE.
+ When ABSOLUTE is true the range reflects the absolute value of
+ the argument. When ABSOLUTE is false, negative bounds of
+ the determined range are replaced with NEGBOUND. */
+
+static bool
+get_int_range (tree arg, tree type, HOST_WIDE_INT *pmin, HOST_WIDE_INT *pmax,
+ bool absolute, HOST_WIDE_INT negbound)
+{
+ bool knownrange = false;
+
+ if (!arg)
+ {
+ *pmin = (TYPE_UNSIGNED (type)
+ ? tree_to_uhwi (TYPE_MIN_VALUE (type))
+ : tree_to_shwi (TYPE_MIN_VALUE (type)));
+ *pmax = tree_to_uhwi (TYPE_MAX_VALUE (type));
+ }
+ else if (TREE_CODE (arg) == INTEGER_CST)
+ {
+ /* For a constant argument return its value adjusted as specified
+ by NEGATIVE and NEGBOUND and return true to indicate that the
+ result is known. */
+ *pmin = tree_fits_shwi_p (arg) ? tree_to_shwi (arg) : tree_to_uhwi (arg);
+ *pmax = *pmin;
+ knownrange = true;
+ }
+ else
+ {
+ /* True if the argument's range cannot be determined. */
+ bool unknown = true;
+
+ type = TREE_TYPE (arg);
+
+ if (TREE_CODE (arg) == SSA_NAME
+ && TREE_CODE (type) == INTEGER_TYPE)
+ {
+ /* Try to determine the range of values of the integer argument. */
+ wide_int min, max;
+ enum value_range_type range_type = get_range_info (arg, &min, &max);
+ if (range_type == VR_RANGE)
+ {
+ HOST_WIDE_INT type_min
+ = (TYPE_UNSIGNED (type)
+ ? tree_to_uhwi (TYPE_MIN_VALUE (type))
+ : tree_to_shwi (TYPE_MIN_VALUE (type)));
+
+ HOST_WIDE_INT type_max = tree_to_uhwi (TYPE_MAX_VALUE (type));
+
+ *pmin = min.to_shwi ();
+ *pmax = max.to_shwi ();
+
+ /* Return true if the adjusted range is a subrange of
+ the full range of the argument's type. */
+ knownrange = type_min < *pmin || *pmax < type_max;
+
+ unknown = false;
+ }
+ }
+
+ /* Handle an argument with an unknown range as if none had been
+ provided. */
+ if (unknown)
+ return get_int_range (NULL_TREE, type, pmin, pmax, absolute, negbound);
+ }
+
+ /* Adjust each bound as specified by ABSOLUTE and NEGBOUND. */
+ if (absolute)
+ {
+ if (*pmin < 0)
+ {
+ if (*pmin == *pmax)
+ *pmin = *pmax = -*pmin;
+ else
+ {
+ HOST_WIDE_INT tmp = -*pmin;
+ *pmin = 0;
+ if (*pmax < tmp)
+ *pmax = tmp;
+ }
+ }
+ }
+ else if (*pmin < negbound)
+ *pmin = negbound;
+
+ return knownrange;
+}
+
/* With the range [*ARGMIN, *ARGMAX] of an integer directive's actual
argument, due to the conversion from either *ARGMIN or *ARGMAX to
the type of the directive's formal argument it's possible for both
}
/* Return a range representing the minimum and maximum number of bytes
- that the directive DIR will write on output for the integer argument
- ARG when non-null. ARG may be null (for vararg functions). */
+ that the format directive DIR will output for any argument given
+ the WIDTH and PRECISION (extracted from DIR). This function is
+ used when the directive argument or its value isn't known. */
static fmtresult
format_integer (const directive &dir, tree arg)
{
/* When a constant argument has been provided use its value
rather than type to determine the length of the output. */
-
fmtresult res;
- if ((dir.prec == HOST_WIDE_INT_MIN || dir.prec == 0)
- && integer_zerop (arg))
+ if ((dir.prec[0] <= 0 && dir.prec[1] >= 0) && integer_zerop (arg))
{
/* As a special case, a precision of zero with a zero argument
results in zero bytes except in base 8 when the '#' flag is
effect). This must extend to the case of a specified precision
with an unknown value because it can be zero. */
res.range.min = ((base == 8 && dir.get_flag ('#')) || maybesign);
- if (res.range.min == 0 && dir.prec == HOST_WIDE_INT_MIN)
+ if (res.range.min == 0 && dir.prec[0] != dir.prec[1])
{
res.range.max = 1;
res.range.likely = 1;
/* Convert the argument to the type of the directive. */
arg = fold_convert (dirtype, arg);
- res.range.min = tree_digits (arg, base, dir.prec,
+ res.range.min = tree_digits (arg, base, dir.prec[0],
maybesign, maybebase);
-
- /* Set the maximum to INT_MAX when precision is specified
- but unknown (because it can be as large as that) otherwise
- to the minimum and have it adjusted below. */
- if (dir.prec == HOST_WIDE_INT_MIN)
- res.range.max = target_int_max ();
- else
+ if (dir.prec[0] == dir.prec[1])
res.range.max = res.range.min;
-
+ else
+ res.range.max = tree_digits (arg, base, dir.prec[1],
+ maybesign, maybebase);
res.range.likely = res.range.min;
}
/* Bump up the counters again if PRECision is greater still. */
res.adjust_for_width_or_precision (dir.prec, dirtype, base,
(sign | maybebase) + (base == 16));
+
return res;
}
else if (TREE_CODE (TREE_TYPE (arg)) == INTEGER_TYPE
can output. When precision may be zero, use zero as the minimum
since it results in no bytes on output (unless width is specified
to be greater than 0). */
- bool zero = dir.prec == 0 || dir.prec == HOST_WIDE_INT_MIN;
+ bool zero = dir.prec[0] <= 0 && dir.prec[1] >= 0;
argmin = build_int_cst (argtype, !zero);
int typeprec = TYPE_PRECISION (dirtype);
round-to-nearest mode. */
mpfr_t x;
mpfr_init2 (x, rfmt->p);
- mpfr_from_real (x, &rv, GMP_RNDN);
+ mpfr_from_real (x, &rv, MPFR_RNDN);
/* Return a value one greater to account for the leading minus sign. */
return 1 + get_mpfr_format_length (x, "", prec, spec, 'D');
}
/* Return a range representing the minimum and maximum number of bytes
- that the format directive DIR will output for any argument given
- the WIDTH and PRECISION (extracted from DIR). This function is
- used when the directive argument or its value isn't known. */
+ that the directive DIR will output for any argument. This function
+ is used when the directive argument or its value isn't known. */
static fmtresult
format_floating (const directive &dir)
case 'a':
{
HOST_WIDE_INT minprec = 6 + !radix /* decimal point */;
- if (dir.prec <= 0)
+ if (dir.prec[0] <= 0)
minprec = 0;
- else if (dir.prec > 0)
- minprec = dir.prec + !radix /* decimal point */;
+ else if (dir.prec[0] > 0)
+ minprec = dir.prec[0] + !radix /* decimal point */;
res.range.min = (2 /* 0x */
+ flagmin
+ minprec
+ 3 /* p+0 */);
- HOST_WIDE_INT maxprec
- = dir.prec == HOST_WIDE_INT_MIN ? target_int_max () : dir.prec;
- res.range.max = format_floating_max (type, 'a', maxprec);
+ res.range.max = format_floating_max (type, 'a', dir.prec[1]);
res.range.likely = res.range.min;
/* The unlikely maximum accounts for the longest multibyte
decimal point character. */
- if (dir.prec != 0)
- res.range.unlikely = res.range.max + target_mb_len_max () - 1;
- else
- res.range.unlikely = res.range.max;
+ res.range.unlikely = res.range.max;
+ if (dir.prec[0] != dir.prec[1]
+ || dir.prec[0] == -1 || dir.prec[0] > 0)
+ res.range.unlikely += target_mb_len_max () - 1;
break;
}
/* The minimum output is "[-+]1.234567e+00" regardless
of the value of the actual argument. */
HOST_WIDE_INT minprec = 6 + !radix /* decimal point */;
- if (dir.prec == HOST_WIDE_INT_MIN || dir.prec == 0)
+ if ((dir.prec[0] < 0 && dir.prec[1] > -1) || dir.prec[0] == 0)
minprec = 0;
- else if (dir.prec > 0)
- minprec = dir.prec + !radix /* decimal point */;
+ else if (dir.prec[0] > 0)
+ minprec = dir.prec[0] + !radix /* decimal point */;
res.range.min = (flagmin
+ radix
+ minprec
+ 2 /* e+ */ + 2);
-
/* MPFR uses a precision of 16 by default for some reason.
Set it to the C default of 6. */
- HOST_WIDE_INT maxprec
- = (dir.prec == HOST_WIDE_INT_MIN ? target_int_max ()
- : dir.prec < 0 ? 6 : dir.prec);
+ int maxprec = dir.prec[1] < 0 ? 6 : dir.prec[1];
res.range.max = format_floating_max (type, 'e', maxprec);
res.range.likely = res.range.min;
/* The unlikely maximum accounts for the longest multibyte
decimal point character. */
- if (dir.prec != 0)
+ if (dir.prec[0] != dir.prec[1]
+ || dir.prec[0] == -1 || dir.prec[0] > 0)
res.range.unlikely = res.range.max + target_mb_len_max () -1;
else
res.range.unlikely = res.range.max;
when precision is greater than zero, then the lower bound
is 2 plus precision (plus flags). */
HOST_WIDE_INT minprec = 0;
- if (dir.prec == HOST_WIDE_INT_MIN)
- minprec = 0;
- else if (dir.prec < 0)
- minprec = 6 + !radix /* decimal point */;
- else if (dir.prec)
- minprec = dir.prec + !radix /* decimal point */;
+ if (dir.prec[0] < 0)
+ minprec = dir.prec[1] < 0 ? 6 + !radix /* decimal point */ : 0;
+ else if (dir.prec[0])
+ minprec = dir.prec[0] + !radix /* decimal point */;
res.range.min = flagmin + radix + minprec;
/* Compute the upper bound for -TYPE_MAX. */
- HOST_WIDE_INT maxprec
- = dir.prec == HOST_WIDE_INT_MIN ? target_int_max () : dir.prec;
- res.range.max = format_floating_max (type, 'f', maxprec);
+ res.range.max = format_floating_max (type, 'f', dir.prec[1]);
res.range.likely = res.range.min;
/* The unlikely maximum accounts for the longest multibyte
decimal point character. */
- if (dir.prec != 0)
+ if (dir.prec[0] != dir.prec[1]
+ || dir.prec[0] == -1 || dir.prec[0] > 0)
res.range.unlikely = res.range.max + target_mb_len_max () - 1;
break;
}
the lower bound on the range of bytes (not counting flags
or width) is 1. */
res.range.min = flagmin;
-
- HOST_WIDE_INT maxprec
- = dir.prec == HOST_WIDE_INT_MIN ? target_int_max () : dir.prec;
- res.range.max = format_floating_max (type, 'g', maxprec);
+ res.range.max = format_floating_max (type, 'g', dir.prec[1]);
res.range.likely = res.range.max;
/* The unlikely maximum accounts for the longest multibyte
/* Bump up the byte counters if WIDTH is greater. */
res.adjust_for_width_or_precision (dir.width);
-
return res;
}
/* Return a range representing the minimum and maximum number of bytes
- that the conversion specification DIR will write on output for the
- floating argument ARG. */
+ that the directive DIR will write on output for the floating argument
+ ARG. */
static fmtresult
format_floating (const directive &dir, tree arg)
if (!arg || TREE_CODE (arg) != REAL_CST)
return format_floating (dir);
- HOST_WIDE_INT prec[] = { dir.prec, dir.prec };
+ HOST_WIDE_INT prec[] = { dir.prec[0], dir.prec[1] };
if (TOUPPER (dir.specifier) == 'A')
{
}
res.adjust_for_width_or_precision (dir.width);
-
return res;
}
if (dir.modifier == FMT_LEN_l)
{
- unsigned HOST_WIDE_INT val;
-
- if (arg && TREE_CODE (arg) == INTEGER_CST && tree_fits_shwi_p (arg))
- val = tree_to_shwi (arg);
- else
- val = HOST_WIDE_INT_MAX;
-
/* A wide character can result in as few as zero bytes. */
res.range.min = 0;
- if (val == 0)
- {
- /* The NUL wide character results in no bytes. */
- res.range.max = 0;
- res.range.likely = 0;
- res.range.unlikely = 0;
- }
- else if (0 < val && val < 128)
+ HOST_WIDE_INT min, max;
+ if (get_int_range (arg, integer_type_node, &min, &max, false, 0))
{
- /* A wide character in the ASCII range most likely results
- in a single byte, and only unlikely in up to MB_LEN_MAX. */
- res.range.max = 1;
- res.range.likely = 1;
- res.range.unlikely = target_mb_len_max ();
+ if (min == 0 && max == 0)
+ {
+ /* The NUL wide character results in no bytes. */
+ res.range.max = 0;
+ res.range.likely = 0;
+ res.range.unlikely = 0;
+ }
+ else if (0 < min && min < 128)
+ {
+ /* A wide character in the ASCII range most likely results
+ in a single byte, and only unlikely in up to MB_LEN_MAX. */
+ res.range.max = 1;
+ res.range.likely = 1;
+ res.range.unlikely = target_mb_len_max ();
+ }
+ else
+ {
+ /* A wide character outside the ASCII range likely results
+ in up to two bytes, and only unlikely in up to MB_LEN_MAX. */
+ res.range.max = target_mb_len_max ();
+ res.range.likely = 2;
+ res.range.unlikely = res.range.max;
+ }
}
else
{
- /* A wide character outside the ASCII range likely results
- in up to two bytes, and only unlikely in up to MB_LEN_MAX. */
+ /* An unknown wide character is treated the same as a wide
+ character outside the ASCII range. */
res.range.max = target_mb_len_max ();
res.range.likely = 2;
res.range.unlikely = res.range.max;
{
/* A plain '%c' directive. Its ouput is exactly 1. */
res.range.min = res.range.max = 1;
- res.range.likely = res.range.unlikely = res.range.min;
+ res.range.likely = res.range.unlikely = 1;
res.knownrange = true;
}
}
/* Return the minimum and maximum number of characters formatted
- by the '%c' and '%s' format directives and ther wide character
- forms for the argument ARG. ARG can be null (for functions
- such as vsprinf). */
+ by the '%s' format directive and its wide character form for
+ the argument ARG. ARG can be null (for functions such as
+ vsprinf). */
static fmtresult
format_string (const directive &dir, tree arg)
2 * wcslen (S).*/
res.range.likely = res.range.min * 2;
- /* For a wide character string, use precision as the maximum
- even if precision is greater than the string length since
- the number of bytes the string converts to may be greater
- (due to MB_CUR_MAX). */
- if (0 <= dir.prec
- && (unsigned HOST_WIDE_INT)dir.prec < res.range.max)
+ if (0 <= dir.prec[1]
+ && (unsigned HOST_WIDE_INT)dir.prec[1] < res.range.max)
{
- res.range.max = dir.prec;
- res.range.likely = dir.prec;
- res.range.unlikely = dir.prec;
+ res.range.max = dir.prec[1];
+ res.range.likely = dir.prec[1];
+ res.range.unlikely = dir.prec[1];
}
+ if (dir.prec[0] < 0 && dir.prec[1] > -1)
+ res.range.min = 0;
+ else if (0 <= dir.prec[0])
+ res.range.likely = dir.prec[0];
+
/* Even a non-empty wide character string need not convert into
any bytes. */
res.range.min = 0;
{
res.knownrange = true;
- if (dir.prec == HOST_WIDE_INT_MIN)
+ if (dir.prec[0] < 0 && dir.prec[1] > -1)
res.range.min = 0;
- else if ((unsigned HOST_WIDE_INT)dir.prec < res.range.min)
- {
- res.range.min = dir.prec;
- res.range.max = dir.prec;
- res.range.likely = dir.prec;
- res.range.unlikely = dir.prec;
- }
- else if ((unsigned HOST_WIDE_INT)dir.prec < res.range.max)
+ else if ((unsigned HOST_WIDE_INT)dir.prec[0] < res.range.min)
+ res.range.min = dir.prec[0];
+
+ if ((unsigned HOST_WIDE_INT)dir.prec[1] < res.range.max)
{
- res.range.max = dir.prec;
- res.range.likely = dir.prec;
- res.range.unlikely = dir.prec;
+ res.range.max = dir.prec[1];
+ res.range.likely = dir.prec[1];
+ res.range.unlikely = dir.prec[1];
}
}
}
in mode 2, and the maximum is PRECISION or -1 to disable
tracking. */
- if (0 <= dir.prec)
+ if (0 <= dir.prec[0])
{
if (slen.range.min >= target_int_max ())
slen.range.min = 0;
- else if ((unsigned HOST_WIDE_INT)dir.prec < slen.range.min)
+ else if ((unsigned HOST_WIDE_INT)dir.prec[0] < slen.range.min)
{
- slen.range.min = dir.prec;
+ slen.range.min = dir.prec[0];
slen.range.likely = slen.range.min;
}
- if ((unsigned HOST_WIDE_INT)dir.prec < slen.range.max
+ if ((unsigned HOST_WIDE_INT)dir.prec[1] < slen.range.max
|| slen.range.max >= target_int_max ())
{
- slen.range.max = dir.prec;
+ slen.range.max = dir.prec[1];
slen.range.likely = slen.range.max;
}
}
avail_range.min, avail_range.max);
}
-/* Compute the length of the output resulting from the conversion
- specification DIR with the argument ARG in a call described by INFO
- and update the overall result of the call in *RES. The format directive
- corresponding to DIR starts at CVTBEG and is CVTLEN characters long. */
+/* Compute the length of the output resulting from the directive DIR
+ in a call described by INFO and update the overall result of the call
+ in *RES. Return true if the directive has been handled. */
static bool
format_directive (const pass_sprintf_length::call_info &info,
format_result *res, const directive &dir)
{
- const char *cvtbeg = dir.beg;
- size_t cvtlen = dir.len;
- tree arg = dir.arg;
-
/* Offset of the beginning of the directive from the beginning
of the format string. */
- size_t offset = cvtbeg - info.fmtstr;
+ size_t offset = dir.beg - info.fmtstr;
+ size_t start = offset;
+ size_t length = offset + dir.len - !!dir.len;
/* Create a location for the whole directive from the % to the format
specifier. */
substring_loc dirloc (info.fmtloc, TREE_TYPE (info.format),
- offset, offset, offset + cvtlen - 1);
+ offset, start, length);
/* Also create a location range for the argument if possible.
This doesn't work for integer literals or function calls. */
source_range argrange;
source_range *pargrange;
- if (arg && CAN_HAVE_LOCATION_P (arg))
+ if (dir.arg && CAN_HAVE_LOCATION_P (dir.arg))
{
- argrange = EXPR_LOCATION_RANGE (arg);
+ argrange = EXPR_LOCATION_RANGE (dir.arg);
pargrange = &argrange;
}
else
if (!dir.fmtfunc || res->range.min >= HOST_WIDE_INT_MAX)
return false;
- /* Compute the (approximate) length of the formatted output. */
- fmtresult fmtres = dir.fmtfunc (dir, arg);
+ /* Compute the range of lengths of the formatted output. */
+ fmtresult fmtres = dir.fmtfunc (dir, dir.arg);
/* Record whether the output of all directives is known to be
bounded by some maximum, implying that their arguments are
}
}
- /* Has the minimum directive output length exceeded INT_MAX? */
/* Has the likely and maximum directive output exceeded INT_MAX? */
bool likelyximax = *dir.beg && res->range.likely > target_int_max ();
bool maxximax = *dir.beg && res->range.max > target_int_max ();
{
/* Width specified by a va_list takes on the range [0, -INT_MIN]
(width is the absolute value of that specified). */
- dir.width = HOST_WIDE_INT_MIN;
+ dir.width[0] = 0;
+ dir.width[1] = target_int_max () + 1;
}
}
else
{
/* Precision specified by a va_list takes on the range [-1, INT_MAX]
(unlike width, negative precision is ignored). */
- dir.prec = HOST_WIDE_INT_MIN;
+ dir.prec[0] = -1;
+ dir.prec[1] = target_int_max ();
}
}
else
dir.dirno, (size_t)(dir.beg - info.fmtstr),
(int)dir.len, dir.beg);
if (star_width)
- fprintf (dump_file, ", width = %lli", (long long)dir.width);
+ {
+ if (dir.width[0] == dir.width[1])
+ fprintf (dump_file, ", width = %lli", (long long)dir.width[0]);
+ else
+ fprintf (dump_file, ", width in range [%lli, %lli]",
+ (long long)dir.width[0], (long long)dir.width[1]);
+ }
if (star_precision)
- fprintf (dump_file, ", precision = %lli", (long long)dir.prec);
-
+ {
+ if (dir.prec[0] == dir.prec[1])
+ fprintf (dump_file, ", precision = %lli", (long long)dir.prec[0]);
+ else
+ fprintf (dump_file, ", precision in range [%lli, %lli]",
+ (long long)dir.prec[0], (long long)dir.prec[1]);
+ }
fputc ('\n', dump_file);
}
(unsigned long long)info.objsize, info.fmtstr);
}
- /* Reset the minimum and maximum bytes counters. */
+ /* Reset the minimum and maximum byte counters. */
res->range.min = res->range.max = 0;
/* No directive has been seen yet so the length of output is bounded
+2017-01-26 Martin Sebor <msebor@redhat.com>
+
+ PR middle-end/78703
+ * gcc.dg/tree-ssa/builtin-snprintf-warn-1.c: Update
+ * gcc.dg/tree-ssa/builtin-sprintf-warn-9.c: Rename...
+ * gcc.dg/tree-ssa/builtin-sprintf-warn-10.c: ...to this.
+ * gcc.dg/tree-ssa/builtin-sprintf-warn-9.c: New test.
+
2017-01-26 Jakub Jelinek <jakub@redhat.com>
PR debug/79129
typedef struct
{
char a0[0];
+ /* Separate a0 from a1 to prevent the former from being substituted
+ for the latter and causing false positives. */
+ int: 8;
char a1[1];
char a2[2];
char a3[3];
#define R(min, max) value_range (min, max)
+extern void sink (void*);
+
/* Verify that calls to snprintf whose return value is unused are
diagnosed if certain or possible truncation is detected. */
#define T(size, ...) \
- __builtin_snprintf (buffer (size), size, __VA_ARGS__)
+ __builtin_snprintf (buffer (size), size, __VA_ARGS__), sink (buffer)
void test_int_retval_unused (void)
{
void test_string_retval_unused (const Arrays *ar)
{
+ /* At level 1 strings of unknown length are assumed to be empty so
+ the following is not diagnosed. */
T (1, "%-s", ar->a0);
+ /* A one-byte array can only hold an empty string, so the following
+ isn't diagnosed. */
T (1, "%-s", ar->a1);
- T (1, "%-s", ar->a2); /* { dg-warning "output may be truncated" } */
+ /* Unlike the ar->a0 case above, at level 1, the length of an unknown
+ string that points to an array of known size is assumed to be the
+ size of the array minus 1. */
+ T (1, "%-s", ar->a2); /* { dg-warning "output may be truncated" } */
+ T (1, "%-s", ar->a3); /* { dg-warning "output may be truncated" } */
+ T (1, "%-s", ar->a4); /* { dg-warning "output may be truncated" } */
+ /* Same as the ar->a0 case above. */
+ T (1, "%-s", ar->ax);
}
T (1, "%-s", ar->a0);
T (1, "%-s", ar->a1);
T (1, "%-s", ar->a2);
+ T (1, "%-s", ar->a3);
T (1, "%-s", ar->a4);
T (1, "%-s", "123"); /* { dg-warning "output truncated" } */
}
--- /dev/null
+/* 78696 - -fprintf-return-value misoptimizes %.Ng where N is greater than 10
+ Test to verify the correctness of ranges of output computed for floating
+ point directives.
+ { dg-do compile }
+ { dg-options "-O2 -Wformat -Wformat-overflow -ftrack-macro-expansion=0" } */
+
+typedef __builtin_va_list va_list;
+
+char dst[1];
+
+extern void sink (int, void*);
+
+/* Macro to test either width or precision specified by the asterisk
+ (but not both). */
+#define T1(fmt, a) sink (__builtin_sprintf (dst + 1, fmt, a, x), dst)
+
+/* Macro to test both width and precision specified by the asterisk. */
+#define T2(fmt, w, p) sink (__builtin_sprintf (dst + 1, fmt, w, p, x), dst)
+
+/* Macro to test vsprintf with both width and precision specified by
+ the asterisk. */
+#define T(fmt) sink (__builtin_vsprintf (dst + 1, fmt, va), dst)
+
+/* Exercise %a. */
+void test_a (int w, int p, double x)
+{
+ T1 ("%.*a", 0); /* { dg-warning "between 6 and 10 bytes" } */
+ T1 ("%.*a", 1); /* { dg-warning "between 8 and 12 bytes" } */
+ T1 ("%.*a", 2); /* { dg-warning "between 9 and 13 bytes" } */
+ T1 ("%.*a", 99); /* { dg-warning "between 106 and 110 bytes" } */
+ T1 ("%.*a", 199); /* { dg-warning "between 206 and 210 bytes" } */
+ T1 ("%.*a", 1099); /* { dg-warning "between 1106 and 1110 bytes" } */
+
+ T1 ("%*.a", 0); /* { dg-warning "between 6 and 10 bytes" } */
+ T1 ("%*.a", 1); /* { dg-warning "between 6 and 10 bytes" } */
+ T1 ("%*.a", 3); /* { dg-warning "between 6 and 10 bytes" } */
+ T1 ("%*.a", 6); /* { dg-warning "between 6 and 10 bytes" } */
+ T1 ("%*.a", 7); /* { dg-warning "between 7 and 10 bytes" } */
+
+ T1 ("%*.a", w); /* { dg-warning "writing between 6 and 2147483648 bytes" } */
+ T1 ("%*.0a", w); /* { dg-warning "writing between 6 and 2147483648 bytes" } */
+ T1 ("%*.1a", w); /* { dg-warning "writing between 8 and 2147483648 bytes" } */
+ T1 ("%*.2a", w); /* { dg-warning "writing between 9 and 2147483648 bytes" } */
+
+ T1 ("%.*a", p); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
+ T1 ("%1.*a", p); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
+ T1 ("%2.*a", p); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
+ T1 ("%3.*a", p); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
+
+ T2 ("%*.*a", w, p); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
+}
+
+/* Exercise %e. */
+void test_e (int w, int p, double x)
+{
+ T1 ("%.*e", 0); /* { dg-warning "between 5 and 7 bytes" } */
+ T1 ("%.*e", 1); /* { dg-warning "between 7 and 9 bytes" } */
+ T1 ("%.*e", 2); /* { dg-warning "between 8 and 10 bytes" } */
+ T1 ("%.*e", 99); /* { dg-warning "between 105 and 107 bytes" } */
+ T1 ("%.*e", 199); /* { dg-warning "between 205 and 207 bytes" } */
+ T1 ("%.*e", 1099); /* { dg-warning "between 1105 and 1107 bytes" } */
+
+ T1 ("%*.e", 0); /* { dg-warning "between 5 and 7 bytes" } */
+ T1 ("%*.e", 1); /* { dg-warning "between 5 and 7 bytes" } */
+ T1 ("%*.e", 1); /* { dg-warning "between 5 and 7 bytes" } */
+ T1 ("%*.e", 3); /* { dg-warning "between 5 and 7 bytes" } */
+ T1 ("%*.e", 6); /* { dg-warning "between 6 and 7 bytes" } */
+ T1 ("%*.e", 7); /* { dg-warning "writing 7 bytes" } */
+
+ T1 ("%*.e", w); /* { dg-warning "writing between 5 and 2147483648 bytes" } */
+ T1 ("%*.0e", w); /* { dg-warning "writing between 5 and 2147483648 bytes" } */
+ T1 ("%*.1e", w); /* { dg-warning "writing between 7 and 2147483648 bytes" } */
+ T1 ("%*.2e", w); /* { dg-warning "writing between 8 and 2147483648 bytes" } */
+
+ T1 ("%.*e", p); /* { dg-warning "writing between 5 and 2147483655 bytes" } */
+ T1 ("%1.*e", p); /* { dg-warning "writing between 5 and 2147483655 bytes" } */
+ T1 ("%2.*e", p); /* { dg-warning "writing between 5 and 2147483655 bytes" } */
+ T1 ("%3.*e", p); /* { dg-warning "writing between 5 and 2147483655 bytes" } */
+
+ T2 ("%*.*e", w, p); /* { dg-warning "writing between 5 and 2147483655 bytes" } */
+}
+
+/* Exercise %f. */
+void test_f (int w, int p, double x)
+{
+ T1 ("%.*f", 0); /* { dg-warning "between 1 and 310 bytes" } */
+ T1 ("%.*f", 1); /* { dg-warning "between 3 and 312 bytes" } */
+ T1 ("%.*f", 2); /* { dg-warning "between 4 and 313 bytes" } */
+ T1 ("%.*f", 99); /* { dg-warning "between 101 and 410 bytes" } */
+ T1 ("%.*f", 199); /* { dg-warning "between 201 and 510 bytes" } */
+ T1 ("%.*f", 1099); /* { dg-warning "between 1101 and 1410 bytes" } */
+
+ T2 ("%*.*f", 0, 0); /* { dg-warning "between 1 and 310 bytes" } */
+ T2 ("%*.*f", 1, 0); /* { dg-warning "between 1 and 310 bytes" } */
+ T2 ("%*.*f", 2, 0); /* { dg-warning "between 2 and 310 bytes" } */
+ T2 ("%*.*f", 3, 0); /* { dg-warning "between 3 and 310 bytes" } */
+ T2 ("%*.*f", 310, 0); /* { dg-warning "writing 310 bytes" } */
+ T2 ("%*.*f", 311, 0); /* { dg-warning "writing 311 bytes" } */
+ T2 ("%*.*f", 312, 312); /* { dg-warning "between 314 and 623 bytes" } */
+ T2 ("%*.*f", 312, 313); /* { dg-warning "between 315 and 624 bytes" } */
+
+ T1 ("%*.f", w); /* { dg-warning "writing between 1 and 2147483648 bytes" } */
+ T1 ("%*.0f", w); /* { dg-warning "writing between 1 and 2147483648 bytes" } */
+ T1 ("%*.1f", w); /* { dg-warning "writing between 3 and 2147483648 bytes" } */
+ T1 ("%*.2f", w); /* { dg-warning "writing between 4 and 2147483648 bytes" } */
+
+ T1 ("%.*f", p); /* { dg-warning "writing between 1 and 2147483958 bytes" } */
+ T1 ("%1.*f", p); /* { dg-warning "writing between 1 and 2147483958 bytes" } */
+ T1 ("%2.*f", p); /* { dg-warning "writing between 2 and 2147483958 bytes" } */
+ T1 ("%3.*f", p); /* { dg-warning "writing between 3 and 2147483958 bytes" } */
+
+ T2 ("%*.*f", w, p); /* { dg-warning "writing between 1 and 2147483958 bytes" } */
+}
+
+/* Exercise %g. The expected output is the lesser of %e and %f. */
+void test_g (double x)
+{
+ T1 ("%.*g", 0); /* { dg-warning "between 1 and 7 bytes" } */
+ T1 ("%.*g", 1); /* { dg-warning "between 1 and 7 bytes" } */
+ T1 ("%.*g", 2); /* { dg-warning "between 1 and 9 bytes" } */
+ T1 ("%.*g", 99); /* { dg-warning "between 1 and 106 bytes" } */
+ T1 ("%.*g", 199); /* { dg-warning "between 1 and 206 bytes" } */
+ T1 ("%.*g", 1099); /* { dg-warning "between 1 and 310 bytes" } */
+
+ T2 ("%*.*g", 0, 0); /* { dg-warning "between 1 and 7 bytes" } */
+ T2 ("%*.*g", 1, 0); /* { dg-warning "between 1 and 7 bytes" } */
+ T2 ("%*.*g", 2, 0); /* { dg-warning "between 2 and 7 bytes" } */
+ T2 ("%*.*g", 3, 0); /* { dg-warning "between 3 and 7 bytes" } */
+ T2 ("%*.*g", 7, 0); /* { dg-warning "writing 7 bytes" } */
+ T2 ("%*.*g", 310, 0); /* { dg-warning "writing 310 bytes" } */
+ T2 ("%*.*g", 311, 0); /* { dg-warning "writing 311 bytes" } */
+ T2 ("%*.*g", 312, 312); /* { dg-warning "writing 312 bytes" } */
+ T2 ("%*.*g", 312, 313); /* { dg-warning "writing 312 bytes" } */
+ T2 ("%*.*g", 333, 999); /* { dg-warning "writing 333 bytes" } */
+}
+
+/* Exercise %a. */
+void test_a_va (va_list va)
+{
+ T ("%.0a"); /* { dg-warning "between 6 and 10 bytes" } */
+ T ("%.1a"); /* { dg-warning "between 8 and 12 bytes" } */
+ T ("%.2a"); /* { dg-warning "between 9 and 13 bytes" } */
+ T ("%.99a"); /* { dg-warning "between 106 and 110 bytes" } */
+ T ("%.199a"); /* { dg-warning "between 206 and 210 bytes" } */
+ T ("%.1099a"); /* { dg-warning "between 1106 and 1110 bytes" } */
+
+ T ("%0.a"); /* { dg-warning "between 6 and 10 bytes" } */
+ T ("%1.a"); /* { dg-warning "between 6 and 10 bytes" } */
+ T ("%3.a"); /* { dg-warning "between 6 and 10 bytes" } */
+ T ("%6.a"); /* { dg-warning "between 6 and 10 bytes" } */
+ T ("%7.a"); /* { dg-warning "between 7 and 10 bytes" } */
+
+ T ("%*.a"); /* { dg-warning "writing between 6 and 2147483648 bytes" } */
+ T ("%*.0a"); /* { dg-warning "writing between 6 and 2147483648 bytes" } */
+ T ("%*.1a"); /* { dg-warning "writing between 8 and 2147483648 bytes" } */
+ T ("%*.2a"); /* { dg-warning "writing between 9 and 2147483648 bytes" } */
+
+ T ("%.*a"); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
+ T ("%1.*a"); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
+ T ("%2.*a"); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
+ T ("%6.*a"); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
+ T ("%9.*a"); /* { dg-warning "writing between 9 and 2147483658 bytes" } */
+
+ T ("%*.*a"); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
+}
+
+/* Exercise %e. */
+void test_e_va (va_list va)
+{
+ T ("%e"); /* { dg-warning "between 12 and 14 bytes" } */
+ T ("%+e"); /* { dg-warning "between 13 and 14 bytes" } */
+ T ("% e"); /* { dg-warning "between 13 and 14 bytes" } */
+ T ("%#e"); /* { dg-warning "between 12 and 14 bytes" } */
+ T ("%#+e"); /* { dg-warning "between 13 and 14 bytes" } */
+ T ("%# e"); /* { dg-warning "between 13 and 14 bytes" } */
+
+ T ("%.e"); /* { dg-warning "between 5 and 7 bytes" } */
+ T ("%.0e"); /* { dg-warning "between 5 and 7 bytes" } */
+ T ("%.1e"); /* { dg-warning "between 7 and 9 bytes" } */
+ T ("%.2e"); /* { dg-warning "between 8 and 10 bytes" } */
+ T ("%.99e"); /* { dg-warning "between 105 and 107 bytes" } */
+ T ("%.199e"); /* { dg-warning "between 205 and 207 bytes" } */
+ T ("%.1099e"); /* { dg-warning "between 1105 and 1107 bytes" } */
+
+ T ("%0.e"); /* { dg-warning "between 5 and 7 bytes" } */
+ T ("%1.e"); /* { dg-warning "between 5 and 7 bytes" } */
+ T ("%1.e"); /* { dg-warning "between 5 and 7 bytes" } */
+ T ("%3.e"); /* { dg-warning "between 5 and 7 bytes" } */
+ T ("%6.e"); /* { dg-warning "between 6 and 7 bytes" } */
+ T ("%7.e"); /* { dg-warning "writing 7 bytes" } */
+
+ T ("%.*e"); /* { dg-warning "writing between 5 and 2147483655 bytes" } */
+ T ("%1.*e"); /* { dg-warning "writing between 5 and 2147483655 bytes" } */
+ T ("%6.*e"); /* { dg-warning "writing between 6 and 2147483655 bytes" } */
+ T ("%9.*e"); /* { dg-warning "writing between 9 and 2147483655 bytes" } */
+
+ T ("%*.*e"); /* { dg-warning "writing between 5 and 2147483655 bytes" } */
+}
+
+/* Exercise %f. */
+void test_f_va (va_list va)
+{
+ T ("%f"); /* { dg-warning "between 8 and 317 bytes" } */
+ T ("%+f"); /* { dg-warning "between 9 and 317 bytes" } */
+ T ("% f"); /* { dg-warning "between 9 and 317 bytes" } */
+ T ("%#f"); /* { dg-warning "between 8 and 317 bytes" } */
+ T ("%+f"); /* { dg-warning "between 9 and 317 bytes" } */
+ T ("% f"); /* { dg-warning "between 9 and 317 bytes" } */
+ T ("%#+f"); /* { dg-warning "between 9 and 317 bytes" } */
+ T ("%# f"); /* { dg-warning "between 9 and 317 bytes" } */
+
+ T ("%.f"); /* { dg-warning "between 1 and 310 bytes" } */
+ T ("%.0f"); /* { dg-warning "between 1 and 310 bytes" } */
+ T ("%.1f"); /* { dg-warning "between 3 and 312 bytes" } */
+ T ("%.2f"); /* { dg-warning "between 4 and 313 bytes" } */
+ T ("%.99f"); /* { dg-warning "between 101 and 410 bytes" } */
+ T ("%.199f"); /* { dg-warning "between 201 and 510 bytes" } */
+ T ("%.1099f"); /* { dg-warning "between 1101 and 1410 bytes" } */
+
+ T ("%0.0f"); /* { dg-warning "between 1 and 310 bytes" } */
+ T ("%1.0f"); /* { dg-warning "between 1 and 310 bytes" } */
+ T ("%2.0f"); /* { dg-warning "between 2 and 310 bytes" } */
+ T ("%3.0f"); /* { dg-warning "between 3 and 310 bytes" } */
+ T ("%310.0f"); /* { dg-warning "writing 310 bytes" } */
+ T ("%311.0f"); /* { dg-warning "writing 311 bytes" } */
+ T ("%312.312f"); /* { dg-warning "between 314 and 623 bytes" } */
+ T ("%312.313f"); /* { dg-warning "between 315 and 624 bytes" } */
+
+ T ("%.*f"); /* { dg-warning "writing between 1 and 2147483958 bytes" } */
+ T ("%1.*f"); /* { dg-warning "writing between 1 and 2147483958 bytes" } */
+ T ("%3.*f"); /* { dg-warning "writing between 3 and 2147483958 bytes" } */
+
+ T ("%*.*f"); /* { dg-warning "writing between 1 and 2147483958 bytes" } */
+}
+
+/* Exercise %g. The expected output is the lesser of %e and %f. */
+void test_g_va (va_list va)
+{
+ T ("%g"); /* { dg-warning "between 1 and 13 bytes" } */
+ T ("%+g"); /* { dg-warning "between 2 and 13 bytes" } */
+ T ("% g"); /* { dg-warning "between 2 and 13 bytes" } */
+ T ("%#g"); /* { dg-warning "between 1 and 13 bytes" } */
+ T ("%#+g"); /* { dg-warning "between 2 and 13 bytes" } */
+ T ("%# g"); /* { dg-warning "between 2 and 13 bytes" } */
+
+ T ("%.g"); /* { dg-warning "between 1 and 7 bytes" } */
+ T ("%.0g"); /* { dg-warning "between 1 and 7 bytes" } */
+ T ("%.1g"); /* { dg-warning "between 1 and 7 bytes" } */
+ T ("%.2g"); /* { dg-warning "between 1 and 9 bytes" } */
+ T ("%.99g"); /* { dg-warning "between 1 and 106 bytes" } */
+ T ("%.199g"); /* { dg-warning "between 1 and 206 bytes" } */
+ T ("%.1099g"); /* { dg-warning "between 1 and 310 bytes" } */
+
+ T ("%0.0g"); /* { dg-warning "between 1 and 7 bytes" } */
+ T ("%1.0g"); /* { dg-warning "between 1 and 7 bytes" } */
+ T ("%2.0g"); /* { dg-warning "between 2 and 7 bytes" } */
+ T ("%3.0g"); /* { dg-warning "between 3 and 7 bytes" } */
+ T ("%7.0g"); /* { dg-warning "writing 7 bytes" } */
+ T ("%310.0g"); /* { dg-warning "writing 310 bytes" } */
+ T ("%311.0g"); /* { dg-warning "writing 311 bytes" } */
+ T ("%312.312g"); /* { dg-warning "writing 312 bytes" } */
+ T ("%312.313g"); /* { dg-warning "writing 312 bytes" } */
+ T ("%333.999g"); /* { dg-warning "writing 333 bytes" } */
+
+ T ("%.*g"); /* { dg-warning "writing between 1 and 310 bytes" } */
+ T ("%1.*g"); /* { dg-warning "writing between 1 and 310 bytes" } */
+ T ("%4.*g"); /* { dg-warning "writing between 4 and 310 bytes" } */
+
+ T ("%*.*g"); /* { dg-warning "writing between 1 and 2147483648 bytes" } */
+}
-/* 78696 - -fprintf-return-value misoptimizes %.Ng where N is greater than 10
- Test to verify the correctness of ranges of output computed for floating
- point directives.
- { dg-do compile }
- { dg-options "-O2 -Wformat -Wformat-overflow -ftrack-macro-expansion=0" } */
+/* { dg-do compile } */
+/* { dg-options "-O2 -Wformat -Wformat-overflow=2 -ftrack-macro-expansion=0" } */
-typedef __builtin_va_list va_list;
+typedef __SIZE_TYPE__ size_t;
-char dst[1];
+#define INT_MAX __INT_MAX__
+#define INT_MIN (-INT_MAX - 1)
-extern void sink (int, void*);
+#ifndef LINE
+# define LINE 0
+#endif
-/* Macro to test either width or precision specified by the asterisk
- (but not both). */
-#define T1(fmt, a) sink (__builtin_sprintf (dst + 1, fmt, a, x), dst)
+int dummy_sprintf (char*, const char*, ...);
+void sink (void*);
-/* Macro to test both width and precision specified by the asterisk. */
-#define T2(fmt, w, p) sink (__builtin_sprintf (dst + 1, fmt, w, p, x), dst)
+char buffer[4096];
+char *ptr;
-/* Macro to test vsprintf with both width and precision specified by
- the asterisk. */
-#define T(fmt) sink (__builtin_vsprintf (dst + 1, fmt, va), dst)
+/* Helper to expand function to either __builtin_f or dummy_f to
+ make debugging GCC easy. */
+#define FUNC(f) \
+ ((!LINE || LINE == __LINE__) ? __builtin_ ## f : dummy_ ## f)
-/* Exercise %a. */
-void test_a (int w, int p, double x)
-{
- T1 ("%.*a", 0); /* { dg-warning "between 6 and 10 bytes" } */
- T1 ("%.*a", 1); /* { dg-warning "between 8 and 12 bytes" } */
- T1 ("%.*a", 2); /* { dg-warning "between 9 and 13 bytes" } */
- T1 ("%.*a", 99); /* { dg-warning "between 106 and 110 bytes" } */
- T1 ("%.*a", 199); /* { dg-warning "between 206 and 210 bytes" } */
- T1 ("%.*a", 1099); /* { dg-warning "between 1106 and 1110 bytes" } */
-
- T1 ("%*.a", 0); /* { dg-warning "between 6 and 10 bytes" } */
- T1 ("%*.a", 1); /* { dg-warning "between 6 and 10 bytes" } */
- T1 ("%*.a", 3); /* { dg-warning "between 6 and 10 bytes" } */
- T1 ("%*.a", 6); /* { dg-warning "between 6 and 10 bytes" } */
- T1 ("%*.a", 7); /* { dg-warning "between 7 and 10 bytes" } */
-
- T1 ("%*.a", w); /* { dg-warning "writing between 6 and 2147483648 bytes" } */
- T1 ("%*.0a", w); /* { dg-warning "writing between 6 and 2147483648 bytes" } */
- T1 ("%*.1a", w); /* { dg-warning "writing between 8 and 2147483648 bytes" } */
- T1 ("%*.2a", w); /* { dg-warning "writing between 9 and 2147483648 bytes" } */
-
- T1 ("%.*a", p); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
- T1 ("%1.*a", p); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
- T1 ("%2.*a", p); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
- T1 ("%3.*a", p); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
-
- T2 ("%*.*a", w, p); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
-}
+/* Evaluate to an array of SIZE characters when non-negative, or to
+ a pointer to an unknown object otherwise. */
+#define buffer(size) \
+ ((0 <= size) ? buffer + sizeof buffer - (size) : ptr)
-/* Exercise %e. */
-void test_e (int w, int p, double x)
-{
- T1 ("%.*e", 0); /* { dg-warning "between 5 and 7 bytes" } */
- T1 ("%.*e", 1); /* { dg-warning "between 7 and 9 bytes" } */
- T1 ("%.*e", 2); /* { dg-warning "between 8 and 10 bytes" } */
- T1 ("%.*e", 99); /* { dg-warning "between 105 and 107 bytes" } */
- T1 ("%.*e", 199); /* { dg-warning "between 205 and 207 bytes" } */
- T1 ("%.*e", 1099); /* { dg-warning "between 1105 and 1107 bytes" } */
-
- T1 ("%*.e", 0); /* { dg-warning "between 5 and 7 bytes" } */
- T1 ("%*.e", 1); /* { dg-warning "between 5 and 7 bytes" } */
- T1 ("%*.e", 1); /* { dg-warning "between 5 and 7 bytes" } */
- T1 ("%*.e", 3); /* { dg-warning "between 5 and 7 bytes" } */
- T1 ("%*.e", 6); /* { dg-warning "between 6 and 7 bytes" } */
- T1 ("%*.e", 7); /* { dg-warning "writing 7 bytes" } */
-
- T1 ("%*.e", w); /* { dg-warning "writing between 5 and 2147483648 bytes" } */
- T1 ("%*.0e", w); /* { dg-warning "writing between 5 and 2147483648 bytes" } */
- T1 ("%*.1e", w); /* { dg-warning "writing between 7 and 2147483648 bytes" } */
- T1 ("%*.2e", w); /* { dg-warning "writing between 8 and 2147483648 bytes" } */
-
- T1 ("%.*e", p); /* { dg-warning "writing between 5 and 2147483655 bytes" } */
- T1 ("%1.*e", p); /* { dg-warning "writing between 5 and 2147483655 bytes" } */
- T1 ("%2.*e", p); /* { dg-warning "writing between 5 and 2147483655 bytes" } */
- T1 ("%3.*e", p); /* { dg-warning "writing between 5 and 2147483655 bytes" } */
-
- T2 ("%*.*e", w, p); /* { dg-warning "writing between 5 and 2147483655 bytes" } */
-}
+#define T(bufsize, fmt, ...) \
+ do { \
+ char *buf = buffer (bufsize); \
+ FUNC (sprintf)(buf, fmt, __VA_ARGS__); \
+ sink (buf); \
+ } while (0)
-/* Exercise %f. */
-void test_f (int w, int p, double x)
-{
- T1 ("%.*f", 0); /* { dg-warning "between 1 and 310 bytes" } */
- T1 ("%.*f", 1); /* { dg-warning "between 3 and 312 bytes" } */
- T1 ("%.*f", 2); /* { dg-warning "between 4 and 313 bytes" } */
- T1 ("%.*f", 99); /* { dg-warning "between 101 and 410 bytes" } */
- T1 ("%.*f", 199); /* { dg-warning "between 201 and 510 bytes" } */
- T1 ("%.*f", 1099); /* { dg-warning "between 1101 and 1410 bytes" } */
-
- T2 ("%*.*f", 0, 0); /* { dg-warning "between 1 and 310 bytes" } */
- T2 ("%*.*f", 1, 0); /* { dg-warning "between 1 and 310 bytes" } */
- T2 ("%*.*f", 2, 0); /* { dg-warning "between 2 and 310 bytes" } */
- T2 ("%*.*f", 3, 0); /* { dg-warning "between 3 and 310 bytes" } */
- T2 ("%*.*f", 310, 0); /* { dg-warning "writing 310 bytes" } */
- T2 ("%*.*f", 311, 0); /* { dg-warning "writing 311 bytes" } */
- T2 ("%*.*f", 312, 312); /* { dg-warning "between 314 and 623 bytes" } */
- T2 ("%*.*f", 312, 313); /* { dg-warning "between 315 and 624 bytes" } */
-
- T1 ("%*.f", w); /* { dg-warning "writing between 1 and 2147483648 bytes" } */
- T1 ("%*.0f", w); /* { dg-warning "writing between 1 and 2147483648 bytes" } */
- T1 ("%*.1f", w); /* { dg-warning "writing between 3 and 2147483648 bytes" } */
- T1 ("%*.2f", w); /* { dg-warning "writing between 4 and 2147483648 bytes" } */
-
- T1 ("%.*f", p); /* { dg-warning "writing between 1 and 2147483958 bytes" } */
- T1 ("%1.*f", p); /* { dg-warning "writing between 1 and 2147483958 bytes" } */
- T1 ("%2.*f", p); /* { dg-warning "writing between 2 and 2147483958 bytes" } */
- T1 ("%3.*f", p); /* { dg-warning "writing between 3 and 2147483958 bytes" } */
-
- T2 ("%*.*f", w, p); /* { dg-warning "writing between 1 and 2147483958 bytes" } */
-}
-/* Exercise %g. The expected output is the lesser of %e and %f. */
-void test_g (double x)
-{
- T1 ("%.*g", 0); /* { dg-warning "between 1 and 7 bytes" } */
- T1 ("%.*g", 1); /* { dg-warning "between 1 and 7 bytes" } */
- T1 ("%.*g", 2); /* { dg-warning "between 1 and 9 bytes" } */
- T1 ("%.*g", 99); /* { dg-warning "between 1 and 106 bytes" } */
- T1 ("%.*g", 199); /* { dg-warning "between 1 and 206 bytes" } */
- T1 ("%.*g", 1099); /* { dg-warning "between 1 and 310 bytes" } */
-
- T2 ("%*.*g", 0, 0); /* { dg-warning "between 1 and 7 bytes" } */
- T2 ("%*.*g", 1, 0); /* { dg-warning "between 1 and 7 bytes" } */
- T2 ("%*.*g", 2, 0); /* { dg-warning "between 2 and 7 bytes" } */
- T2 ("%*.*g", 3, 0); /* { dg-warning "between 3 and 7 bytes" } */
- T2 ("%*.*g", 7, 0); /* { dg-warning "writing 7 bytes" } */
- T2 ("%*.*g", 310, 0); /* { dg-warning "writing 310 bytes" } */
- T2 ("%*.*g", 311, 0); /* { dg-warning "writing 311 bytes" } */
- T2 ("%*.*g", 312, 312); /* { dg-warning "writing 312 bytes" } */
- T2 ("%*.*g", 312, 313); /* { dg-warning "writing 312 bytes" } */
- T2 ("%*.*g", 333, 999); /* { dg-warning "writing 333 bytes" } */
-}
+/* Identity function to verify that the checker figures out the value
+ of the operand even when it's not constant (i.e., makes use of
+ inlining and constant propagation information). */
-/* Exercise %a. */
-void test_a_va (va_list va)
-{
- T ("%.0a"); /* { dg-warning "between 6 and 10 bytes" } */
- T ("%.1a"); /* { dg-warning "between 8 and 12 bytes" } */
- T ("%.2a"); /* { dg-warning "between 9 and 13 bytes" } */
- T ("%.99a"); /* { dg-warning "between 106 and 110 bytes" } */
- T ("%.199a"); /* { dg-warning "between 206 and 210 bytes" } */
- T ("%.1099a"); /* { dg-warning "between 1106 and 1110 bytes" } */
-
- T ("%0.a"); /* { dg-warning "between 6 and 10 bytes" } */
- T ("%1.a"); /* { dg-warning "between 6 and 10 bytes" } */
- T ("%3.a"); /* { dg-warning "between 6 and 10 bytes" } */
- T ("%6.a"); /* { dg-warning "between 6 and 10 bytes" } */
- T ("%7.a"); /* { dg-warning "between 7 and 10 bytes" } */
-
- T ("%*.a"); /* { dg-warning "writing between 6 and 2147483648 bytes" } */
- T ("%*.0a"); /* { dg-warning "writing between 6 and 2147483648 bytes" } */
- T ("%*.1a"); /* { dg-warning "writing between 8 and 2147483648 bytes" } */
- T ("%*.2a"); /* { dg-warning "writing between 9 and 2147483648 bytes" } */
-
- T ("%.*a"); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
- T ("%1.*a"); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
- T ("%2.*a"); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
- T ("%6.*a"); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
- T ("%9.*a"); /* { dg-warning "writing between 9 and 2147483658 bytes" } */
-
- T ("%*.*a"); /* { dg-warning "writing between 6 and 2147483658 bytes" } */
-}
+int i (int x) { return x; }
+const char* s (const char *str) { return str; }
+
+/* Function to "generate" a unique unknown number (as far as GCC can
+ tell) each time it's called. It prevents the optimizer from being
+ able to narrow down the ranges of possible values in test functions
+ with repeated references to the same variable. */
+extern int value (void);
-/* Exercise %e. */
-void test_e_va (va_list va)
+/* Return a value in the range [MIN, MAX]. */
+int range (int min, int max)
{
- T ("%e"); /* { dg-warning "between 12 and 14 bytes" } */
- T ("%+e"); /* { dg-warning "between 13 and 14 bytes" } */
- T ("% e"); /* { dg-warning "between 13 and 14 bytes" } */
- T ("%#e"); /* { dg-warning "between 12 and 14 bytes" } */
- T ("%#+e"); /* { dg-warning "between 13 and 14 bytes" } */
- T ("%# e"); /* { dg-warning "between 13 and 14 bytes" } */
-
- T ("%.e"); /* { dg-warning "between 5 and 7 bytes" } */
- T ("%.0e"); /* { dg-warning "between 5 and 7 bytes" } */
- T ("%.1e"); /* { dg-warning "between 7 and 9 bytes" } */
- T ("%.2e"); /* { dg-warning "between 8 and 10 bytes" } */
- T ("%.99e"); /* { dg-warning "between 105 and 107 bytes" } */
- T ("%.199e"); /* { dg-warning "between 205 and 207 bytes" } */
- T ("%.1099e"); /* { dg-warning "between 1105 and 1107 bytes" } */
-
- T ("%0.e"); /* { dg-warning "between 5 and 7 bytes" } */
- T ("%1.e"); /* { dg-warning "between 5 and 7 bytes" } */
- T ("%1.e"); /* { dg-warning "between 5 and 7 bytes" } */
- T ("%3.e"); /* { dg-warning "between 5 and 7 bytes" } */
- T ("%6.e"); /* { dg-warning "between 6 and 7 bytes" } */
- T ("%7.e"); /* { dg-warning "writing 7 bytes" } */
-
- T ("%.*e"); /* { dg-warning "writing between 5 and 2147483655 bytes" } */
- T ("%1.*e"); /* { dg-warning "writing between 5 and 2147483655 bytes" } */
- T ("%6.*e"); /* { dg-warning "writing between 6 and 2147483655 bytes" } */
- T ("%9.*e"); /* { dg-warning "writing between 9 and 2147483655 bytes" } */
-
- T ("%*.*e"); /* { dg-warning "writing between 5 and 2147483655 bytes" } */
+ int val = value ();
+ return val < min || max < val ? min : val;
}
-/* Exercise %f. */
-void test_f_va (va_list va)
+#define R(min, max) range (min, max)
+
+/* Verify that the checker can detect buffer overflow when the "%s"
+ argument is in a known range of lengths and one or both of which
+ exceed the size of the destination. */
+
+void test_sprintf_chk_string (const char *s)
{
- T ("%f"); /* { dg-warning "between 8 and 317 bytes" } */
- T ("%+f"); /* { dg-warning "between 9 and 317 bytes" } */
- T ("% f"); /* { dg-warning "between 9 and 317 bytes" } */
- T ("%#f"); /* { dg-warning "between 8 and 317 bytes" } */
- T ("%+f"); /* { dg-warning "between 9 and 317 bytes" } */
- T ("% f"); /* { dg-warning "between 9 and 317 bytes" } */
- T ("%#+f"); /* { dg-warning "between 9 and 317 bytes" } */
- T ("%# f"); /* { dg-warning "between 9 and 317 bytes" } */
-
- T ("%.f"); /* { dg-warning "between 1 and 310 bytes" } */
- T ("%.0f"); /* { dg-warning "between 1 and 310 bytes" } */
- T ("%.1f"); /* { dg-warning "between 3 and 312 bytes" } */
- T ("%.2f"); /* { dg-warning "between 4 and 313 bytes" } */
- T ("%.99f"); /* { dg-warning "between 101 and 410 bytes" } */
- T ("%.199f"); /* { dg-warning "between 201 and 510 bytes" } */
- T ("%.1099f"); /* { dg-warning "between 1101 and 1410 bytes" } */
-
- T ("%0.0f"); /* { dg-warning "between 1 and 310 bytes" } */
- T ("%1.0f"); /* { dg-warning "between 1 and 310 bytes" } */
- T ("%2.0f"); /* { dg-warning "between 2 and 310 bytes" } */
- T ("%3.0f"); /* { dg-warning "between 3 and 310 bytes" } */
- T ("%310.0f"); /* { dg-warning "writing 310 bytes" } */
- T ("%311.0f"); /* { dg-warning "writing 311 bytes" } */
- T ("%312.312f"); /* { dg-warning "between 314 and 623 bytes" } */
- T ("%312.313f"); /* { dg-warning "between 315 and 624 bytes" } */
-
- T ("%.*f"); /* { dg-warning "writing between 1 and 2147483958 bytes" } */
- T ("%1.*f"); /* { dg-warning "writing between 1 and 2147483958 bytes" } */
- T ("%3.*f"); /* { dg-warning "writing between 3 and 2147483958 bytes" } */
-
- T ("%*.*f"); /* { dg-warning "writing between 1 and 2147483958 bytes" } */
+ T (1, "%*s", R (0, 1), ""); /* { dg-warning "may write a terminating nul" } */
+ T (1, "%*s", R (-2, -1), ""); /* { dg-warning "writing up to 2 bytes" } */
+ T (1, "%*s", R (-3, 2), ""); /* { dg-warning "writing up to 3 bytes" } */
+ T (1, "%*s", R (-4, 5), ""); /* { dg-warning "writing up to 5 bytes" } */
+
+ T (1, "%*s", R ( -5, 6), "1"); /* { dg-warning "writing between 1 and 6 bytes" } */
+ T (1, "%*s", R ( -6, 7), "12"); /* { dg-warning "writing between 2 and 7 bytes" } */
+
+ T (1, "%.*s", R (0, 1), "");
+ T (1, "%.*s", R (0, 1), s); /* { dg-warning "may write a terminating nul" } */
+ T (1, "%.*s", R (-2, -1), "");
+ T (1, "%.*s", R (-2, -1), s); /* { dg-warning "may write a terminating nul" } */
+ T (1, "%.*s", R (-3, 2), "");
+ T (1, "%.*s", R (-4, 5), "");
+
+ T (1, "%.*s", R ( -5, 6), "1"); /* { dg-warning "may write a terminating nul" } */
+ T (1, "%.*s", R ( -6, 7), "12"); /* { dg-warning "writing up to 2 bytes " } */
+ T (1, "%.*s", R ( 1, 7), "12"); /* { dg-warning "writing between 1 and 2 bytes " } */
+ T (1, "%.*s", R ( 2, 7), "12"); /* { dg-warning "writing 2 bytes " } */
+
+ T (1, "%*.*s", R (0, 1), R (0, 1), ""); /* { dg-warning "may write a terminating nul" } */
+ T (1, "%*.*s", R (0, 2), R (0, 1), ""); /* { dg-warning "directive writing up to 2 bytes into a region of size 1" } */
+ T (1, "%*.*s", R (0, 3), R (0, 1), ""); /* { dg-warning "writing up to 3 bytes" } */
+
+ T (1, "%*.*s", R (0, 1), R (0, 1), "1"); /* { dg-warning "may write a terminating nul" } */
+ T (1, "%*.*s", R (0, 2), R (0, 1), "1"); /* { dg-warning "writing up to 2 bytes" } */
+ T (1, "%*.*s", R (0, 3), R (0, 1), "1"); /* { dg-warning "writing up to 3 bytes" } */
+
+ T (1, "%*.*s", R (0, 1), R (0, 1), "12"); /* { dg-warning "may write a terminating nul" } */
+ T (1, "%*.*s", R (0, 2), R (0, 1), "12"); /* { dg-warning "writing up to 2 bytes" } */
+ T (1, "%*.*s", R (0, 3), R (0, 1), "12"); /* { dg-warning "writing up to 3 bytes" } */
+
+ T (1, "%*.*s", R (0, 1), R (0, 1), "123"); /* { dg-warning "may write a terminating nul" } */
+ T (1, "%*.*s", R (0, 2), R (0, 1), "123"); /* { dg-warning "writing up to 2 bytes" } */
+ T (1, "%*.*s", R (0, 3), R (0, 1), "123"); /* { dg-warning "writing up to 3 bytes" } */
+ T (1, "%*.*s", R (0, 3), R (0, 1), s); /* { dg-warning "writing up to 3 bytes" } */
+
+ T (1, "%*.*s", R (0, 1), R (0, 2), "123"); /* { dg-warning "writing up to 2 bytes" } */
+ T (1, "%*.*s", R (0, 2), R (0, 2), "123"); /* { dg-warning "writing up to 2 bytes" } */
+ T (1, "%*.*s", R (0, 3), R (0, 2), "123"); /* { dg-warning "writing up to 3 bytes" } */
+ T (1, "%*.*s", R (0, 3), R (0, 2), s); /* { dg-warning "writing up to 3 bytes" } */
+
+ T (1, "%*.*s", R (0, 1), R (0, 3), "123"); /* { dg-warning "writing up to 3 bytes" } */
+ T (1, "%*.*s", R (0, 2), R (0, 3), "123"); /* { dg-warning "writing up to 3 bytes" } */
+ T (1, "%*.*s", R (0, 3), R (0, 3), "123"); /* { dg-warning "writing up to 3 bytes" } */
+ T (1, "%*.*s", R (0, 3), R (0, 3), s); /* { dg-warning "writing up to 3 bytes" } */
+
+ T (1, "%*.*s", R (1, 1), R (0, 3), "123"); /* { dg-warning "writing between 1 and 3 bytes" } */
+ T (1, "%*.*s", R (1, 2), R (0, 3), "123"); /* { dg-warning "writing between 1 and 3 bytes" } */
+ T (1, "%*.*s", R (1, 3), R (0, 3), "123"); /* { dg-warning "writing between 1 and 3 bytes" } */
+ T (1, "%*.*s", R (1, 3), R (0, 3), s); /* { dg-warning "writing between 1 and 3 bytes" } */
+
+ T (1, "%*.*s", R (1, 1), R (1, 3), "123"); /* { dg-warning "writing between 1 and 3 bytes" } */
+ T (1, "%*.*s", R (1, 2), R (1, 3), "123"); /* { dg-warning "writing between 1 and 3 bytes" } */
+ T (1, "%*.*s", R (1, 3), R (1, 3), "123"); /* { dg-warning "writing between 1 and 3 bytes" } */
+ T (1, "%*.*s", R (1, 3), R (1, 3), s); /* { dg-warning "writing between 1 and 3 bytes" } */
+
+ T (1, "%*.*s", R (2, 3), R (1, 3), "123"); /* { dg-warning "writing between 2 and 3 bytes" } */
+ T (1, "%*.*s", R (3, 4), R (1, 3), "123"); /* { dg-warning "writing between 3 and 4 bytes" } */
+ T (1, "%*.*s", R (4, 5), R (1, 3), "123"); /* { dg-warning "writing between 4 and 5 bytes" } */
+ T (1, "%*.*s", R (2, 3), R (1, 3), s); /* { dg-warning "writing between 2 and 3 bytes" } */
}
-/* Exercise %g. The expected output is the lesser of %e and %f. */
-void test_g_va (va_list va)
+void test_sprintf_chk_int (int w, int p, int i)
{
- T ("%g"); /* { dg-warning "between 1 and 13 bytes" } */
- T ("%+g"); /* { dg-warning "between 2 and 13 bytes" } */
- T ("% g"); /* { dg-warning "between 2 and 13 bytes" } */
- T ("%#g"); /* { dg-warning "between 1 and 13 bytes" } */
- T ("%#+g"); /* { dg-warning "between 2 and 13 bytes" } */
- T ("%# g"); /* { dg-warning "between 2 and 13 bytes" } */
-
- T ("%.g"); /* { dg-warning "between 1 and 7 bytes" } */
- T ("%.0g"); /* { dg-warning "between 1 and 7 bytes" } */
- T ("%.1g"); /* { dg-warning "between 1 and 7 bytes" } */
- T ("%.2g"); /* { dg-warning "between 1 and 9 bytes" } */
- T ("%.99g"); /* { dg-warning "between 1 and 106 bytes" } */
- T ("%.199g"); /* { dg-warning "between 1 and 206 bytes" } */
- T ("%.1099g"); /* { dg-warning "between 1 and 310 bytes" } */
-
- T ("%0.0g"); /* { dg-warning "between 1 and 7 bytes" } */
- T ("%1.0g"); /* { dg-warning "between 1 and 7 bytes" } */
- T ("%2.0g"); /* { dg-warning "between 2 and 7 bytes" } */
- T ("%3.0g"); /* { dg-warning "between 3 and 7 bytes" } */
- T ("%7.0g"); /* { dg-warning "writing 7 bytes" } */
- T ("%310.0g"); /* { dg-warning "writing 310 bytes" } */
- T ("%311.0g"); /* { dg-warning "writing 311 bytes" } */
- T ("%312.312g"); /* { dg-warning "writing 312 bytes" } */
- T ("%312.313g"); /* { dg-warning "writing 312 bytes" } */
- T ("%333.999g"); /* { dg-warning "writing 333 bytes" } */
-
- T ("%.*g"); /* { dg-warning "writing between 1 and 310 bytes" } */
- T ("%1.*g"); /* { dg-warning "writing between 1 and 310 bytes" } */
- T ("%4.*g"); /* { dg-warning "writing between 4 and 310 bytes" } */
-
- T ("%*.*g"); /* { dg-warning "writing between 1 and 2147483648 bytes" } */
+ T (1, "%*d", w, 0); /* { dg-warning "may write a terminating nul|directive writing between 1 and \[0-9\]+ bytes" } */
+ T (1, "%*d", w, i); /* { dg-warning "may write a terminating nul|directive writing between 1 and \[0-9\]+ bytes" } */
+
+ T (1, "%*d", R (-1, 1), 0); /* { dg-warning "writing a terminating nul" } */
+ T (1, "%*d", R ( 0, 1), 0); /* { dg-warning "writing a terminating nul" } */
+ T (1, "%+*d", R ( 0, 1), 0); /* { dg-warning "directive writing 2 bytes" } */
+ T (1, "%+*u", R ( 0, 1), 0); /* { dg-warning "writing a terminating nul" } */
+ T (2, "%*d", R (-3, -2), 0); /* { dg-warning "directive writing between 1 and 3 bytes" } */
+ T (2, "%*d", R (-3, -1), 0); /* { dg-warning "directive writing between 1 and 3 bytes" } */
+ T (2, "%*d", R (-3, 0), 0); /* { dg-warning "directive writing between 1 and 3 bytes" } */
+ T (2, "%*d", R (-2, -1), 0); /* { dg-warning "may write a terminating nul" } */
+ T (2, "%*d", R (-2, 2), 0); /* { dg-warning "may write a terminating nul" } */
+ T (2, "%*d", R (-1, 2), 0); /* { dg-warning "may write a terminating nul" } */
+ T (2, "%*d", R ( 0, 2), 0); /* { dg-warning "may write a terminating nul" } */
+ T (2, "%*d", R ( 1, 2), 0); /* { dg-warning "may write a terminating nul" } */
+
+ T (1, "%.*d", p, 0); /* { dg-warning "may write a terminating nul|directive writing up to \[0-9\]+ bytes" } */
+ T (1, "%.*d", p, i); /* { dg-warning "may write a terminating nul||directive writing up to \[0-9\]+ bytes" } */
+ T (1, "%.*d", R (INT_MIN, -1), 0); /* { dg-warning "writing a terminating nul" } */
+ T (1, "%.*d", R (INT_MIN, 0), 0); /* { dg-warning "may write a terminating nul" } */
+ T (1, "%.*d", R (-2, -1), 0); /* { dg-warning "writing a terminating nul" } */
+ T (1, "%.*d", R (-1, 1), 0); /* { dg-warning "may write a terminating nul" } */
+ T (1, "%.*d", R ( 0, 1), 0); /* { dg-warning "may write a terminating nul" } */
+ T (1, "%.*d", R ( 0, 2), 0); /* { dg-warning "directive writing up to 2 bytes" } */
+ T (1, "%.*d", R ( 0, INT_MAX - 1), 0); /* { dg-warning "directive writing up to \[0-9\]+ bytes" } */
+ T (1, "%.*d", R ( 1, INT_MAX - 1), 0); /* { dg-warning "directive writing between 1 and \[0-9\]+ bytes" } */
}
+
+/* { dg-prune-output "flag used with .%.. gnu_printf format" } */