1  /* { dg-options "-Wformat -fdiagnostics-show-caret" } */
       2  
       3  /* See PR 52952. */
       4  
       5  #include "format.h"
       6  
       7  void test_mismatching_types (const char *msg)
       8  {
       9    printf("hello %i", msg);  /* { dg-warning "format '%i' expects argument of type 'int', but argument 2 has type 'const char \\*' " } */
      10  
      11  /* { dg-begin-multiline-output "" }
      12     printf("hello %i", msg);
      13                   ~^   ~~~
      14                    |   |
      15                    int const char *
      16                   %s
      17     { dg-end-multiline-output "" } */
      18  
      19  
      20    printf("hello %s", 42);  /* { dg-warning "format '%s' expects argument of type 'char \\*', but argument 2 has type 'int'" } */
      21  /* { dg-begin-multiline-output "" }
      22     printf("hello %s", 42);
      23                   ~^   ~~
      24                    |   |
      25                    |   int
      26                    char *
      27                   %d
      28     { dg-end-multiline-output "" } */
      29  
      30    printf("hello %i", (long)0);  /* { dg-warning "format '%i' expects argument of type 'int', but argument 2 has type 'long int' " } */
      31  /* { dg-begin-multiline-output "" }
      32     printf("hello %i", (long)0);
      33                   ~^   ~~~~~~~
      34                    |   |
      35                    int long int
      36                   %li
      37     { dg-end-multiline-output "" } */
      38  }
      39  
      40  void test_multiple_arguments (void)
      41  {
      42    printf ("arg0: %i  arg1: %s arg 2: %i", /* { dg-warning "29: format '%s'" } */
      43            100, 101, 102);
      44  /* { dg-begin-multiline-output "" }
      45     printf ("arg0: %i  arg1: %s arg 2: %i",
      46                              ~^
      47                               |
      48                               char *
      49                              %d
      50             100, 101, 102);
      51                  ~~~           
      52                  |
      53                  int
      54     { dg-end-multiline-output "" } */
      55  }
      56  
      57  void test_multiple_arguments_2 (int i, int j)
      58  {
      59    printf ("arg0: %i  arg1: %s arg 2: %i", /* { dg-warning "29: format '%s'" } */
      60            100, i + j, 102);
      61  /* { dg-begin-multiline-output "" }
      62     printf ("arg0: %i  arg1: %s arg 2: %i",
      63                              ~^
      64                               |
      65                               char *
      66                              %d
      67             100, i + j, 102);
      68                  ~~~~~         
      69                    |
      70                    int
      71     { dg-end-multiline-output "" } */
      72  }
      73  
      74  void multiline_format_string (void) {
      75    printf ("before the fmt specifier" /* { dg-warning "11: format '%d' expects a matching 'int' argument" } */
      76  /* { dg-begin-multiline-output "" }
      77     printf ("before the fmt specifier"
      78             ^~~~~~~~~~~~~~~~~~~~~~~~~~
      79     { dg-end-multiline-output "" } */
      80  
      81            "%"
      82            "d" /* { dg-message "12: format string is defined here" } */
      83            "after the fmt specifier");
      84  
      85  /* { dg-begin-multiline-output "" }
      86             "%"
      87              ~~
      88             "d"
      89             ~^
      90              |
      91              int
      92     { dg-end-multiline-output "" } */
      93  }
      94  
      95  void test_hex (const char *msg)
      96  {
      97    /* "%" is \x25
      98       "i" is \x69 */
      99    printf("hello \x25\x69", msg);  /* { dg-warning "format '%i' expects argument of type 'int', but argument 2 has type 'const char \\*' " } */
     100  
     101  /* { dg-begin-multiline-output "" }
     102     printf("hello \x25\x69", msg);
     103                   ~~~~^~~~   ~~~
     104                       |      |
     105                       int    const char *
     106                   \x25s
     107     { dg-end-multiline-output "" } */
     108  }
     109  
     110  void test_oct (const char *msg)
     111  {
     112    /* "%" is octal 045
     113       "i" is octal 151.  */
     114    printf("hello \045\151", msg);  /* { dg-warning "format '%i' expects argument of type 'int', but argument 2 has type 'const char \\*' " } */
     115  
     116  /* { dg-begin-multiline-output "" }
     117     printf("hello \045\151", msg);
     118                   ~~~~^~~~   ~~~
     119                       |      |
     120                       int    const char *
     121                   \045s
     122     { dg-end-multiline-output "" } */
     123  }
     124  
     125  void test_multiple (const char *msg)
     126  {
     127    /* "%" is \x25 in hex
     128       "i" is \151 in octal.  */
     129    printf("prefix"  "\x25"  "\151"  "suffix",  /* { dg-warning "format '%i'" } */
     130           msg);
     131  /* { dg-begin-multiline-output "" }
     132     printf("prefix"  "\x25"  "\151"  "suffix",
     133            ^~~~~~~~
     134            msg);
     135            ~~~
     136            |
     137            const char *
     138    { dg-end-multiline-output "" } */
     139  
     140  /* { dg-begin-multiline-output "" }
     141     printf("prefix"  "\x25"  "\151"  "suffix",
     142                       ~~~~~~~~^~~~
     143                               |
     144                               int
     145                       \x25"  "s
     146    { dg-end-multiline-output "" } */
     147  }
     148  
     149  void test_u8 (const char *msg)
     150  {
     151    printf(u8"hello %i", msg);/* { dg-warning "format '%i' expects argument of type 'int', but argument 2 has type 'const char \\*' " } */
     152  /* { dg-begin-multiline-output "" }
     153     printf(u8"hello %i", msg);
     154                     ~^   ~~~
     155                      |   |
     156                      int const char *
     157                     %s
     158     { dg-end-multiline-output "" } */
     159  }
     160  
     161  void test_param (long long_i, long long_j)
     162  {
     163    printf ("foo %s bar", long_i + long_j); /* { dg-warning "17: format '%s' expects argument of type 'char \\*', but argument 2 has type 'long int'" } */
     164  /* { dg-begin-multiline-output "" }
     165     printf ("foo %s bar", long_i + long_j);
     166                  ~^       ~~~~~~~~~~~~~~~
     167                   |              |
     168                   char *         long int
     169                  %ld
     170     { dg-end-multiline-output "" } */
     171  }
     172  
     173  void test_field_width_specifier (long l, int i1, int i2)
     174  {
     175    printf (" %*.*d ", l, i1, i2); /* { dg-warning "14: field width specifier '\\*' expects argument of type 'int', but argument 2 has type 'long int'" } */
     176  /* { dg-begin-multiline-output "" }
     177     printf (" %*.*d ", l, i1, i2);
     178               ~^~~~    ~
     179                |       |
     180                int     long int
     181     { dg-end-multiline-output "" } */
     182  }
     183  
     184  /* PR c/72857.  */
     185  
     186  void test_field_width_specifier_2 (char *d, long foo, long bar)
     187  {
     188    __builtin_sprintf (d, " %*ld ", foo, foo); /* { dg-warning "28: field width specifier '\\*' expects argument of type 'int', but argument 3 has type 'long int'" } */
     189    /* { dg-begin-multiline-output "" }
     190     __builtin_sprintf (d, " %*ld ", foo, foo);
     191                             ~^~~    ~~~
     192                              |      |
     193                              int    long int
     194     { dg-end-multiline-output "" } */
     195  
     196    __builtin_sprintf (d, " %*ld ", foo + bar, foo); /* { dg-warning "28: field width specifier '\\*' expects argument of type 'int', but argument 3 has type 'long int'" } */
     197    /* { dg-begin-multiline-output "" }
     198     __builtin_sprintf (d, " %*ld ", foo + bar, foo);
     199                             ~^~~    ~~~~~~~~~
     200                              |          |
     201                              int        long int
     202     { dg-end-multiline-output "" } */
     203  }
     204  
     205  void test_field_precision_specifier (char *d, long foo, long bar)
     206  {
     207    __builtin_sprintf (d, " %.*ld ", foo, foo); /* { dg-warning "29: field precision specifier '\\.\\*' expects argument of type 'int', but argument 3 has type 'long int'" } */
     208    /* { dg-begin-multiline-output "" }
     209     __builtin_sprintf (d, " %.*ld ", foo, foo);
     210                             ~~^~~    ~~~
     211                               |      |
     212                               int    long int
     213     { dg-end-multiline-output "" } */
     214  
     215    __builtin_sprintf (d, " %.*ld ", foo + bar, foo); /* { dg-warning "29: field precision specifier '\\.\\*' expects argument of type 'int', but argument 3 has type 'long int'" } */
     216    /* { dg-begin-multiline-output "" }
     217     __builtin_sprintf (d, " %.*ld ", foo + bar, foo);
     218                             ~~^~~    ~~~~~~~~~
     219                               |          |
     220                               int        long int
     221     { dg-end-multiline-output "" } */
     222  }
     223  
     224  void test_spurious_percent (void)
     225  {
     226    printf("hello world %"); /* { dg-warning "23: spurious trailing" } */
     227  
     228  /* { dg-begin-multiline-output "" }
     229     printf("hello world %");
     230                         ^
     231     { dg-end-multiline-output "" } */
     232  }
     233  
     234  void test_empty_precision (char *s, size_t m, double d)
     235  {
     236    strfmon (s, m, "%#.5n", d); /* { dg-warning "20: empty left precision in gnu_strfmon format" } */
     237  /* { dg-begin-multiline-output "" }
     238     strfmon (s, m, "%#.5n", d);
     239                      ^
     240     { dg-end-multiline-output "" } */
     241  
     242    strfmon (s, m, "%#5.n", d); /* { dg-warning "22: empty precision in gnu_strfmon format" } */
     243  /* { dg-begin-multiline-output "" }
     244     strfmon (s, m, "%#5.n", d);
     245                        ^
     246     { dg-end-multiline-output "" } */
     247  }
     248  
     249  void test_repeated (int i)
     250  {
     251    printf ("%++d", i); /* { dg-warning "14: repeated '\\+' flag in format" } */
     252  /* { dg-begin-multiline-output "" }
     253     printf ("%++d", i);
     254                ^
     255     { dg-end-multiline-output "" } */
     256  }
     257  
     258  void test_conversion_lacks_type (void)
     259  {
     260    printf (" %h"); /* { dg-warning "14:conversion lacks type at end of format" } */
     261  /* { dg-begin-multiline-output "" }
     262     printf (" %h");
     263                ^
     264     { dg-end-multiline-output "" } */
     265  }
     266  
     267  void test_embedded_nul (void)
     268  {
     269    printf (" \0 "); /* { dg-warning "13:embedded" "warning for embedded NUL" } */
     270  /* { dg-begin-multiline-output "" }
     271     printf (" \0 ");
     272               ^~
     273     { dg-end-multiline-output "" } */
     274  }
     275  
     276  void test_macro (const char *msg)
     277  {
     278  #define INT_FMT "%i" /* { dg-message "19: format string is defined here" } */
     279    printf("hello " INT_FMT " world", msg);  /* { dg-warning "10: format '%i' expects argument of type 'int', but argument 2 has type 'const char \\*' " } */
     280  /* { dg-begin-multiline-output "" }
     281     printf("hello " INT_FMT " world", msg);
     282            ^~~~~~~~                   ~~~
     283                                       |
     284                                       const char *
     285     { dg-end-multiline-output "" } */
     286  /* { dg-begin-multiline-output "" }
     287   #define INT_FMT "%i"
     288                    ~^
     289                     |
     290                     int
     291                    %s
     292     { dg-end-multiline-output "" } */
     293  #undef INT_FMT
     294  }
     295  
     296  void test_macro_2 (const char *msg)
     297  {
     298  #define PRIu32 "u" /* { dg-message "17: format string is defined here" } */
     299    printf("hello %" PRIu32 " world", msg);  /* { dg-warning "10: format '%u' expects argument of type 'unsigned int', but argument 2 has type 'const char \\*' " } */
     300  /* { dg-begin-multiline-output "" }
     301     printf("hello %" PRIu32 " world", msg);
     302            ^~~~~~~~~                  ~~~
     303                                       |
     304                                       const char *
     305     { dg-end-multiline-output "" } */
     306  /* { dg-begin-multiline-output "" }
     307   #define PRIu32 "u"
     308                   ^
     309                   |
     310                   unsigned int
     311     { dg-end-multiline-output "" } */
     312  #undef PRIu32
     313  }
     314  
     315  void test_macro_3 (const char *msg)
     316  {
     317  #define FMT_STRING "hello %i world" /* { dg-line test_macro_3_macro_line } */
     318    /* { dg-warning "20: format '%i' expects argument of type 'int', but argument 2 has type 'const char \\*'" "" { target *-*-*} .-1 } */
     319    printf(FMT_STRING, msg);  /* { dg-message "10: in expansion of macro 'FMT_STRING" } */
     320  /* { dg-begin-multiline-output "" }
     321   #define FMT_STRING "hello %i world"
     322                      ^~~~~~~~~~~~~~~~
     323     { dg-end-multiline-output "" } */
     324  /* { dg-begin-multiline-output "" }
     325     printf(FMT_STRING, msg);
     326            ^~~~~~~~~~
     327     { dg-end-multiline-output "" } */
     328  /* { dg-message "28: format string is defined here" "" { target *-*-* } test_macro_3_macro_line } */
     329  /* { dg-begin-multiline-output "" }
     330   #define FMT_STRING "hello %i world"
     331                             ~^
     332                              |
     333                              int
     334                             %s
     335     { dg-end-multiline-output "" } */
     336  #undef FMT_STRING
     337  }
     338  
     339  void test_macro_4 (const char *msg)
     340  {
     341  #define FMT_STRING "hello %i world" /* { dg-warning "20: format '%i' expects argument of type 'int', but argument 2 has type 'const char \\*' " } */
     342    printf(FMT_STRING "\n", msg);  /* { dg-message "10: in expansion of macro 'FMT_STRING" } */
     343  /* { dg-begin-multiline-output "" }
     344   #define FMT_STRING "hello %i world"
     345                      ^~~~~~~~~~~~~~~~
     346     { dg-end-multiline-output "" } */
     347  /* { dg-begin-multiline-output "" }
     348     printf(FMT_STRING "\n", msg);
     349            ^~~~~~~~~~
     350     { dg-end-multiline-output "" } */
     351  /* { dg-begin-multiline-output "" }
     352   #define FMT_STRING "hello %i world"
     353                             ~^
     354                              |
     355                              int
     356                             %s
     357     { dg-end-multiline-output "" } */
     358  #undef FMT_STRING
     359  }
     360  
     361  void test_non_contiguous_strings (void)
     362  {
     363    __builtin_printf(" %" "d ", 0.5); /* { dg-warning "20: format .%d. expects argument of type .int., but argument 2 has type .double." } */
     364                                      /* { dg-message "26: format string is defined here" "" { target *-*-* } .-1 } */
     365    /* { dg-begin-multiline-output "" }
     366     __builtin_printf(" %" "d ", 0.5);
     367                      ^~~~       ~~~
     368                                 |
     369                                 double
     370     { dg-end-multiline-output "" } */
     371    /* { dg-begin-multiline-output "" }
     372     __builtin_printf(" %" "d ", 0.5);
     373                        ~~~~^
     374                            |
     375                            int
     376                        %" "f
     377     { dg-end-multiline-output "" } */
     378  }
     379  
     380  void test_const_arrays (void)
     381  {
     382    /* TODO: ideally we'd highlight both the format string *and* the use of
     383       it here.  For now, just verify that we gracefully handle this case.  */
     384    const char a[] = " %d ";
     385    __builtin_printf(a, 0.5); /* { dg-warning "20: format .%d. expects argument of type .int., but argument 2 has type .double." } */
     386    /* { dg-begin-multiline-output "" }
     387     __builtin_printf(a, 0.5);
     388                      ^  ~~~
     389                         |
     390                         double
     391     { dg-end-multiline-output "" } */
     392  }