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; ++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);
522 warningf(pos, "too few arguments for format string");
528 format_flags_t wrong_flags = fmt_flags & ~allowed_flags;
529 if (wrong_flags != 0) {
532 if (wrong_flags & FMT_FLAG_HASH) *p++ = '#';
533 if (wrong_flags & FMT_FLAG_ZERO) *p++ = '0';
534 if (wrong_flags & FMT_FLAG_MINUS) *p++ = '-';
535 if (wrong_flags & FMT_FLAG_SPACE) *p++ = ' ';
536 if (wrong_flags & FMT_FLAG_PLUS) *p++ = '+';
537 if (wrong_flags & FMT_FLAG_TICK) *p++ = '\'';
540 warningf(pos, "invalid format flags \"%s\" in conversion specification %%%c at position %u", wrong, fmt, num_fmt);
544 warningf(pos, "too few arguments for format string");
548 { /* create a scope here to prevent warning about the jump to next_arg */
549 type_t *const arg_type = arg->expression->base.type;
550 type_t *const arg_skip = skip_typeref(arg_type);
551 type_t *const expected_type_skip = skip_typeref(expected_type);
554 /* allow any pointer type for %p, not just void */
555 if (is_type_pointer(arg_skip))
559 if (is_type_pointer(expected_type_skip)) {
560 if (is_type_pointer(arg_skip)) {
561 type_t *const exp_to = skip_typeref(expected_type_skip->pointer.points_to);
562 type_t *const arg_to = skip_typeref(arg_skip->pointer.points_to);
563 if ((arg_to->base.qualifiers & ~expected_qual) == 0 &&
564 get_unqualified_type(arg_to) == exp_to) {
569 if (get_unqualified_type(arg_skip) == expected_type_skip) {
573 if (is_type_valid(arg_skip)) {
575 "argument type '%T' does not match conversion specifier '%%%s%c' at position %u",
576 arg_type, get_length_modifier_name(fmt_mod), (char)fmt, num_fmt);
582 if (!atend(&vchar)) {
583 warningf(pos, "format string contains '\\0'");
586 unsigned num_args = num_fmt;
587 while (arg != NULL) {
591 warningf(pos, "%u argument%s but only %u format specifier%s",
592 num_args, num_args != 1 ? "s" : "",
593 num_fmt, num_fmt != 1 ? "s" : "");
598 * Check scanf-style format.
600 static void check_scanf_format(const call_argument_t *arg, const format_spec_t *spec)
602 /* find format arg */
604 for (; idx < spec->fmt_idx; ++idx) {
610 const expression_t *fmt_expr = arg->expression;
611 if (fmt_expr->kind == EXPR_UNARY_CAST_IMPLICIT) {
612 fmt_expr = fmt_expr->unary.value;
616 if (fmt_expr->kind == EXPR_WIDE_STRING_LITERAL) {
617 vchar.string = &fmt_expr->wide_string.value;
618 vchar.size = fmt_expr->wide_string.value.size;
619 vchar.first = wstring_first;
620 vchar.next = wstring_next;
621 vchar.is_digit = wstring_isdigit;
622 } else if (fmt_expr->kind == EXPR_STRING_LITERAL) {
623 vchar.string = &fmt_expr->string.value;
624 vchar.size = fmt_expr->string.value.size;
625 vchar.first = string_first;
626 vchar.next = string_next;
627 vchar.is_digit = string_isdigit;
631 /* find the real args */
632 for (; idx < spec->arg_idx && arg != NULL; ++idx)
635 const source_position_t *pos = &fmt_expr->base.source_position;
636 unsigned fmt = vchar.first(&vchar);
637 unsigned num_fmt = 0;
638 for (; fmt != '\0'; fmt = vchar.next(&vchar)) {
641 fmt = vchar.next(&vchar);
648 /* length modifier */
649 format_length_modifier_t fmt_mod;
652 fmt = vchar.next(&vchar);
654 fmt = vchar.next(&vchar);
655 fmt_mod = FMT_MOD_hh;
662 fmt = vchar.next(&vchar);
664 fmt = vchar.next(&vchar);
665 fmt_mod = FMT_MOD_ll;
671 case 'L': fmt = vchar.next(&vchar); fmt_mod = FMT_MOD_L; break;
672 case 'j': fmt = vchar.next(&vchar); fmt_mod = FMT_MOD_j; break;
673 case 't': fmt = vchar.next(&vchar); fmt_mod = FMT_MOD_t; break;
674 case 'z': fmt = vchar.next(&vchar); fmt_mod = FMT_MOD_z; break;
678 fmt = vchar.next(&vchar); fmt_mod = FMT_MOD_w;
680 fmt_mod = FMT_MOD_NONE;
685 fmt = vchar.next(&vchar); fmt_mod = FMT_MOD_I;
687 fmt = vchar.next(&vchar);
689 fmt = vchar.next(&vchar);
690 fmt_mod = FMT_MOD_I32;
695 } else if (fmt == '6') {
696 fmt = vchar.next(&vchar);
698 fmt = vchar.next(&vchar);
699 fmt_mod = FMT_MOD_I64;
706 fmt_mod = FMT_MOD_NONE;
710 fmt_mod = FMT_MOD_NONE;
715 warningf(pos, "dangling %% in format string");
719 type_t *expected_type;
724 case FMT_MOD_NONE: expected_type = type_int; break;
725 case FMT_MOD_hh: expected_type = type_signed_char; break;
726 case FMT_MOD_h: expected_type = type_short; break;
727 case FMT_MOD_l: expected_type = type_long; break;
728 case FMT_MOD_ll: expected_type = type_long_long; break;
729 case FMT_MOD_j: expected_type = type_intmax_t; break;
730 case FMT_MOD_z: expected_type = type_ssize_t; break;
731 case FMT_MOD_t: expected_type = type_ptrdiff_t; break;
732 case FMT_MOD_I: expected_type = type_ptrdiff_t; break;
733 case FMT_MOD_I32: expected_type = type_int32; break;
734 case FMT_MOD_I64: expected_type = type_int64; break;
737 warn_invalid_length_modifier(pos, fmt_mod, fmt);
745 goto eval_fmt_mod_unsigned;
748 eval_fmt_mod_unsigned:
750 case FMT_MOD_NONE: expected_type = type_unsigned_int; break;
751 case FMT_MOD_hh: expected_type = type_unsigned_char; break;
752 case FMT_MOD_h: expected_type = type_unsigned_short; break;
753 case FMT_MOD_l: expected_type = type_unsigned_long; break;
754 case FMT_MOD_ll: expected_type = type_unsigned_long_long; break;
755 case FMT_MOD_j: expected_type = type_uintmax_t; break;
756 case FMT_MOD_z: expected_type = type_size_t; break;
757 case FMT_MOD_t: expected_type = type_uptrdiff_t; break;
758 case FMT_MOD_I: expected_type = type_size_t; break;
759 case FMT_MOD_I32: expected_type = type_unsigned_int32; break;
760 case FMT_MOD_I64: expected_type = type_unsigned_int64; break;
763 warn_invalid_length_modifier(pos, fmt_mod, fmt);
777 case FMT_MOD_l: /* l modifier is ignored */
778 case FMT_MOD_NONE: expected_type = type_double; break;
779 case FMT_MOD_L: expected_type = type_long_double; break;
782 warn_invalid_length_modifier(pos, fmt_mod, fmt);
788 if (fmt_mod != FMT_MOD_NONE) {
789 warn_invalid_length_modifier(pos, fmt_mod, fmt);
792 expected_type = type_wchar_t;
796 expected_type = type_int;
798 case FMT_MOD_NONE: expected_type = type_int; break; /* TODO promoted char */
799 case FMT_MOD_l: expected_type = type_wint_t; break;
800 case FMT_MOD_w: expected_type = type_wchar_t; break;
803 warn_invalid_length_modifier(pos, fmt_mod, fmt);
809 if (fmt_mod != FMT_MOD_NONE) {
810 warn_invalid_length_modifier(pos, fmt_mod, fmt);
813 expected_type = type_wchar_t;
819 case FMT_MOD_NONE: expected_type = type_char; break;
820 case FMT_MOD_l: expected_type = type_wchar_t; break;
821 case FMT_MOD_w: expected_type = type_wchar_t; break;
824 warn_invalid_length_modifier(pos, fmt_mod, fmt);
830 if (fmt_mod != FMT_MOD_NONE) {
831 warn_invalid_length_modifier(pos, fmt_mod, fmt);
834 expected_type = type_void_ptr;
839 case FMT_MOD_NONE: expected_type = type_int; break;
840 case FMT_MOD_hh: expected_type = type_signed_char; break;
841 case FMT_MOD_h: expected_type = type_short; break;
842 case FMT_MOD_l: expected_type = type_long; break;
843 case FMT_MOD_ll: expected_type = type_long_long; break;
844 case FMT_MOD_j: expected_type = type_intmax_t; break;
845 case FMT_MOD_z: expected_type = type_ssize_t; break;
846 case FMT_MOD_t: expected_type = type_ptrdiff_t; break;
849 warn_invalid_length_modifier(pos, fmt_mod, fmt);
855 warningf(pos, "encountered unknown conversion specifier '%%%C' at position %u", (wint_t)fmt, num_fmt);
857 warningf(pos, "too few arguments for format string");
864 warningf(pos, "too few arguments for format string");
868 { /* create a scope here to prevent warning about the jump to next_arg */
869 type_t *const arg_type = arg->expression->base.type;
870 type_t *const arg_skip = skip_typeref(arg_type);
871 type_t *const expected_type_skip = skip_typeref(expected_type);
873 if (! is_type_pointer(arg_skip))
875 type_t *const ptr_skip = skip_typeref(arg_skip->pointer.points_to);
878 /* allow any pointer type for %p, not just void */
879 if (is_type_pointer(ptr_skip))
883 /* do NOT allow const or restrict, all other should be ok */
884 if (ptr_skip->base.qualifiers & (TYPE_QUALIFIER_CONST | TYPE_QUALIFIER_VOLATILE))
886 type_t *const unqual_ptr = get_unqualified_type(ptr_skip);
887 if (unqual_ptr == expected_type_skip) {
889 } else if (expected_type_skip == type_char) {
890 /* char matches with unsigned char AND signed char */
891 if (unqual_ptr == type_signed_char || unqual_ptr == type_unsigned_char)
895 if (is_type_valid(arg_skip)) {
897 "argument type '%T' does not match conversion specifier '%%%s%c' at position %u",
898 arg_type, get_length_modifier_name(fmt_mod), (char)fmt, num_fmt);
904 if (!atend(&vchar)) {
905 warningf(pos, "format string contains '\\0'");
908 unsigned num_args = num_fmt;
909 while (arg != NULL) {
913 warningf(pos, "%u argument%s but only %u format specifier%s",
914 num_args, num_args != 1 ? "s" : "",
915 num_fmt, num_fmt != 1 ? "s" : "");
919 static const format_spec_t builtin_table[] = {
920 { "printf", FORMAT_PRINTF, 0, 1 },
921 { "wprintf", FORMAT_PRINTF, 0, 1 },
922 { "sprintf", FORMAT_PRINTF, 1, 2 },
923 { "swprintf", FORMAT_PRINTF, 1, 2 },
924 { "snprintf", FORMAT_PRINTF, 2, 3 },
925 { "snwprintf", FORMAT_PRINTF, 2, 3 },
926 { "fprintf", FORMAT_PRINTF, 1, 2 },
927 { "fwprintf", FORMAT_PRINTF, 1, 2 },
928 { "snwprintf", FORMAT_PRINTF, 2, 3 },
929 { "snwprintf", FORMAT_PRINTF, 2, 3 },
931 { "scanf", FORMAT_SCANF, 0, 1 },
932 { "wscanf", FORMAT_SCANF, 0, 1 },
933 { "sscanf", FORMAT_SCANF, 1, 2 },
934 { "swscanf", FORMAT_SCANF, 1, 2 },
935 { "fscanf", FORMAT_SCANF, 1, 2 },
936 { "fwscanf", FORMAT_SCANF, 1, 2 },
938 { "strftime", FORMAT_STRFTIME, 3, 4 },
939 { "wcstrftime", FORMAT_STRFTIME, 3, 4 },
941 { "strfmon", FORMAT_STRFMON, 3, 4 },
944 { "_snprintf", FORMAT_PRINTF, 2, 3 },
945 { "_snwprintf", FORMAT_PRINTF, 2, 3 },
946 { "_scrintf", FORMAT_PRINTF, 0, 1 },
947 { "_scwprintf", FORMAT_PRINTF, 0, 1 },
948 { "printf_s", FORMAT_PRINTF, 0, 1 },
949 { "wprintf_s", FORMAT_PRINTF, 0, 1 },
950 { "sprintf_s", FORMAT_PRINTF, 3, 4 },
951 { "swprintf_s", FORMAT_PRINTF, 3, 4 },
952 { "fprintf_s", FORMAT_PRINTF, 1, 2 },
953 { "fwprintf_s", FORMAT_PRINTF, 1, 2 },
954 { "_sprintf_l", FORMAT_PRINTF, 1, 3 },
955 { "_swprintf_l", FORMAT_PRINTF, 1, 3 },
956 { "_printf_l", FORMAT_PRINTF, 0, 2 },
957 { "_wprintf_l", FORMAT_PRINTF, 0, 2 },
958 { "_fprintf_l", FORMAT_PRINTF, 1, 3 },
959 { "_fwprintf_l", FORMAT_PRINTF, 1, 3 },
960 { "_printf_s_l", FORMAT_PRINTF, 0, 2 },
961 { "_wprintf_s_l", FORMAT_PRINTF, 0, 2 },
962 { "_sprintf_s_l", FORMAT_PRINTF, 3, 5 },
963 { "_swprintf_s_l", FORMAT_PRINTF, 3, 5 },
964 { "_fprintf_s_l", FORMAT_PRINTF, 1, 3 },
965 { "_fwprintf_s_l", FORMAT_PRINTF, 1, 3 },
968 void check_format(const call_expression_t *const call)
973 const expression_t *const func_expr = call->function;
974 if (func_expr->kind != EXPR_REFERENCE)
977 const entity_t *const entity = func_expr->reference.entity;
978 const call_argument_t * arg = call->arguments;
981 /* the declaration has a GNU format attribute, check it */
984 * For some functions we always check the format, even if it was not specified.
985 * This allows to check format even in MS mode or without header included.
987 const char *const name = entity->base.symbol->string;
988 for (size_t i = 0; i < sizeof(builtin_table) / sizeof(builtin_table[0]); ++i) {
989 if (strcmp(name, builtin_table[i].name) == 0) {
990 switch (builtin_table[i].fmt_kind) {
992 check_printf_format(arg, &builtin_table[i]);
995 check_scanf_format(arg, &builtin_table[i]);
997 case FORMAT_STRFTIME:
999 /* TODO: implement other cases */