reference, declarationdefinition
definition → references, declarations, derived classes, virtual overrides
reference to multiple definitions → definitions
unreferenced
    1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
  196
  197
  198
  199
  200
  201
  202
  203
  204
  205
  206
  207
  208
  209
  210
  211
  212
  213
  214
  215
  216
  217
  218
  219
  220
  221
  222
  223
  224
  225
  226
  227
  228
  229
  230
  231
  232
  233
  234
  235
  236
  237
  238
  239
  240
  241
  242
  243
// RUN: %clang_cc1 -std=c11 -fsyntax-only -verify -Wformat-nonliteral %s

// Test that -Wformat=0 works:
// RUN: %clang_cc1 -std=c11 -fsyntax-only -Werror -Wformat=0 %s

#include <stdarg.h>
typedef __SIZE_TYPE__ size_t;
#define __SSIZE_TYPE__                                                         \
  __typeof__(_Generic((__SIZE_TYPE__)0,                                        \
                      unsigned long long int : (long long int)0,               \
                      unsigned long int : (long int)0,                         \
                      unsigned int : (int)0,                                   \
                      unsigned short : (short)0,                               \
                      unsigned char : (signed char)0))
typedef __SSIZE_TYPE__ ssize_t;                         

typedef __PTRDIFF_TYPE__ ptrdiff_t;
#define __UNSIGNED_PTRDIFF_TYPE__                                              \
  __typeof__(_Generic((__PTRDIFF_TYPE__)0,                                     \
                      long long int : (unsigned long long int)0,               \
                      long int : (unsigned long int)0,                         \
                      int : (unsigned int)0,                                   \
                      short : (unsigned short)0,                               \
                      signed char : (unsigned char)0))

typedef struct _FILE FILE;
typedef __WCHAR_TYPE__ wchar_t;

int fscanf(FILE * restrict, const char * restrict, ...) ;
int scanf(const char * restrict, ...) ;
int sscanf(const char * restrict, const char * restrict, ...) ;
int my_scanf(const char * restrict, ...) __attribute__((__format__(__scanf__, 1, 2)));

int vscanf(const char * restrict, va_list);
int vfscanf(FILE * restrict, const char * restrict, va_list);
int vsscanf(const char * restrict, const char * restrict, va_list);

void test(const char *s, int *i) {
  scanf(s, i); // expected-warning{{format string is not a string literal}}
  scanf("%0d", i); // expected-warning{{zero field width in scanf format string is unused}}
  scanf("%00d", i); // expected-warning{{zero field width in scanf format string is unused}}
  scanf("%d%[asdfasdfd", i, s); // expected-warning{{no closing ']' for '%[' in scanf format string}}

  unsigned short s_x;
  scanf ("%" "hu" "\n", &s_x); // no-warning
  scanf("%y", i); // expected-warning{{invalid conversion specifier 'y'}}
  scanf("%%"); // no-warning
  scanf("%%%1$d", i); // no-warning
  scanf("%1$d%%", i); // no-warning
  scanf("%d", i, i); // expected-warning{{data argument not used by format string}}
  scanf("%*d", i); // // expected-warning{{data argument not used by format string}}
  scanf("%*d", i); // // expected-warning{{data argument not used by format string}}
  scanf("%*d%1$d", i); // no-warning

  scanf("%s", (char*)0); // no-warning
  scanf("%s", (volatile char*)0); // no-warning
  scanf("%s", (signed char*)0); // no-warning
  scanf("%s", (unsigned char*)0); // no-warning
  scanf("%hhu", (signed char*)0); // no-warning
}

void bad_length_modifiers(char *s, void *p, wchar_t *ws, long double *ld) {
  scanf("%hhs", "foo"); // expected-warning{{length modifier 'hh' results in undefined behavior or no effect with 's' conversion specifier}}
  scanf("%1$zp", &p); // expected-warning{{length modifier 'z' results in undefined behavior or no effect with 'p' conversion specifier}}
  scanf("%ls", ws); // no-warning
  scanf("%#.2Lf", ld); // expected-warning{{invalid conversion specifier '#'}}
}

// Test that the scanf call site is where the warning is attached.  If the
// format string is somewhere else, point to it in a note.
void pr9751() {
  int *i;
  char str[100];
  const char kFormat1[] = "%00d"; // expected-note{{format string is defined here}}}
  scanf(kFormat1, i); // expected-warning{{zero field width in scanf format string is unused}}
  scanf("%00d", i); // expected-warning{{zero field width in scanf format string is unused}}
  const char kFormat2[] = "%["; // expected-note{{format string is defined here}}}
  scanf(kFormat2, str); // expected-warning{{no closing ']' for '%[' in scanf format string}}
  scanf("%[", str); // expected-warning{{no closing ']' for '%[' in scanf format string}}
  const char kFormat3[] = "%hu"; // expected-note{{format string is defined here}}}
  scanf(kFormat3, &i); // expected-warning {{format specifies type 'unsigned short *' but the argument}}
  const char kFormat4[] = "%lp"; // expected-note{{format string is defined here}}}
  scanf(kFormat4, &i); // expected-warning {{length modifier 'l' results in undefined behavior or no effect with 'p' conversion specifier}}
}

