- add '['
[cparser] / format_check.c
1 /*
2  * This file is part of cparser.
3  * Copyright (C) 2007-2008 Matthias Braun <matze@braunis.de>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
18  * 02111-1307, USA.
19  */
20 #include <ctype.h>
21 #include <wctype.h>
22
23 #include "format_check.h"
24 #include "symbol_t.h"
25 #include "ast_t.h"
26 #include "entity_t.h"
27 #include "diagnostic.h"
28 #include "types.h"
29 #include "type_t.h"
30 #include "warning.h"
31 #include "lang_features.h"
32
33 typedef enum format_flag_t {
34         FMT_FLAG_NONE  = 0,
35         FMT_FLAG_HASH  = 1U << 0,
36         FMT_FLAG_ZERO  = 1U << 1,
37         FMT_FLAG_MINUS = 1U << 2,
38         FMT_FLAG_SPACE = 1U << 3,
39         FMT_FLAG_PLUS  = 1U << 4,
40         FMT_FLAG_TICK  = 1U << 5
41 } format_flag_t;
42
43 typedef unsigned format_flags_t;
44
45 typedef enum format_length_modifier_t {
46         FMT_MOD_NONE,
47         FMT_MOD_L,
48         FMT_MOD_hh,
49         FMT_MOD_h,
50         FMT_MOD_l,
51         FMT_MOD_ll,
52         FMT_MOD_j,
53         FMT_MOD_t,
54         FMT_MOD_z,
55         FMT_MOD_q,
56         /* only in microsoft mode */
57         FMT_MOD_w,
58         FMT_MOD_I,
59         FMT_MOD_I32,
60         FMT_MOD_I64
61 } format_length_modifier_t;
62
63 typedef struct format_spec_t {
64         const char    *name;     /**< name of the function */
65         format_kind_t  fmt_kind; /**< kind */
66         unsigned       fmt_idx;  /**< index of the format string */
67         unsigned       arg_idx;  /**< index of the first argument */
68 } format_spec_t;
69
70 static const char* get_length_modifier_name(const format_length_modifier_t mod)
71 {
72         static const char* const names[] = {
73                 [FMT_MOD_NONE] = "",
74                 [FMT_MOD_L]    = "L",
75                 [FMT_MOD_hh]   = "hh",
76                 [FMT_MOD_h]    = "h",
77                 [FMT_MOD_l]    = "l",
78                 [FMT_MOD_ll]   = "ll",
79                 [FMT_MOD_j]    = "j",
80                 [FMT_MOD_t]    = "t",
81                 [FMT_MOD_z]    = "z",
82                 [FMT_MOD_q]    = "q",
83                 /* only in microsoft mode */
84                 [FMT_MOD_w]    = "w",
85                 [FMT_MOD_I]    = "I",
86                 [FMT_MOD_I32]  = "I32",
87                 [FMT_MOD_I64]  = "I64"
88         };
89         assert(mod < sizeof(names) / sizeof(*names));
90         return names[mod];
91 }
92
93 static void warn_invalid_length_modifier(const source_position_t *pos,
94                                          const format_length_modifier_t mod,
95                                          const wchar_rep_t conversion)
96 {
97         warningf(pos,
98                 "invalid length modifier '%s' for conversion specifier '%%%c'",
99                 get_length_modifier_name(mod), conversion
100         );
101 }
102
103 typedef struct vchar_t vchar_t;
104 struct vchar_t {
105         const void *string;   /**< the string */
106         size_t     position;  /**< current position */
107         size_t     size;      /**< size of the string */
108
109         /** return the first character of the string and setthe position to 0. */
110         unsigned (*first)(vchar_t *self);
111         /** return the next character of the string */
112         unsigned (*next)(vchar_t *self);
113         /** return non_zero if the given character is a digit */
114         int (*is_digit)(unsigned vchar);
115 };
116
117 static unsigned string_first(vchar_t *self) {
118         self->position = 0;
119         const string_t *string = self->string;
120         return string->begin[0];
121 }
122
123 static unsigned string_next(vchar_t *self) {
124         ++self->position;
125         const string_t *string = self->string;
126         return string->begin[self->position];
127 }
128
129 static int string_isdigit(unsigned vchar) {
130         return isdigit(vchar);
131 }
132
133 static unsigned wstring_first(vchar_t *self) {
134         self->position = 0;
135         const wide_string_t *wstring = self->string;
136         return wstring->begin[0];
137 }
138
139 static unsigned wstring_next(vchar_t *self) {
140         ++self->position;
141         const wide_string_t *wstring = self->string;
142         return wstring->begin[self->position];
143 }
144
145 static int wstring_isdigit(unsigned vchar) {
146         return iswdigit(vchar);
147 }
148
149 static bool atend(vchar_t *self) {
150         return self->position + 1 == self->size;
151 }
152
153 /**
154  * Check printf-style format.
155  */
156 static void check_printf_format(const call_argument_t *arg, const format_spec_t *spec)
157 {
158         /* find format arg */
159         unsigned idx = 0;
160         for (; idx < spec->fmt_idx; ++idx)
161                 arg = arg->next;
162
163         const expression_t *fmt_expr = arg->expression;
164         if (fmt_expr->kind == EXPR_UNARY_CAST_IMPLICIT) {
165                 fmt_expr = fmt_expr->unary.value;
166         }
167
168         vchar_t vchar;
169         if (fmt_expr->kind == EXPR_WIDE_STRING_LITERAL) {
170                 vchar.string   = &fmt_expr->wide_string.value;
171                 vchar.size     = fmt_expr->wide_string.value.size;
172                 vchar.first    = wstring_first;
173                 vchar.next     = wstring_next;
174                 vchar.is_digit = wstring_isdigit;
175         } else if (fmt_expr->kind == EXPR_STRING_LITERAL) {
176                 vchar.string   = &fmt_expr->string.value;
177                 vchar.size     = fmt_expr->string.value.size;
178                 vchar.first    = string_first;
179                 vchar.next     = string_next;
180                 vchar.is_digit = string_isdigit;
181         } else {
182                 return;
183         }
184         /* find the real args */
185         for(; idx < spec->arg_idx; ++idx)
186                 arg = arg->next;
187
188         const source_position_t *pos = &fmt_expr->base.source_position;
189         unsigned fmt     = vchar.first(&vchar);
190         unsigned num_fmt = 0;
191         for (; fmt != '\0'; fmt = vchar.next(&vchar)) {
192                 if (fmt != '%')
193                         continue;
194                 fmt = vchar.next(&vchar);
195
196                 if (fmt == '%')
197                         continue;
198
199                 ++num_fmt;
200
201                 format_flags_t fmt_flags = FMT_FLAG_NONE;
202                 if (fmt == '0') {
203                         fmt = vchar.next(&vchar);
204                         fmt_flags |= FMT_FLAG_ZERO;
205                 }
206
207                 /* argument selector or minimum field width */
208                 if (vchar.is_digit(fmt)) {
209                         do {
210                                 fmt = vchar.next(&vchar);
211                         } while (vchar.is_digit(fmt));
212
213                         /* digit string was ... */
214                         if (fmt == '$') {
215                                 /* ... argument selector */
216                                 fmt_flags = FMT_FLAG_NONE; /* reset possibly set 0-flag */
217                                 /* TODO implement */
218                                 return;
219                         }
220                         /* ... minimum field width */
221                 } else {
222                         /* flags */
223                         for (;;) {
224                                 format_flags_t flag;
225                                 switch (fmt) {
226                                         case '#':  flag = FMT_FLAG_HASH;  break;
227                                         case '0':  flag = FMT_FLAG_ZERO;  break;
228                                         case '-':  flag = FMT_FLAG_MINUS; break;
229                                         case '\'': flag = FMT_FLAG_TICK;  break;
230
231                                         case ' ':
232                                                 if (fmt_flags & FMT_FLAG_PLUS) {
233                                                         warningf(pos, "' ' is overridden by prior '+' in conversion specification %u", num_fmt);
234                                                 }
235                                                 flag = FMT_FLAG_SPACE;
236                                                 break;
237
238                                         case '+':
239                                                 if (fmt_flags & FMT_FLAG_SPACE) {
240                                                         warningf(pos, "'+' overrides prior ' ' in conversion specification %u", num_fmt);
241                                                 }
242                                                 flag = FMT_FLAG_PLUS;
243                                                 break;
244
245                                         default: goto break_fmt_flags;
246                                 }
247                                 if (fmt_flags & flag) {
248                                         warningf(pos, "repeated flag '%c' in conversion specification %u", (char)fmt, num_fmt);
249                                 }
250                                 fmt_flags |= flag;
251                                 fmt = vchar.next(&vchar);
252                         }
253 break_fmt_flags:
254
255                         /* minimum field width */
256                         if (fmt == '*') {
257                                 fmt = vchar.next(&vchar);
258                                 if (arg == NULL) {
259                                         warningf(pos, "missing argument for '*' field width in conversion specification %u", num_fmt);
260                                         return;
261                                 }
262                                 const type_t *const arg_type = arg->expression->base.type;
263                                 if (arg_type != type_int) {
264                                         warningf(pos, "argument for '*' field width in conversion specification %u is not an 'int', but an '%T'", num_fmt, arg_type);
265                                 }
266                                 arg = arg->next;
267                         } else {
268                                 while (vchar.is_digit(fmt)) {
269                                         fmt = vchar.next(&vchar);
270                                 }
271                         }
272                 }
273
274                 /* precision */
275                 if (fmt == '.') {
276                         fmt = vchar.next(&vchar);
277                         if (fmt == '*') {
278                                 fmt = vchar.next(&vchar);
279                                 if (arg == NULL) {
280                                         warningf(pos, "missing argument for '*' precision in conversion specification %u", num_fmt);
281                                         return;
282                                 }
283                                 const type_t *const arg_type = arg->expression->base.type;
284                                 if (arg_type != type_int) {
285                                         warningf(pos, "argument for '*' precision in conversion specification %u is not an 'int', but an '%T'", num_fmt, arg_type);
286                                 }
287                                 arg = arg->next;
288                         } else {
289                                 /* digit string may be omitted */
290                                 while (vchar.is_digit(fmt)) {
291                                         fmt = vchar.next(&vchar);
292                                 }
293                         }
294                 }
295
296                 /* length modifier */
297                 format_length_modifier_t fmt_mod;
298                 switch (fmt) {
299                         case 'h':
300                                 fmt = vchar.next(&vchar);
301                                 if (fmt == 'h') {
302                                         fmt = vchar.next(&vchar);
303                                         fmt_mod = FMT_MOD_hh;
304                                 } else {
305                                         fmt_mod = FMT_MOD_h;
306                                 }
307                                 break;
308
309                         case 'l':
310                                 fmt = vchar.next(&vchar);
311                                 if (fmt == 'l') {
312                                         fmt = vchar.next(&vchar);
313                                         fmt_mod = FMT_MOD_ll;
314                                 } else {
315                                         fmt_mod = FMT_MOD_l;
316                                 }
317                                 break;
318
319                         case 'L': fmt = vchar.next(&vchar); fmt_mod = FMT_MOD_L;    break;
320                         case 'j': fmt = vchar.next(&vchar); fmt_mod = FMT_MOD_j;    break;
321                         case 't': fmt = vchar.next(&vchar); fmt_mod = FMT_MOD_t;    break;
322                         case 'z': fmt = vchar.next(&vchar); fmt_mod = FMT_MOD_z;    break;
323                         case 'q': fmt = vchar.next(&vchar); fmt_mod = FMT_MOD_q;    break;
324                         /* microsoft mode */
325                         case 'w':
326                                 if (c_mode & _MS) {
327                                         fmt = vchar.next(&vchar); fmt_mod = FMT_MOD_w;
328                                 } else {
329                                         fmt_mod = FMT_MOD_NONE;
330                                 }
331                                 break;
332                         case 'I':
333                                 if (c_mode & _MS) {
334                                         fmt = vchar.next(&vchar); fmt_mod = FMT_MOD_I;
335                                         if (fmt == '3') {
336                                                 fmt = vchar.next(&vchar);
337                                                 if (fmt == '2') {
338                                                         fmt = vchar.next(&vchar);
339                                                         fmt_mod = FMT_MOD_I32;
340                                                 } else {
341                                                         /* rewind */
342                                                         --vchar.position;
343                                                 }
344                                         } else if (fmt == '6') {
345                                                 fmt = vchar.next(&vchar);
346                                                 if (fmt == '4') {
347                                                         fmt = vchar.next(&vchar);
348                                                         fmt_mod = FMT_MOD_I64;
349                                                 } else {
350                                                         /* rewind */
351                                                         --vchar.position;
352                                                 }
353                                         }
354                                 } else {
355                                         fmt_mod = FMT_MOD_NONE;
356                                 }
357                                 break;
358                         default:
359                                 fmt_mod = FMT_MOD_NONE;
360                                 break;
361                 }
362
363                 if (fmt == '\0') {
364                         warningf(pos, "dangling %% in format string");
365                         break;
366                 }
367
368                 type_t            *expected_type;
369                 type_qualifiers_t  expected_qual = TYPE_QUALIFIER_NONE;
370                 format_flags_t     allowed_flags;
371                 switch (fmt) {
372                         case 'd':
373                         case 'i':
374                                 switch (fmt_mod) {
375                                         case FMT_MOD_NONE: expected_type = type_int;       break;
376                                         case FMT_MOD_hh:   expected_type = type_int;       break; /* TODO promoted signed char */
377                                         case FMT_MOD_h:    expected_type = type_int;       break; /* TODO promoted short */
378                                         case FMT_MOD_l:    expected_type = type_long;      break;
379                                         case FMT_MOD_ll:   expected_type = type_long_long; break;
380                                         case FMT_MOD_j:    expected_type = type_intmax_t;  break;
381                                         case FMT_MOD_z:    expected_type = type_ssize_t;   break;
382                                         case FMT_MOD_t:    expected_type = type_ptrdiff_t; break;
383                                         case FMT_MOD_I:    expected_type = type_ptrdiff_t; break;
384                                         case FMT_MOD_I32:  expected_type = type_int32;     break;
385                                         case FMT_MOD_I64:  expected_type = type_int64;     break;
386
387                                         default:
388                                                 warn_invalid_length_modifier(pos, fmt_mod, fmt);
389                                                 goto next_arg;
390                                 }
391                                 allowed_flags = FMT_FLAG_MINUS | FMT_FLAG_SPACE | FMT_FLAG_PLUS | FMT_FLAG_ZERO;
392                                 break;
393
394                         case 'o':
395                         case 'X':
396                         case 'x':
397                                 allowed_flags = FMT_FLAG_MINUS | FMT_FLAG_HASH | FMT_FLAG_ZERO;
398                                 goto eval_fmt_mod_unsigned;
399
400                         case 'u':
401                                 allowed_flags = FMT_FLAG_MINUS | FMT_FLAG_ZERO;
402 eval_fmt_mod_unsigned:
403                                 switch (fmt_mod) {
404                                         case FMT_MOD_NONE: expected_type = type_unsigned_int;       break;
405                                         case FMT_MOD_hh:   expected_type = type_int;                break; /* TODO promoted unsigned char */
406                                         case FMT_MOD_h:    expected_type = type_int;                break; /* TODO promoted unsigned short */
407                                         case FMT_MOD_l:    expected_type = type_unsigned_long;      break;
408                                         case FMT_MOD_ll:   expected_type = type_unsigned_long_long; break;
409                                         case FMT_MOD_j:    expected_type = type_uintmax_t;          break;
410                                         case FMT_MOD_z:    expected_type = type_size_t;             break;
411                                         case FMT_MOD_t:    expected_type = type_uptrdiff_t;         break;
412                                         case FMT_MOD_I:    expected_type = type_size_t;             break;
413                                         case FMT_MOD_I32:  expected_type = type_unsigned_int32;     break;
414                                         case FMT_MOD_I64:  expected_type = type_unsigned_int64;     break;
415
416                                         default:
417                                                 warn_invalid_length_modifier(pos, fmt_mod, fmt);
418                                                 goto next_arg;
419                                 }
420                                 break;
421
422                         case 'A':
423                         case 'a':
424                         case 'E':
425                         case 'e':
426                         case 'F':
427                         case 'f':
428                         case 'G':
429                         case 'g':
430                                 switch (fmt_mod) {
431                                         case FMT_MOD_l:    /* l modifier is ignored */
432                                         case FMT_MOD_NONE: expected_type = type_double;      break;
433                                         case FMT_MOD_L:    expected_type = type_long_double; break;
434
435                                         default:
436                                                 warn_invalid_length_modifier(pos, fmt_mod, fmt);
437                                                 goto next_arg;
438                                 }
439                                 allowed_flags = FMT_FLAG_MINUS | FMT_FLAG_SPACE | FMT_FLAG_PLUS | FMT_FLAG_HASH | FMT_FLAG_ZERO;
440                                 break;
441
442                         case 'C':
443                                 if (fmt_mod != FMT_MOD_NONE) {
444                                         warn_invalid_length_modifier(pos, fmt_mod, fmt);
445                                         goto next_arg;
446                                 }
447                                 expected_type = type_wchar_t;
448                                 allowed_flags = FMT_FLAG_NONE;
449                                 break;
450
451                         case 'c':
452                                 expected_type = type_int;
453                                 switch (fmt_mod) {
454                                         case FMT_MOD_NONE: expected_type = type_int;     break; /* TODO promoted char */
455                                         case FMT_MOD_l:    expected_type = type_wint_t;  break;
456                                         case FMT_MOD_w:    expected_type = type_wchar_t; break;
457
458                                         default:
459                                                 warn_invalid_length_modifier(pos, fmt_mod, fmt);
460                                                 goto next_arg;
461                                 }
462                                 allowed_flags = FMT_FLAG_NONE;
463                                 break;
464
465                         case 'S':
466                                 if (fmt_mod != FMT_MOD_NONE) {
467                                         warn_invalid_length_modifier(pos, fmt_mod, fmt);
468                                         goto next_arg;
469                                 }
470                                 expected_type = type_wchar_t_ptr;
471                                 expected_qual = TYPE_QUALIFIER_CONST;
472                                 allowed_flags = FMT_FLAG_MINUS;
473                                 break;
474
475                         case 's':
476                                 switch (fmt_mod) {
477                                         case FMT_MOD_NONE: expected_type = type_char_ptr;    break;
478                                         case FMT_MOD_l:    expected_type = type_wchar_t_ptr; break;
479                                         case FMT_MOD_w:    expected_type = type_wchar_t_ptr; break;
480
481                                         default:
482                                                 warn_invalid_length_modifier(pos, fmt_mod, fmt);
483                                                 goto next_arg;
484                                 }
485                                 expected_qual = TYPE_QUALIFIER_CONST;
486                                 allowed_flags = FMT_FLAG_MINUS;
487                                 break;
488
489                         case 'p':
490                                 if (fmt_mod != FMT_MOD_NONE) {
491                                         warn_invalid_length_modifier(pos, fmt_mod, fmt);
492                                         goto next_arg;
493                                 }
494                                 expected_type = type_void_ptr;
495                                 allowed_flags = FMT_FLAG_NONE;
496                                 break;
497
498                         case 'n':
499                                 switch (fmt_mod) {
500                                         case FMT_MOD_NONE: expected_type = type_int_ptr;         break;
501                                         case FMT_MOD_hh:   expected_type = type_signed_char_ptr; break;
502                                         case FMT_MOD_h:    expected_type = type_short_ptr;       break;
503                                         case FMT_MOD_l:    expected_type = type_long_ptr;        break;
504                                         case FMT_MOD_ll:   expected_type = type_long_long_ptr;   break;
505                                         case FMT_MOD_j:    expected_type = type_intmax_t_ptr;    break;
506                                         case FMT_MOD_z:    expected_type = type_ssize_t_ptr;     break;
507                                         case FMT_MOD_t:    expected_type = type_ptrdiff_t_ptr;   break;
508
509                                         default:
510                                                 warn_invalid_length_modifier(pos, fmt_mod, fmt);
511                                                 goto next_arg;
512                                 }
513                                 allowed_flags = FMT_FLAG_NONE;
514                                 break;
515
516                         default:
517                                 warningf(pos, "encountered unknown conversion specifier '%%%C' at position %u", (wint_t)fmt, num_fmt);
518                                 goto next_arg;
519                 }
520
521                 format_flags_t wrong_flags = fmt_flags & ~allowed_flags;
522                 if (wrong_flags != 0) {
523                         char wrong[8];
524                         int idx = 0;
525                         if (wrong_flags & FMT_FLAG_HASH)  wrong[idx++] = '#';
526                         if (wrong_flags & FMT_FLAG_ZERO)  wrong[idx++] = '0';
527                         if (wrong_flags & FMT_FLAG_MINUS) wrong[idx++] = '-';
528                         if (wrong_flags & FMT_FLAG_SPACE) wrong[idx++] = ' ';
529                         if (wrong_flags & FMT_FLAG_PLUS)  wrong[idx++] = '+';
530                         if (wrong_flags & FMT_FLAG_TICK)  wrong[idx++] = '\'';
531                         wrong[idx] = '\0';
532
533                         warningf(pos, "invalid format flags \"%s\" in conversion specification %%%c at position %u", wrong, fmt, num_fmt);
534                 }
535
536                 if (arg == NULL) {
537                         warningf(pos, "too few arguments for format string");
538                         return;
539                 }
540
541                 {       /* create a scope here to prevent warning about the jump to next_arg */
542                         type_t *const arg_type           = arg->expression->base.type;
543                         type_t *const arg_skip           = skip_typeref(arg_type);
544                         type_t *const expected_type_skip = skip_typeref(expected_type);
545
546                         if (fmt == 'p') {
547                                 /* allow any pointer type for %p, not just void */
548                                 if (is_type_pointer(arg_skip))
549                                         goto next_arg;
550                         }
551
552                         if (is_type_pointer(expected_type_skip)) {
553                                 if (is_type_pointer(arg_skip)) {
554                                         type_t *const exp_to = skip_typeref(expected_type_skip->pointer.points_to);
555                                         type_t *const arg_to = skip_typeref(arg_skip->pointer.points_to);
556                                         if ((arg_to->base.qualifiers & ~expected_qual) == 0 &&
557                                                 get_unqualified_type(arg_to) == exp_to) {
558                                                 goto next_arg;
559                                         }
560                                 }
561                         } else {
562                                 if (get_unqualified_type(arg_skip) == expected_type_skip) {
563                                         goto next_arg;
564                                 }
565                         }
566                         if (is_type_valid(arg_skip)) {
567                                 warningf(pos,
568                                         "argument type '%T' does not match conversion specifier '%%%s%c' at position %u",
569                                         arg_type, get_length_modifier_name(fmt_mod), (char)fmt, num_fmt);
570                         }
571                 }
572 next_arg:
573                 arg = arg->next;
574         }
575         if (!atend(&vchar)) {
576                 warningf(pos, "format string contains NUL");
577         }
578         if (arg != NULL) {
579                 unsigned num_args = num_fmt;
580                 while (arg != NULL) {
581                         ++num_args;
582                         arg = arg->next;
583                 }
584                 warningf(pos, "%u argument%s but only %u format string%s",
585                         num_args, num_args != 1 ? "s" : "",
586                         num_fmt, num_fmt != 1 ? "s" : "");
587         }
588 }
589
590 /**
591  * Check scanf-style format.
592  */
593 static void check_scanf_format(const call_argument_t *arg, const format_spec_t *spec)
594 {
595         /* find format arg */
596         unsigned idx = 0;
597         for (; idx < spec->fmt_idx; ++idx)
598                 arg = arg->next;
599
600         const expression_t *fmt_expr = arg->expression;
601         if (fmt_expr->kind == EXPR_UNARY_CAST_IMPLICIT) {
602                 fmt_expr = fmt_expr->unary.value;
603         }
604
605         vchar_t vchar;
606         if (fmt_expr->kind == EXPR_WIDE_STRING_LITERAL) {
607                 vchar.string   = &fmt_expr->wide_string.value;
608                 vchar.size     = fmt_expr->wide_string.value.size;
609                 vchar.first    = wstring_first;
610                 vchar.next     = wstring_next;
611                 vchar.is_digit = wstring_isdigit;
612         } else if (fmt_expr->kind == EXPR_STRING_LITERAL) {
613                 vchar.string   = &fmt_expr->string.value;
614                 vchar.size     = fmt_expr->string.value.size;
615                 vchar.first    = string_first;
616                 vchar.next     = string_next;
617                 vchar.is_digit = string_isdigit;
618         } else {
619                 return;
620         }
621         /* find the real args */
622         for (; idx < spec->arg_idx; ++idx)
623                 arg = arg->next;
624
625         const source_position_t *pos = &fmt_expr->base.source_position;
626         unsigned fmt     = vchar.first(&vchar);
627         unsigned num_fmt = 0;
628         for (; fmt != '\0'; fmt = vchar.next(&vchar)) {
629                 if (fmt != '%')
630                         continue;
631                 fmt = vchar.next(&vchar);
632
633                 if (fmt == '%')
634                         continue;
635
636                 ++num_fmt;
637
638                 /* length modifier */
639                 format_length_modifier_t fmt_mod;
640                 switch (fmt) {
641                         case 'h':
642                                 fmt = vchar.next(&vchar);
643                                 if (fmt == 'h') {
644                                         fmt = vchar.next(&vchar);
645                                         fmt_mod = FMT_MOD_hh;
646                                 } else {
647                                         fmt_mod = FMT_MOD_h;
648                                 }
649                                 break;
650
651                         case 'l':
652                                 fmt = vchar.next(&vchar);
653                                 if (fmt == 'l') {
654                                         fmt = vchar.next(&vchar);
655                                         fmt_mod = FMT_MOD_ll;
656                                 } else {
657                                         fmt_mod = FMT_MOD_l;
658                                 }
659                                 break;
660
661                         case 'L': fmt = vchar.next(&vchar); fmt_mod = FMT_MOD_L;    break;
662                         case 'j': fmt = vchar.next(&vchar); fmt_mod = FMT_MOD_j;    break;
663                         case 't': fmt = vchar.next(&vchar); fmt_mod = FMT_MOD_t;    break;
664                         case 'z': fmt = vchar.next(&vchar); fmt_mod = FMT_MOD_z;    break;
665                         /* microsoft mode */
666                         case 'w':
667                                 if (c_mode & _MS) {
668                                         fmt = vchar.next(&vchar); fmt_mod = FMT_MOD_w;
669                                 } else {
670                                         fmt_mod = FMT_MOD_NONE;
671                                 }
672                                 break;
673                         case 'I':
674                                 if (c_mode & _MS) {
675                                         fmt = vchar.next(&vchar); fmt_mod = FMT_MOD_I;
676                                         if (fmt == '3') {
677                                                 fmt = vchar.next(&vchar);
678                                                 if (fmt == '2') {
679                                                         fmt = vchar.next(&vchar);
680                                                         fmt_mod = FMT_MOD_I32;
681                                                 } else {
682                                                         /* rewind */
683                                                         --vchar.position;
684                                                 }
685                                         } else if (fmt == '6') {
686                                                 fmt = vchar.next(&vchar);
687                                                 if (fmt == '4') {
688                                                         fmt = vchar.next(&vchar);
689                                                         fmt_mod = FMT_MOD_I64;
690                                                 } else {
691                                                         /* rewind */
692                                                         --vchar.position;
693                                                 }
694                                         }
695                                 } else {
696                                         fmt_mod = FMT_MOD_NONE;
697                                 }
698                                 break;
699                         default:
700                                 fmt_mod = FMT_MOD_NONE;
701                                 break;
702                 }
703
704                 if (fmt == '\0') {
705                         warningf(pos, "dangling %% in format string");
706                         break;
707                 }
708
709                 type_t            *expected_type;
710                 switch (fmt) {
711                         case 'd':
712                         case 'i':
713                                 switch (fmt_mod) {
714                                         case FMT_MOD_NONE: expected_type = type_int;         break;
715                                         case FMT_MOD_hh:   expected_type = type_signed_char; break;
716                                         case FMT_MOD_h:    expected_type = type_short;       break;
717                                         case FMT_MOD_l:    expected_type = type_long;        break;
718                                         case FMT_MOD_ll:   expected_type = type_long_long;   break;
719                                         case FMT_MOD_j:    expected_type = type_intmax_t;    break;
720                                         case FMT_MOD_z:    expected_type = type_ssize_t;     break;
721                                         case FMT_MOD_t:    expected_type = type_ptrdiff_t;   break;
722                                         case FMT_MOD_I:    expected_type = type_ptrdiff_t;   break;
723                                         case FMT_MOD_I32:  expected_type = type_int32;       break;
724                                         case FMT_MOD_I64:  expected_type = type_int64;       break;
725
726                                         default:
727                                                 warn_invalid_length_modifier(pos, fmt_mod, fmt);
728                                                 goto next_arg;
729                                 }
730                                 break;
731
732                         case 'o':
733                         case 'X':
734                         case 'x':
735                                 goto eval_fmt_mod_unsigned;
736
737                         case 'u':
738 eval_fmt_mod_unsigned:
739                                 switch (fmt_mod) {
740                                         case FMT_MOD_NONE: expected_type = type_unsigned_int;       break;
741                                         case FMT_MOD_hh:   expected_type = type_unsigned_char;      break;
742                                         case FMT_MOD_h:    expected_type = type_unsigned_short;     break;
743                                         case FMT_MOD_l:    expected_type = type_unsigned_long;      break;
744                                         case FMT_MOD_ll:   expected_type = type_unsigned_long_long; break;
745                                         case FMT_MOD_j:    expected_type = type_uintmax_t;          break;
746                                         case FMT_MOD_z:    expected_type = type_size_t;             break;
747                                         case FMT_MOD_t:    expected_type = type_uptrdiff_t;         break;
748                                         case FMT_MOD_I:    expected_type = type_size_t;             break;
749                                         case FMT_MOD_I32:  expected_type = type_unsigned_int32;     break;
750                                         case FMT_MOD_I64:  expected_type = type_unsigned_int64;     break;
751
752                                         default:
753                                                 warn_invalid_length_modifier(pos, fmt_mod, fmt);
754                                                 goto next_arg;
755                                 }
756                                 break;
757
758                         case 'A':
759                         case 'a':
760                         case 'E':
761                         case 'e':
762                         case 'F':
763                         case 'f':
764                         case 'G':
765                         case 'g':
766                                 switch (fmt_mod) {
767                                         case FMT_MOD_l:    /* l modifier is ignored */
768                                         case FMT_MOD_NONE: expected_type = type_double;      break;
769                                         case FMT_MOD_L:    expected_type = type_long_double; break;
770
771                                         default:
772                                                 warn_invalid_length_modifier(pos, fmt_mod, fmt);
773                                                 goto next_arg;
774                                 }
775                                 break;
776
777                         case 'C':
778                                 if (fmt_mod != FMT_MOD_NONE) {
779                                         warn_invalid_length_modifier(pos, fmt_mod, fmt);
780                                         goto next_arg;
781                                 }
782                                 expected_type = type_wchar_t;
783                                 break;
784
785                         case 'c':
786                                 expected_type = type_int;
787                                 switch (fmt_mod) {
788                                         case FMT_MOD_NONE: expected_type = type_int;     break; /* TODO promoted char */
789                                         case FMT_MOD_l:    expected_type = type_wint_t;  break;
790                                         case FMT_MOD_w:    expected_type = type_wchar_t; break;
791
792                                         default:
793                                                 warn_invalid_length_modifier(pos, fmt_mod, fmt);
794                                                 goto next_arg;
795                                 }
796                                 break;
797
798                         case 'S':
799                                 if (fmt_mod != FMT_MOD_NONE) {
800                                         warn_invalid_length_modifier(pos, fmt_mod, fmt);
801                                         goto next_arg;
802                                 }
803                                 expected_type = type_wchar_t;
804                                 break;
805
806                         case 's':
807                         case '[':
808                                 switch (fmt_mod) {
809                                         case FMT_MOD_NONE: expected_type = type_char;    break;
810                                         case FMT_MOD_l:    expected_type = type_wchar_t; break;
811                                         case FMT_MOD_w:    expected_type = type_wchar_t; break;
812
813                                         default:
814                                                 warn_invalid_length_modifier(pos, fmt_mod, fmt);
815                                                 goto next_arg;
816                                 }
817                                 break;
818
819                         case 'p':
820                                 if (fmt_mod != FMT_MOD_NONE) {
821                                         warn_invalid_length_modifier(pos, fmt_mod, fmt);
822                                         goto next_arg;
823                                 }
824                                 expected_type = type_void_ptr;
825                                 break;
826
827                         case 'n':
828                                 switch (fmt_mod) {
829                                         case FMT_MOD_NONE: expected_type = type_int;         break;
830                                         case FMT_MOD_hh:   expected_type = type_signed_char; break;
831                                         case FMT_MOD_h:    expected_type = type_short;       break;
832                                         case FMT_MOD_l:    expected_type = type_long;        break;
833                                         case FMT_MOD_ll:   expected_type = type_long_long;   break;
834                                         case FMT_MOD_j:    expected_type = type_intmax_t;    break;
835                                         case FMT_MOD_z:    expected_type = type_ssize_t;     break;
836                                         case FMT_MOD_t:    expected_type = type_ptrdiff_t;   break;
837
838                                         default:
839                                                 warn_invalid_length_modifier(pos, fmt_mod, fmt);
840                                                 goto next_arg;
841                                 }
842                                 break;
843
844                         default:
845                                 warningf(pos, "encountered unknown conversion specifier '%%%C' at position %u", (wint_t)fmt, num_fmt);
846                                 goto next_arg;
847                 }
848
849                 if (arg == NULL) {
850                         warningf(pos, "too few arguments for format string");
851                         return;
852                 }
853
854                 {       /* create a scope here to prevent warning about the jump to next_arg */
855                         type_t *const arg_type           = arg->expression->base.type;
856                         type_t *const arg_skip           = skip_typeref(arg_type);
857                         type_t *const expected_type_skip = skip_typeref(expected_type);
858
859                         if (! is_type_pointer(arg_skip))
860                                 goto error_arg_type;
861                         type_t *const ptr_skip = skip_typeref(arg_skip->pointer.points_to);
862
863                         if (fmt == 'p') {
864                                 /* allow any pointer type for %p, not just void */
865                                 if (is_type_pointer(ptr_skip))
866                                         goto next_arg;
867                         }
868
869                         if (ptr_skip == expected_type_skip) {
870                                 goto next_arg;
871                         } else if (expected_type_skip == type_char) {
872                                 /* char matches with unsigned char AND signed char */
873                                 if (ptr_skip == type_signed_char || ptr_skip == type_unsigned_char)
874                                         goto next_arg;
875                         }
876 error_arg_type:
877                         if (is_type_valid(arg_skip)) {
878                                 warningf(pos,
879                                         "argument type '%T' does not match conversion specifier '%%%s%c' at position %u",
880                                         arg_type, get_length_modifier_name(fmt_mod), (char)fmt, num_fmt);
881                         }
882                 }
883 next_arg:
884                 arg = arg->next;
885         }
886         if (!atend(&vchar)) {
887                 warningf(pos, "format string contains NUL");
888         }
889         if (arg != NULL) {
890                 unsigned num_args = num_fmt;
891                 while (arg != NULL) {
892                         ++num_args;
893                         arg = arg->next;
894                 }
895                 warningf(pos, "%u argument%s but only %u format string%s",
896                         num_args, num_args != 1 ? "s" : "",
897                         num_fmt, num_fmt != 1 ? "s" : "");
898         }
899 }
900
901 static const format_spec_t builtin_table[] = {
902         { "printf",        FORMAT_PRINTF,   0, 1 },
903         { "wprintf",       FORMAT_PRINTF,   0, 1 },
904         { "sprintf",       FORMAT_PRINTF,   1, 2 },
905         { "swprintf",      FORMAT_PRINTF,   1, 2 },
906         { "snprintf",      FORMAT_PRINTF,   2, 3 },
907         { "snwprintf",     FORMAT_PRINTF,   2, 3 },
908         { "fprintf",       FORMAT_PRINTF,   1, 2 },
909         { "fwprintf",      FORMAT_PRINTF,   1, 2 },
910         { "snwprintf",     FORMAT_PRINTF,   2, 3 },
911         { "snwprintf",     FORMAT_PRINTF,   2, 3 },
912
913         { "scanf",         FORMAT_SCANF,    0, 1 },
914         { "wscanf",        FORMAT_SCANF,    0, 1 },
915         { "sscanf",        FORMAT_SCANF,    1, 2 },
916         { "swscanf",       FORMAT_SCANF,    1, 2 },
917         { "fscanf",        FORMAT_SCANF,    1, 2 },
918         { "fwscanf",       FORMAT_SCANF,    1, 2 },
919
920         { "strftime",      FORMAT_STRFTIME, 3, 4 },
921         { "wcstrftime",    FORMAT_STRFTIME, 3, 4 },
922
923         { "strfmon",       FORMAT_STRFMON,  3, 4 },
924
925         /* MS extensions */
926         { "_snprintf",     FORMAT_PRINTF,   2, 3 },
927         { "_snwprintf",    FORMAT_PRINTF,   2, 3 },
928         { "_scrintf",      FORMAT_PRINTF,   0, 1 },
929         { "_scwprintf",    FORMAT_PRINTF,   0, 1 },
930         { "printf_s",      FORMAT_PRINTF,   0, 1 },
931         { "wprintf_s",     FORMAT_PRINTF,   0, 1 },
932         { "sprintf_s",     FORMAT_PRINTF,   3, 4 },
933         { "swprintf_s",    FORMAT_PRINTF,   3, 4 },
934         { "fprintf_s",     FORMAT_PRINTF,   1, 2 },
935         { "fwprintf_s",    FORMAT_PRINTF,   1, 2 },
936         { "_sprintf_l",    FORMAT_PRINTF,   1, 3 },
937         { "_swprintf_l",   FORMAT_PRINTF,   1, 3 },
938         { "_printf_l",     FORMAT_PRINTF,   0, 2 },
939         { "_wprintf_l",    FORMAT_PRINTF,   0, 2 },
940         { "_fprintf_l",    FORMAT_PRINTF,   1, 3 },
941         { "_fwprintf_l",   FORMAT_PRINTF,   1, 3 },
942         { "_printf_s_l",   FORMAT_PRINTF,   0, 2 },
943         { "_wprintf_s_l",  FORMAT_PRINTF,   0, 2 },
944         { "_sprintf_s_l",  FORMAT_PRINTF,   3, 5 },
945         { "_swprintf_s_l", FORMAT_PRINTF,   3, 5 },
946         { "_fprintf_s_l",  FORMAT_PRINTF,   1, 3 },
947         { "_fwprintf_s_l", FORMAT_PRINTF,   1, 3 },
948 };
949
950 void check_format(const call_expression_t *const call)
951 {
952         if (!warning.format)
953                 return;
954
955         const expression_t *const func_expr = call->function;
956         if (func_expr->kind != EXPR_REFERENCE)
957                 return;
958
959         const entity_t        *const entity = func_expr->reference.entity;
960         const call_argument_t *      arg    = call->arguments;
961
962         if (false) {
963                 /* the declaration has a GNU format attribute, check it */
964         } else {
965                 /*
966                  * For some functions we always check the format, even if it was not specified.
967                  * This allows to check format even in MS mode or without header included.
968                  */
969                 const char *const name = entity->base.symbol->string;
970                 for (size_t i = 0; i < sizeof(builtin_table) / sizeof(builtin_table[0]); ++i) {
971                         if (strcmp(name, builtin_table[i].name) == 0) {
972                                 switch (builtin_table[i].fmt_kind) {
973                                 case FORMAT_PRINTF:
974                                         check_printf_format(arg, &builtin_table[i]);
975                                         break;
976                                 case FORMAT_SCANF:
977                                         check_scanf_format(arg, &builtin_table[i]);
978                                         break;
979                                 }
980                                 break;
981                         }
982                 }
983         }
984 }