[PATCH/RFA] Add -Wformat-zero-length for zero-length format warning

Jason R Thorpe thorpej@wasabisystems.com
Fri May 17 20:30:00 GMT 2002


I inquired about the purpose of the zero-length format string warning
some time ago, but never got a response.  I figured probably the best
way to deal with that is to simply post a patch for the problem :-)

The Problem:

-Wformat currently warns about zero-length format strings, e.g.:

	printf("");

Yes, that printf() is totally useless, but that's not the point.  C99
seems to specifically allow it.  See section 7.19.6.1, item #3:

	... The format is composed of zero or more directives: ordinary
	multibyte characters (not %), which are copied unchanged to the
	output stream; ...

ANSI X3.159-1989 section 4.9.6.1 has pretty much the same language.

Note the use of "zero or more".  Since "" contains zero directives, it
clearly seems to be allowed.

"zero or more directives" is also specified in 7.19.6.2 for fscanf
formats.

Now, why do I care about this?  Code may provide a printf-like function
which does something before it handles the format.  There is at least one
such function in the NetBSD kernel (it's been in the BSD kernel since at
least 4.3BSD).  Since the function is printf-like, we want to do format
checking on it.  But, "" is a legitimate (and not unheard of) format to
pass to said function.  Since GCC 3.x warns about the zero-length format,
we croak because we build with -Werror.

To alleviate my suffering, I've added a -Wformat-zero-length option which
is used to explicitly enable the zero-length format warning for printf
and scanf formats (and added -Wformat=3 which also enables it).  For formats
types for which zero-length formats are not valid (all but printf and
scanf), the warning still trips with just -Wformat.

Included here are doc and testsuite updates.

OK to commit?

	* c-common.c (c_common_post_options): Warn if -Wformat-zero-length
	is used without -Wformat.
	* c-common.h (warn_format_zero_length): Declare extern.
	* c-decl.c (warn_options): Add "format-zero-length".
	* c-format.c (warn_format_zero_length): Declare.
	(set_Wformat): Set warn_format_zero_length for setting >= 3.
	(FORMAT_ZERO_LENGTH_ALLOWED_P): New macro to test if a format
	type is allowed to have zero-length.
	(check_format_info): Only warn about zero-length formats if
	warn_format_zero_length is true or if the format is not allowed
	to have zero-length.  Include the format type name in the warning
	message.
	* doc/invoke.texi: Document -Wformat=3 and -Wformat-zero-length.
	* testsuite/gcc.dg/format/c90-printf-1.c: Move zero-length format
	test from here...
	* testsuite/gcc.dg/format/c90-printf-4.c: ...to here.  Build with
	-Wformat-zero-length.
	* testsuite/gcc.dg/format/c90-scanf-1.c: Move zero-length format
	test from here...
	* testsuite/gcc.dg/format/c90-scanf-5.c: ...to here.  Build with
	-Wformat-zero-length.
	* testsuite/gcc.dg/format/plus-1.c: Build with -Wformat=3.

-- 
        -- Jason R. Thorpe <thorpej@wasabisystems.com>
-------------- next part --------------
Index: c-common.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/c-common.c,v
retrieving revision 1.324
diff -u -r1.324 c-common.c
--- c-common.c	16 May 2002 19:09:36 -0000	1.324
+++ c-common.c	18 May 2002 01:42:05 -0000
@@ -4309,6 +4309,8 @@
     warning ("-Wformat-y2k ignored without -Wformat");
   if (warn_format_extra_args && !warn_format)
     warning ("-Wformat-extra-args ignored without -Wformat");
+  if (warn_format_zero_length && !warn_format)
+    warning ("-Wformat-zero-length ignored without -Wformat");
   if (warn_format_nonliteral && !warn_format)
     warning ("-Wformat-nonliteral ignored without -Wformat");
   if (warn_format_security && !warn_format)
Index: c-common.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/c-common.h,v
retrieving revision 1.135
diff -u -r1.135 c-common.h
--- c-common.h	9 May 2002 22:48:32 -0000	1.135
+++ c-common.h	18 May 2002 01:42:06 -0000
@@ -414,6 +414,10 @@
 
 extern int warn_format_extra_args;
 
+/* Warn about zero-length formats.  */
+
+extern int warn_format_zero_length;
+
 /* Warn about non-literal format arguments.  */
 
 extern int warn_format_nonliteral;
Index: c-decl.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/c-decl.c,v
retrieving revision 1.325
diff -u -r1.325 c-decl.c
--- c-decl.c	9 May 2002 22:48:32 -0000	1.325
+++ c-decl.c	18 May 2002 01:42:12 -0000
@@ -474,6 +474,7 @@
     { "div-by-zero", &warn_div_by_zero },
     { "float-equal", &warn_float_equal },
     { "format-extra-args", &warn_format_extra_args },
