From c7f8f0d0e0038662949843a32233a97d6d1fba3a Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Thu, 16 Jun 2016 04:12:38 -0400
Subject: [PATCH 08/31] FIXME: WIP on fixits for -Wformat

PR c/64955 ("RFE: have -Wformat suggest the correct format string to use")
---
 gcc/c-family/c-common.h                         |   7 +
 gcc/c-family/c-format.c                         | 243 ++++++++++++++++++++++--
 gcc/c/c-lang.c                                  |  22 +++
 gcc/langhooks-def.h                             |   4 +-
 gcc/langhooks.h                                 |   3 +
 gcc/selftest-run-tests.c                        |   5 +
 gcc/selftest.c                                  |   4 +
 gcc/testsuite/gcc.dg/format/diagnostic-ranges.c |  29 ++-
 8 files changed, 295 insertions(+), 22 deletions(-)

diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 0dc2a80..98f7dd1 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -1503,4 +1503,11 @@ extern bool valid_array_size_p (location_t, tree, tree);
 extern bool cilk_ignorable_spawn_rhs_op (tree);
 extern bool cilk_recognize_spawn (tree, tree *);
 
+#if CHECKING_P
+namespace selftest {
+  extern void c_format_c_tests (void);
+  extern void run_c_tests (void);
+} // namespace selftest
+#endif /* #if CHECKING_P */
+
 #endif /* ! GCC_C_COMMON_H */
diff --git a/gcc/c-family/c-format.c b/gcc/c-family/c-format.c
index 202d4f4..4752e99 100644
--- a/gcc/c-family/c-format.c
+++ b/gcc/c-family/c-format.c
@@ -30,6 +30,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "langhooks.h"
 #include "c-format.h"
 #include "diagnostic.h"
+#include "selftest.h"
 
 /* Handle attributes associated with format checking.  */
 
@@ -67,10 +68,10 @@ static const char *format_name (int format_num);
 static int format_flags (int format_num);
 
 /* FIXME */
