2 * This file is part of cparser.
3 * Copyright (C) 2007-2008 Matthias Braun <matze@braunis.de>
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.
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.
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
23 #include "format_check.h"
27 #include "diagnostic.h"
31 #include "lang_features.h"
33 typedef enum format_flag_t {
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
43 typedef unsigned format_flags_t;
45 typedef enum format_length_modifier_t {
56 /* only in microsoft mode */
61 } format_length_modifier_t;
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 */
70 static const char* get_length_modifier_name(const format_length_modifier_t mod)
72 static const char* const names[] = {
83 /* only in microsoft mode */
86 [FMT_MOD_I32] = "I32",
89 assert(mod < sizeof(names) / sizeof(*names));
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)
98 "invalid length modifier '%s' for conversion specifier '%%%c'",
99 get_length_modifier_name(mod), conversion
103 typedef struct vchar_t vchar_t;
105 const void *string; /**< the string */
106 size_t position; /**< current position */
107 size_t size; /**< size of the string */
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);
117 static unsigned string_first(vchar_t *self) {
119 const string_t *string = self->string;
120 return string->begin[0];
123 static unsigned string_next(vchar_t *self) {
125 const string_t *string = self->string;
126 return string->begin[self->position];
129 static int string_isdigit(unsigned vchar) {
130 return isdigit(vchar);
133 static unsigned wstring_first(vchar_t *self) {
135 const wide_string_t *wstring = self->string;
136 return wstring->begin[0];
139 static unsigned wstring_next(vchar_t *self) {
141 const wide_string_t *wstring = self->string;
142 return wstring->begin[self->position];
145 static int wstring_isdigit(unsigned vchar) {
146 return iswdigit(vchar);
149 static bool atend(vchar_t *self) {
150 return self->position + 1 == self->size;
154 * Check printf-style format.
156 static void check_printf_format(const call_argument_t *arg, const format_spec_t *spec)
158 /* find format arg */
160 for (; idx < spec->fmt_idx && arg != NULL; ++idx)
166 const expression_t *fmt_expr = arg->expression;
167 if (fmt_expr->kind == EXPR_UNARY_CAST_IMPLICIT) {
168 fmt_expr = fmt_expr->unary.value;
172 if (fmt_expr->kind == EXPR_WIDE_STRING_LITERAL) {
173 vchar.string = &fmt_expr->wide_string.value;
174 vchar.size = fmt_expr->wide_string.value.size;
175 vchar.first = wstring_first;
176 vchar.next = wstring_next;
177 vchar.is_digit = wstring_isdigit;
178 } else if (fmt_expr->kind == EXPR_STRING_LITERAL) {
179 vchar.string = &fmt_expr->string.value;
180 vchar.size = fmt_expr->string.value.size;
181 vchar.first = string_first;
182 vchar.next = string_next;
183 vchar.is_digit = string_isdigit;
187 /* find the real args */
188 for(; idx < spec->arg_idx && arg != NULL; ++idx)
191 const source_position_t *pos = &fmt_expr->base.source_position;
192 unsigned fmt = vchar.first(&vchar);
193 unsigned num_fmt = 0;
194 for (; fmt != '\0'; fmt = vchar.next(&vchar)) {
197 fmt = vchar.next(&vchar);
204 format_flags_t fmt_flags = FMT_FLAG_NONE;
206 fmt = vchar.next(&vchar);
207 fmt_flags |= FMT_FLAG_ZERO;
210 /* argument selector or minimum field width */
211 if (vchar.is_digit(fmt)) {
213 fmt = vchar.next(&vchar);
214 } while (vchar.is_digit(fmt));
216 /* digit string was ... */
218 /* ... argument selector */
219 fmt_flags = FMT_FLAG_NONE; /* reset possibly set 0-flag */
223 /* ... minimum field width */
229 case '#': flag = FMT_FLAG_HASH; break;
230 case '0': flag = FMT_FLAG_ZERO; break;
231 case '-': flag = FMT_FLAG_MINUS; break;
232 case '\'': flag = FMT_FLAG_TICK; break;
235 if (fmt_flags & FMT_FLAG_PLUS) {
236 warningf(pos, "' ' is overridden by prior '+' in conversion specification %u", num_fmt);
238 flag = FMT_FLAG_SPACE;
242 if (fmt_flags & FMT_FLAG_SPACE) {
243 warningf(pos, "'+' overrides prior ' ' in conversion specification %u", num_fmt);
245 flag = FMT_FLAG_PLUS;
248 default: goto break_fmt_flags;
250 if (fmt_flags & flag) {
251 warningf(pos, "repeated flag '%c' in conversion specification %u", (char)fmt, num_fmt);
254 fmt = vchar.next(&vchar);
258 /* minimum field width */
260 fmt = vchar.next(&vchar);
262 warningf(pos, "missing argument for '*' field width in conversion specification %u", num_fmt);
265 const type_t *const arg_type = arg->expression->base.type;
266 if (arg_type != type_int) {
267 warningf(pos, "argument for '*' field width in conversion specification %u is not an 'int', but an '%T'", num_fmt, arg_type);
271 while (vchar.is_digit(fmt)) {
272 fmt = vchar.next(&vchar);
279 fmt = vchar.next(&vchar);
281 fmt = vchar.next(&vchar);
283 warningf(pos, "missing argument for '*' precision in conversion specification %u", num_fmt);
286 const type_t *const arg_type = arg->expression->base.type;
287 if (arg_type != type_int) {
288 warningf(pos, "argument for '*' precision in conversion specification %u is not an 'int', but an '%T'", num_fmt, arg_type);
292 /* digit string may be omitted */
293 while (vchar.is_digit(fmt)) {
294 fmt = vchar.next(&vchar);
299 /* length modifier */
300 format_length_modifier_t fmt_mod;
303 fmt = vchar.next(&vchar);
305 fmt = vchar.next(&vchar);
306 fmt_mod = FMT_MOD_hh;
313 fmt = vchar.next(&vchar);
315 fmt = vchar.next(&vchar);
316 fmt_mod = FMT_MOD_ll;
322 case 'L': fmt = vchar.next(&vchar); fmt_mod = FMT_MOD_L; break;
323 case 'j': fmt = vchar.next(&vchar); fmt_mod = FMT_MOD_j; break;
324 case 't': fmt = vchar.next(&vchar); fmt_mod = FMT_MOD_t; break;
325 case 'z': fmt = vchar.next(&vchar); fmt_mod = FMT_MOD_z; break;
326 case 'q': fmt = vchar.next(&vchar); fmt_mod = FMT_MOD_q; break;
330 fmt = vchar.next(&vchar); fmt_mod = FMT_MOD_w;
332 fmt_mod = FMT_MOD_NONE;
337 fmt = vchar.next(&vchar); fmt_mod = FMT_MOD_I;
339 fmt = vchar.next(&vchar);
341 fmt = vchar.next(&vchar);
342 fmt_mod = FMT_MOD_I32;
347 } else if (fmt == '6') {
348 fmt = vchar.next(&vchar);
350 fmt = vchar.next(&vchar);
351 fmt_mod = FMT_MOD_I64;
358 fmt_mod = FMT_MOD_NONE;
362 fmt_mod = FMT_MOD_NONE;
367 warningf(pos, "dangling %% in format string");
371 type_t *expected_type;
372 type_qualifiers_t expected_qual = TYPE_QUALIFIER_NONE;
373 format_flags_t allowed_flags;
378 case FMT_MOD_NONE: expected_type = type_int; break;
379 case FMT_MOD_hh: expected_type = type_int; break; /* TODO promoted signed char */
380 case FMT_MOD_h: expected_type = type_int; break; /* TODO promoted short */
381 case FMT_MOD_l: expected_type = type_long; break;
382 case FMT_MOD_ll: expected_type = type_long_long; break;
383 case FMT_MOD_j: expected_type = type_intmax_t; break;
384 case FMT_MOD_z: expected_type = type_ssize_t; break;
385 case FMT_MOD_t: expected_type = type_ptrdiff_t; break;
386 case FMT_MOD_I: expected_type = type_ptrdiff_t; break;
387 case FMT_MOD_I32: expected_type = type_int32; break;
388 case FMT_MOD_I64: expected_type = type_int64; break;
391 warn_invalid_length_modifier(pos, fmt_mod, fmt);
394 allowed_flags = FMT_FLAG_MINUS | FMT_FLAG_SPACE | FMT_FLAG_PLUS | FMT_FLAG_ZERO;
400 allowed_flags = FMT_FLAG_MINUS | FMT_FLAG_HASH | FMT_FLAG_ZERO;
401 goto eval_fmt_mod_unsigned;
404 allowed_flags = FMT_FLAG_MINUS | FMT_FLAG_ZERO;
405 eval_fmt_mod_unsigned:
407 case FMT_MOD_NONE: expected_type = type_unsigned_int; break;
408 case FMT_MOD_hh: expected_type = type_int; break; /* TODO promoted unsigned char */
409 case FMT_MOD_h: expected_type = type_int; break; /* TODO promoted unsigned short */
410 case FMT_MOD_l: expected_type = type_unsigned_long; break;
411 case FMT_MOD_ll: expected_type = type_unsigned_long_long; break;
412 case FMT_MOD_j: expected_type = type_uintmax_t; break;
413 case FMT_MOD_z: expected_type = type_size_t; break;
414 case FMT_MOD_t: expected_type = type_uptrdiff_t; break;
415 case FMT_MOD_I: expected_type = type_size_t; break;
416 case FMT_MOD_I32: expected_type = type_unsigned_int32; break;
417 case FMT_MOD_I64: expected_type = type_unsigned_int64; break;
420 warn_invalid_length_modifier(pos, fmt_mod, fmt);
434 case FMT_MOD_l: /* l modifier is ignored */
435 case FMT_MOD_NONE: expected_type = type_double; break;
436 case FMT_MOD_L: expected_type = type_long_double; break;
439 warn_invalid_length_modifier(pos, fmt_mod, fmt);
442 allowed_flags = FMT_FLAG_MINUS | FMT_FLAG_SPACE | FMT_FLAG_PLUS | FMT_FLAG_HASH | FMT_FLAG_ZERO;
446 if (fmt_mod != FMT_MOD_NONE) {
447 warn_invalid_length_modifier(pos, fmt_mod, fmt);
450 expected_type = type_wchar_t;
451 allowed_flags = FMT_FLAG_NONE;
455 expected_type = type_int;
457 case FMT_MOD_NONE: expected_type = type_int; break; /* TODO promoted char */
458 case FMT_MOD_l: expected_type = type_wint_t; break;
459 case FMT_MOD_w: expected_type = type_wchar_t; break;
462 warn_invalid_length_modifier(pos, fmt_mod, fmt);
465 allowed_flags = FMT_FLAG_NONE;
469 if (fmt_mod != FMT_MOD_NONE) {
470 warn_invalid_length_modifier(pos, fmt_mod, fmt);
473 expected_type = type_wchar_t_ptr;
474 expected_qual = TYPE_QUALIFIER_CONST;
475 allowed_flags = FMT_FLAG_MINUS;
480 case FMT_MOD_NONE: expected_type = type_char_ptr; break;
481 case FMT_MOD_l: expected_type = type_wchar_t_ptr; break;
482 case FMT_MOD_w: expected_type = type_wchar_t_ptr; break;
485 warn_invalid_length_modifier(pos, fmt_mod, fmt);
488 expected_qual = TYPE_QUALIFIER_CONST;
489 allowed_flags = FMT_FLAG_MINUS;
493 if (fmt_mod != FMT_MOD_NONE) {
494 warn_invalid_length_modifier(pos, fmt_mod, fmt);
497 expected_type = type_void_ptr;
498 allowed_flags = FMT_FLAG_NONE;
503 case FMT_MOD_NONE: expected_type = type_int_ptr; break;
504 case FMT_MOD_hh: expected_type = type_signed_char_ptr; break;
505 case FMT_MOD_h: expected_type = type_short_ptr; break;
506 case FMT_MOD_l: expected_type = type_long_ptr; break;
507 case FMT_MOD_ll: expected_type = type_long_long_ptr; break;
508 case FMT_MOD_j: expected_type = type_intmax_t_ptr; break;
509 case FMT_MOD_z: expected_type = type_ssize_t_ptr; break;
510 case FMT_MOD_t: expected_type = type_ptrdiff_t_ptr; break;
513 warn_invalid_length_modifier(pos, fmt_mod, fmt);
516 allowed_flags = FMT_FLAG_NONE;
520 warningf(pos, "encountered unknown conversion specifier '%%%C' at position %u", (wint_t)fmt, num_fmt);
524 format_flags_t wrong_flags = fmt_flags & ~allowed_flags;
525 if (wrong_flags != 0) {
528 if (wrong_flags & FMT_FLAG_HASH) *p++ = '#';
529 if (wrong_flags & FMT_FLAG_ZERO) *p++ = '0';
530 if (wrong_flags & FMT_FLAG_MINUS) *p++ = '-';
531 if (wrong_flags & FMT_FLAG_SPACE) *p++ = ' ';
532 if (wrong_flags & FMT_FLAG_PLUS) *p++ = '+';
533 if (wrong_flags & FMT_FLAG_TICK) *p++ = '\'';
536 warningf(pos, "invalid format flags \"%s\" in conversion specification %%%c at position %u", wrong, fmt, num_fmt);
540 warningf(pos, "too few arguments for format string");
544 { /* create a scope here to prevent warning about the jump to next_arg */
545 type_t *const arg_type = arg->expression->base.type;
546 type_t *const arg_skip = skip_typeref(arg_type);
547 type_t *const expected_type_skip = skip_typeref(expected_type);
550 /* allow any pointer type for %p, not just void */
551 if (is_type_pointer(arg_skip))
555 if (is_type_pointer(expected_type_skip)) {
556 if (is_type_pointer(arg_skip)) {
557 type_t *const exp_to = skip_typeref(expected_type_skip->pointer.points_to);
558 type_t *const arg_to = skip_typeref(arg_skip->pointer.points_to);
559 if ((arg_to->base.qualifiers & ~expected_qual) == 0 &&
560 get_unqualified_type(arg_to) == exp_to) {
565 if (get_unqualified_type(arg_skip) == expected_type_skip) {
569 if (is_type_valid(arg_skip)) {
571 "argument type '%T' does not match conversion specifier '%%%s%c' at position %u",
572 arg_type, get_length_modifier_name(fmt_mod), (char)fmt, num_fmt);
578 if (!atend(&vchar)) {
579 warningf(pos, "format string contains NUL");
582 unsigned num_args = num_fmt;
583 while (arg != NULL) {
587 warningf(pos, "%u argument%s but only %u format specifier%s",
588 num_args, num_args != 1 ? "s" : "",
589 num_fmt, num_fmt != 1 ? "s" : "");
594 * Check scanf-style format.
596 static void check_scanf_format(const call_argument_t *arg, const format_spec_t *spec)
598 /* find format arg */
600 for (; idx < spec->fmt_idx; ++idx)
603 const expression_t *fmt_expr = arg->expression;
604 if (fmt_expr->kind == EXPR_UNARY_CAST_IMPLICIT) {
605 fmt_expr = fmt_expr->unary.value;
609 if (fmt_expr->kind == EXPR_WIDE_STRING_LITERAL) {
610 vchar.string = &fmt_expr->wide_string.value;
611 vchar.size = fmt_expr->wide_string.value.size;
612 vchar.first = wstring_first;
613 vchar.next = wstring_next;
614 vchar.is_digit = wstring_isdigit;
615 } else if (fmt_expr->kind == EXPR_STRING_LITERAL) {
616 vchar.string = &fmt_expr->string.value;
617 vchar.size = fmt_expr->string.value.size;
618 vchar.first = string_first;
619 vchar.next = string_next;
620 vchar.is_digit = string_isdigit;
624 /* find the real args */
625 for (; idx < spec->arg_idx; ++idx)
628 const source_position_t *pos = &fmt_expr->base.source_position;
629 unsigned fmt = vchar.first(&vchar);
630 unsigned num_fmt = 0;
631 for (; fmt != '\0'; fmt = vchar.next(&vchar)) {
634 fmt = vchar.next(&vchar);
641 /* length modifier */
642 format_length_modifier_t fmt_mod;
645 fmt = vchar.next(&vchar);
647 fmt = vchar.next(&vchar);
648 fmt_mod = FMT_MOD_hh;
655 fmt = vchar.next(&vchar);
657 fmt = vchar.next(&vchar);
658 fmt_mod = FMT_MOD_ll;
664 case 'L': fmt = vchar.next(&vchar); fmt_mod = FMT_MOD_L; break;
665 case 'j': fmt = vchar.next(&vchar); fmt_mod = FMT_MOD_j; break;
666 case 't': fmt = vchar.next(&vchar); fmt_mod = FMT_MOD_t; break;
667 case 'z': fmt = vchar.next(&vchar); fmt_mod = FMT_MOD_z; break;
671 fmt = vchar.next(&vchar); fmt_mod = FMT_MOD_w;
673 fmt_mod = FMT_MOD_NONE;
678 fmt = vchar.next(&vchar); fmt_mod = FMT_MOD_I;
680 fmt = vchar.next(&vchar);
682 fmt = vchar.next(&vchar);
683 fmt_mod = FMT_MOD_I32;
688 } else if (fmt == '6') {
689 fmt = vchar.next(&vchar);
691 fmt = vchar.next(&vchar);
692 fmt_mod = FMT_MOD_I64;
699 fmt_mod = FMT_MOD_NONE;
703 fmt_mod = FMT_MOD_NONE;
708 warningf(pos, "dangling %% in format string");
712 type_t *expected_type;
717 case FMT_MOD_NONE: expected_type = type_int; break;
718 case FMT_MOD_hh: expected_type = type_signed_char; break;
719 case FMT_MOD_h: expected_type = type_short; break;
720 case FMT_MOD_l: expected_type = type_long; break;
721 case FMT_MOD_ll: expected_type = type_long_long; break;
722 case FMT_MOD_j: expected_type = type_intmax_t; break;
723 case FMT_MOD_z: expected_type = type_ssize_t; break;
724 case FMT_MOD_t: expected_type = type_ptrdiff_t; break;
725 case FMT_MOD_I: expected_type = type_ptrdiff_t; break;
726 case FMT_MOD_I32: expected_type = type_int32; break;
727 case FMT_MOD_I64: expected_type = type_int64; break;
730 warn_invalid_length_modifier(pos, fmt_mod, fmt);
738 goto eval_fmt_mod_unsigned;
741 eval_fmt_mod_unsigned:
743 case FMT_MOD_NONE: expected_type = type_unsigned_int; break;
744 case FMT_MOD_hh: expected_type = type_unsigned_char; break;
745 case FMT_MOD_h: expected_type = type_unsigned_short; break;
746 case FMT_MOD_l: expected_type = type_unsigned_long; break;
747 case FMT_MOD_ll: expected_type = type_unsigned_long_long; break;
748 case FMT_MOD_j: expected_type = type_uintmax_t; break;
749 case FMT_MOD_z: expected_type = type_size_t; break;
750 case FMT_MOD_t: expected_type = type_uptrdiff_t; break;
751 case FMT_MOD_I: expected_type = type_size_t; break;
752 case FMT_MOD_I32: expected_type = type_unsigned_int32; break;
753 case FMT_MOD_I64: expected_type = type_unsigned_int64; break;
756 warn_invalid_length_modifier(pos, fmt_mod, fmt);
770 case FMT_MOD_l: /* l modifier is ignored */
771 case FMT_MOD_NONE: expected_type = type_double; break;
772 case FMT_MOD_L: expected_type = type_long_double; break;
775 warn_invalid_length_modifier(pos, fmt_mod, fmt);
781 if (fmt_mod != FMT_MOD_NONE) {
782 warn_invalid_length_modifier(pos, fmt_mod, fmt);
785 expected_type = type_wchar_t;
789 expected_type = type_int;
791 case FMT_MOD_NONE: expected_type = type_int; break; /* TODO promoted char */
792 case FMT_MOD_l: expected_type = type_wint_t; break;
793 case FMT_MOD_w: expected_type = type_wchar_t; break;
796 warn_invalid_length_modifier(pos, fmt_mod, fmt);
802 if (fmt_mod != FMT_MOD_NONE) {
803 warn_invalid_length_modifier(pos, fmt_mod, fmt);
806 expected_type = type_wchar_t;
812 case FMT_MOD_NONE: expected_type = type_char; break;
813 case FMT_MOD_l: expected_type = type_wchar_t; break;
814 case FMT_MOD_w: expected_type = type_wchar_t; break;
817 warn_invalid_length_modifier(pos, fmt_mod, fmt);
823 if (fmt_mod != FMT_MOD_NONE) {
824 warn_invalid_length_modifier(pos, fmt_mod, fmt);
827 expected_type = type_void_ptr;
832 case FMT_MOD_NONE: expected_type = type_int; break;
833 case FMT_MOD_hh: expected_type = type_signed_char; break;
834 case FMT_MOD_h: expected_type = type_short; break;
835 case FMT_MOD_l: expected_type = type_long; break;
836 case FMT_MOD_ll: expected_type = type_long_long; break;
837 case FMT_MOD_j: expected_type = type_intmax_t; break;
838 case FMT_MOD_z: expected_type = type_ssize_t; break;
839 case FMT_MOD_t: expected_type = type_ptrdiff_t; break;
842 warn_invalid_length_modifier(pos, fmt_mod, fmt);
848 warningf(pos, "encountered unknown conversion specifier '%%%C' at position %u", (wint_t)fmt, num_fmt);
853 warningf(pos, "too few arguments for format string");
857 { /* create a scope here to prevent warning about the jump to next_arg */
858 type_t *const arg_type = arg->expression->base.type;
859 type_t *const arg_skip = skip_typeref(arg_type);
860 type_t *const expected_type_skip = skip_typeref(expected_type);
862 if (! is_type_pointer(arg_skip))
864 type_t *const ptr_skip = skip_typeref(arg_skip->pointer.points_to);
867 /* allow any pointer type for %p, not just void */
868 if (is_type_pointer(ptr_skip))
872 /* do NOT allow const or restrict, all other should be ok */
873 if (ptr_skip->base.qualifiers & (TYPE_QUALIFIER_CONST | TYPE_QUALIFIER_VOLATILE))
875 type_t *const unqual_ptr = get_unqualified_type(ptr_skip);
876 if (unqual_ptr == expected_type_skip) {
878 } else if (expected_type_skip == type_char) {
879 /* char matches with unsigned char AND signed char */
880 if (unqual_ptr == type_signed_char || unqual_ptr == type_unsigned_char)
884 if (is_type_valid(arg_skip)) {
886 "argument type '%T' does not match conversion specifier '%%%s%c' at position %u",
887 arg_type, get_length_modifier_name(fmt_mod), (char)fmt, num_fmt);
893 if (!atend(&vchar)) {
894 warningf(pos, "format string contains NUL");
897 unsigned num_args = num_fmt;
898 while (arg != NULL) {
902 warningf(pos, "%u argument%s but only %u format string%s",
903 num_args, num_args != 1 ? "s" : "",
904 num_fmt, num_fmt != 1 ? "s" : "");
908 static const format_spec_t builtin_table[] = {
909 { "printf", FORMAT_PRINTF, 0, 1 },
910 { "wprintf", FORMAT_PRINTF, 0, 1 },
911 { "sprintf", FORMAT_PRINTF, 1, 2 },
912 { "swprintf", FORMAT_PRINTF, 1, 2 },
913 { "snprintf", FORMAT_PRINTF, 2, 3 },
914 { "snwprintf", FORMAT_PRINTF, 2, 3 },
915 { "fprintf", FORMAT_PRINTF, 1, 2 },
916 { "fwprintf", FORMAT_PRINTF, 1, 2 },
917 { "snwprintf", FORMAT_PRINTF, 2, 3 },
918 { "snwprintf", FORMAT_PRINTF, 2, 3 },
920 { "scanf", FORMAT_SCANF, 0, 1 },
921 { "wscanf", FORMAT_SCANF, 0, 1 },
922 { "sscanf", FORMAT_SCANF, 1, 2 },
923 { "swscanf", FORMAT_SCANF, 1, 2 },
924 { "fscanf", FORMAT_SCANF, 1, 2 },
925 { "fwscanf", FORMAT_SCANF, 1, 2 },
927 { "strftime", FORMAT_STRFTIME, 3, 4 },
928 { "wcstrftime", FORMAT_STRFTIME, 3, 4 },
930 { "strfmon", FORMAT_STRFMON, 3, 4 },
933 { "_snprintf", FORMAT_PRINTF, 2, 3 },
934 { "_snwprintf", FORMAT_PRINTF, 2, 3 },
935 { "_scrintf", FORMAT_PRINTF, 0, 1 },
936 { "_scwprintf", FORMAT_PRINTF, 0, 1 },
937 { "printf_s", FORMAT_PRINTF, 0, 1 },
938 { "wprintf_s", FORMAT_PRINTF, 0, 1 },
939 { "sprintf_s", FORMAT_PRINTF, 3, 4 },
940 { "swprintf_s", FORMAT_PRINTF, 3, 4 },
941 { "fprintf_s", FORMAT_PRINTF, 1, 2 },
942 { "fwprintf_s", FORMAT_PRINTF, 1, 2 },
943 { "_sprintf_l", FORMAT_PRINTF, 1, 3 },
944 { "_swprintf_l", FORMAT_PRINTF, 1, 3 },
945 { "_printf_l", FORMAT_PRINTF, 0, 2 },
946 { "_wprintf_l", FORMAT_PRINTF, 0, 2 },
947 { "_fprintf_l", FORMAT_PRINTF, 1, 3 },
948 { "_fwprintf_l", FORMAT_PRINTF, 1, 3 },
949 { "_printf_s_l", FORMAT_PRINTF, 0, 2 },
950 { "_wprintf_s_l", FORMAT_PRINTF, 0, 2 },
951 { "_sprintf_s_l", FORMAT_PRINTF, 3, 5 },
952 { "_swprintf_s_l", FORMAT_PRINTF, 3, 5 },
953 { "_fprintf_s_l", FORMAT_PRINTF, 1, 3 },
954 { "_fwprintf_s_l", FORMAT_PRINTF, 1, 3 },
957 void check_format(const call_expression_t *const call)
962 const expression_t *const func_expr = call->function;
963 if (func_expr->kind != EXPR_REFERENCE)
966 const entity_t *const entity = func_expr->reference.entity;
967 const call_argument_t * arg = call->arguments;
970 /* the declaration has a GNU format attribute, check it */
973 * For some functions we always check the format, even if it was not specified.
974 * This allows to check format even in MS mode or without header included.
976 const char *const name = entity->base.symbol->string;
977 for (size_t i = 0; i < sizeof(builtin_table) / sizeof(builtin_table[0]); ++i) {
978 if (strcmp(name, builtin_table[i].name) == 0) {
979 switch (builtin_table[i].fmt_kind) {
981 check_printf_format(arg, &builtin_table[i]);
984 check_scanf_format(arg, &builtin_table[i]);
986 case FORMAT_STRFTIME:
988 /* TODO: implement other cases */