c-common.c (init_dollar_format_checking, [...]): New functions.

* c-common.c (init_dollar_format_checking,
	maybe_read_dollar_number, finish_dollar_format_checking): New
	functions.
	(dollar_arguments_used, dollar_arguments_count,
	dollar_first_arg_num, dollar_max_arg_used, dollar_format_warned):
	New variables.
	(check_format_info): Support $ formats for scanf and printf width
	and precision.  Always increment format_chars to advance past the
	'*' of precision, not just when the format parameters are
	available to check.

testsuite:
	* gcc.dg/c90-printf-3.c, gcc.dg/c90-scanf-2.c,
	gcc.dg/c90-scanf-3.c, gcc.dg/c90-scanf-4.c,
	gcc.dg/c90-strftime-1.c, gcc.dg/c99-printf-3.c,
	gcc.dg/c99-scanf-1.c, gcc.dg/c99-scanf-2.c, gcc.dg/c99-scanf-3.c,
	gcc.dg/format-xopen-1.c: New tests.

From-SVN: r35856
This commit is contained in:
Joseph Myers 2000-08-21 20:38:35 +01:00 committed by Joseph Myers
parent 95adab8e11
commit 5804a754e5
13 changed files with 864 additions and 32 deletions

View file

@ -1,3 +1,16 @@
2000-08-21 Joseph S. Myers <jsm28@cam.ac.uk>
* c-common.c (init_dollar_format_checking,
maybe_read_dollar_number, finish_dollar_format_checking): New
functions.
(dollar_arguments_used, dollar_arguments_count,
dollar_first_arg_num, dollar_max_arg_used, dollar_format_warned):
New variables.
(check_format_info): Support $ formats for scanf and printf width
and precision. Always increment format_chars to advance past the
'*' of precision, not just when the format parameters are
available to check.
2000-08-21 Kaveh R. Ghazi <ghazi@caip.rutgers.edu>
* mips.c (block_move_loop, expand_block_move,

View file

@ -1324,6 +1324,11 @@ static international_format_info *international_format_list = NULL;
static void check_format_info PARAMS ((function_format_info *, tree));
static void init_dollar_format_checking PARAMS ((int, tree));
static int maybe_read_dollar_number PARAMS ((const char **, int,
tree, tree *));
static void finish_dollar_format_checking PARAMS ((void));
/* Initialize the table of functions to perform format checking on.
The ISO C functions are always checked (whether <stdio.h> is
included or not), since it is common to call printf without
@ -1501,6 +1506,169 @@ check_function_format (name, assembler_name, params)
}
}
/* Variables used by the checking of $ operand number formats. */
static char *dollar_arguments_used = NULL;
static int dollar_arguments_alloc = 0;
static int dollar_arguments_count;
static int dollar_first_arg_num;
static int dollar_max_arg_used;
static int dollar_format_warned;
/* Initialize the checking for a format string that may contain $
parameter number specifications; we will need to keep track of whether
each parameter has been used. FIRST_ARG_NUM is the number of the first
argument that is a parameter to the format, or 0 for a vprintf-style
function; PARAMS is the list of arguments starting at this argument. */
static void
init_dollar_format_checking (first_arg_num, params)
int first_arg_num;
tree params;
{
dollar_first_arg_num = first_arg_num;
dollar_arguments_count = 0;
dollar_max_arg_used = 0;
dollar_format_warned = 0;
if (first_arg_num > 0)
{
while (params)
{
dollar_arguments_count++;
params = TREE_CHAIN (params);
}
}
if (dollar_arguments_alloc < dollar_arguments_count)
{
if (dollar_arguments_used)
free (dollar_arguments_used);
dollar_arguments_alloc = dollar_arguments_count;
dollar_arguments_used = xmalloc (dollar_arguments_alloc);
}
if (dollar_arguments_alloc)
memset (dollar_arguments_used, 0, dollar_arguments_alloc);
}
/* Look for a decimal number followed by a $ in *FORMAT. If DOLLAR_NEEDED
is set, it is an error if one is not found; otherwise, it is OK. If
such a number is found, check whether it is within range and mark that
numbered operand as being used for later checking. Returns the operand
number if found and within range, zero if no such number was found and
this is OK, or -1 on error. PARAMS points to the first operand of the
format; PARAM_PTR is made to point to the parameter referred to. If
a $ format is found, *FORMAT is updated to point just after it. */
static int
maybe_read_dollar_number (format, dollar_needed, params, param_ptr)
const char **format;
int dollar_needed;
tree params;
tree *param_ptr;
{
int argnum;
int overflow_flag;
const char *fcp = *format;
if (*fcp < '0' || *fcp > '9')
{
if (dollar_needed)
{
warning ("missing $ operand number in format");
return -1;
}
else
return 0;
}
argnum = 0;
overflow_flag = 0;
while (*fcp >= '0' && *fcp <= '9')
{
int nargnum;
nargnum = 10 * argnum + (*fcp - '0');
if (nargnum < 0 || nargnum / 10 != argnum)
overflow_flag = 1;
argnum = nargnum;
fcp++;
}
if (*fcp != '$')
{
if (dollar_needed)
{
warning ("missing $ operand number in format");
return -1;
}
else
return 0;
}
*format = fcp + 1;
if (pedantic && !dollar_format_warned)
{
warning ("ISO C does not support %%n$ operand number formats");
dollar_format_warned = 1;
}
if (overflow_flag || argnum == 0
|| (dollar_first_arg_num && argnum > dollar_arguments_count))
{
warning ("operand number out of range in format");
return -1;
}
if (argnum > dollar_max_arg_used)
dollar_max_arg_used = argnum;
/* For vprintf-style functions we may need to allocate more memory to
track which arguments are used. */
while (dollar_arguments_alloc < dollar_max_arg_used)
{
int nalloc;
nalloc = 2 * dollar_arguments_alloc + 16;
dollar_arguments_used = xrealloc (dollar_arguments_used, nalloc);
memset (dollar_arguments_used + dollar_arguments_alloc, 0,
nalloc - dollar_arguments_alloc);
dollar_arguments_alloc = nalloc;
}
dollar_arguments_used[argnum - 1] = 1;
if (dollar_first_arg_num)
{
int i;
*param_ptr = params;
for (i = 1; i < argnum && *param_ptr != 0; i++)
*param_ptr = TREE_CHAIN (*param_ptr);
if (*param_ptr == 0)
{
/* This case shouldn't be caught here. */
abort ();
}
}
else
*param_ptr = 0;
return argnum;
}
/* Finish the checking for a format string that used $ operand number formats
instead of non-$ formats. We check for unused operands before used ones
(a serious error, since the implementation of the format function
can't know what types to pass to va_arg to find the later arguments).
and for unused operands at the end of the format (if we know how many
arguments the format had, so not for vprintf). If there were operand
numbers out of range on a non-vprintf-style format, we won't have reached
here. */
static void
finish_dollar_format_checking ()
{
int i;
for (i = 0; i < dollar_max_arg_used; i++)
{
if (!dollar_arguments_used[i])
warning ("format argument %d unused before used argument %d in $-style format",
i + 1, dollar_max_arg_used);
}
if (dollar_first_arg_num && dollar_max_arg_used < dollar_arguments_count)
warning ("unused arguments in $-style format");
}
/* Check the argument list of a call to printf, scanf, etc.
INFO points to the function_format_info structure.
PARAMS is the list of argument values. */
@ -1524,7 +1692,9 @@ check_format_info (info, params)
const char *format_chars;
format_char_info *fci = NULL;
char flag_chars[8];
int has_operand_number = 0;
/* -1 if no conversions taking an operand have been found; 0 if one has
and it didn't use $; 1 if $ formats are in use. */
int has_operand_number = -1;
/* Skip to format argument. If the argument isn't available, there's
no work for us to do; prototype checking will catch the problem. */
@ -1624,6 +1794,7 @@ check_format_info (info, params)
}
first_fillin_param = params;
init_dollar_format_checking (info->first_arg_num, first_fillin_param);
while (1)
{
int aflag;
@ -1633,8 +1804,11 @@ check_format_info (info, params)
{
if (format_chars - TREE_STRING_POINTER (format_tree) != format_length)
warning ("embedded `\\0' in format");
if (info->first_arg_num != 0 && params != 0 && ! has_operand_number)
if (info->first_arg_num != 0 && params != 0
&& has_operand_number <= 0)
warning ("too many arguments for format");
if (has_operand_number > 0)
finish_dollar_format_checking ();
return;
}
if (*format_chars++ != '%')
@ -1657,6 +1831,22 @@ check_format_info (info, params)
suppressed = *format_chars == '*';
if (suppressed)
++format_chars;
else if (has_operand_number != 0)
{
int opnum;
opnum = maybe_read_dollar_number (&format_chars,
has_operand_number == 1,
first_fillin_param, &params);
if (opnum == -1)
return;
else if (opnum > 0)
{
has_operand_number = 1;
arg_num = opnum + info->first_arg_num - 1;
}
else
has_operand_number = 0;
}
while (ISDIGIT (*format_chars))
{
wide = TRUE;
@ -1709,35 +1899,21 @@ check_format_info (info, params)
}
else if (info->format_type == printf_format_type)
{
/* See if we have a number followed by a dollar sign. If we do,
it is an operand number, so set PARAMS to that operand. */
if (*format_chars >= '0' && *format_chars <= '9')
if (has_operand_number != 0)
{
const char *p = format_chars;
while (*p >= '0' && *p++ <= '9')
;
if (*p == '$')
int opnum;
opnum = maybe_read_dollar_number (&format_chars,
has_operand_number == 1,
first_fillin_param, &params);
if (opnum == -1)
return;
else if (opnum > 0)
{
int opnum = atoi (format_chars);
if (pedantic)
warning ("ISO C does not support printf %%n$");
params = first_fillin_param;
format_chars = p + 1;
has_operand_number = 1;
for (i = 1; i < opnum && params != 0; i++)
params = TREE_CHAIN (params);
if (opnum == 0 || params == 0)
{
warning ("operand number out of range in format");
return;
}
arg_num = opnum + info->first_arg_num - 1;
}
else
has_operand_number = 0;
}
while (*format_chars != 0 && index (" +#0-'", *format_chars) != 0)
@ -1774,11 +1950,25 @@ check_format_info (info, params)
tfaff ();
return;
}
if (has_operand_number > 0)
{
int opnum;
opnum = maybe_read_dollar_number (&format_chars, 1,
first_fillin_param,
&params);
if (opnum <= 0)
return;
else
arg_num = opnum + info->first_arg_num - 1;
}
if (info->first_arg_num != 0)
{
cur_param = TREE_VALUE (params);
params = TREE_CHAIN (params);
++arg_num;
if (has_operand_number <= 0)
{
params = TREE_CHAIN (params);
++arg_num;
}
/* size_t is generally not valid here.
It will work on most machines, because size_t and int
have the same mode. But might as well warn anyway,
@ -1807,17 +1997,31 @@ check_format_info (info, params)
In this case, an int argument supplies the...precision." */
if (*format_chars == '*')
{
++format_chars;
if (has_operand_number > 0)
{
int opnum;
opnum = maybe_read_dollar_number (&format_chars, 1,
first_fillin_param,
&params);
if (opnum <= 0)
return;
else
arg_num = opnum + info->first_arg_num - 1;
}
if (info->first_arg_num != 0)
{
++format_chars;
if (params == 0)
{
tfaff ();
return;
}
cur_param = TREE_VALUE (params);
params = TREE_CHAIN (params);
++arg_num;
if (has_operand_number <= 0)
{
params = TREE_CHAIN (params);
++arg_num;
}
if ((TYPE_MAIN_VARIANT (TREE_TYPE (cur_param))
!= integer_type_node)
&&

View file

@ -1,3 +1,11 @@
2000-08-21 Joseph S. Myers <jsm28@cam.ac.uk>
* gcc.dg/c90-printf-3.c, gcc.dg/c90-scanf-2.c,
gcc.dg/c90-scanf-3.c, gcc.dg/c90-scanf-4.c,
gcc.dg/c90-strftime-1.c, gcc.dg/c99-printf-3.c,
gcc.dg/c99-scanf-1.c, gcc.dg/c99-scanf-2.c, gcc.dg/c99-scanf-3.c,
gcc.dg/format-xopen-1.c: New tests.
2000-08-21 Joseph S. Myers <jsm28@cam.ac.uk>
* gcc.dg/c90-scanf-1.c, gcc.dg/c94-scanf-1.c: New tests.

View file

@ -0,0 +1,60 @@
/* Test for printf formats. Test that the C90 functions get their default
attributes in strict C90 mode, but the C99 and gettext functions
do not.
*/
/* Origin: Joseph Myers <jsm28@cam.ac.uk> */
/* { dg-do compile } */
/* { dg-options "-std=iso9899:1990 -pedantic -Wformat" } */
/* This may not be correct in the particular case, but allows the
prototypes to be declared, and we don't try to link.
*/
typedef struct _FILE FILE;
extern FILE *stdout;
typedef __SIZE_TYPE__ size_t;
typedef __builtin_va_list va_list;
extern int fprintf (FILE *, const char *, ...);
extern int printf (const char *, ...);
extern int sprintf (char *, const char *, ...);
extern int vfprintf (FILE *, const char *, va_list);
extern int vprintf (const char *, va_list);
extern int vsprintf (char *, const char *, va_list);
extern int snprintf (char *, size_t, const char *, ...);
extern int vsnprintf (char *, size_t, const char *, va_list);
extern char *gettext (const char *);
extern char *dgettext (const char *, const char *);
extern char *dcgettext (const char *, const char *, int);
void
foo (int i, char *s, size_t n, va_list v0, va_list v1, va_list v2, va_list v3,
va_list v4, va_list v5, va_list v6, va_list v7, va_list v8)
{
fprintf (stdout, "%d", i);
fprintf (stdout, "%ld", i); /* { dg-warning "format" "fprintf" } */
printf ("%d", i);
printf ("%ld", i); /* { dg-warning "format" "printf" } */
sprintf (s, "%d", i);
sprintf (s, "%ld", i); /* { dg-warning "format" "sprintf" } */
vfprintf (stdout, "%d", v0);
vfprintf (stdout, "%Y", v1); /* { dg-warning "format" "vfprintf" } */
vprintf ("%d", v2);
vprintf ("%Y", v3); /* { dg-warning "format" "vprintf" } */
/* The following used to give a bogus warning. */
vprintf ("%*.*d", v8);
vsprintf (s, "%d", v4);
vsprintf (s, "%Y", v5); /* { dg-warning "format" "vsprintf" } */
snprintf (s, n, "%d", i);
snprintf (s, n, "%ld", i);
vsnprintf (s, n, "%d", v6);
vsnprintf (s, n, "%Y", v7);
printf (gettext ("%d"), i);
printf (gettext ("%ld"), i);
printf (dgettext ("", "%d"), i);
printf (dgettext ("", "%ld"), i);
printf (dcgettext ("", "%d", 0), i);
printf (dcgettext ("", "%ld", 0), i);
}

View file

@ -0,0 +1,37 @@
/* Test for scanf formats. Formats using C99 features should be rejected
outside of C99 mode.
*/
/* Origin: Joseph Myers <jsm28@cam.ac.uk> */
/* { dg-do compile } */
/* { dg-options "-std=iso9899:1990 -pedantic -Wformat" } */
typedef __SIZE_TYPE__ size_t;
typedef __PTRDIFF_TYPE__ ptrdiff_t;
__extension__ typedef long long int llong;
/* This next definition is broken. When GCC has a <stdint.h> and
an internal understanding of intmax_t, it should be
replaced by an include of <stdint.h> or by a definition for internal
macros or typedefs.
*/
__extension__ typedef long long int intmax_t;
extern int scanf (const char *, ...);
void
foo (signed char *hhp, float *fp, llong *llp, intmax_t *jp,
size_t *zp, ptrdiff_t *tp)
{
/* Some tests already in c90-scanf-1.c. */
/* The widths hh, ll, j, z, t are new. */
scanf ("%hhd", hhp); /* { dg-warning "length character|C" "%hh in C90" } */
scanf ("%lld", llp); /* { dg-warning "length character|C" "%ll in C90" } */
scanf ("%jd", jp); /* { dg-warning "length character|C" "%j in C90" } */
scanf ("%zu", zp); /* { dg-warning "length character|C" "%z in C90" } */
scanf ("%td", tp); /* { dg-warning "length character|C" "%t in C90" } */
/* The formats F, a, A are new. */
scanf ("%F", fp); /* { dg-warning "C" "%F in C90" } */
scanf ("%a", fp); /* { dg-warning "C" "%a in C90" } */
scanf ("%A", fp); /* { dg-warning "C" "%A in C90" } */
}

View file

@ -0,0 +1,21 @@
/* Test for scanf formats. Formats using extensions to the standard
should be rejected in strict pedantic mode.
*/
/* Origin: Joseph Myers <jsm28@cam.ac.uk> */
/* { dg-do compile } */
/* { dg-options "-std=iso9899:1990 -pedantic -Wformat" } */
typedef __WCHAR_TYPE__ wchar_t;
extern int scanf (const char *, ...);
void
foo (char **sp, wchar_t **lsp)
{
/* %a formats for allocation, only recognised in C90 mode, are a
GNU extension.
*/
scanf ("%as", sp); /* { dg-warning "C" "%as" } */
scanf ("%aS", lsp); /* { dg-warning "C" "%aS" } */
scanf ("%a[bcd]", sp); /* { dg-warning "C" "%a[]" } */
}

View file

@ -0,0 +1,51 @@
/* Test for scanf formats. Test that the C90 functions get their default
attributes in strict C90 mode, but the C99 and gettext functions
do not.
*/
/* Origin: Joseph Myers <jsm28@cam.ac.uk> */
/* { dg-do compile } */
/* { dg-options "-std=iso9899:1990 -pedantic -Wformat" } */
/* This may not be correct in the particular case, but allows the
prototypes to be declared, and we don't try to link.
*/
typedef struct _FILE FILE;
extern FILE *stdin;
typedef __builtin_va_list va_list;
extern int fscanf (FILE *, const char *, ...);
extern int scanf (const char *, ...);
extern int sscanf (const char *, const char *, ...);
extern int vfscanf (FILE *, const char *, va_list);
extern int vscanf (const char *, va_list);
extern int vsscanf (const char *, const char *, va_list);
extern char *gettext (const char *);
extern char *dgettext (const char *, const char *);
extern char *dcgettext (const char *, const char *, int);
void
foo (int *ip, char *s, va_list v0, va_list v1, va_list v2, va_list v3,
va_list v4, va_list v5)
{
fscanf (stdin, "%d", ip);
fscanf (stdin, "%ld", ip); /* { dg-warning "format" "fscanf" } */
scanf ("%d", ip);
scanf ("%ld", ip); /* { dg-warning "format" "scanf" } */
sscanf (s, "%d", ip);
sscanf (s, "%ld", ip); /* { dg-warning "format" "sscanf" } */
vfscanf (stdin, "%d", v0);
vfscanf (stdin, "%Y", v1);
vscanf ("%d", v2);
vscanf ("%Y", v3);
vsscanf (s, "%d", v4);
vsscanf (s, "%Y", v5);
scanf (gettext ("%d"), ip);
scanf (gettext ("%ld"), ip);
scanf (dgettext ("", "%d"), ip);
scanf (dgettext ("", "%ld"), ip);
scanf (dcgettext ("", "%d", 0), ip);
scanf (dcgettext ("", "%ld", 0), ip);
}

View file

@ -0,0 +1,23 @@
/* Test for strftime formats. Formats using C90 features. */
/* Origin: Joseph Myers <jsm28@cam.ac.uk> */
/* { dg-do compile } */
/* { dg-options "-std=iso9899:1990 -pedantic -Wformat" } */
typedef __SIZE_TYPE__ size_t;
struct tm;
extern size_t strftime (char *, size_t, const char *, const struct tm *);
void
foo (char *s, size_t m, const struct tm *tp)
{
/* See ISO/IEC 9899:1990 (E) subclause 7.12.3.5 (pages 174-175). */
/* Formats which are Y2K-compliant (no 2-digit years). */
strftime (s, m, "%a%A%b%B%d%H%I%j%m%M%p%S%U%w%W%X%Y%Z%%", tp);
/* Formats with 2-digit years. */
strftime (s, m, "%y", tp); /* { dg-warning "only last 2" "2-digit year" } */
/* Formats with 2-digit years in some locales. */
strftime (s, m, "%c", tp); /* { dg-warning "some locales" "2-digit year" } */
strftime (s, m, "%x", tp); /* { dg-warning "some locales" "2-digit year" } */
}

View file

@ -0,0 +1,56 @@
/* Test for printf formats. Test that the C99 functions get their default
attributes in strict C99 mode, but the gettext functions do not.
*/
/* Origin: Joseph Myers <jsm28@cam.ac.uk> */
/* { dg-do compile } */
/* { dg-options "-std=iso9899:1999 -pedantic -Wformat" } */
/* This may not be correct in the particular case, but allows the
prototypes to be declared, and we don't try to link.
*/
typedef struct _FILE FILE;
extern FILE *stdout;
typedef __SIZE_TYPE__ size_t;
typedef __builtin_va_list va_list;
extern int fprintf (FILE *restrict, const char *restrict, ...);
extern int printf (const char *restrict, ...);
extern int sprintf (char *restrict, const char *restrict, ...);
extern int vfprintf (FILE *restrict, const char *restrict, va_list);
extern int vprintf (const char *restrict, va_list);
extern int vsprintf (char *restrict, const char *restrict, va_list);
extern int snprintf (char *restrict, size_t, const char *restrict, ...);
extern int vsnprintf (char *restrict, size_t, const char *restrict, va_list);
extern char *gettext (const char *);
extern char *dgettext (const char *, const char *);
extern char *dcgettext (const char *, const char *, int);
void
foo (int i, char *s, size_t n, va_list v0, va_list v1, va_list v2, va_list v3,
va_list v4, va_list v5, va_list v6, va_list v7)
{
fprintf (stdout, "%d", i);
fprintf (stdout, "%ld", i); /* { dg-warning "format" "fprintf" } */
printf ("%d", i);
printf ("%ld", i); /* { dg-warning "format" "printf" } */
sprintf (s, "%d", i);
sprintf (s, "%ld", i); /* { dg-warning "format" "sprintf" } */
snprintf (s, n, "%d", i);
snprintf (s, n, "%ld", i); /* { dg-warning "format" "snprintf" } */
vfprintf (stdout, "%d", v0);
vfprintf (stdout, "%Y", v1); /* { dg-warning "format" "vfprintf" } */
vprintf ("%d", v0);
vprintf ("%Y", v1); /* { dg-warning "format" "vprintf" } */
vsprintf (s, "%d", v0);
vsprintf (s, "%Y", v1); /* { dg-warning "format" "vsprintf" } */
vsnprintf (s, n, "%d", v0);
vsnprintf (s, n, "%Y", v1); /* { dg-warning "format" "vsnprintf" } */
printf (gettext ("%d"), i);
printf (gettext ("%ld"), i);
printf (dgettext ("", "%d"), i);
printf (dgettext ("", "%ld"), i);
printf (dcgettext ("", "%d", 0), i);
printf (dcgettext ("", "%ld", 0), i);
}

View file

@ -0,0 +1,165 @@
/* Test for scanf formats. Formats using C99 features, including cases
where C99 specifies some aspect of the format to be ignored or where
the behaviour is undefined.
*/
/* Origin: Joseph Myers <jsm28@cam.ac.uk> */
/* { dg-do compile } */
/* { dg-options "-std=iso9899:1999 -pedantic -Wformat" } */
typedef __WCHAR_TYPE__ wchar_t;
typedef __SIZE_TYPE__ size_t;
typedef __PTRDIFF_TYPE__ ptrdiff_t;
/* Kludges to get types corresponding to size_t and ptrdiff_t. */
#define unsigned signed
typedef __SIZE_TYPE__ signed_size_t;
#undef unsigned
#define signed /* Type might or might not have explicit 'signed'. */
typedef unsigned __PTRDIFF_TYPE__ unsigned_ptrdiff_t;
#undef signed
/* These next definitions are broken. When GCC has a <stdint.h> and
an internal understanding of intmax_t and uintmax_t, they should be
replaced by an include of <stdint.h> or by definitions for internal
macros or typedefs, and the corresponding xfails removed.
*/
typedef long long int intmax_t;
typedef unsigned long long int uintmax_t;
extern int scanf (const char *, ...);
void
foo (int *ip, unsigned int *uip, short int *hp, unsigned short int *uhp,
signed char *hhp, unsigned char *uhhp, long int *lp,
unsigned long int *ulp, float *fp, double *dp, long double *ldp, char *s,
void **pp, int *n, long long *llp, unsigned long long *ullp, wchar_t *ls,
short int *hn, signed char *hhn, long int *ln, long long int *lln,
intmax_t *jp, uintmax_t *ujp, intmax_t *jn, size_t *zp,
signed_size_t *szp, signed_size_t *zn, ptrdiff_t *tp,
unsigned_ptrdiff_t *utp, ptrdiff_t *tn)
{
/* See ISO/IEC 9899:1999 (E) subclause 7.19.6.2 (pages 281-288).
We do not repeat here most of the checks for correct C90 formats
or completely broken formats.
*/
/* Valid, invalid and silly assignment-suppression
and width constructions.
*/
scanf ("%*d%*i%*o%*u%*x%*X%*a%*A%*e%*E%*f%*F%*g%*G%*s%*[abc]%*c%*p");
scanf ("%*2d%*8s%*3c");
scanf ("%*n"); /* { dg-warning "suppress" "suppression of %n" } */
scanf ("%*hd"); /* { dg-warning "together" "suppression with length" } */
scanf ("%2d%3i%4o%5u%6x%7X%8a%9A%10e%11E%12f%13F%14g%15G%16s%3[abc]%4c%5p",
ip, ip, uip, uip, uip, uip, fp, fp, fp, fp, fp, fp, fp, fp,
s, s, s, pp);
scanf ("%0d", ip); /* { dg-warning "width" "warning for zero width" } */
scanf ("%3n", n); /* { dg-warning "width" "width with %n" } */
/* Valid and invalid %h, %hh, %l, %ll, %j, %z, %t, %L constructions. */
scanf ("%hd%hi%ho%hu%hx%hX%hn", hp, hp, uhp, uhp, uhp, uhp, hn);
scanf ("%ha", fp); /* { dg-warning "length character" "bad use of %h" } */
scanf ("%hA", fp); /* { dg-warning "length character" "bad use of %h" } */
scanf ("%he", fp); /* { dg-warning "length character" "bad use of %h" } */
scanf ("%hE", fp); /* { dg-warning "length character" "bad use of %h" } */
scanf ("%hf", fp); /* { dg-warning "length character" "bad use of %h" } */
scanf ("%hF", fp); /* { dg-warning "length character" "bad use of %h" } */
scanf ("%hg", fp); /* { dg-warning "length character" "bad use of %h" } */
scanf ("%hG", fp); /* { dg-warning "length character" "bad use of %h" } */
scanf ("%hs", s); /* { dg-warning "length character" "bad use of %h" } */
scanf ("%h[ac]", s); /* { dg-warning "length character" "bad use of %h" } */
scanf ("%hc", s); /* { dg-warning "length character" "bad use of %h" } */
scanf ("%hp", pp); /* { dg-warning "length character" "bad use of %h" } */
scanf ("%hhd%hhi%hho%hhu%hhx%hhX%hhn", hhp, hhp, uhhp, uhhp, uhhp, uhhp,
hhn);
scanf ("%hha", fp); /* { dg-warning "length character" "bad use of %hh" } */
scanf ("%hhA", fp); /* { dg-warning "length character" "bad use of %hh" } */
scanf ("%hhe", fp); /* { dg-warning "length character" "bad use of %hh" } */
scanf ("%hhE", fp); /* { dg-warning "length character" "bad use of %hh" } */
scanf ("%hhf", fp); /* { dg-warning "length character" "bad use of %hh" } */
scanf ("%hhF", fp); /* { dg-warning "length character" "bad use of %hh" } */
scanf ("%hhg", fp); /* { dg-warning "length character" "bad use of %hh" } */
scanf ("%hhG", fp); /* { dg-warning "length character" "bad use of %hh" } */
scanf ("%hhs", s); /* { dg-warning "length character" "bad use of %hh" } */
scanf ("%hh[ac]", s); /* { dg-warning "length character" "bad use of %hh" } */
scanf ("%hhc", s); /* { dg-warning "length character" "bad use of %hh" } */
scanf ("%hhp", pp); /* { dg-warning "length character" "bad use of %hh" } */
scanf ("%ld%li%lo%lu%lx%lX%ln", lp, lp, ulp, ulp, ulp, ulp, ln);
scanf ("%la%lA%le%lE%lf%lF%lg%lG", dp, dp, dp, dp, dp, dp, dp, dp);
scanf ("%lp", pp); /* { dg-warning "length character" "bad use of %l" } */
scanf ("%ls", ls);
scanf ("%l[ac]", ls);
scanf ("%lc", ls);
scanf ("%lld%lli%llo%llu%llx%llX%lln", llp, llp, ullp, ullp, ullp, ullp,
lln);
scanf ("%lla", fp); /* { dg-warning "length character" "bad use of %ll" } */
scanf ("%llA", fp); /* { dg-warning "length character" "bad use of %ll" } */
scanf ("%lle", fp); /* { dg-warning "length character" "bad use of %ll" } */
scanf ("%llE", fp); /* { dg-warning "length character" "bad use of %ll" } */
scanf ("%llf", fp); /* { dg-warning "length character" "bad use of %ll" } */
scanf ("%llF", fp); /* { dg-warning "length character" "bad use of %ll" } */
scanf ("%llg", fp); /* { dg-warning "length character" "bad use of %ll" } */
scanf ("%llG", fp); /* { dg-warning "length character" "bad use of %ll" } */
scanf ("%lls", s); /* { dg-warning "length character" "bad use of %ll" } */
scanf ("%ll[ac]", s); /* { dg-warning "length character" "bad use of %ll" } */
scanf ("%llc", s); /* { dg-warning "length character" "bad use of %ll" } */
scanf ("%llp", pp); /* { dg-warning "length character" "bad use of %ll" } */
scanf ("%jd%ji%jo%ju%jx%jX%jn", jp, jp, ujp, ujp, ujp, ujp, jn); /* { dg-bogus "length character" "bogus %j warning" { xfail *-*-* } } */
scanf ("%ja", fp); /* { dg-warning "length character" "bad use of %j" } */
scanf ("%jA", fp); /* { dg-warning "length character" "bad use of %j" } */
scanf ("%je", fp); /* { dg-warning "length character" "bad use of %j" } */
scanf ("%jE", fp); /* { dg-warning "length character" "bad use of %j" } */
scanf ("%jf", fp); /* { dg-warning "length character" "bad use of %j" } */
scanf ("%jF", fp); /* { dg-warning "length character" "bad use of %j" } */
scanf ("%jg", fp); /* { dg-warning "length character" "bad use of %j" } */
scanf ("%jG", fp); /* { dg-warning "length character" "bad use of %j" } */
scanf ("%js", s); /* { dg-warning "length character" "bad use of %j" } */
scanf ("%j[ac]", s); /* { dg-warning "length character" "bad use of %j" } */
scanf ("%jc", s); /* { dg-warning "length character" "bad use of %j" } */
scanf ("%jp", pp); /* { dg-warning "length character" "bad use of %j" } */
scanf ("%zd%zi%zo%zu%zx%zX%zn", szp, szp, zp, zp, zp, zp, zn);
scanf ("%za", fp); /* { dg-warning "length character" "bad use of %z" } */
scanf ("%zA", fp); /* { dg-warning "length character" "bad use of %z" } */
scanf ("%ze", fp); /* { dg-warning "length character" "bad use of %z" } */
scanf ("%zE", fp); /* { dg-warning "length character" "bad use of %z" } */
scanf ("%zf", fp); /* { dg-warning "length character" "bad use of %z" } */
scanf ("%zF", fp); /* { dg-warning "length character" "bad use of %z" } */
scanf ("%zg", fp); /* { dg-warning "length character" "bad use of %z" } */
scanf ("%zG", fp); /* { dg-warning "length character" "bad use of %z" } */
scanf ("%zs", s); /* { dg-warning "length character" "bad use of %z" } */
scanf ("%z[ac]", s); /* { dg-warning "length character" "bad use of %z" } */
scanf ("%zc", s); /* { dg-warning "length character" "bad use of %z" } */
scanf ("%zp", pp); /* { dg-warning "length character" "bad use of %z" } */
scanf ("%td%ti%to%tu%tx%tX%tn", tp, tp, utp, utp, utp, utp, tn);
scanf ("%ta", fp); /* { dg-warning "length character" "bad use of %t" } */
scanf ("%tA", fp); /* { dg-warning "length character" "bad use of %t" } */
scanf ("%te", fp); /* { dg-warning "length character" "bad use of %t" } */
scanf ("%tE", fp); /* { dg-warning "length character" "bad use of %t" } */
scanf ("%tf", fp); /* { dg-warning "length character" "bad use of %t" } */
scanf ("%tF", fp); /* { dg-warning "length character" "bad use of %t" } */
scanf ("%tg", fp); /* { dg-warning "length character" "bad use of %t" } */
scanf ("%tG", fp); /* { dg-warning "length character" "bad use of %t" } */
scanf ("%ts", s); /* { dg-warning "length character" "bad use of %t" } */
scanf ("%t[ac]", s); /* { dg-warning "length character" "bad use of %t" } */
scanf ("%tc", s); /* { dg-warning "length character" "bad use of %t" } */
scanf ("%tp", pp); /* { dg-warning "length character" "bad use of %t" } */
scanf ("%La%LA%Le%LE%Lf%LF%Lg%LG", ldp, ldp, ldp, ldp, ldp, ldp, ldp, ldp);
scanf ("%Ld", llp); /* { dg-warning "does not support" "bad use of %L" } */
scanf ("%Li", llp); /* { dg-warning "does not support" "bad use of %L" } */
scanf ("%Lo", ullp); /* { dg-warning "does not support" "bad use of %L" } */
scanf ("%Lu", ullp); /* { dg-warning "does not support" "bad use of %L" } */
scanf ("%Lx", ullp); /* { dg-warning "does not support" "bad use of %L" } */
scanf ("%LX", ullp); /* { dg-warning "does not support" "bad use of %L" } */
scanf ("%Ls", s); /* { dg-warning "length character" "bad use of %L" } */
scanf ("%L[ac]", s); /* { dg-warning "length character" "bad use of %L" } */
scanf ("%Lc", s); /* { dg-warning "length character" "bad use of %L" } */
scanf ("%Lp", pp); /* { dg-warning "length character" "bad use of %L" } */
scanf ("%Ln", n); /* { dg-warning "length character" "bad use of %L" } */
/* Valid uses of each bare conversion. */
scanf ("%d%i%o%u%x%X%a%A%e%E%f%F%g%G%s%[abc]%c%p%n%%", ip, ip, uip, uip, uip,
uip, fp, fp, fp, fp, fp, fp, fp, fp, s, s, s, pp, n);
/* Assert that %as is not treated as an extension in C99 mode. */
scanf ("%as", fp);
scanf ("%a[", fp);
/* Tests for bad argument types: pointer target sign with %hh. */
scanf ("%hhd", uhhp); /* { dg-warning "format" "%hhd sign" } */
scanf ("%hhu", hhp); /* { dg-warning "format" "%hhu sign" } */
}

View file

@ -0,0 +1,27 @@
/* Test for scanf formats. Formats using extensions to the standard
should be rejected in strict pedantic mode.
*/
/* Origin: Joseph Myers <jsm28@cam.ac.uk> */
/* { dg-do compile } */
/* { dg-options "-std=iso9899:1999 -pedantic -Wformat" } */
typedef __SIZE_TYPE__ size_t;
typedef __WCHAR_TYPE__ wchar_t;
extern int scanf (const char *, ...);
void
foo (int *ip, long long int *llp, size_t *zp, wchar_t *ls)
{
/* The length modifiers q, Z and L as applied to integer formats are
extensions.
*/
scanf ("%qd", llp); /* { dg-warning "C" "%q length" } */
scanf ("%Ld", llp); /* { dg-warning "C" "%L length" } */
scanf ("%Zu", zp); /* { dg-warning "C" "%Z length" } */
/* The conversion specifiers C and S are X/Open extensions. */
scanf ("%C", ls); /* { dg-warning "C" "scanf %C" } */
scanf ("%S", ls); /* { dg-warning "C" "scanf %S" } */
/* The use of operand number $ formats is an X/Open extension. */
scanf ("%1$d", ip); /* { dg-warning "C" "scanf $ format" } */
}

View file

@ -0,0 +1,49 @@
/* Test for scanf formats. Test that the C99 functions get their default
attributes in strict C99 mode, but the gettext functions do not.
*/
/* Origin: Joseph Myers <jsm28@cam.ac.uk> */
/* { dg-do compile } */
/* { dg-options "-std=iso9899:1999 -pedantic -Wformat" } */
/* This may not be correct in the particular case, but allows the
prototypes to be declared, and we don't try to link.
*/
typedef struct _FILE FILE;
extern FILE *stdin;
typedef __builtin_va_list va_list;
extern int fscanf (FILE *restrict, const char *restrict, ...);
extern int scanf (const char *restrict, ...);
extern int sscanf (const char *restrict, const char *restrict, ...);
extern int vfscanf (FILE *restrict, const char *restrict, va_list);
extern int vscanf (const char *restrict, va_list);
extern int vsscanf (const char *restrict, const char *restrict, va_list);
extern char *gettext (const char *);
extern char *dgettext (const char *, const char *);
extern char *dcgettext (const char *, const char *, int);
void
foo (int *ip, char *s, va_list v0, va_list v1, va_list v2, va_list v3,
va_list v4, va_list v5)
{
fscanf (stdin, "%d", ip);
fscanf (stdin, "%ld", ip); /* { dg-warning "format" "fscanf" } */
scanf ("%d", ip);
scanf ("%ld", ip); /* { dg-warning "format" "scanf" } */
sscanf (s, "%d", ip);
sscanf (s, "%ld", ip); /* { dg-warning "format" "sscanf" } */
vfscanf (stdin, "%d", v0);
vfscanf (stdin, "%Y", v1); /* { dg-warning "format" "vfscanf" } */
vscanf ("%d", v2);
vscanf ("%Y", v3); /* { dg-warning "format" "vscanf" } */
vsscanf (s, "%d", v4);
vsscanf (s, "%Y", v5); /* { dg-warning "format" "vsscanf" } */
scanf (gettext ("%d"), ip);
scanf (gettext ("%ld"), ip);
scanf (dgettext ("", "%d"), ip);
scanf (dgettext ("", "%ld"), ip);
scanf (dcgettext ("", "%d", 0), ip);
scanf (dcgettext ("", "%ld", 0), ip);
}

View file

@ -0,0 +1,118 @@
/* Test for X/Open format extensions, as found in the
Single Unix Specification and in Austin Group draft 4, subject to some
Aardvark problem reports submitted.
*/
/* Origin: Joseph Myers <jsm28@cam.ac.uk> */
/* { dg-do compile } */
/* { dg-options "-std=gnu99 -Wformat" } */
typedef __WCHAR_TYPE__ wchar_t;
typedef __WINT_TYPE__ wint_t;
typedef __builtin_va_list va_list;
extern int printf (const char *, ...);
extern int vprintf (const char *, va_list);
extern int scanf (const char *, ...);
void
foo (int i, unsigned int u, wint_t lc, wchar_t *ls, int *ip, double d,
char *s, void *p, int *n, long int l, int i2, float *fp, long int *lp,
va_list va)
{
/* The conversion specifiers C and S, for both printf and scanf,
are X/Open extensions.
*/
printf ("%C", lc);
printf ("%3C", lc);
printf ("%.3C", lc); /* { dg-warning "precision" "precision with %C" } */
printf ("%hC", lc); /* { dg-warning "length character" "bad %hC" } */
printf ("%hhC", lc); /* { dg-warning "length character" "bad %hhC" } */
printf ("%lC", lc); /* { dg-warning "length character" "bad %lC" } */
printf ("%llC", lc); /* { dg-warning "length character" "bad %llC" } */
printf ("%jC", lc); /* { dg-warning "length character" "bad %jC" } */
printf ("%zC", lc); /* { dg-warning "length character" "bad %zC" } */
printf ("%tC", lc); /* { dg-warning "length character" "bad %tC" } */
printf ("%LC", lc); /* { dg-warning "length character" "bad %LC" } */
printf ("%-C", lc);
printf ("%+C", lc); /* { dg-warning "flag" "bad %+C" } */
printf ("% C", lc); /* { dg-warning "flag" "bad % C" } */
printf ("%#C", lc); /* { dg-warning "flag" "bad %#C" } */
printf ("%0C", lc); /* { dg-warning "flag" "bad %0C" } */
printf ("%S", ls);
printf ("%3S", ls);
printf ("%.3S", ls);
printf ("%hS", ls); /* { dg-warning "length character" "bad %hS" } */
printf ("%hhS", ls); /* { dg-warning "length character" "bad %hhS" } */
printf ("%lS", ls); /* { dg-warning "length character" "bad %lS" } */
printf ("%llS", ls); /* { dg-warning "length character" "bad %llS" } */
printf ("%jS", ls); /* { dg-warning "length character" "bad %jS" } */
printf ("%zS", ls); /* { dg-warning "length character" "bad %zS" } */
printf ("%tS", ls); /* { dg-warning "length character" "bad %tS" } */
printf ("%LS", ls); /* { dg-warning "length character" "bad %LS" } */
printf ("%-S", ls);
printf ("%+S", ls); /* { dg-warning "flag" "bad %+S" } */
printf ("% S", ls); /* { dg-warning "flag" "bad % S" } */
printf ("%#S", ls); /* { dg-warning "flag" "bad %#S" } */
printf ("%0S", ls); /* { dg-warning "flag" "bad %0S" } */
scanf ("%C", ls);
scanf ("%S", ls);
scanf ("%*C%*S");
scanf ("%2C%3S", ls, ls);
scanf ("%hC", ls); /* { dg-warning "length character" "bad %hC" } */
scanf ("%hhC", ls); /* { dg-warning "length character" "bad %hhC" } */
scanf ("%lC", ls); /* { dg-warning "length character" "bad %lC" } */
scanf ("%llC", ls); /* { dg-warning "length character" "bad %llC" } */
scanf ("%jC", ls); /* { dg-warning "length character" "bad %jC" } */
scanf ("%zC", ls); /* { dg-warning "length character" "bad %zC" } */
scanf ("%tC", ls); /* { dg-warning "length character" "bad %tC" } */
scanf ("%LC", ls); /* { dg-warning "length character" "bad %LC" } */
scanf ("%hS", ls); /* { dg-warning "length character" "bad %hS" } */
scanf ("%hhS", ls); /* { dg-warning "length character" "bad %hhS" } */
scanf ("%lS", ls); /* { dg-warning "length character" "bad %lS" } */
scanf ("%llS", ls); /* { dg-warning "length character" "bad %llS" } */
scanf ("%jS", ls); /* { dg-warning "length character" "bad %jS" } */
scanf ("%zS", ls); /* { dg-warning "length character" "bad %zS" } */
scanf ("%tS", ls); /* { dg-warning "length character" "bad %tS" } */
scanf ("%LS", ls); /* { dg-warning "length character" "bad %LS" } */
/* In C99 mode (even with extensions), %aS is a floating point
format followed by an S.
*/
scanf ("%aS", fp);
/* The printf flag character ' is an X/Open extension. */
/* Allowing %'F here presumes acceptance of the corresponding Aardvark
report.
*/
printf ("%'d%'i%'u%'f%'F%'g%'G", i, i, u, d, d, d, d);
printf ("%'o", u); /* { dg-warning "flag" "bad use of ' flag" } */
printf ("%'x", u); /* { dg-warning "flag" "bad use of ' flag" } */
printf ("%'X", u); /* { dg-warning "flag" "bad use of ' flag" } */
printf ("%'e", d); /* { dg-warning "flag" "bad use of ' flag" } */
printf ("%'E", d); /* { dg-warning "flag" "bad use of ' flag" } */
printf ("%'a", d); /* { dg-warning "flag" "bad use of ' flag" } */
printf ("%'A", d); /* { dg-warning "flag" "bad use of ' flag" } */
printf ("%'c", i); /* { dg-warning "flag" "bad use of ' flag" } */
printf ("%'s", s); /* { dg-warning "flag" "bad use of ' flag" } */
printf ("%'p", p); /* { dg-warning "flag" "bad use of ' flag" } */
printf ("%'n", n); /* { dg-warning "flag" "bad use of ' flag" } */
/* The use of operand number $ formats is an X/Open extension. */
/* Banning gaps in the arguments used with scanf, and not objecting to
multiple use of an argument with scanf, presumes acceptance of the
corresponding Aardvark reports.
*/
scanf ("%1$d", ip);
printf ("%1$d", i);
printf ("%3$*2$.*1$d", i2, i, l);
printf ("%4$ld%7$ld%5$d%6$d%3$d%1$d%2$d", i, i, i, l, i, i, l);
scanf ("%4$ld%7$ld%5$d%6$d%3$d%1$d%2$d", ip, ip, ip, lp, ip, ip, lp);
printf ("%1$d%d", i, i); /* { dg-warning "missing" "mixing $ and non-$ formats" } */
printf ("%%%1$d%%%2$d", i, i);
printf ("%d%2$d", i); /* { dg-warning "type character" "mixing $ and non-$ formats" } */
printf ("%1$*d", i, i); /* { dg-warning "missing" "mixing $ and non-$ formats" } */
scanf ("%1$d%d", ip, ip); /* { dg-warning "missing" "mixing $ and non-$ formats" } */
scanf ("%*f%%%1$d%%%2$d", ip, ip);
printf ("%2$d", i); /* { dg-warning "operand" "$ number too large" } */
printf ("%0$d", i); /* { dg-warning "operand" "$ number too small" } */
printf ("%3$d%1$d", i, i, i); /* { dg-warning "before used" "unused $ operand" } */
printf ("%2$d%1$d", i, i, i); /* { dg-warning "unused" "unused $ operand" } */
vprintf ("%3$d%1$d", va); /* { dg-warning "before used" "unused $ operand" } */
}