This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Format checking patch


This patch makes miscellaneous mostly C99/C94-related changes to format
checking.

A flag_isoc94 flag is added to allow for the proper pedantic handling
of %lc, %ls, %l[.  It is also added to cp/decl.c alongside flag_isoc99
there, since it is used in c-common.c, though shouldn't these common
flags be defined in a common file (c-common.c?)?  (I don't know C++ -
what format features should be accepted by the C++ compiler with
-pedantic?)

The %t and %j (ptrdiff_t and intmax_t) format widths are added.
Though the %j width entries are added to the tables, they are defined
to NULL for now, pending GCC implementation of intmax_t and <stdint.h>
(not something I'll be working on for now).

This patch adds some -pedantic warnings: %p should only be passed
pointers to void and to character types (in particular, function
pointers are obviously wrong), and the signs of targets of pointers
other than to character types should not be allowed to be wrong.  The
recent thread on comp.std.c seemed inconclusive about whether pointers
to void and to character types are, in fact, interchangable here.
Some %z and %t entries in the table are technically of the wrong sign,
but I don't think a conforming program can actually determine the
corresponding signed type to size_t or unsigned type to ptrdiff_t that
would be required to use these formats correctly, so this isn't very
important; I can fix it when adding tests for C99 formats.
Conversely, the allowance for arguments other than pointers to have
different sign is retained, and extended to precision arguments for
consistency with width arguments.

The warning for a bad character in a printf precision is removed,
since an empty precision (optional decimal integer) is a legitimate
way of specifying zero precision.  In the case of a genuinely bad
format, rather than simply an empty precision such as %.e, the error
will be detected later in the format checking.

Thorough testcases covering printf formats using features from the
1990/1994 C standards (used both correctly and incorrectly) are
included in the patch.  With the patch applied, all these tests pass.
There may still be bugs in scanf, strftime and C99 features.

This patch depends on my previously sent wint_t patch:

http://gcc.gnu.org/ml/gcc-patches/2000-07/msg00266.html

Bootstrapped (all languages) and full testsuite run on
i686-pc-linux-gnu; no regressions.

Some new warnings are generated by the bootstrap; a patch for these
may be sent separately.


gcc/ChangeLog:
2000-07-18  Joseph S. Myers  <jsm28@cam.ac.uk>

	* c-common.h (flag_isoc94): Declare.
	* c-decl.c (flag_isoc94): Define.
	(c_decode_option): Set flag_isoc94 as appropriate.
	* c-common.c (T_PD, T_IM, T_UIM): Define.
	(format_char_info): Add tlen and jlen.
	(print_char_table): Add entries for %t and %j.  Allow %zn.  Allow
	%F.  Allow %lf.
	(scan_char_table): Add entries for %t and %j.  Allow %F.  Allow
	%l[.
	(time_char_table): Add NULL entries for %t and %j.
	(check_format_info): Allow for %t and %j.  Warn for %F if pedantic
	and not C99.  Warn for %lc, %ls and %l[ if pedantic and not C94.
	Warn for printf %lf if pedantic and not C99.  Don't warn for empty
	precision.  Allow precision argument to be unsigned int.  If
	pedantic, warn for %p passed an argument not a pointer to possibly
	qualified void or a possibly qualified character type, and for
	pointer targets of the wrong sign, except for character pointers.

gcc/cp/ChangeLog:
2000-07-18  Joseph S. Myers  <jsm28@cam.ac.uk>

	* decl.c (flag_isoc94): New variable.

gcc/testsuite/ChangeLog:
2000-07-18  Joseph S. Myers  <jsm28@cam.ac.uk>

	* gcc.dg/c90-printf-1.c, gcc.dg/c94-printf-1.c: New tests.

--- c-common.h.orig	Tue Jul 18 13:59:58 2000
+++ c-common.h	Tue Jul 18 14:10:14 2000
@@ -179,6 +179,10 @@
 
 extern int flag_traditional;
 
+/* Nonzero means enable C89 Amendment 1 features, other than digraphs.  */
+
+extern int flag_isoc94;
+
 /* Nonzero means use the ISO C99 dialect of C.  */
 
 extern int flag_isoc99;
--- c-decl.c.orig	Tue Jul 18 13:59:58 2000
+++ c-decl.c	Tue Jul 18 14:10:14 2000
@@ -330,6 +330,10 @@
 
 int flag_traditional;
 
+/* Nonzero means enable C89 Amendment 1 features, other than digraphs.  */
+
+int flag_isoc94 = 0;
+
 /* Nonzero means use the ISO C99 dialect of C.  */
 
 int flag_isoc99 = 0;
@@ -541,6 +545,7 @@
 	{
 	iso_1990:
 	  flag_digraphs = 0;
+	  flag_isoc94 = 0;
 	iso_1990_digraphs:
 	  flag_traditional = 0;
 	  flag_writable_strings = 0;
@@ -551,7 +556,7 @@
       else if (!strcmp (argstart, "iso9899:199409"))
 	{
 	  flag_digraphs = 1;
-	  /* ??? The other changes since ISO C 1990 are not supported.  */
+	  flag_isoc94 = 1;
 	  goto iso_1990_digraphs;
 	}
       else if (!strcmp (argstart, "iso9899:199x")
@@ -565,6 +570,7 @@
 	  flag_no_nonansi_builtin = 1;
 	  flag_isoc99 = 1;
 	  flag_digraphs = 1;
+	  flag_isoc94 = 1;
 	}
       else if (!strcmp (argstart, "gnu89"))
 	{
@@ -574,6 +580,7 @@
 	  flag_no_nonansi_builtin = 0;
 	  flag_isoc99 = 0;
 	  flag_digraphs = 1;
+	  flag_isoc94 = 0;
 	}
       else if (!strcmp (argstart, "gnu9x") || !strcmp (argstart, "gnu99"))
 	{
@@ -583,6 +590,7 @@
 	  flag_no_nonansi_builtin = 0;
 	  flag_isoc99 = 1;
 	  flag_digraphs = 1;
+	  flag_isoc94 = 1;
 	}
       else
 	error ("unknown C standard `%s'", argstart);
--- c-common.c.orig	Tue Jul 18 13:59:59 2000
+++ c-common.c	Tue Jul 18 14:10:14 2000
@@ -1195,6 +1195,9 @@
 #define T_W	&wchar_type_node
 #define T_WI	&wint_type_node
 #define T_ST    &sizetype
+#define T_PD    &ptrdiff_type_node
+#define T_IM    NULL /* intmax_t not yet implemented.  */
+#define T_UIM   NULL /* uintmax_t not yet implemented.  */
 
 typedef struct {
   const char *format_chars;
@@ -1219,38 +1222,44 @@
   /* Type of argument if length modifiers 'z' or `Z' is used.
      If NULL, then this modifier is not allowed.  */
   tree *zlen;
+  /* Type of argument if length modifier 't' is used.
+     If NULL, then this modifier is not allowed.  */
+  tree *tlen;
+  /* Type of argument if length modifier 'j' is used.
+     If NULL, then this modifier is not allowed.  */
+  tree *jlen;
   /* List of other modifier characters allowed with these options.  */
   const char *flag_chars;
 } format_char_info;
 
 static format_char_info print_char_table[] = {
-  { "di",	0,	T_I,	T_I,	T_I,	T_L,	T_LL,	T_LL,	T_ST,	"-wp0 +"	},
-  { "oxX",	0,	T_UI,	T_UI,	T_UI,	T_UL,	T_ULL,	T_ULL,	T_ST,	"-wp0#"		},
-  { "u",	0,	T_UI,	T_UI,	T_UI,	T_UL,	T_ULL,	T_ULL,	T_ST,	"-wp0"		},
+  { "di",	0,	T_I,	T_I,	T_I,	T_L,	T_LL,	T_LL,	T_ST,	T_PD,	T_IM,	"-wp0 +"	},
+  { "oxX",	0,	T_UI,	T_UI,	T_UI,	T_UL,	T_ULL,	T_ULL,	T_ST,	T_PD,	T_UIM,	"-wp0#"		},
+  { "u",	0,	T_UI,	T_UI,	T_UI,	T_UL,	T_ULL,	T_ULL,	T_ST,	T_PD,	T_UIM,	"-wp0"		},
 /* A GNU extension.  */
-  { "m",	0,	T_V,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	"-wp"		},
-  { "feEgGaA",	0,	T_D,	NULL,	NULL,	NULL,	NULL,	T_LD,	NULL,	"-wp0 +#"	},
-  { "c",	0,	T_I,	NULL,	NULL,	T_WI,	NULL,	NULL,	NULL,	"-w"		},
-  { "C",	0,	T_W,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	"-w"		},
-  { "s",	1,	T_C,	NULL,	NULL,	T_W,	NULL,	NULL,	NULL,	"-wp"		},
-  { "S",	1,	T_W,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	"-wp"		},
-  { "p",	1,	T_V,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	"-w"		},
-  { "n",	1,	T_I,	NULL,	T_S,	T_L,	T_LL,	NULL,	NULL,	""		},
-  { NULL,	0,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL		}
+  { "m",	0,	T_V,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	"-wp"		},
+  { "fFeEgGaA",	0,	T_D,	NULL,	NULL,	T_D,	NULL,	T_LD,	NULL,	NULL,	NULL,	"-wp0 +#"	},
+  { "c",	0,	T_I,	NULL,	NULL,	T_WI,	NULL,	NULL,	NULL,	NULL,	NULL,	"-w"		},
+  { "C",	0,	T_W,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	"-w"		},
+  { "s",	1,	T_C,	NULL,	NULL,	T_W,	NULL,	NULL,	NULL,	NULL,	NULL,	"-wp"		},
+  { "S",	1,	T_W,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	"-wp"		},
+  { "p",	1,	T_V,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	"-w"		},
+  { "n",	1,	T_I,	NULL,	T_S,	T_L,	T_LL,	NULL,	T_ST,	T_PD,	T_IM,	""		},
+  { NULL,	0,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL		}
 };
 
 static format_char_info scan_char_table[] = {
-  { "di",	1,	T_I,	T_C,	T_S,	T_L,	T_LL,	T_LL,	T_ST,	"*"	},
-  { "ouxX",	1,	T_UI,	T_UC,	T_US,	T_UL,	T_ULL,	T_ULL,	T_ST,	"*"	},
-  { "efgEGaA",	1,	T_F,	NULL,	NULL,	T_D,	NULL,	T_LD,	NULL,	"*"	},
-  { "c",	1,	T_C,	NULL,	NULL,	T_W,	NULL,	NULL,	NULL,	"*"	},
-  { "s",	1,	T_C,	NULL,	NULL,	T_W,	NULL,	NULL,	NULL,	"*a"	},
-  { "[",	1,	T_C,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	"*a"	},
-  { "C",	1,	T_W,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	"*"	},
-  { "S",	1,	T_W,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	"*a"	},
-  { "p",	2,	T_V,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	"*"	},
-  { "n",	1,	T_I,	T_C,	T_S,	T_L,	T_LL,	NULL,	T_ST,	""	},
-  { NULL,	0,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL	}
+  { "di",	1,	T_I,	T_C,	T_S,	T_L,	T_LL,	T_LL,	T_ST,	T_PD,	T_IM,	"*"	},
+  { "ouxX",	1,	T_UI,	T_UC,	T_US,	T_UL,	T_ULL,	T_ULL,	T_ST,	T_PD,	T_UIM,	"*"	},
+  { "efFgEGaA",	1,	T_F,	NULL,	NULL,	T_D,	NULL,	T_LD,	NULL,	NULL,	NULL,	"*"	},
+  { "c",	1,	T_C,	NULL,	NULL,	T_W,	NULL,	NULL,	NULL,	NULL,	NULL,	"*"	},
+  { "s",	1,	T_C,	NULL,	NULL,	T_W,	NULL,	NULL,	NULL,	NULL,	NULL,	"*a"	},
+  { "[",	1,	T_C,	NULL,	NULL,	T_W,	NULL,	NULL,	NULL,	NULL,	NULL,	"*a"	},
+  { "C",	1,	T_W,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	"*"	},
+  { "S",	1,	T_W,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	"*a"	},
+  { "p",	2,	T_V,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	"*"	},
+  { "n",	1,	T_I,	T_C,	T_S,	T_L,	T_LL,	NULL,	T_ST,	T_PD,	T_IM,	""	},
+  { NULL,	0,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL,	NULL	}
 };
 
 /* Handle format characters recognized by glibc's strftime.c.
@@ -1262,20 +1271,20 @@
    'G' - other GNU extensions  */
 
 static format_char_info time_char_table[] = {
-  { "y", 		0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "2EO-_0w" },
-  { "D", 		0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "2" },
-  { "g", 		0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "2O-_0w" },
-  { "cx", 		0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "3E" },
-  { "%RTXnrt",		0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "" },
-  { "P",		0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "G" },
-  { "HIMSUWdemw",	0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "-_0Ow" },
-  { "Vju",		0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "-_0Oow" },
-  { "Gklsz",		0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "-_0OGw" },
-  { "ABZa",		0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "^#" },
-  { "p",		0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "#" },
-  { "bh",		0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "^" },
-  { "CY",		0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "-_0EOw" },
-  { NULL,		0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
+  { "y", 		0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "2EO-_0w" },
+  { "D", 		0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "2" },
+  { "g", 		0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "2O-_0w" },
+  { "cx", 		0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "3E" },
+  { "%RTXnrt",		0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "" },
+  { "P",		0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "G" },
+  { "HIMSUWdemw",	0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "-_0Ow" },
+  { "Vju",		0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "-_0Oow" },
+  { "Gklsz",		0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "-_0OGw" },
+  { "ABZa",		0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "^#" },
+  { "p",		0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "#" },
+  { "bh",		0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "^" },
+  { "CY",		0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "-_0EOw" },
+  { NULL,		0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
 };
 
 typedef struct function_format_info
@@ -1580,6 +1589,7 @@
   while (1)
     {
       int aflag;
+      int char_type_flag = 0;
       if (*format_chars == 0)
 	{
 	  if (format_chars - TREE_STRING_POINTER (format_tree) != format_length)
@@ -1741,8 +1751,6 @@
 	    {
 	      precise = TRUE;
 	      ++format_chars;
-	      if (*format_chars != '*' && !ISDIGIT (*format_chars))
-		warning ("`.' not followed by `*' or digit in format");
 	      /* "...a...precision...may be indicated by an asterisk.
 		 In this case, an int argument supplies the...precision."  */
 	      if (*format_chars == '*')
@@ -1758,9 +1766,12 @@
 		      cur_param = TREE_VALUE (params);
 		      params = TREE_CHAIN (params);
 		      ++arg_num;
-		      if (TYPE_MAIN_VARIANT (TREE_TYPE (cur_param))
-			  != integer_type_node)
-			warning ("field width is not type int (arg %d)",
+		      if ((TYPE_MAIN_VARIANT (TREE_TYPE (cur_param))
+			   != integer_type_node)
+			  &&
+			  (TYPE_MAIN_VARIANT (TREE_TYPE (cur_param))
+			   != unsigned_type_node))
+			warning ("field precision is not type int (arg %d)",
 				 arg_num);
 		    }
 		}
@@ -1792,6 +1803,13 @@
 		warning ("ANSI C does not support the `%c' length modifier",
 			 length_char);
 	    }
+	  else if (*format_chars == 't' || *format_chars == 'j')
+	    {
+	      length_char = *format_chars++;
+	      if (pedantic && !flag_isoc99)
+		warning ("ANSI C does not support the `%c' length modifier",
+			 length_char);
+	    }
 	  else
 	    length_char = 0;
 	  if (length_char == 'l' && *format_chars == 'l')
@@ -1830,9 +1848,9 @@
       if (pedantic && info->format_type != strftime_format_type
 	  && (format_char == 'm' || format_char == 'C' || format_char == 'S'))
 	warning ("ANSI C does not support the `%c' format", format_char);
-      /* The a and A formats are C99 extensions.  */
+      /* The a, A and F formats are C99 extensions.  */
       if (pedantic && info->format_type != strftime_format_type
-	  && (format_char == 'a' || format_char == 'A')
+	  && (format_char == 'a' || format_char == 'A' || format_char == 'F')
 	  && !flag_isoc99)
 	warning ("ANSI C does not support the `%c' format", format_char);
       format_chars++;
@@ -1937,6 +1955,8 @@
 					      ? TYPE_DOMAIN (*fci->zlen)
 					      : *fci->zlen)
 					   : 0); break;
+	case 't': wanted_type = fci->tlen ? *(fci->tlen) : 0; break;
+	case 'j': wanted_type = fci->jlen ? *(fci->jlen) : 0; break;
 	}
       if (wanted_type == 0)
 	warning ("use of `%c' length character with `%c' type character",
@@ -1948,6 +1968,19 @@
 		    || format_char == 'g' || format_char == 'G'))
 	warning ("ANSI C does not support the `L' length modifier with the `%c' type character",
 		 format_char);
+      else if (length_char == 'l'
+	       && (format_char == 'c' || format_char == 's'
+		   || format_char == '[')
+	       && pedantic && !flag_isoc94)
+	warning ("ANSI C89 does not support the `l' length modifier with the `%c' type character",
+		 format_char);
+      else if (info->format_type == printf_format_type && pedantic
+	       && !flag_isoc99 && length_char == 'l'
+	       && (format_char == 'f' || format_char == 'e'
+		   || format_char == 'E' || format_char == 'g'
+		   || format_char == 'G'))
+	warning ("ANSI C does not support the `l' length modifier with the `%c' type character",
+		 format_char);
 
       /* Finally. . .check type of argument against desired type!  */
       if (info->first_arg_num == 0)
@@ -2006,23 +2039,41 @@
 		      || (DECL_P (cur_param) && TREE_READONLY (cur_param))))))
 	warning ("writing into constant object (arg %d)", arg_num);
 