-ATTRIBUTE_GCC_DIAG (5,0)
+ATTRIBUTE_GCC_DIAG (6,0)
 static bool
 format_warning_va (location_t fmt_string_loc, source_range fmt_substring_range,
-		   source_range *param_range,
+		   source_range *param_range, const char *corrected_substring,
 		   int opt, const char *gmsgid, va_list *ap)
 {
   bool substring_within_range;
@@ -98,6 +99,9 @@ format_warning_va (location_t fmt_string_loc, source_range fmt_substring_range,
       richloc.add_range (param_loc, false);
     }
 
+  if (corrected_substring && substring_within_range)
+    richloc.add_fixit_replace (fmt_substring_range, corrected_substring);
+
   diagnostic_info diagnostic;
   diagnostic_set_info (&diagnostic, gmsgid, ap, &richloc, DK_WARNING);
   diagnostic.option_index = opt;
@@ -105,22 +109,30 @@ format_warning_va (location_t fmt_string_loc, source_range fmt_substring_range,
 
   if (!substring_within_range)
     if (warned)
-      inform (substring_loc, "format string is defined here");
+      {
+	rich_location substring_richloc (line_table, substring_loc);
+	if (corrected_substring)
+	  substring_richloc.add_fixit_replace (fmt_substring_range,
+					       corrected_substring);
+	inform_at_rich_loc (&substring_richloc,
+			    "format string is defined here");
+      }
 
   return warned;
 }
 
 /* FIXME */
-ATTRIBUTE_GCC_DIAG (5,0)
+ATTRIBUTE_GCC_DIAG (6,0)
 static bool
 format_warning (location_t fmt_string_loc, source_range fmt_substring_range,
-		source_range *param_range,
+		source_range *param_range, const char *corrected_substring,
 		int opt, const char *gmsgid, ...)
 {
   va_list ap;
   va_start (ap, gmsgid);
   bool warned = format_warning_va (fmt_string_loc, fmt_substring_range,
-				   param_range, opt, gmsgid, &ap);
+				   param_range, corrected_substring, opt, gmsgid,
+				   &ap);
   va_end (ap);
 
   return warned;
@@ -140,7 +152,7 @@ format_warning (location_t fmt_string_loc, tree format_string_cst,
   va_list ap;
   va_start (ap, gmsgid);
   bool warned = format_warning_va (fmt_string_loc, fmt_substring_range, NULL,
-				   opt, gmsgid, &ap);
+				   NULL, opt, gmsgid, &ap);
   va_end (ap);
 
   return warned;
@@ -1042,12 +1054,14 @@ static const format_flag_spec *get_flag_spec (const format_flag_spec *,
 
 static void check_format_types (location_t fmt_loc,
 				source_range fmt_substring_range,
-				format_wanted_type *);
+				format_wanted_type *,
+				const format_kind_info *fki);
 static void format_type_warning (location_t fmt_loc,
 				 source_range fmt_substring_range,
 				 source_range *param_range,
 				 format_wanted_type *, tree,
-				 tree);
+				 tree,
+				 const format_kind_info *fki);
 
 /* Decode a format type from a string, returning the type, or
    format_type_error if not valid, in which case the caller should print an
@@ -2451,7 +2465,7 @@ check_format_info_main (format_check_results *res,
 	    = strloc->get_range_between_indices (offset_to_format_start,
 						 offset_to_format_end);
 	  check_format_types (fmt_param_loc, fmt_substring_range,
-			      first_wanted_type);
+			      first_wanted_type, fki);
 	}
     }
 
@@ -2476,7 +2490,7 @@ check_format_info_main (format_check_results *res,
    location of the format string.  */
 static void
 check_format_types (location_t fmt_loc, source_range fmt_substring_range,
-		    format_wanted_type *types)
+		    format_wanted_type *types, const format_kind_info *fki)
 {
   for (; types != 0; types = types->next)
     {
@@ -2504,7 +2518,7 @@ check_format_types (location_t fmt_loc, source_range fmt_substring_range,
       if (!cur_param)
         {
           format_type_warning (fmt_loc, fmt_substring_range, NULL, types,
-			       wanted_type, NULL);
+			       wanted_type, NULL, fki);
           continue;
         }
 
@@ -2589,7 +2603,7 @@ check_format_types (location_t fmt_loc, source_range fmt_substring_range,
 	  else
 	    {
               format_type_warning (fmt_loc, fmt_substring_range, param_range_ptr,
-				   types, wanted_type, orig_cur_type);
+				   types, wanted_type, orig_cur_type, fki);
 	      break;
 	    }
 	}
@@ -2658,10 +2672,113 @@ check_format_types (location_t fmt_loc, source_range fmt_substring_range,
 	continue;
       /* Now we have a type mismatch.  */
       format_type_warning (fmt_loc, fmt_substring_range, param_range_ptr, types,
-			   wanted_type, orig_cur_type);
+			   wanted_type, orig_cur_type, fki);
+    }
+}
+
+/* FIXME.  */
+
+static tree
+deref_n_times (tree type, int n)
+{
+  gcc_assert (type);
+
+  for (int i = n; i > 0; i--)
+    {
+      if (TREE_CODE (type) != POINTER_TYPE)
+	return NULL_TREE;
+      type = TREE_TYPE (type);
+    }
+  /* Strip off any "const" etc.  */
+  return build_qualified_type (type, 0);
+}
+
+/* FIXME.  */
+#if 0
+static const format_length_info printf_length_specs[] =
+{
+  { "h", FMT_LEN_h, STD_C89, "hh", FMT_LEN_hh, STD_C99, 0 },
+  { "l", FMT_LEN_l, STD_C89, "ll", FMT_LEN_ll, STD_C9L, 0 },
+  { "q", FMT_LEN_ll, STD_EXT, NO_FMT, 0 },
+  { "L", FMT_LEN_L, STD_C89, NO_FMT, 0 },
+  { "z", FMT_LEN_z, STD_C99, NO_FMT, 0 },
+  { "Z", FMT_LEN_z, STD_EXT, NO_FMT, 0 },
+  { "t", FMT_LEN_t, STD_C99, NO_FMT, 0 },
+  { "j", FMT_LEN_j, STD_C99, NO_FMT, 0 },
+  { "H", FMT_LEN_H, STD_EXT, NO_FMT, 0 },
+  { "D", FMT_LEN_D, STD_EXT, "DD", FMT_LEN_DD, STD_EXT, 0 },
+  { NO_FMT, NO_FMT, 0 }
+};
+#endif
+static const char *
+get_modifier_for_format_len (const format_length_info *fli,
+			     enum format_lengths format_len)
+{
+  for (; fli->name; fli++)
+    {
+      if (fli->index == format_len)
+	return fli->name;
+      if (fli->double_index == format_len)
+	return fli->double_name;
     }
+  return NULL;
 }
 
