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]
Other format: [Raw text]

Patch to update format checking for final Austin Group draft


This patch brings format checking up to date with Austin Group draft 7
(which will become the Single Unix Specification version 3 and the next
edition of POSIX).  In particular, it allows unused arguments to scanf
formats between used ones provided all the unused arguments are pointers
(and the draft allows pointer types to be interchangable as arguments to
va_arg so this is implementable).

Bootstrapped with no regressions on i686-pc-linux.gnu.  Applied to
mainline.

2001-11-25  Joseph S. Myers  <jsm28@cam.ac.uk>

	* c-format.c (FMT_FLAG_DOLLAR_GAP_POINTER_OK): New.
	(format_types): Use it for scanf.
	(dollar_arguments_pointer_p): New.
	(init_dollar_format_checking): Store details of which arguments
	are pointers.
	(maybe_read_dollar_number): Reallocate dollar_arguments_pointer_p.
	(finish_dollar_format_checking): Take extra parameter
	pointer_gap_ok.  Treat unused arguments differently if
	pointer_gap_ok and the unused arguments are pointers.
	(check_format_info_main): Pass extra argument to
	finish_dollar_format_checking.
	* doc/invoke.texi (-Wno-format-extra-args): Document behavior when
	unused arguments are present between used arguments with operand
	numbers.

testsuite:
2001-11-25  Joseph S. Myers  <jsm28@cam.ac.uk>

	* gcc.dg/format/strfmon-1.c: Update comments.  Adjust examples
	from Austin Group draft 7.
	* gcc.dg/format/xopen-1.c: Update comments.  Add tests for gaps in
	scanf format arguments.
	* gcc.dg/format/no-exargs-2.c: New test.

diff -rupN gcc.orig/c-format.c gcc/c-format.c
--- gcc.orig/c-format.c	Tue Oct 30 10:27:26 2001
+++ gcc/c-format.c	Sun Nov 25 16:13:20 2001
@@ -374,7 +374,10 @@ enum
   /* Zero width is bad in this type of format (scanf).  */
   FMT_FLAG_ZERO_WIDTH_BAD = 32,
   /* Empty precision specification is OK in this type of format (printf).  */
-  FMT_FLAG_EMPTY_PREC_OK = 64
+  FMT_FLAG_EMPTY_PREC_OK = 64,
+  /* Gaps are allowed in the arguments with $ operand numbers if all
+     arguments are pointers (scanf).  */
+  FMT_FLAG_DOLLAR_GAP_POINTER_OK = 128
   /* Not included here: details of whether width or precision may occur
      (controlled by width_char and precision_char); details of whether
      '*' can be used for these (width_type and precision_type); details
@@ -848,7 +851,7 @@ static const format_kind_info format_typ
   },
   { "scanf",    scanf_length_specs,   scan_char_table,  "*'I", NULL, 
     scanf_flag_specs, scanf_flag_pairs,
-    FMT_FLAG_ARG_CONVERT|FMT_FLAG_SCANF_A_KLUDGE|FMT_FLAG_USE_DOLLAR|FMT_FLAG_ZERO_WIDTH_BAD,
+    FMT_FLAG_ARG_CONVERT|FMT_FLAG_SCANF_A_KLUDGE|FMT_FLAG_USE_DOLLAR|FMT_FLAG_ZERO_WIDTH_BAD|FMT_FLAG_DOLLAR_GAP_POINTER_OK,
     'w', 0, 0, '*', 'L',
     NULL, NULL
   },
@@ -907,7 +910,7 @@ static void init_dollar_format_checking	
 static int maybe_read_dollar_number		PARAMS ((int *, const char **, int,
 							 tree, tree *,
 							 const format_kind_info *));
-static void finish_dollar_format_checking	PARAMS ((int *, format_check_results *));
+static void finish_dollar_format_checking	PARAMS ((int *, format_check_results *, int));
 
 static const format_flag_spec *get_flag_spec	PARAMS ((const format_flag_spec *,
 							 int, const char *));
@@ -1029,6 +1032,7 @@ status_warning VPARAMS ((int *status, co
 
 /* Variables used by the checking of $ operand number formats.  */
 static char *dollar_arguments_used = NULL;
