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)
163 const expression_t *fmt_expr = arg->expression;
164 if (fmt_expr->kind == EXPR_UNARY_CAST_IMPLICIT) {
165 fmt_expr = fmt_expr->unary.value;
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;
184 /* find the real args */
185 for(; idx < spec->arg_idx; ++idx)
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)) {
194 fmt = vchar.next(&vchar);
201 format_flags_t fmt_flags = FMT_FLAG_NONE;
203 fmt = vchar.next(&vchar);
204 fmt_flags |= FMT_FLAG_ZERO;
207 /* argument selector or minimum field width */
208 if (vchar.is_digit(fmt)) {
210 fmt = vchar.next(&vchar);
211 } while (vchar.is_digit(fmt));
213 /* digit string was ... */
215 /* ... argument selector */
216 fmt_flags = FMT_FLAG_NONE; /* reset possibly set 0-flag */
220 /* ... minimum field width */
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;
232 if (fmt_flags & FMT_FLAG_PLUS) {
233 warningf(pos, "' ' is overridden by prior '+' in conversion specification %u", num_fmt);
235 flag = FMT_FLAG_SPACE;
239 if (fmt_flags & FMT_FLAG_SPACE) {
240 warningf(pos, "'+' overrides prior ' ' in conversion specification %u", num_fmt);
242 flag = FMT_FLAG_PLUS;
245 default: goto break_fmt_flags;
247 if (fmt_flags & flag) {
248 warningf(pos, "repeated flag '%c' in conversion specification %u", (char)fmt, num_fmt);
251 fmt = vchar.next(&vchar);
255 /* minimum field width */
257 fmt = vchar.next(&vchar);
259 warningf(pos, "missing argument for '*' field width in conversion specification %u", num_fmt);
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);
268 while (vchar.is_digit(fmt)) {
269 fmt = vchar.next(&vchar);
276 fmt = vchar.next(&vchar);
278 fmt = vchar.next(&vchar);
280 warningf(pos, "missing argument for '*' precision in conversion specification %u", num_fmt);
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);
289 /* digit string may be omitted */
290 while (vchar.is_digit(fmt)) {
291 fmt = vchar.next(&vchar);
296 /* length modifier */
297 format_length_modifier_t fmt_mod;
300 fmt = vchar.next(&vchar);
302 fmt = vchar.next(&vchar);
303 fmt_mod = FMT_MOD_hh;
310 fmt = vchar.next(&vchar);
312 fmt = vchar.next(&vchar);
313 fmt_mod = FMT_MOD_ll;
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;
327 fmt = vchar.next(&vchar); fmt_mod = FMT_MOD_w;
329 fmt_mod = FMT_MOD_NONE;
334 fmt = vchar.next(&vchar); fmt_mod = FMT_MOD_I;
336 fmt = vchar.next(&vchar);
338 fmt = vchar.next(&vchar);
339 fmt_mod = FMT_MOD_I32;
344 } else if (fmt == '6') {
345 fmt = vchar.next(&vchar);
347 fmt = vchar.next(&vchar);
348 fmt_mod = FMT_MOD_I64;
355 fmt_mod = FMT_MOD_NONE;
359 fmt_mod = FMT_MOD_NONE;
364 warningf(pos, "dangling %% in format string");
368 type_t *expected_type;
369 type_qualifiers_t expected_qual = TYPE_QUALIFIER_NONE;
370 format_flags_t allowed_flags;
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;
388 warn_invalid_length_modifier(pos, fmt_mod, fmt);
391 allowed_flags = FMT_FLAG_MINUS | FMT_FLAG_SPACE | FMT_FLAG_PLUS | FMT_FLAG_ZERO;
397 allowed_flags = FMT_FLAG_MINUS | FMT_FLAG_HASH | FMT_FLAG_ZERO;
398 goto eval_fmt_mod_unsigned;
401 allowed_flags = FMT_FLAG_MINUS | FMT_FLAG_ZERO;
402 eval_fmt_mod_unsigned:
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;
417 warn_invalid_length_modifier(pos, fmt_mod, fmt);
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;
436 warn_invalid_length_modifier(pos, fmt_mod, fmt);
439 allowed_flags = FMT_FLAG_MINUS | FMT_FLAG_SPACE | FMT_FLAG_PLUS | FMT_FLAG_HASH | FMT_FLAG_ZERO;
443 if (fmt_mod != FMT_MOD_NONE) {
444 warn_invalid_length_modifier(pos, fmt_mod, fmt);
447 expected_type = type_wchar_t;
448 allowed_flags = FMT_FLAG_NONE;
452 expected_type = type_int;
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;
459 warn_invalid_length_modifier(pos, fmt_mod, fmt);
462 allowed_flags = FMT_FLAG_NONE;
466 if (fmt_mod != FMT_MOD_NONE) {
467 warn_invalid_length_modifier(pos, fmt_mod, fmt);
470 expected_type = type_wchar_t_ptr;
471 expected_qual = TYPE_QUALIFIER_CONST;
472 allowed_flags = FMT_FLAG_MINUS;
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;
482 warn_invalid_length_modifier(pos, fmt_mod, fmt);
485 expected_qual = TYPE_QUALIFIER_CONST;
486 allowed_flags = FMT_FLAG_MINUS;
490 if (fmt_mod != FMT_MOD_NONE) {
491 warn_invalid_length_modifier(pos, fmt_mod, fmt);
494 expected_type = type_void_ptr;
495 allowed_flags = FMT_FLAG_NONE;
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;
510 warn_invalid_length_modifier(pos, fmt_mod, fmt);
513 allowed_flags = FMT_FLAG_NONE;
517 warningf(pos, "encountered unknown conversion specifier '%%%C' at position %u", (wint_t)fmt, num_fmt);
521 format_flags_t wrong_flags = fmt_flags & ~allowed_flags;
522 if (wrong_flags != 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++] = '\'';
533 warningf(pos, "invalid format flags \"%s\" in conversion specification %%%c at position %u", wrong, fmt, num_fmt);
537 warningf(pos, "too few arguments for format string");
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);
547 /* allow any pointer type for %p, not just void */
548 if (is_type_pointer(arg_skip))
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) {
562 if (get_unqualified_type(arg_skip) == expected_type_skip) {
566 if (is_type_valid(arg_skip)) {
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);
575 if (!atend(&vchar)) {
576 warningf(pos, "format string contains NUL");
579 unsigned num_args = num_fmt;
580 while (arg != NULL) {
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" : "");
591 * Check scanf-style format.
593 static void check_scanf_format(const call_argument_t *arg, const format_spec_t *spec)
595 /* find format arg */
597 for (; idx < spec->fmt_idx; ++idx)
600 const expression_t *fmt_expr = arg->expression;
601 if (fmt_expr->kind == EXPR_UNARY_CAST_IMPLICIT) {
602 fmt_expr = fmt_expr->unary.value;
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;
621 /* find the real args */
622 for (; idx < spec->arg_idx; ++idx)
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)) {
631 fmt = vchar.next(&vchar);
638 /* length modifier */
639 format_length_modifier_t fmt_mod;
642 fmt = vchar.next(&vchar);
644 fmt = vchar.next(&vchar);
645 fmt_mod = FMT_MOD_hh;
652 fmt = vchar.next(&vchar);
654 fmt = vchar.next(&vchar);
655 fmt_mod = FMT_MOD_ll;
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;
668 fmt = vchar.next(&vchar); fmt_mod = FMT_MOD_w;
670 fmt_mod = FMT_MOD_NONE;
675 fmt = vchar.next(&vchar); fmt_mod = FMT_MOD_I;
677 fmt = vchar.next(&vchar);
679 fmt = vchar.next(&vchar);
680 fmt_mod = FMT_MOD_I32;
685 } else if (fmt == '6') {
686 fmt = vchar.next(&vchar);
688 fmt = vchar.next(&vchar);
689 fmt_mod = FMT_MOD_I64;
696 fmt_mod = FMT_MOD_NONE;
700 fmt_mod = FMT_MOD_NONE;
705 warningf(pos, "dangling %% in format string");
709 type_t *expected_type;
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;
727 warn_invalid_length_modifier(pos, fmt_mod, fmt);
735 goto eval_fmt_mod_unsigned;
738 eval_fmt_mod_unsigned:
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;
753 warn_invalid_length_modifier(pos, fmt_mod, fmt);
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;
772 warn_invalid_length_modifier(pos, fmt_mod, fmt);
778 if (fmt_mod != FMT_MOD_NONE) {
779 warn_invalid_length_modifier(pos, fmt_mod, fmt);
782 expected_type = type_wchar_t;
786 expected_type = type_int;
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;
793 warn_invalid_length_modifier(pos, fmt_mod, fmt);
799 if (fmt_mod != FMT_MOD_NONE) {
800 warn_invalid_length_modifier(pos, fmt_mod, fmt);
803 expected_type = type_wchar_t;
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;
814 warn_invalid_length_modifier(pos, fmt_mod, fmt);
820 if (fmt_mod != FMT_MOD_NONE) {
821 warn_invalid_length_modifier(pos, fmt_mod, fmt);
824 expected_type = type_void_ptr;
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;
839 warn_invalid_length_modifier(pos, fmt_mod, fmt);
845 warningf(pos, "encountered unknown conversion specifier '%%%C' at position %u", (wint_t)fmt, num_fmt);
850 warningf(pos, "too few arguments for format string");
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);
859 if (! is_type_pointer(arg_skip))
861 type_t *const ptr_skip = skip_typeref(arg_skip->pointer.points_to);
864 /* allow any pointer type for %p, not just void */
865 if (is_type_pointer(ptr_skip))
869 /* do NOT allow const or restrict, all other should be ok */
870 if (ptr_skip->base.qualifiers & (TYPE_QUALIFIER_CONST | TYPE_QUALIFIER_VOLATILE))
872 type_t *const unqual_ptr = get_unqualified_type(ptr_skip);
873 if (unqual_ptr == expected_type_skip) {
875 } else if (expected_type_skip == type_char) {
876 /* char matches with unsigned char AND signed char */
877 if (unqual_ptr == type_signed_char || unqual_ptr == type_unsigned_char)
881 if (is_type_valid(arg_skip)) {
883 "argument type '%T' does not match conversion specifier '%%%s%c' at position %u",
884 arg_type, get_length_modifier_name(fmt_mod), (char)fmt, num_fmt);
890 if (!atend(&vchar)) {
891 warningf(pos, "format string contains NUL");
894 unsigned num_args = num_fmt;
895 while (arg != NULL) {
899 warningf(pos, "%u argument%s but only %u format string%s",
900 num_args, num_args != 1 ? "s" : "",
901 num_fmt, num_fmt != 1 ? "s" : "");
905 static const format_spec_t builtin_table[] = {
906 { "printf", FORMAT_PRINTF, 0, 1 },
907 { "wprintf", FORMAT_PRINTF, 0, 1 },
908 { "sprintf", FORMAT_PRINTF, 1, 2 },
909 { "swprintf", FORMAT_PRINTF, 1, 2 },
910 { "snprintf", FORMAT_PRINTF, 2, 3 },
911 { "snwprintf", FORMAT_PRINTF, 2, 3 },
912 { "fprintf", FORMAT_PRINTF, 1, 2 },
913 { "fwprintf", FORMAT_PRINTF, 1, 2 },
914 { "snwprintf", FORMAT_PRINTF, 2, 3 },
915 { "snwprintf", FORMAT_PRINTF, 2, 3 },
917 { "scanf", FORMAT_SCANF, 0, 1 },
918 { "wscanf", FORMAT_SCANF, 0, 1 },
919 { "sscanf", FORMAT_SCANF, 1, 2 },
920 { "swscanf", FORMAT_SCANF, 1, 2 },
921 { "fscanf", FORMAT_SCANF, 1, 2 },
922 { "fwscanf", FORMAT_SCANF, 1, 2 },
924 { "strftime", FORMAT_STRFTIME, 3, 4 },
925 { "wcstrftime", FORMAT_STRFTIME, 3, 4 },
927 { "strfmon", FORMAT_STRFMON, 3, 4 },
930 { "_snprintf", FORMAT_PRINTF, 2, 3 },
931 { "_snwprintf", FORMAT_PRINTF, 2, 3 },
932 { "_scrintf", FORMAT_PRINTF, 0, 1 },
933 { "_scwprintf", FORMAT_PRINTF, 0, 1 },
934 { "printf_s", FORMAT_PRINTF, 0, 1 },
935 { "wprintf_s", FORMAT_PRINTF, 0, 1 },
936 { "sprintf_s", FORMAT_PRINTF, 3, 4 },
937 { "swprintf_s", FORMAT_PRINTF, 3, 4 },
938 { "fprintf_s", FORMAT_PRINTF, 1, 2 },
939 { "fwprintf_s", FORMAT_PRINTF, 1, 2 },
940 { "_sprintf_l", FORMAT_PRINTF, 1, 3 },
941 { "_swprintf_l", FORMAT_PRINTF, 1, 3 },
942 { "_printf_l", FORMAT_PRINTF, 0, 2 },
943 { "_wprintf_l", FORMAT_PRINTF, 0, 2 },
944 { "_fprintf_l", FORMAT_PRINTF, 1, 3 },
945 { "_fwprintf_l", FORMAT_PRINTF, 1, 3 },
946 { "_printf_s_l", FORMAT_PRINTF, 0, 2 },
947 { "_wprintf_s_l", FORMAT_PRINTF, 0, 2 },
948 { "_sprintf_s_l", FORMAT_PRINTF, 3, 5 },
949 { "_swprintf_s_l", FORMAT_PRINTF, 3, 5 },
950 { "_fprintf_s_l", FORMAT_PRINTF, 1, 3 },
951 { "_fwprintf_s_l", FORMAT_PRINTF, 1, 3 },
954 void check_format(const call_expression_t *const call)
959 const expression_t *const func_expr = call->function;
960 if (func_expr->kind != EXPR_REFERENCE)
963 const entity_t *const entity = func_expr->reference.entity;
964 const call_argument_t * arg = call->arguments;
967 /* the declaration has a GNU format attribute, check it */
970 * For some functions we always check the format, even if it was not specified.
971 * This allows to check format even in MS mode or without header included.
973 const char *const name = entity->base.symbol->string;
974 for (size_t i = 0; i < sizeof(builtin_table) / sizeof(builtin_table[0]); ++i) {
975 if (strcmp(name, builtin_table[i].name) == 0) {
976 switch (builtin_table[i].fmt_kind) {
978 check_printf_format(arg, &builtin_table[i]);
981 check_scanf_format(arg, &builtin_table[i]);
983 case FORMAT_STRFTIME:
985 /* TODO: implement other cases */