+/* FIXME.  */
+
+static char *
+get_format_for_type (const format_kind_info *fki, tree arg_type)
+{
+  gcc_assert (arg_type);
+
+  const format_char_info *spec;
+  for (spec = &fki->conversion_specs[0];
+       spec->format_chars;
+       spec++)
+    {
+      tree effective_arg_type = deref_n_times (arg_type,
+					       spec->pointer_count);
+      if (!effective_arg_type)
+	continue;
+      for (int i = 0; i < FMT_LEN_MAX; i++)
+	{
+	  const format_type_detail *ftd = &spec->types[i];
+	  if (!ftd->type)
+	    continue;
+	  if (TYPE_CANONICAL (*ftd->type)
+	      == TYPE_CANONICAL (effective_arg_type))
+	    {
+	      /* FIXME: need a % prefix, and maybe other prefix. */
+	      const char *len_modifier
+		= get_modifier_for_format_len (fki->length_char_specs,
+					       (enum format_lengths)i);
+	      if (!len_modifier)
+		len_modifier = "";
+#if 0
+static const format_char_info print_char_table[] =
+{
+  /* C89 conversion specifiers.  */
+  { "di",  0, STD_C89, { T89_I,   T99_SC,  T89_S,   T89_L,   T9L_LL,  TEX_LL,  T99_SST, T99_PD,  T99_IM,  BADLEN,  BADLEN,  BADLEN  }, "-wp0 +'I",  "i",  NULL },
+
+#define T_I	&integer_type_node
+#define T89_I	{ STD_C89, NULL, T_I }
+
+	      // TODO: handle length prefixes like:
+static const format_length_info printf_length_specs[] =
+{
+  { "h", FMT_LEN_h, STD_C89, "hh", FMT_LEN_hh, STD_C99, 0 },
+  { "l", FMT_LEN_l, STD_C89, "ll", FMT_LEN_ll, STD_C9L, 0 },
+#endif
+  //fki->length_char_specs[i].name,
+
+	      return xasprintf ("%%%s%c",
+				len_modifier,
+				spec->format_chars[0]);
+	    }
+	}
+   }
+  return NULL;
+}
 
 /* Give a warning at FMT_LOC about a format argument of different type
    from that expected.  FMT_SUBSTRING_RANGE gives the location of the
@@ -2677,9 +2794,10 @@ static void
 format_type_warning (location_t fmt_loc, source_range fmt_substring_range,
 		     source_range *param_range,
 		     format_wanted_type *type,
-		     tree wanted_type, tree arg_type)
+		     tree wanted_type, tree arg_type,
+		     const format_kind_info *fki)
 {
-  int kind = type->kind;
+  enum format_specifier_kind kind = type->kind;
   const char *wanted_type_name = type->wanted_type_name;
   const char *format_start = type->format_start;
   int format_length = type->format_length;
@@ -2718,11 +2836,17 @@ format_type_warning (location_t fmt_loc, source_range fmt_substring_range,
       p[pointer_count + 1] = 0;
     }
 
+  /* Attempt to provide hints for argument types, but not for field widths
+     and precisions.  */
+  char *format_for_type = NULL;
+  if (arg_type && kind == CF_KIND_FORMAT)
+    format_for_type = get_format_for_type (fki, arg_type);
+
   if (wanted_type_name)
     {
       if (arg_type)
 	format_warning (fmt_loc, fmt_substring_range, param_range,
-			OPT_Wformat_,
+			format_for_type, OPT_Wformat_,
 			"%s %<%s%.*s%> expects argument of type %<%s%s%>, "
 			"but argument %d has type %qT",
 			gettext (kind_descriptions[kind]),
@@ -2731,7 +2855,7 @@ format_type_warning (location_t fmt_loc, source_range fmt_substring_range,
 			wanted_type_name, p, arg_num, arg_type);
       else
 	format_warning (fmt_loc, fmt_substring_range, param_range,
-			OPT_Wformat_,
+			format_for_type, OPT_Wformat_,
 			"%s %<%s%.*s%> expects a matching %<%s%s%> argument",
 			gettext (kind_descriptions[kind]),
 			(kind == CF_KIND_FORMAT ? "%" : ""),
@@ -2741,7 +2865,7 @@ format_type_warning (location_t fmt_loc, source_range fmt_substring_range,
     {
       if (arg_type)
 	format_warning (fmt_loc, fmt_substring_range, param_range,
-			OPT_Wformat_,
+			format_for_type, OPT_Wformat_,
 			"%s %<%s%.*s%> expects argument of type %<%T%s%>, "
 			"but argument %d has type %qT",
 			gettext (kind_descriptions[kind]),
@@ -2750,12 +2874,14 @@ format_type_warning (location_t fmt_loc, source_range fmt_substring_range,
 			wanted_type, p, arg_num, arg_type);
       else
 	format_warning (fmt_loc, fmt_substring_range, param_range,
-			OPT_Wformat_,
+			format_for_type, OPT_Wformat_,
 			"%s %<%s%.*s%> expects a matching %<%T%s%> argument",
 			gettext (kind_descriptions[kind]),
 			(kind == CF_KIND_FORMAT ? "%" : ""),
 			format_length, format_start, wanted_type, p);
     }
+
+  free (format_for_type);
 }
 
 
@@ -3276,3 +3402,80 @@ handle_format_attribute (tree *node, tree ARG_UNUSED (name), tree args,
 
   return NULL_TREE;
 }
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Selftests of location handling.  */
+
+/* Get the format_kind_info with the given name.  */
+
+static const format_kind_info *
+get_info (const char *name)
+{
+  int idx = decode_format_type (name);
+  const format_kind_info *fki = &format_types[idx];
+  ASSERT_STREQ (fki->name, name);
+  return fki;
+}
+
+/* Verify that get_format_for_type (FKI, TYPE) is EXPECTED_FORMAT.  */
+
+static void
+assert_format_for_type_streq (const location &loc, const format_kind_info *fki,
+			      const char *expected_format, tree type)
+{
+  gcc_assert (fki);
+  gcc_assert (expected_format);
+  gcc_assert (type);
+
+  char *actual_format = get_format_for_type (fki, type);
+  ASSERT_STREQ_AT (loc, expected_format, actual_format);
+  free (actual_format);
+}
+
+/* Selftest for get_format_for_type.  */
+
+static void
+test_get_format_for_type ()
+{
+#define ASSERT_FORMAT_FOR_TYPE_STREQ(EXPECTED_FORMAT, TYPE) \
+  assert_format_for_type_streq (SELFTEST_LOCATION, (fki), (EXPECTED_FORMAT), (TYPE))
+
+  const format_kind_info *fki = get_info ("gnu_printf");
+  ASSERT_NE (fki, NULL);
+
+  //ASSERT_FORMAT_FOR_TYPE_STREQ ("%d", float_type_node);
+  ASSERT_FORMAT_FOR_TYPE_STREQ ("%f", double_type_node);
+  ASSERT_FORMAT_FOR_TYPE_STREQ ("%Lf", long_double_type_node);
+  //ASSERT_FORMAT_FOR_TYPE_STREQ ("%c", char_type_node);
+  //ASSERT_FORMAT_FOR_TYPE_STREQ ("%c", short_integer_type_node);
+  ASSERT_FORMAT_FOR_TYPE_STREQ ("%d", integer_type_node);
+  ASSERT_FORMAT_FOR_TYPE_STREQ ("%o", unsigned_type_node);
+  ASSERT_FORMAT_FOR_TYPE_STREQ ("%ld", long_integer_type_node);
+  ASSERT_FORMAT_FOR_TYPE_STREQ ("%lo", long_unsigned_type_node);
+  ASSERT_FORMAT_FOR_TYPE_STREQ ("%lld", long_long_integer_type_node);
+  ASSERT_FORMAT_FOR_TYPE_STREQ ("%llo", long_long_unsigned_type_node);
+  ASSERT_FORMAT_FOR_TYPE_STREQ ("%s", build_pointer_type (char_type_node));
+
+  // TODO: other format kinds
+
+  fki = get_info ("gnu_scanf");
+  ASSERT_NE (fki, NULL);
+  ASSERT_FORMAT_FOR_TYPE_STREQ ("%d", build_pointer_type (integer_type_node));
+
+#undef ASSERT_FORMAT_FOR_TYPE_STREQ
+}
+
+/* Run all of the selftests within this file.  */
+
+void
+c_format_c_tests ()
+{
+  test_get_format_for_type ();
+}
+
+} // namespace selftest
+
+#endif /* CHECKING_P */
diff --git a/gcc/c/c-lang.c b/gcc/c/c-lang.c
index 89954b7..b26be6a 100644
--- a/gcc/c/c-lang.c
+++ b/gcc/c/c-lang.c
@@ -38,7 +38,29 @@ enum c_language_kind c_language = clk_c;
 #undef LANG_HOOKS_INIT_TS
 #define LANG_HOOKS_INIT_TS c_common_init_ts
 
+#if CHECKING_P
+#undef LANG_HOOKS_RUN_LANG_SELFTESTS
+#define LANG_HOOKS_RUN_LANG_SELFTESTS selftest::run_c_tests
+#endif /* #if CHECKING_P */
+
 /* Each front end provides its own lang hook initializer.  */
 struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER;
 
+#if CHECKING_P
+
+namespace selftest {
+
+/* Implementation of LANG_HOOKS_RUN_LANG_SELFTESTS for the C frontend.  */
+
+void
+run_c_tests (void)
+{
+  c_format_c_tests ();
+}
+
+} // namespace selftest
+
+#endif /* #if CHECKING_P */
+
+
 #include "gtype-c.h"
diff --git a/gcc/langhooks-def.h b/gcc/langhooks-def.h
index 034b3b7..c17f998 100644
--- a/gcc/langhooks-def.h
+++ b/gcc/langhooks-def.h
@@ -120,6 +120,7 @@ extern bool lhd_omp_mappable_type (tree);
 #define LANG_HOOKS_BLOCK_MAY_FALLTHRU	hook_bool_const_tree_true
 #define LANG_HOOKS_EH_USE_CXA_END_CLEANUP	false
 #define LANG_HOOKS_DEEP_UNSHARING	false
+#define LANG_HOOKS_RUN_LANG_SELFTESTS   lhd_do_nothing
 
 /* Attribute hooks.  */
 #define LANG_HOOKS_ATTRIBUTE_TABLE		NULL
@@ -319,7 +320,8 @@ extern void lhd_end_section (void);
   LANG_HOOKS_EH_PROTECT_CLEANUP_ACTIONS, \
   LANG_HOOKS_BLOCK_MAY_FALLTHRU, \
   LANG_HOOKS_EH_USE_CXA_END_CLEANUP, \
-  LANG_HOOKS_DEEP_UNSHARING \
+  LANG_HOOKS_DEEP_UNSHARING, \
+  LANG_HOOKS_RUN_LANG_SELFTESTS \
 }
 
 #endif /* GCC_LANG_HOOKS_DEF_H */
diff --git a/gcc/langhooks.h b/gcc/langhooks.h
index 0593424..150ddce 100644
--- a/gcc/langhooks.h
+++ b/gcc/langhooks.h
@@ -505,6 +505,9 @@ struct lang_hooks
      gimplification.  */
   bool deep_unsharing;
 
+  /* FIXME.  */
+  void (*run_lang_selftests) (void);
+
   /* Whenever you add entries here, make sure you adjust langhooks-def.h
      and langhooks.c accordingly.  */
 };
diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c
index bddf0b2..c759525 100644
--- a/gcc/selftest-run-tests.c
+++ b/gcc/selftest-run-tests.c
@@ -21,6 +21,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "system.h"
 #include "coretypes.h"
 #include "selftest.h"
+#include "tree.h"
+#include "langhooks.h"
 
 /* This function needed to be split out from selftest.c as it references
    tests from the whole source tree, and so is within
@@ -68,6 +70,9 @@ selftest::run_tests ()
   /* This one relies on most of the above.  */
   function_tests_c_tests ();
 
+  /* Run any lang-specific selftests.  */
+  lang_hooks.run_lang_selftests ();
+
   /* Finished running tests.  */
   long finish_time = get_run_time ();
   long elapsed_time = finish_time - start_time;
diff --git a/gcc/selftest.c b/gcc/selftest.c
index ed6e517..4f5ea29 100644
--- a/gcc/selftest.c
+++ b/gcc/selftest.c
@@ -67,6 +67,10 @@ selftest::assert_streq (const location &loc,
 			const char *desc_expected, const char *desc_actual,
 			const char *val_expected, const char *val_actual)
 {
+  if (val_actual == NULL)
+    ::selftest::fail_formatted
+	(loc, "ASSERT_STREQ (%s, %s) expected=\"%s\" actual=NULL",
+	 desc_expected, desc_actual, val_expected);
   if (0 == strcmp (val_expected, val_actual))
     ::selftest::pass (loc, "ASSERT_STREQ");
   else
diff --git a/gcc/testsuite/gcc.dg/format/diagnostic-ranges.c b/gcc/testsuite/gcc.dg/format/diagnostic-ranges.c
index 631b68d..87e3b18 100644
--- a/gcc/testsuite/gcc.dg/format/diagnostic-ranges.c
+++ b/gcc/testsuite/gcc.dg/format/diagnostic-ranges.c
@@ -7,11 +7,29 @@
 void test_mismatching_types (const char *msg)
 {
   printf("hello %i", msg);  /* { dg-warning "format '%i' expects argument of type 'int', but argument 2 has type 'const char \\*' " } */
-
 /* TODO: ideally would also underline "msg".  */
 /* { dg-begin-multiline-output "" }
    printf("hello %i", msg);
                  ~^
+                 %s
+   { dg-end-multiline-output "" } */
+
+
+  printf("hello %s", 42);  /* { dg-warning "format '%s' expects argument of type 'char \\*', but argument 2 has type 'int'" } */
+/* TODO: ideally would also underline "42".  */
+/* { dg-begin-multiline-output "" }
+   printf("hello %s", 42);
+                 ~^
+                 %d
+   { dg-end-multiline-output "" } */
+
+
+  printf("hello %i", (long)0);  /* { dg-warning "format '%i' expects argument of type 'int', but argument 2 has type 'long int' " } */
+/* TODO: ideally would also underline the argument.  */
+/* { dg-begin-multiline-output "" }
+   printf("hello %i", (long)0);
+                 ~^
+                 %ld
    { dg-end-multiline-output "" } */
 }
 
@@ -23,6 +41,7 @@ void test_multiple_arguments (void)
 /* { dg-begin-multiline-output "" }
    printf ("arg0: %i  arg1: %s arg 2: %i",
                             ~^
+                            %d
    { dg-end-multiline-output "" } */
 }
 
@@ -33,6 +52,7 @@ void test_multiple_arguments_2 (int i, int j)
 /* { dg-begin-multiline-output "" }
    printf ("arg0: %i  arg1: %s arg 2: %i",
                             ~^
+                            %d
            100, i + j, 102);
                 ~~~~~         
    { dg-end-multiline-output "" } */
@@ -67,6 +87,7 @@ void test_hex (const char *msg)
 /* { dg-begin-multiline-output "" }
    printf("hello \x25\x69", msg);
                  ~~~~~~~^
+                 %s
    { dg-end-multiline-output "" } */
 }
 
@@ -80,6 +101,7 @@ void test_oct (const char *msg)
 /* { dg-begin-multiline-output "" }
    printf("hello \045\151", msg);
                  ~~~~~~~^
+                 %s
    { dg-end-multiline-output "" } */
 }
 
@@ -98,6 +120,7 @@ void test_multiple (const char *msg)
 /* { dg-begin-multiline-output "" }
    printf("prefix"  "\x25"  "\151"  "suffix",
                      ~~~~~~~~~~~^
+                     %s
   { dg-end-multiline-output "" } */
 }
 
