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] -Wformat: fix nonsensical "wide character" message (PR c/84258)


PR c/84258 reports that we issue this:

 warning: format is a wide character string [-Wformat=]

on this code:

  const unsigned char cuc[] = "%i";
  sprintf(buf, (char *)cuc, 1);

despite the absence of wide characters.

This wording dates back 17.5 years to r36586:

 2000-09-24  Joseph S. Myers  <jsm28@cam.ac.uk>

      * c-common.c (check_format_info): Warn for a wide character string
       used as a non-wide format argument.

       * gcc.dg/c90-printf-1.c: Add test for wide string format.

which handled this case:

  printf ((const char *)L"foo"); /* { dg-warning "wide" "wide string" } */

The code in question is currently just checking for something that isn't
char_type_node.

digest_init converts the string literal to the type of the array, converting
it from array of char to array of unsigned char, leading to the condition
failing.

This patch fixes the nonsensical message by checking for the other string
types that lex_string can emit - char16_array_type_node,
char32_array_type_node, and wchar_array_type_node - and only referring
to "wide character string" if it's one of them.  Otherwise it uses a new,
more generic wording.

Successfully bootstrapped&regrtested on x86_64-pc-linux-gnu.

This isn't a regression, but is fairly low risk.

OK for trunk, either now, or for next stage 1?

gcc/c-family/ChangeLog:
	PR c/84258
	* c-format.c (struct format_check_results): Add field
	"number_non_char".
	(check_format_info): Initialize it, and warn if encountered.
	(check_format_arg): Distinguish between wide char and
	everything else when detecting arrays of non-char.

gcc/testsuite/ChangeLog:
	PR c/84258
	* c-c++-common/Wformat-pr84258.c: New test.
---
 gcc/c-family/c-format.c                      | 18 ++++++++++++++++--
 gcc/testsuite/c-c++-common/Wformat-pr84258.c | 19 +++++++++++++++++++
 2 files changed, 35 insertions(+), 2 deletions(-)
 create mode 100644 gcc/testsuite/c-c++-common/Wformat-pr84258.c

diff --git a/gcc/c-family/c-format.c b/gcc/c-family/c-format.c
index 7a69d5a..3e5b0c7 100644
--- a/gcc/c-family/c-format.c
+++ b/gcc/c-family/c-format.c
@@ -974,6 +974,8 @@ struct format_check_results
   /* Number of leaves of the format argument that were wide string
      literals.  */
   int number_wide;
+  /* Number of leaves of the format argument that are not array of "char".  */
+  int number_non_char;
   /* Number of leaves of the format argument that were empty strings.  */
   int number_empty;
   /* Number of leaves of the format argument that were unterminated
@@ -1433,6 +1435,7 @@ check_format_info (function_format_info *info, tree params,
   res.extra_arg_loc = UNKNOWN_LOCATION;
   res.number_dollar_extra_args = 0;
   res.number_wide = 0;
+  res.number_non_char = 0;
   res.number_empty = 0;
   res.number_unterminated = 0;
   res.number_other = 0;
@@ -1509,6 +1512,10 @@ check_format_info (function_format_info *info, tree params,
   if (res.number_wide > 0)
     warning_at (loc, OPT_Wformat_, "format is a wide character string");
 
+  if (res.number_non_char > 0)
+    warning_at (loc, OPT_Wformat_,
+		"format string is not an array of type %qs", "char");
+
   if (res.number_unterminated > 0)
     warning_at (loc, OPT_Wformat_, "unterminated format string");
 }
@@ -1654,9 +1661,16 @@ check_format_arg (void *ctx, tree format_tree,
       res->number_non_literal++;
       return;
     }
-  if (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (format_tree))) != char_type_node)
+  tree underlying_type
+    = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (format_tree)));
+  if (underlying_type != char_type_node)
     {
-      res->number_wide++;
+      if (underlying_type == char16_type_node
+	  || underlying_type == char32_type_node
+	  || underlying_type == wchar_type_node)
+	res->number_wide++;
+      else
+	res->number_non_char++;
       return;
     }
   format_chars = TREE_STRING_POINTER (format_tree);
diff --git a/gcc/testsuite/c-c++-common/Wformat-pr84258.c b/gcc/testsuite/c-c++-common/Wformat-pr84258.c
new file mode 100644
index 0000000..d2870a8
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wformat-pr84258.c
@@ -0,0 +1,19 @@
+/* { dg-options "-Wformat" } */
+
+int main (int argc, char **argv)
+{
+  char buf[10];
+
+  char c[] = "%i";
+  unsigned char uc[] = "%i";
+  const char cc[] = "%i";
+  const unsigned char cuc[] = "%i";
+
+  __builtin_sprintf(buf, (char *)c, 1);
+  __builtin_sprintf(buf, (char *)uc, 1);
+  __builtin_sprintf(buf, (char *)cc, 1);
+  __builtin_sprintf(buf, (char *)cuc, 1); /* { dg-warning "format string is not an array of type 'char'" } */
+  __builtin_sprintf(buf, (const char *)L"foo"); /* { dg-warning "format is a wide character string" } */
+
+  return 0;
+}
-- 
1.8.5.3


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