+    { "format-zero-length", &warn_format_zero_length },
     { "format-nonliteral", &warn_format_nonliteral },
     { "format-security", &warn_format_security },
     { "format-y2k", &warn_format_y2k },
Index: c-format.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/c-format.c,v
retrieving revision 1.20
diff -u -r1.20 c-format.c
--- c-format.c	3 May 2002 20:17:53 -0000	1.20
+++ c-format.c	18 May 2002 01:42:14 -0000
@@ -44,6 +44,10 @@
 
 int warn_format_extra_args;
 
+/* Warn about zero-length formats.  */
+
+int warn_format_zero_length;
+
 /* Warn about non-literal format arguments.  */
 
 int warn_format_nonliteral;
@@ -63,8 +67,9 @@
   warn_format_extra_args = setting;
   if (setting != 1)
     {
-      warn_format_nonliteral = setting;
-      warn_format_security = setting;
+      warn_format_nonliteral = setting >= 2;
+      warn_format_security = setting >= 2;
+      warn_format_zero_length = setting >= 3;
     }
 }
 
@@ -77,6 +82,11 @@
 		   strftime_format_type, strfmon_format_type,
 		   format_type_error };
 
+/* Some format types are specifically allowed to have zero-length formats.
+   See ISO 9899:1999 7.19.6.1 and 7.19.6.2.  */
+#define FORMAT_ZERO_LENGTH_ALLOWED_P(ft) \
+  ((ft) == printf_format_type || (ft) == scanf_format_type)
+
 typedef struct function_format_info
 {
   enum format_type format_type;	/* type of format (printf, scanf, etc.) */
@@ -1351,18 +1361,22 @@
      the standard does say extra arguments are ignored, so in the specific
      case where we have multiple leaves (conditional expressions or
      ngettext) allow extra arguments if at least one leaf didn't have extra
-     arguments, but was otherwise OK (either non-literal or checked OK).
-     If the format is an empty string, this should be counted similarly to the
-     case of extra format arguments.  */
+     arguments, but was otherwise OK (either non-literal or checked OK).  */
   if (res.number_extra_args > 0 && res.number_non_literal == 0
       && res.number_other == 0 && warn_format_extra_args)
     status_warning (status, "too many arguments for format");
   if (res.number_dollar_extra_args > 0 && res.number_non_literal == 0
       && res.number_other == 0 && warn_format_extra_args)
     status_warning (status, "unused arguments in $-style format");
+
+  /* Warn about zero-length formats if explicitly asked to do so,
+     or if the format type does not allow for zero-length formats.  */
   if (res.number_empty > 0 && res.number_non_literal == 0
-      && res.number_other == 0)
-    status_warning (status, "zero-length format string");
+      && res.number_other == 0
+      && (warn_format_zero_length
+          || ! FORMAT_ZERO_LENGTH_ALLOWED_P (info->format_type)))
+    status_warning (status, "zero-length %s format string",
+		    format_types[info->format_type].name);
 
   if (res.number_wide > 0)
     status_warning (status, "format is a wide character string");
Index: doc/invoke.texi
===================================================================
RCS file: /cvs/gcc/gcc/gcc/doc/invoke.texi,v
retrieving revision 1.145
diff -u -r1.145 invoke.texi
--- doc/invoke.texi	15 May 2002 09:00:28 -0000	1.145
+++ doc/invoke.texi	18 May 2002 01:43:10 -0000
@@ -215,7 +215,7 @@
 -Wcast-align  -Wcast-qual  -Wchar-subscripts  -Wcomment @gol
 -Wconversion  -Wno-deprecated-declarations @gol
 -Wdisabled-optimization  -Wno-div-by-zero  -Werror @gol
--Wfloat-equal  -Wformat  -Wformat=2 @gol
+-Wfloat-equal  -Wformat  -Wformat=2  -Wformat=3 @gol
 -Wformat-nonliteral  -Wformat-security @gol
 -Wimplicit  -Wimplicit-int  @gol
 -Wimplicit-function-declaration @gol
@@ -1844,9 +1844,10 @@
 
 @option{-Wformat} is included in @option{-Wall}.  For more control over some
 aspects of format checking, the options @option{-Wno-format-y2k},
-@option{-Wno-format-extra-args}, @option{-Wformat-nonliteral},
-@option{-Wformat-security} and @option{-Wformat=2} are available, but are
-not included in @option{-Wall}.
+@option{-Wno-format-extra-args}, @option{-Wformat-zero-length},
+@option{-Wformat-nonliteral}, @option{-Wformat-security},
+@option{-Wformat=2}, and @option{-Wformat=3} are available, but are not
+included in @option{-Wall}.
 
 @item -Wno-format-y2k
 @opindex Wno-format-y2k