@@ -108,6 +131,7 @@ void test_u8 (const char *msg)
 /* { dg-begin-multiline-output "" }
    printf(u8"hello %i", msg);
                    ~^
+                   %s
    { dg-end-multiline-output "" } */
 }
 
@@ -117,6 +141,7 @@ void test_array (long long_value)
 /* { dg-begin-multiline-output "" }
    const char array[] = "foo %d bar";
                              ~^
+                             %ld
    { dg-end-multiline-output "" } */
 
   printf (array, long_value); /* { dg-warning "format '%d' expects argument of type 'int', but argument 2 has type 'long int' " } */
@@ -132,12 +157,14 @@ void test_param (long long_i, long long_j)
 /* { dg-begin-multiline-output "" }
    printf ("foo %s bar", long_i + long_j);
                 ~^       ~~~~~~~~~~~~~~~
+                %ld
    { dg-end-multiline-output "" } */
 
   const char array[] = "foo %s bar"; /* { dg-message "30: format string is defined here" } */
 /* { dg-begin-multiline-output "" }
    const char array[] = "foo %s bar";
                              ~^
+                             %ld
    { dg-end-multiline-output "" } */
 
   printf (array, long_i + long_j); /* { dg-warning "11: format '%s' expects argument of type 'char \\*', but argument 2 has type 'long int'" } */
-- 
1.8.5.3