void test_variants(int *i, const char *s, ...) {
  FILE *f = 0;
  char buf[100];

  fscanf(f, "%ld", i); // expected-warning{{format specifies type 'long *' but the argument has type 'int *'}}
  sscanf(buf, "%ld", i); // expected-warning{{format specifies type 'long *' but the argument has type 'int *'}}
  my_scanf("%ld", i); // expected-warning{{format specifies type 'long *' but the argument has type 'int *'}}

  va_list ap;
  va_start(ap, s);

  vscanf("%[abc", ap); // expected-warning{{no closing ']' for '%[' in scanf format string}}
  vfscanf(f, "%[abc", ap); // expected-warning{{no closing ']' for '%[' in scanf format string}}
  vsscanf(buf, "%[abc", ap); // expected-warning{{no closing ']' for '%[' in scanf format string}}
}

void test_scanlist(int *ip, char *sp, wchar_t *ls) {
  scanf("%[abc]", ip); // expected-warning{{format specifies type 'char *' but the argument has type 'int *'}}
  scanf("%h[abc]", sp); // expected-warning{{length modifier 'h' results in undefined behavior or no effect with '[' conversion specifier}}
  scanf("%l[xyx]", ls); // no-warning
  scanf("%ll[xyx]", ls); // expected-warning {{length modifier 'll' results in undefined behavior or no effect with '[' conversion specifier}}

  // PR19559
  scanf("%[]% ]", sp); // no-warning
  scanf("%[^]% ]", sp); // no-warning
  scanf("%[a^]% ]", sp); // expected-warning {{invalid conversion specifier ' '}}
}

void test_alloc_extension(char **sp, wchar_t **lsp, float *fp) {
  /* Make sure "%a" gets parsed as a conversion specifier for float,
   * even when followed by an 's', 'S' or '[', which would cause it to be
   * parsed as a length modifier in C90. */
  scanf("%as", sp); // expected-warning{{format specifies type 'float *' but the argument has type 'char **'}}
  scanf("%aS", lsp); // expected-warning{{format specifies type 'float *' but the argument has type 'wchar_t **'}}
  scanf("%a[bcd]", sp); // expected-warning{{format specifies type 'float *' but the argument has type 'char **'}}

  // Test that the 'm' length modifier is only allowed with s, S, c, C or [.
  // TODO: Warn that 'm' is an extension.
  scanf("%ms", sp); // No warning.
  scanf("%mS", lsp); // No warning.
  scanf("%mc", sp); // No warning.
  scanf("%mC", lsp); // No warning.
  scanf("%m[abc]", sp); // No warning.
  scanf("%md", sp); // expected-warning{{length modifier 'm' results in undefined behavior or no effect with 'd' conversion specifier}}

  // Test argument type check for the 'm' length modifier.
  scanf("%ms", fp); // expected-warning{{format specifies type 'char **' but the argument has type 'float *'}}
  scanf("%mS", fp); // expected-warning-re{{format specifies type 'wchar_t **' (aka '{{[^']+}}') but the argument has type 'float *'}}
  scanf("%mc", fp); // expected-warning{{format specifies type 'char **' but the argument has type 'float *'}}
  scanf("%mC", fp); // expected-warning-re{{format specifies type 'wchar_t **' (aka '{{[^']+}}') but the argument has type 'float *'}}
  scanf("%m[abc]", fp); // expected-warning{{format specifies type 'char **' but the argument has type 'float *'}}
}

void test_quad(int *x, long long *llx) {
  scanf("%qd", x); // expected-warning{{format specifies type 'long long *' but the argument has type 'int *'}}
  scanf("%qd", llx); // no-warning
}