@@ -1867,6 +1868,10 @@
 warning if the unused arguments are all pointers, since the Single
 Unix Specification says that such unused arguments are allowed.
 
+@item -Wformat-zero-length
+@opindex Wformat-zero-length
+If @option{-Wformat} is specified, warn about zero-length formats.
+
 @item -Wformat-nonliteral
 @opindex Wformat-nonliteral
 If @option{-Wformat} is specified, also warn if the format string is not a
@@ -1890,6 +1895,11 @@
 Enable @option{-Wformat} plus format checks not included in
 @option{-Wformat}.  Currently equivalent to @samp{-Wformat
 -Wformat-nonliteral -Wformat-security}.
+
+@item -Wformat=3
+@opindex Wformat=3
+This is like @option{-Wformat=2}, but also enables
+@option{-Wformat-zero-length}.
 
 @item -Wimplicit-int
 @opindex Wimplicit-int
Index: testsuite/gcc.dg/format/c90-printf-1.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/testsuite/gcc.dg/format/c90-printf-1.c,v
retrieving revision 1.2
diff -u -r1.2 c90-printf-1.c
--- testsuite/gcc.dg/format/c90-printf-1.c	7 Jan 2001 10:44:59 -0000	1.2
+++ testsuite/gcc.dg/format/c90-printf-1.c	18 May 2002 01:43:20 -0000
@@ -223,7 +223,6 @@
   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" } */
Index: testsuite/gcc.dg/format/c90-printf-4.c
===================================================================
RCS file: testsuite/gcc.dg/format/c90-printf-4.c
diff -N testsuite/gcc.dg/format/c90-printf-4.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ testsuite/gcc.dg/format/c90-printf-4.c	18 May 2002 01:43:20 -0000
@@ -0,0 +1,17 @@
+/* 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 -Wformat-zero-length" } */
+
+#include "format.h"
+
+void
+foo (void)
+{
+  /* See ISO/IEC 9899:1990 (E) subclause 7.9.6.1 (pages 131-134).  */
+  /* Miscellaneous bogus constructions.  */
+  printf (""); /* { dg-warning "zero-length" "warning for empty format" } */
+}
Index: testsuite/gcc.dg/format/c90-scanf-1.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/testsuite/gcc.dg/format/c90-scanf-1.c,v
retrieving revision 1.2
diff -u -r1.2 c90-scanf-1.c
--- testsuite/gcc.dg/format/c90-scanf-1.c	7 Jan 2001 10:44:59 -0000	1.2
+++ testsuite/gcc.dg/format/c90-scanf-1.c	18 May 2002 01:43:20 -0000
@@ -109,7 +109,6 @@
   scanf ("%d%d", ip); /* { dg-warning "arguments" "wrong number of args" } */
   scanf ("%d", ip, ip); /* { dg-warning "arguments" "wrong number of args" } */
   /* Miscellaneous bogus constructions.  */
-  scanf (""); /* { dg-warning "zero-length" "warning for empty format" } */
   scanf ("\0"); /* { dg-warning "embedded" "warning for embedded NUL" } */
   scanf ("%d\0", ip); /* { dg-warning "embedded" "warning for embedded NUL" } */
   scanf ("%d\0%d", ip, ip); /* { dg-warning "embedded|too many" "warning for embedded NUL" } */
Index: testsuite/gcc.dg/format/c90-scanf-5.c
===================================================================
RCS file: testsuite/gcc.dg/format/c90-scanf-5.c
diff -N testsuite/gcc.dg/format/c90-scanf-5.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ testsuite/gcc.dg/format/c90-scanf-5.c	18 May 2002 01:43:20 -0000
@@ -0,0 +1,17 @@
+/* Test for scanf 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 -Wformat-zero-length" } */
+
+#include "format.h"
+
+void
+foo (void)
+{
+  /* See ISO/IEC 9899:1990 (E) subclause 7.9.6.2 (pages 134-138).  */
+  /* Miscellaneous bogus constructions.  */
+  scanf (""); /* { dg-warning "zero-length" "warning for empty format" } */
+}
Index: testsuite/gcc.dg/format/plus-1.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/testsuite/gcc.dg/format/plus-1.c,v
retrieving revision 1.1
diff -u -r1.1 plus-1.c
--- testsuite/gcc.dg/format/plus-1.c	25 Apr 2001 21:18:20 -0000	1.1
+++ testsuite/gcc.dg/format/plus-1.c	18 May 2002 01:43:20 -0000
@@ -2,7 +2,7 @@
  */
 /* Origin: Jakub Jelinek <jakub@redhat.com> */
 /* { dg-do compile } */
-/* { dg-options "-std=iso9899:1990 -pedantic -Wformat=2" } */
+/* { dg-options "-std=iso9899:1990 -pedantic -Wformat=3" } */
 
 #include "format.h"
 


More information about the Gcc-patches mailing list