+      /* Check whether the argument type is a character type.  */
+      if (TREE_CODE (cur_type) != ERROR_MARK)
+	char_type_flag = (TYPE_MAIN_VARIANT (cur_type) == char_type_node
+			  || TYPE_MAIN_VARIANT (cur_type) == signed_char_type_node
+			  || TYPE_MAIN_VARIANT (cur_type) == unsigned_char_type_node);
+
       /* Check the type of the "real" argument, if there's a type we want.  */
       if (i == fci->pointer_count + aflag && wanted_type != 0
 	  && TREE_CODE (cur_type) != ERROR_MARK
 	  && wanted_type != TYPE_MAIN_VARIANT (cur_type)
 	  /* If we want `void *', allow any pointer type.
-	     (Anything else would already have got a warning.)  */
+	     (Anything else would already have got a warning.)
+	     With -pedantic, only allow pointers to void and to character
+	     types.
+	  */
 	  && ! (wanted_type == void_type_node
-		&& fci->pointer_count > 0)
-	  /* Don't warn about differences merely in signedness.  */
+		&& fci->pointer_count > 0
+		&& (! pedantic
+		    || TYPE_MAIN_VARIANT (cur_type) == void_type_node
+		    || char_type_flag))
+	  /* Don't warn about differences merely in signedness, unless
+	     -pedantic.  With -pedantic, warn if the type is a pointer
+	     target and not a character type, and for character types at
+	     a second level of indirection.
+	  */
 	  && !(TREE_CODE (wanted_type) == INTEGER_TYPE
 	       && TREE_CODE (TYPE_MAIN_VARIANT (cur_type)) == INTEGER_TYPE
+	       && (! pedantic || i == 0 || (i == 1 && char_type_flag))
 	       && (TREE_UNSIGNED (wanted_type)
 		   ? wanted_type == (cur_type = unsigned_type (cur_type))
 		   : wanted_type == (cur_type = signed_type (cur_type))))
 	  /* Likewise, "signed char", "unsigned char" and "char" are
 	     equivalent but the above test won't consider them equivalent.  */
 	  && ! (wanted_type == char_type_node
+		&& (! pedantic || i < 2)
 		&& (TYPE_MAIN_VARIANT (cur_type) == signed_char_type_node
 		    || TYPE_MAIN_VARIANT (cur_type) == unsigned_char_type_node)))
 	{
--- cp/decl.c.orig	Mon Jul 17 22:11:52 2000
+++ cp/decl.c	Tue Jul 18 14:50:56 2000
@@ -339,6 +339,10 @@
 
 tree current_function_return_value;
 
+/* Nonzero means use the ISO C94 dialect of C.  */
+
+int flag_isoc94;
+
 /* Nonzero means use the ISO C99 dialect of C.  */
 
 int flag_isoc99;
--- testsuite/gcc.dg/c90-printf-1.c.orig	Thu Jan  1 00:00:00 1970
+++ testsuite/gcc.dg/c90-printf-1.c	Mon Jul 17 17:24:54 2000
@@ -0,0 +1,247 @@
+/* Test for printf formats.  Formats using C90 features, including cases
+   where C90 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:1990 -pedantic -Wformat" } */
+
+typedef __WCHAR_TYPE__ wchar_t;
+
+#ifndef __WINT_TYPE__
+#define __WINT_TYPE__ unsigned int
+#endif
+typedef __WINT_TYPE__ wint_t;
+
+__extension__ typedef long long int llong;
+__extension__ typedef unsigned long long int ullong;
+
+extern int printf (const char *, ...);
+
+#define NULL ((void *)0)
+
+void
+foo (int i, int i1, int i2, unsigned int u, double d, char *s, void *p,
+     int *n, short int *hn, long int l, unsigned long int ul,
+     long int *ln, long double ld, wint_t lc, wchar_t *ls, llong ll,
+     ullong ull, unsigned int *un, const int *cn, signed char *ss,
+     unsigned char *us, const signed char *css, unsigned int u1,
+     unsigned int u2)
+{
+  /* See ISO/IEC 9899:1990 (E) subclause 7.9.6.1 (pages 131-134).  */
+  /* Basic sanity checks for the different components of a format.  */
+  printf ("%d\n", i);
+  printf ("%+d\n", i);
+  printf ("%3d\n", i);
+  printf ("%-3d\n", i);
+  printf ("%.7d\n", i);
+  printf ("%+9.4d\n", i);
+  printf ("%.3ld\n", l);
+  printf ("%*d\n", i1, i);
+  printf ("%.*d\n", i2, i);
+  printf ("%*.*ld\n", i1, i2, l);
+  printf ("%d %lu\n", i, ul);
+  /* GCC has objected to the next one in the past, but it is a valid way
+     of specifying zero precision.
+  */
+  printf ("%.e\n", d); /* { dg-bogus "precision" "bogus precision warning" } */
+  /* Bogus use of width.  */
+  printf ("%5n\n", n); /* { dg-warning "width" "width with %n" } */
+  /* Erroneous, ignored or pointless constructs with precision.  */
+  /* Whether negative values for precision may be included in the format
+     string is not entirely clear; presume not, following Clive Feather's
+     proposed resolution to DR#220 against C99.  In any case, such a
+     construct should be warned about.
+  */
+  printf ("%.-5d\n", i); /* { dg-warning "format|precision" "negative precision warning" } */
+  printf ("%.-*d\n", i); /* { dg-warning "format" "broken %.-*d format" } */
+  printf ("%.3c\n", i); /* { dg-warning "precision" "precision with %c" } */
+  printf ("%.3p\n", p); /* { dg-warning "precision" "precision with %p" } */
+  printf ("%.3n\n", n); /* { dg-warning "precision" "precision with %n" } */
+  /* Valid and invalid %% constructions.  Some of the warning messages
+     are non-optimal, but they do detect the errorneous nature of the
+     format string.
+  */
+  printf ("%%");
+  printf ("%.3%"); /* { dg-warning "format" "bogus %%" } */
+  printf ("%-%"); /* { dg-warning "format" "bogus %%" } */
+  printf ("%-%\n"); /* { dg-warning "format" "bogus %%" } */
+  printf ("%5%\n"); /* { dg-warning "format" "bogus %%" } */
+  printf ("%h%\n"); /* { dg-warning "format" "bogus %%" } */
+  /* Valid and invalid %h, %l, %L constructions.  */
+  printf ("%hd", i);
+  printf ("%hi", i);
+  /* Strictly, these parameters should be int or unsigned int according to
+     what unsigned short promotes to.  However, GCC ignores sign
+     differences in format checking here, and this is relied on to get the
+     correct checking without print_char_table needing to know whether
+     int and short are the same size.
+  */
+  printf ("%ho%hu%hx%hX", u, u, u, u);
+  printf ("%hn", hn);
+  printf ("%hf", d); /* { dg-warning "length character" "bad use of %h" } */
+  printf ("%he", d); /* { dg-warning "length character" "bad use of %h" } */
+  printf ("%hE", d); /* { dg-warning "length character" "bad use of %h" } */
+  printf ("%hg", d); /* { dg-warning "length character" "bad use of %h" } */
+  printf ("%hG", d); /* { dg-warning "length character" "bad use of %h" } */
+  printf ("%hc", i); /* { dg-warning "length character" "bad use of %h" } */
+  printf ("%hs", s); /* { dg-warning "length character" "bad use of %h" } */
+  printf ("%hp", p); /* { dg-warning "length character" "bad use of %h" } */
+  printf ("%h"); /* { dg-warning "conversion lacks type" "bare %h" } */
+  printf ("%h."); /* { dg-warning "conversion" "bogus %h." } */
+  printf ("%ld%li%lo%lu%lx%lX", l, l, ul, ul, ul, ul);
+  printf ("%ln", ln);
+  printf ("%lf", d); /* { dg-warning "length character|C" "bad use of %l" } */
+  printf ("%le", d); /* { dg-warning "length character|C" "bad use of %l" } */
+  printf ("%lE", d); /* { dg-warning "length character|C" "bad use of %l" } */
+  printf ("%lg", d); /* { dg-warning "length character|C" "bad use of %l" } */
+  printf ("%lG", d); /* { dg-warning "length character|C" "bad use of %l" } */
+  printf ("%lp", p); /* { dg-warning "length character|C" "bad use of %l" } */
+  /* These next two were added in C94, but should be objected to in C90.
+     For the first one, GCC has wanted wchar_t instead of the correct C94
+     and C99 wint_t.
+  */
+  printf ("%lc", lc); /* { dg-warning "length character|C" "C90 bad use of %l" } */
+  printf ("%ls", ls); /* { dg-warning "length character|C" "C90 bad use of %l" } */
+  /* These uses of %L are legitimate, though GCC has wrongly warned for
+     them in the past.
+  */
+  printf ("%Le%LE%Lf%Lg%LG", ld, ld, ld, ld, ld);
+  /* These next six are accepted by GCC as referring to long long,
+     but -pedantic correctly warns.
+  */
+  printf ("%Ld", ll); /* { dg-warning "does not support" "bad use of %L" } */
+  printf ("%Li", ll); /* { dg-warning "does not support" "bad use of %L" } */
+  printf ("%Lo", ull); /* { dg-warning "does not support" "bad use of %L" } */
+  printf ("%Lu", ull); /* { dg-warning "does not support" "bad use of %L" } */
+  printf ("%Lx", ull); /* { dg-warning "does not support" "bad use of %L" } */
+  printf ("%LX", ull); /* { dg-warning "does not support" "bad use of %L" } */
+  printf ("%Lc", i); /* { dg-warning "length character" "bad use of %L" } */
+  printf ("%Ls", s); /* { dg-warning "length character" "bad use of %L" } */
+  printf ("%Lp", p); /* { dg-warning "length character" "bad use of %L" } */
+  printf ("%Ln", n); /* { dg-warning "length character" "bad use of %L" } */
+  /* Valid uses of each bare conversion.  */
+  printf ("%d%i%o%u%x%X%f%e%E%g%G%c%s%p%n%%", i, i, u, u, u, u, d, d, d, d, d,
+	  i, s, p, n);
+  /* Uses of the - flag (valid on all non-%, non-n conversions).  */
+  printf ("%-d%-i%-o%-u%-x%-X%-f%-e%-E%-g%-G%-c%-s%-p", i, i, u, u, u, u,
+	  d, d, d, d, d, i, s, p);
+  printf ("%-n", n); /* { dg-warning "flag" "bad use of %-n" } */
+  /* Uses of the + flag (valid on signed conversions only).  */
+  printf ("%+d%+i%+f%+e%+E%+g%+G\n", i, i, d, d, d, d, d);
+  printf ("%+o", u); /* { dg-warning "flag" "bad use of + flag" } */
+  printf ("%+u", 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 ("%+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" } */
+  /* Uses of the space flag (valid on signed conversions only, and ignored
+     with +).
+  */
+  printf ("% +d", i); /* { dg-warning "use of both" "use of space and + flags" } */
+  printf ("%+ d", i); /* { dg-warning "use of both" "use of space and + flags" } */
+  printf ("% d% i% f% e% E% g% G\n", i, i, d, d, d, d, d);
+  printf ("% o", u); /* { dg-warning "flag" "bad use of space flag" } */
+  printf ("% u", u); /* { dg-warning "flag" "bad use of space flag" } */
+  printf ("% x", u); /* { dg-warning "flag" "bad use of space flag" } */
+  printf ("% X", u); /* { dg-warning "flag" "bad use of space flag" } */
+  printf ("% c", i); /* { dg-warning "flag" "bad use of space flag" } */
+  printf ("% s", s); /* { dg-warning "flag" "bad use of space flag" } */
+  printf ("% p", p); /* { dg-warning "flag" "bad use of space flag" } */
+  printf ("% n", n); /* { dg-warning "flag" "bad use of space flag" } */
+  /* Uses of the # flag.  */
+  printf ("%#o%#x%#X%#e%#E%#f%#g%#G", u, u, u, d, d, d, d, d);
+  printf ("%#d", i); /* { dg-warning "flag" "bad use of # flag" } */
+  printf ("%#i", i); /* { dg-warning "flag" "bad use of # flag" } */
+  printf ("%#u", u); /* { 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" } */
+  /* Uses of the 0 flag.  */
+  printf ("%08d%08i%08o%08u%08x%08X%08e%08E%08f%08g%08G", i, i, u, u, u, u,
+	  d, d, d, d, d);
+  printf ("%0c", i); /* { dg-warning "flag" "bad use of 0 flag" } */
+  printf ("%0s", s); /* { dg-warning "flag" "bad use of 0 flag" } */
+  printf ("%0p", p); /* { dg-warning "flag" "bad use of 0 flag" } */
+  printf ("%0n", n); /* { dg-warning "flag" "bad use of 0 flag" } */
+  /* 0 flag ignored with precision for certain types, not others.  */
+  printf ("%08.5d", i); /* { dg-warning "ignored" "0 flag ignored with precision" } */
+  printf ("%08.5i", i); /* { dg-warning "ignored" "0 flag ignored with precision" } */
+  printf ("%08.5o", u); /* { dg-warning "ignored" "0 flag ignored with precision" } */
+  printf ("%08.5u", u); /* { dg-warning "ignored" "0 flag ignored with precision" } */
+  printf ("%08.5x", u); /* { dg-warning "ignored" "0 flag ignored with precision" } */
+  printf ("%08.5X", u); /* { dg-warning "ignored" "0 flag ignored with precision" } */
+  printf ("%08.5f%08.5e%08.5E%08.5g%08.5G", d, d, d, d, d);
+  /* 0 flag ignored with - flag.  */
+  printf ("%-08d", i); /* { dg-warning "flags" "0 flag ignored with - flag" } */
+  printf ("%-08i", i); /* { dg-warning "flags" "0 flag ignored with - flag" } */
+  printf ("%-08o", u); /* { dg-warning "flags" "0 flag ignored with - flag" } */
+  printf ("%-08u", u); /* { dg-warning "flags" "0 flag ignored with - flag" } */
+  printf ("%-08x", u); /* { dg-warning "flags" "0 flag ignored with - flag" } */
+  printf ("%-08X", u); /* { dg-warning "flags" "0 flag ignored with - flag" } */
+  printf ("%-08e", d); /* { dg-warning "flags" "0 flag ignored with - flag" } */
+  printf ("%-08E", d); /* { dg-warning "flags" "0 flag ignored with - flag" } */
+  printf ("%-08f", d); /* { dg-warning "flags" "0 flag ignored with - flag" } */
+  printf ("%-08g", d); /* { dg-warning "flags" "0 flag ignored with - flag" } */
+  printf ("%-08G", d); /* { dg-warning "flags" "0 flag ignored with - flag" } */
+  /* Various tests of bad argument types.  */
+  printf ("%d", l); /* { dg-warning "format" "bad argument types" } */
+  printf ("%*.*d", l, i2, i); /* { dg-warning "field" "bad * argument types" } */
+  printf ("%*.*d", i1, l, i); /* { dg-warning "field" "bad * argument types" } */
+  printf ("%ld", i); /* { dg-warning "format" "bad argument types" } */
+  printf ("%s", n); /* { dg-warning "format" "bad argument types" } */
+  printf ("%p", i); /* { dg-warning "format" "bad argument types" } */
+  printf ("%n", p); /* { dg-warning "format" "bad argument types" } */
+  /* With -pedantic, we want some further checks for pointer targets:
+     %p should allow only pointers to void (possibly qualified) and
+     to character types (possibly qualified), but not function pointers
+     or pointers to other types.  (Whether, in fact, character types are
+     allowed here is unclear; see thread on comp.std.c, July 2000 for
+     discussion of the requirements of rules on identical representation,
+     and of the application of the as if rule with the new va_arg
+     allowances in C99 to printf.)  Likewise, we should warn if
+     pointer targets differ in signedness, except in some circumstances
+     for character pointers.  (In C99 we should consider warning for
+     char * or unsigned char * being passed to %hhn, even if strictly
+     legitimate by the standard.)
+  */
+  printf ("%p", foo); /* { dg-warning "format" "bad argument types" } */
+  printf ("%n", un); /* { dg-warning "format" "bad argument types" } */
+  printf ("%p", n); /* { dg-warning "format" "bad argument types" } */
+  /* Allow character pointers with %p.  */
+  printf ("%p%p%p%p", s, ss, us, css);
+  /* %s allows any character type.  */
+  printf ("%s%s%s%s", s, ss, us, css);
+  /* Warning for void * arguments for %s is GCC's historical behaviour,
+     and seems useful to keep, even if some standard versions might be
+     read to permit it.
+  */
+  printf ("%s", p); /* { dg-warning "format" "bad argument types" } */
+  /* The historical behaviour is to allow signed / unsigned types
+     interchangably as arguments.  For values representable in both types,
+     such usage may be correct.  For now preserve the behaviour of GCC
+     in such cases.
+  */
+  printf ("%d", u);
+  /* Also allow the same for width and precision arguments.  In the past,
+     GCC has been inconsistent and allowed unsigned for width but not
+     precision.
+  */
+  printf ("%*.*d", u1, u2, i);
+  /* Wrong number of arguments.  */
+  printf ("%d%d", i); /* { dg-warning "arguments" "wrong number of args" } */
+  printf ("%d", i, i); /* { dg-warning "arguments" "wrong number of args" } */
+  /* Miscellaneous bogus constructions.  */
+  printf (""); /* { dg-warning "zero-length" "warning for empty format" } */
+  printf ("\0"); /* { dg-warning "embedded" "warning for embedded NUL" } */
+  printf ("%d\0", i); /* { dg-warning "embedded" "warning for embedded NUL" } */
+  printf ("%d\0%d", i, i); /* { dg-warning "embedded|too many" "warning for embedded NUL" } */
+  printf (NULL); /* { dg-warning "null" "null format string warning" } */
+  printf ("%"); /* { dg-warning "trailing" "trailing % warning" } */
+  printf ("%++d", i); /* { dg-warning "repeated" "repeated flag warning" } */
+  printf ("%n", cn); /* { dg-warning "constant" "%n with const" } */
+  /* Can we test for the warning for unterminated string formats?  */
+}
--- testsuite/gcc.dg/c94-printf-1.c.orig	Thu Jan  1 00:00:00 1970
+++ testsuite/gcc.dg/c94-printf-1.c	Mon Jul 17 17:26:43 2000
@@ -0,0 +1,25 @@
+/* Test for printf formats.  Changes in C94 to C90.  */
+/* Origin: Joseph Myers <jsm28@cam.ac.uk> */
+/* { dg-do compile } */
+/* { dg-options "-std=iso9899:199409 -pedantic -Wformat" } */
+
+typedef __WCHAR_TYPE__ wchar_t;
+
+#ifndef __WINT_TYPE__
+#define __WINT_TYPE__ unsigned int
+#endif
+typedef __WINT_TYPE__ wint_t;
+
+extern int printf (const char *, ...);
+
+void
+foo (wint_t lc, wchar_t *ls)
+{
+  /* See ISO/IEC 9899:1990 (E) subclause 7.9.6.1 (pages 131-134),
+     as amended by ISO/IEC 9899:1990/Amd.1:1995 (E) (pages 4-5).
+     We do not repeat here all the C90 format checks, but just verify
+     that %ls and %lc are accepted without warning.
+  */
+  printf ("%lc", lc);
+  printf ("%ls", ls);
+}


-- 
Joseph S. Myers
jsm28@cam.ac.uk


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]