void test_writeback(int *x) {
  scanf("%n", (void*)0); // expected-warning{{format specifies type 'int *' but the argument has type 'void *'}}
  scanf("%n %c", x, x); // expected-warning{{format specifies type 'char *' but the argument has type 'int *'}}

  scanf("%hhn", (signed char*)0); // no-warning
  scanf("%hhn", (char*)0); // no-warning
  scanf("%hhn", (unsigned char*)0); // no-warning
  scanf("%hhn", (int*)0); // expected-warning{{format specifies type 'signed char *' but the argument has type 'int *'}}

  scanf("%hn", (short*)0); // no-warning
  scanf("%hn", (unsigned short*)0); // no-warning
  scanf("%hn", (int*)0); // expected-warning{{format specifies type 'short *' but the argument has type 'int *'}}

  scanf("%n", (int*)0); // no-warning
  scanf("%n", (unsigned int*)0); // no-warning
  scanf("%n", (char*)0); // expected-warning{{format specifies type 'int *' but the argument has type 'char *'}}

  scanf("%ln", (long*)0); // no-warning
  scanf("%ln", (unsigned long*)0); // no-warning
  scanf("%ln", (int*)0); // expected-warning{{format specifies type 'long *' but the argument has type 'int *'}}

  scanf("%lln", (long long*)0); // no-warning
  scanf("%lln", (unsigned long long*)0); // no-warning
  scanf("%lln", (int*)0); // expected-warning{{format specifies type 'long long *' but the argument has type 'int *'}}

  scanf("%qn", (long long*)0); // no-warning
  scanf("%qn", (unsigned long long*)0); // no-warning
  scanf("%qn", (int*)0); // expected-warning{{format specifies type 'long long *' but the argument has type 'int *'}}

}

void test_qualifiers(const int *cip, volatile int* vip,
                     const char *ccp, volatile char* vcp,
                     const volatile int *cvip) {
  scanf("%d", cip); // expected-warning{{format specifies type 'int *' but the argument has type 'const int *'}}
  scanf("%n", cip); // expected-warning{{format specifies type 'int *' but the argument has type 'const int *'}}
  scanf("%s", ccp); // expected-warning{{format specifies type 'char *' but the argument has type 'const char *'}}
  scanf("%d", cvip); // expected-warning{{format specifies type 'int *' but the argument has type 'const volatile int *'}}

  scanf("%d", vip); // No warning.
  scanf("%n", vip); // No warning.
  scanf("%c", vcp); // No warning.

  typedef int* ip_t;
  typedef const int* cip_t;
  scanf("%d", (ip_t)0); // No warning.
  scanf("%d", (cip_t)0); // expected-warning{{format specifies type 'int *' but the argument has type 'cip_t' (aka 'const int *')}}
}

void test_size_types() {
  size_t s = 0;
  scanf("%zu", &s); // No warning.

  double d1 = 0.;
  scanf("%zu", &d1); // expected-warning-re{{format specifies type 'size_t *' (aka '{{.+}}') but the argument has type 'double *'}}

  ssize_t ss = 0;
  scanf("%zd", &s); // No warning.

  double d2 = 0.;
  scanf("%zd", &d2); // expected-warning-re{{format specifies type 'ssize_t *' (aka '{{.+}}') but the argument has type 'double *'}}

  ssize_t sn = 0;
  scanf("%zn", &sn); // No warning.

  double d3 = 0.;
  scanf("%zn", &d3); // expected-warning-re{{format specifies type 'ssize_t *' (aka '{{.+}}') but the argument has type 'double *'}}
}

void test_ptrdiff_t_types() {
  __UNSIGNED_PTRDIFF_TYPE__ p1 = 0;
  scanf("%tu", &p1); // No warning.

  double d1 = 0.;
  scanf("%tu", &d1); // expected-warning-re{{format specifies type 'unsigned ptrdiff_t *' (aka '{{.+}}') but the argument has type 'double *'}}

  ptrdiff_t p2 = 0;
  scanf("%td", &p2); // No warning.
  
  double d2 = 0.;
  scanf("%td", &d2); // expected-warning-re{{format specifies type 'ptrdiff_t *' (aka '{{.+}}') but the argument has type 'double *'}}

  ptrdiff_t p3 = 0;
  scanf("%tn", &p3); // No warning.
  
  double d3 = 0.;
  scanf("%tn", &d3); // expected-warning-re{{format specifies type 'ptrdiff_t *' (aka '{{.+}}') but the argument has type 'double *'}}
}

void check_conditional_literal(char *s, int *i) {
  scanf(0 ? "%s" : "%d", i); // no warning
  scanf(1 ? "%s" : "%d", i); // expected-warning{{format specifies type 'char *'}}
  scanf(0 ? "%d %d" : "%d", i); // no warning
  scanf(1 ? "%d %d" : "%d", i); // expected-warning{{more '%' conversions than data arguments}}
  scanf(0 ? "%d %d" : "%d", i, s); // expected-warning{{data argument not used}}
  scanf(1 ? "%d %s" : "%d", i, s); // no warning
  scanf(i ? "%d %s" : "%d", i, s); // no warning
  scanf(i ? "%d" : "%d", i, s); // expected-warning{{data argument not used}}
  scanf(i ? "%s" : "%d", s); // expected-warning{{format specifies type 'int *'}}
}