+static char *dollar_arguments_pointer_p = NULL;
 static int dollar_arguments_alloc = 0;
 static int dollar_arguments_count;
 static int dollar_first_arg_num;
@@ -1046,6 +1050,8 @@ init_dollar_format_checking (first_arg_n
      int first_arg_num;
      tree params;
 {
+  tree oparams = params;
+
   dollar_first_arg_num = first_arg_num;
   dollar_arguments_count = 0;
   dollar_max_arg_used = 0;
@@ -1062,11 +1068,28 @@ init_dollar_format_checking (first_arg_n
     {
       if (dollar_arguments_used)
 	free (dollar_arguments_used);
+      if (dollar_arguments_pointer_p)
+	free (dollar_arguments_pointer_p);
       dollar_arguments_alloc = dollar_arguments_count;
       dollar_arguments_used = xmalloc (dollar_arguments_alloc);
+      dollar_arguments_pointer_p = xmalloc (dollar_arguments_alloc);
     }
   if (dollar_arguments_alloc)
-    memset (dollar_arguments_used, 0, dollar_arguments_alloc);
+    {
+      memset (dollar_arguments_used, 0, dollar_arguments_alloc);
+      if (first_arg_num > 0)
+	{
+	  int i = 0;
+	  params = oparams;
+	  while (params)
+	    {
+	      dollar_arguments_pointer_p[i] = (TREE_CODE (TREE_TYPE (TREE_VALUE (params)))
+					       == POINTER_TYPE);
+	      params = TREE_CHAIN (params);
+	      i++;
+	    }
+	}
+    }
 }
 
 
@@ -1146,6 +1169,8 @@ maybe_read_dollar_number (status, format
       int nalloc;
       nalloc = 2 * dollar_arguments_alloc + 16;
       dollar_arguments_used = xrealloc (dollar_arguments_used, nalloc);
+      dollar_arguments_pointer_p = xrealloc (dollar_arguments_pointer_p,
+					     nalloc);
       memset (dollar_arguments_used + dollar_arguments_alloc, 0,
 	      nalloc - dollar_arguments_alloc);
       dollar_arguments_alloc = nalloc;
@@ -1186,21 +1211,32 @@ maybe_read_dollar_number (status, format
    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.  */
+   here.  If POINTER_GAP_OK, unused arguments are OK if all arguments are
+   pointers.  */
 
 static void
-finish_dollar_format_checking (status, res)
+finish_dollar_format_checking (status, res, pointer_gap_ok)
      int *status;
      format_check_results *res;
+     int pointer_gap_ok;
 {
   int i;
+  bool found_pointer_gap = false;
   for (i = 0; i < dollar_max_arg_used; i++)
     {
       if (!dollar_arguments_used[i])
-	status_warning (status, "format argument %d unused before used argument %d in $-style format",
-		 i + 1, dollar_max_arg_used);
+	{
+	  if (pointer_gap_ok && (dollar_first_arg_num == 0
+				 || dollar_arguments_pointer_p[i]))
+	    found_pointer_gap = true;
+	  else
+	    status_warning (status, "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)
+  if (found_pointer_gap
+      || (dollar_first_arg_num
+	  && dollar_max_arg_used < dollar_arguments_count))
     {
       res->number_other--;
       res->number_dollar_extra_args++;
@@ -1639,7 +1675,7 @@ check_format_info_main (status, res, inf
 	      res->number_extra_args++;
 	    }
 	  if (has_operand_number > 0)
-	    finish_dollar_format_checking (status, res);
+	    finish_dollar_format_checking (status, res, fki->flags & (int) FMT_FLAG_DOLLAR_GAP_POINTER_OK);
 	  return;
 	}
       if (*format_chars++ != '%')
diff -rupN gcc.orig/doc/invoke.texi gcc/doc/invoke.texi
--- gcc.orig/doc/invoke.texi	Thu Nov 22 21:50:54 2001
+++ gcc/doc/invoke.texi	Sun Nov 25 02:26:42 2001
@@ -1849,6 +1849,14 @@ If @option{-Wformat} is specified, do no
 @code{printf} or @code{scanf} format function.  The C standard specifies
 that such arguments are ignored.
 
+Where the unused arguments lie between used arguments that are
+specified with @samp{$} operand number specifications, normally
+warnings are still given, since the implementation could not know what
+type to pass to @code{va_arg} to skip the unused arguments.  However,
+in the case of @code{scanf} formats, this option will suppress the
+warning if the unused arguments are all pointers, since the Single
+Unix Specification says that such unused arguments are allowed.
+
 @item -Wformat-nonliteral
 @opindex Wformat-nonliteral
 If @option{-Wformat} is specified, also warn if the format string is not a
diff -rupN gcc.orig/testsuite/gcc.dg/format/no-exargs-2.c gcc/testsuite/gcc.dg/format/no-exargs-2.c
--- gcc.orig/testsuite/gcc.dg/format/no-exargs-2.c	Thu Jan  1 00:00:00 1970
+++ gcc/testsuite/gcc.dg/format/no-exargs-2.c	Sun Nov 25 02:40:44 2001
@@ -0,0 +1,27 @@
+/* Test for warnings for extra format arguments being disabled by
+   -Wno-format-extra-args.  Test which warnings still apply with $
+   operand numbers.  */
+/* Origin: Joseph Myers <jsm28@cam.ac.uk> */
+/* { dg-do compile } */
+/* { dg-options "-std=gnu99 -Wformat -Wno-format-extra-args" } */
+
+#include "format.h"
+
+void
+foo (int i, int *ip, va_list va)
+{
+  printf ("%3$d%1$d", i, i, i); /* { dg-warning "before used" "unused $ operand" } */
+  printf ("%2$d%1$d", i, i, i);
+  vprintf ("%3$d%1$d", va); /* { dg-warning "before used" "unused $ operand" } */
+  /* With scanf formats, gaps in the used arguments are allowed only if the
+     arguments are all pointers.  In such a case, should only give the lesser
+     warning about unused arguments rather than the more serious one about
+     argument gaps.  */
+  scanf ("%3$d%1$d", ip, ip, ip);
+  /* If there are non-pointer arguments unused at the end, this is also OK.  */
+  scanf ("%3$d%1$d", ip, ip, ip, i);
+  scanf ("%3$d%1$d", ip, i, ip); /* { dg-warning "before used" "unused $ scanf non-pointer operand" } */
+  /* Can't check the arguments in the vscanf case, so should suppose the
+     lesser problem.  */
+  vscanf ("%3$d%1$d", va);
+}
diff -rupN gcc.orig/testsuite/gcc.dg/format/strfmon-1.c gcc/testsuite/gcc.dg/format/strfmon-1.c
--- gcc.orig/testsuite/gcc.dg/format/strfmon-1.c	Sun Jan  7 10:41:52 2001
+++ gcc/testsuite/gcc.dg/format/strfmon-1.c	Sun Nov 25 01:57:29 2001
@@ -8,7 +8,7 @@
 void
 foo (char *s, size_t m, double d, long double ld)
 {
-  /* Examples of valid formats from Austin Group draft 5.  */
+  /* Examples of valid formats from Austin Group draft 7.  */
   strfmon (s, m, "%n", d);
   strfmon (s, m, "%11n", d);
   strfmon (s, m, "%#5n", d);
@@ -18,7 +18,9 @@ foo (char *s, size_t m, double d, long d
   strfmon (s, m, "%^#5.0n", d);
   strfmon (s, m, "%^#5.4n", d);
   strfmon (s, m, "%(#5n", d);
-  strfmon (s, m, "%(!#5n", d);
+  strfmon (s, m, "%!(#5n", d);
+  strfmon (s, m, "%-14#5.4n", d);
+  strfmon (s, m, "%14#5.4n", d);
   /* Some more valid formats, including the GNU L length extension.  */
   strfmon (s, m, "abc%-11ndef%==i%%", d, d);
   strfmon (s, m, "%%abc%-11ndef%==Li%=%i", d, ld, d);
@@ -31,7 +33,9 @@ foo (char *s, size_t m, double d, long d
   strfmon (s, m, "%^#5.0Li", ld);
   strfmon (s, m, "%^#5.4Li", ld);
   strfmon (s, m, "%(#5Li", ld);
-  strfmon (s, m, "%(!#5Li", ld);
+  strfmon (s, m, "%!(#5Li", ld);
+  strfmon (s, m, "%-14#5.4Li", ld);
+  strfmon (s, m, "%14#5.4Li", ld);
   /* Formats with the wrong types used.  */
   strfmon (s, m, "%Ln", d); /* { dg-warning "format" "wrong type" } */
   strfmon (s, m, "%n", ld); /* { dg-warning "format" "wrong type" } */
diff -rupN gcc.orig/testsuite/gcc.dg/format/xopen-1.c gcc/testsuite/gcc.dg/format/xopen-1.c
--- gcc.orig/testsuite/gcc.dg/format/xopen-1.c	Sun Jan  7 10:41:52 2001
+++ gcc/testsuite/gcc.dg/format/xopen-1.c	Sun Nov 25 02:34:33 2001
@@ -1,6 +1,5 @@
 /* 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 approved as changes.
+   Single Unix Specification and in Austin Group draft 7.
 */
 /* Origin: Joseph Myers <jsm28@cam.ac.uk> */
 /* { dg-do compile } */
@@ -88,10 +87,6 @@ foo (int i, unsigned int u, wint_t lc, w
   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 was covered in Aardvark
-     report XSHd4 ERN 164, which was rejected, but implementation without
-     such a ban still isn't possible within ISO C.
-  */
   scanf ("%1$d", ip);
   printf ("%1$d", i);
   printf ("%1$d", l); /* { dg-warning "arg 2" "mismatched args with $ format" } */
@@ -110,6 +105,20 @@ foo (int i, unsigned int u, wint_t lc, w
   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" } */
+  /* With scanf formats, gaps in the used arguments are allowed only if the
+     arguments are all pointers.  In such a case, should only give the lesser
+     warning about unused arguments rather than the more serious one about
+     argument gaps.  */
+  scanf ("%3$d%1$d", ip, ip, ip); /* { dg-bogus "before used" "unused $ scanf pointer operand" } */
+  /* { dg-warning "unused" "unused $ scanf pointer operand" { target *-*-* } 112 } */
+  /* If there are non-pointer arguments unused at the end, this is also OK.  */
+  scanf ("%3$d%1$d", ip, ip, ip, i); /* { dg-bogus "before used" "unused $ scanf pointer operand" } */
+  /* { dg-warning "unused" "unused $ scanf pointer operand" { target *-*-* } 115 } */
+  scanf ("%3$d%1$d", ip, i, ip); /* { dg-warning "before used" "unused $ scanf non-pointer operand" } */
+  /* Can't check the arguments in the vscanf case, so should suppose the
+     lesser problem.  */
+  vscanf ("%3$d%1$d", va); /* { dg-bogus "before used" "unused $ scanf pointer operand" } */
+  /* { dg-warning "unused" "unused $ scanf pointer operand" { target *-*-* } 120 } */
   scanf ("%2$*d%1$d", ip, ip); /* { dg-warning "operand" "operand number with suppression" } */
   printf ("%1$d%1$d", i);
   scanf ("%1$d%1$d", ip); /* { dg-warning "more than once" "multiple use of scanf argument" } */

-- 
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]