2 * This file is part of cparser.
3 * Copyright (C) 2012 Matthias Braun <matze@braunis.de>
9 #include "adt/strutil.h"
11 #include "format_check.h"
15 #include "diagnostic.h"
20 #include "lang_features.h"
22 typedef enum format_flag_t {
24 FMT_FLAG_HASH = 1U << 0,
25 FMT_FLAG_ZERO = 1U << 1,
26 FMT_FLAG_MINUS = 1U << 2,
27 FMT_FLAG_SPACE = 1U << 3,
28 FMT_FLAG_PLUS = 1U << 4,
29 FMT_FLAG_TICK = 1U << 5
32 typedef unsigned format_flags_t;
34 typedef enum format_length_modifier_t {
45 /* only in microsoft mode */
50 } format_length_modifier_t;
52 typedef struct format_spec_t {
53 const char *name; /**< name of the function */
54 format_kind_t fmt_kind; /**< kind */
55 unsigned fmt_idx; /**< index of the format string */
56 unsigned arg_idx; /**< index of the first argument */
59 static const char* get_length_modifier_name(const format_length_modifier_t mod)
61 static const char* const names[] = {
72 /* only in microsoft mode */
75 [FMT_MOD_I32] = "I32",
78 assert((size_t)mod < lengthof(names));
82 static void warn_invalid_length_modifier(const position_t *pos,
83 const format_length_modifier_t mod,
84 const char conversion)
86 char const *const lmod = get_length_modifier_name(mod);
87 warningf(WARN_FORMAT, pos, "invalid length modifier '%s' for conversion specifier '%%%c'", lmod, conversion);
91 * Check printf-style format. Returns number of expected arguments.
93 static int internal_check_printf_format(const expression_t *fmt_expr,
94 const call_argument_t *arg,
95 const format_spec_t *spec)
97 while (fmt_expr->kind == EXPR_UNARY_CAST) {
98 fmt_expr = fmt_expr->unary.value;
102 * gettext results in expressions like (X ? "format_string" : Y)
103 * we assume the left part is the format string
105 if (fmt_expr->kind == EXPR_CONDITIONAL) {
106 conditional_expression_t const *const c = &fmt_expr->conditional;
107 expression_t const * t = c->true_expression;
110 int const nt = internal_check_printf_format(t, arg, spec);
111 int const nf = internal_check_printf_format(c->false_expression, arg, spec);
115 if (fmt_expr->kind != EXPR_STRING_LITERAL)
118 const char *string = fmt_expr->string_literal.value.begin;
119 size_t size = fmt_expr->string_literal.value.size;
120 const char *c = string;
122 const position_t *pos = &fmt_expr->base.pos;
123 unsigned num_fmt = 0;
124 unsigned num_args = 0;
126 for (fmt = *c; fmt != '\0'; fmt = *(++c)) {
137 format_flags_t fmt_flags = FMT_FLAG_NONE;
140 fmt_flags |= FMT_FLAG_ZERO;
143 /* argument selector or minimum field width */
147 } while (isdigit(fmt));
149 /* digit string was ... */
151 /* ... argument selector */
152 fmt_flags = FMT_FLAG_NONE; /* reset possibly set 0-flag */
156 /* ... minimum field width */
162 case '#': flag = FMT_FLAG_HASH; break;
163 case '0': flag = FMT_FLAG_ZERO; break;
164 case '-': flag = FMT_FLAG_MINUS; break;
165 case '\'': flag = FMT_FLAG_TICK; break;
168 if (fmt_flags & FMT_FLAG_PLUS) {
169 warningf(WARN_FORMAT, pos, "' ' is overridden by prior '+' in conversion specification %u", num_fmt);
171 flag = FMT_FLAG_SPACE;
175 if (fmt_flags & FMT_FLAG_SPACE) {
176 warningf(WARN_FORMAT, pos, "'+' overrides prior ' ' in conversion specification %u", num_fmt);
178 flag = FMT_FLAG_PLUS;
181 default: goto break_fmt_flags;
183 if (fmt_flags & flag) {
184 warningf(WARN_FORMAT, pos, "repeated flag '%c' in conversion specification %u", (char)fmt, num_fmt);
191 /* minimum field width */
196 warningf(WARN_FORMAT, pos, "missing argument for '*' field width in conversion specification %u", num_fmt);
199 const type_t *const arg_type = arg->expression->base.type;
200 if (arg_type != type_int) {
201 warningf(WARN_FORMAT, pos, "argument for '*' field width in conversion specification %u is not an 'int', but an '%T'", num_fmt, arg_type);
205 while (isdigit(fmt)) {
213 if (fmt_flags & FMT_FLAG_ZERO) {
214 warningf(WARN_FORMAT, pos, "'0' flag ignored with precision in conversion specification %u", num_fmt);
222 warningf(WARN_FORMAT, pos, "missing argument for '*' precision in conversion specification %u", num_fmt);
225 const type_t *const arg_type = arg->expression->base.type;
226 if (arg_type != type_int) {
227 warningf(WARN_FORMAT, pos, "argument for '*' precision in conversion specification %u is not an 'int', but an '%T'", num_fmt, arg_type);
231 /* digit string may be omitted */
232 while (isdigit(fmt)) {
238 /* length modifier */
239 format_length_modifier_t fmt_mod;
245 fmt_mod = FMT_MOD_hh;
255 fmt_mod = FMT_MOD_ll;
261 case 'L': fmt = *(++c); fmt_mod = FMT_MOD_L; break;
262 case 'j': fmt = *(++c); fmt_mod = FMT_MOD_j; break;
263 case 't': fmt = *(++c); fmt_mod = FMT_MOD_t; break;
264 case 'z': fmt = *(++c); fmt_mod = FMT_MOD_z; break;
265 case 'q': fmt = *(++c); fmt_mod = FMT_MOD_q; break;
269 fmt = *(++c); fmt_mod = FMT_MOD_w;
271 fmt_mod = FMT_MOD_NONE;
276 fmt = *(++c); fmt_mod = FMT_MOD_I;
281 fmt_mod = FMT_MOD_I32;
286 } else if (fmt == '6') {
290 fmt_mod = FMT_MOD_I64;
297 fmt_mod = FMT_MOD_NONE;
301 fmt_mod = FMT_MOD_NONE;
306 warningf(WARN_FORMAT, pos, "dangling %% in format string");
310 type_t *expected_type;
311 type_qualifiers_t expected_qual = TYPE_QUALIFIER_NONE;
312 format_flags_t allowed_flags;
317 case FMT_MOD_NONE: expected_type = type_int; break;
318 case FMT_MOD_hh: expected_type = type_signed_char; break;
319 case FMT_MOD_h: expected_type = type_short; break;
320 case FMT_MOD_l: expected_type = type_long; break;
321 case FMT_MOD_ll: expected_type = type_long_long; break;
322 case FMT_MOD_j: expected_type = type_intmax_t; break;
323 case FMT_MOD_z: expected_type = type_ssize_t; break;
324 case FMT_MOD_t: expected_type = type_ptrdiff_t; break;
325 case FMT_MOD_I: expected_type = type_ptrdiff_t; break;
326 case FMT_MOD_I32: expected_type = type_int32; break;
327 case FMT_MOD_I64: expected_type = type_int64; break;
330 warn_invalid_length_modifier(pos, fmt_mod, fmt);
333 allowed_flags = FMT_FLAG_MINUS | FMT_FLAG_SPACE | FMT_FLAG_PLUS | FMT_FLAG_ZERO;
339 allowed_flags = FMT_FLAG_MINUS | FMT_FLAG_HASH | FMT_FLAG_ZERO;
340 goto eval_fmt_mod_unsigned;
343 allowed_flags = FMT_FLAG_MINUS | FMT_FLAG_ZERO;
344 eval_fmt_mod_unsigned:
346 case FMT_MOD_NONE: expected_type = type_unsigned_int; break;
347 case FMT_MOD_hh: expected_type = type_unsigned_char; break;
348 case FMT_MOD_h: expected_type = type_unsigned_short; break;
349 case FMT_MOD_l: expected_type = type_unsigned_long; break;
350 case FMT_MOD_ll: expected_type = type_unsigned_long_long; break;
351 case FMT_MOD_j: expected_type = type_uintmax_t; break;
352 case FMT_MOD_z: expected_type = type_size_t; break;
353 case FMT_MOD_t: expected_type = type_uptrdiff_t; break;
354 case FMT_MOD_I: expected_type = type_size_t; break;
355 case FMT_MOD_I32: expected_type = type_unsigned_int32; break;
356 case FMT_MOD_I64: expected_type = type_unsigned_int64; break;
359 warn_invalid_length_modifier(pos, fmt_mod, fmt);
373 case FMT_MOD_l: /* l modifier is ignored */
374 case FMT_MOD_NONE: expected_type = type_double; break;
375 case FMT_MOD_L: expected_type = type_long_double; break;
378 warn_invalid_length_modifier(pos, fmt_mod, fmt);
381 allowed_flags = FMT_FLAG_MINUS | FMT_FLAG_SPACE | FMT_FLAG_PLUS | FMT_FLAG_HASH | FMT_FLAG_ZERO;
385 if (fmt_mod != FMT_MOD_NONE) {
386 warn_invalid_length_modifier(pos, fmt_mod, fmt);
389 expected_type = type_wchar_t;
390 allowed_flags = FMT_FLAG_NONE;
394 expected_type = type_int;
396 case FMT_MOD_NONE: expected_type = type_int; break; /* TODO promoted char */
397 case FMT_MOD_l: expected_type = type_wint_t; break;
398 case FMT_MOD_w: expected_type = type_wchar_t; break;
401 warn_invalid_length_modifier(pos, fmt_mod, fmt);
404 allowed_flags = FMT_FLAG_NONE;
408 if (fmt_mod != FMT_MOD_NONE) {
409 warn_invalid_length_modifier(pos, fmt_mod, fmt);
412 expected_type = type_wchar_t_ptr;
413 expected_qual = TYPE_QUALIFIER_CONST;
414 allowed_flags = FMT_FLAG_MINUS;
419 case FMT_MOD_NONE: expected_type = type_char_ptr; break;
420 case FMT_MOD_l: expected_type = type_wchar_t_ptr; break;
421 case FMT_MOD_w: expected_type = type_wchar_t_ptr; break;
424 warn_invalid_length_modifier(pos, fmt_mod, fmt);
427 expected_qual = TYPE_QUALIFIER_CONST;
428 allowed_flags = FMT_FLAG_MINUS;
432 if (fmt_mod != FMT_MOD_NONE) {
433 warn_invalid_length_modifier(pos, fmt_mod, fmt);
436 expected_type = type_void_ptr;
437 allowed_flags = FMT_FLAG_NONE;
442 case FMT_MOD_NONE: expected_type = type_int_ptr; break;
443 case FMT_MOD_hh: expected_type = type_signed_char_ptr; break;
444 case FMT_MOD_h: expected_type = type_short_ptr; break;
445 case FMT_MOD_l: expected_type = type_long_ptr; break;
446 case FMT_MOD_ll: expected_type = type_long_long_ptr; break;
447 case FMT_MOD_j: expected_type = type_intmax_t_ptr; break;
448 case FMT_MOD_z: expected_type = type_ssize_t_ptr; break;
449 case FMT_MOD_t: expected_type = type_ptrdiff_t_ptr; break;
452 warn_invalid_length_modifier(pos, fmt_mod, fmt);
455 allowed_flags = FMT_FLAG_NONE;
459 warningf(WARN_FORMAT, pos, "encountered unknown conversion specifier '%%%c' at position %u", fmt, num_fmt);
466 format_flags_t wrong_flags = fmt_flags & ~allowed_flags;
467 if (wrong_flags != 0) {
470 if (wrong_flags & FMT_FLAG_HASH) *p++ = '#';
471 if (wrong_flags & FMT_FLAG_ZERO) *p++ = '0';
472 if (wrong_flags & FMT_FLAG_MINUS) *p++ = '-';
473 if (wrong_flags & FMT_FLAG_SPACE) *p++ = ' ';
474 if (wrong_flags & FMT_FLAG_PLUS) *p++ = '+';
475 if (wrong_flags & FMT_FLAG_TICK) *p++ = '\'';
478 warningf(WARN_FORMAT, pos, "invalid format flags \"%s\" in conversion specification %%%c at position %u", wrong, fmt, num_fmt);
483 warningf(WARN_FORMAT, pos, "too few arguments for format string");
487 { /* create a scope here to prevent warning about the jump to next_arg */
488 type_t *const arg_type = arg->expression->base.type;
489 type_t *const arg_skip = skip_typeref(arg_type);
490 type_t *const expected_type_skip = skip_typeref(expected_type);
493 /* allow any pointer type for %p, not just void */
494 if (is_type_pointer(arg_skip))
498 if (is_type_pointer(expected_type_skip)) {
499 if (is_type_pointer(arg_skip)) {
500 type_t *const exp_to = skip_typeref(expected_type_skip->pointer.points_to);
501 type_t *const arg_to = skip_typeref(arg_skip->pointer.points_to);
502 if ((arg_to->base.qualifiers & ~expected_qual) == 0 &&
503 get_unqualified_type(arg_to) == exp_to) {
507 } else if (get_unqualified_type(arg_skip) == expected_type_skip) {
509 } else if (arg->expression->kind == EXPR_UNARY_CAST) {
510 expression_t const *const expr = arg->expression->unary.value;
511 type_t *const unprom_type = skip_typeref(expr->base.type);
512 if (get_unqualified_type(unprom_type) == expected_type_skip) {
515 if (expected_type_skip == type_unsigned_int && !is_type_signed(unprom_type)) {
519 if (is_type_valid(arg_skip)) {
520 position_t const *const apos = &arg->expression->base.pos;
521 char const *const mod = get_length_modifier_name(fmt_mod);
522 warningf(WARN_FORMAT, apos, "conversion '%%%s%c' at position %u specifies type '%T' but the argument has type '%T'", mod, (char)fmt, num_fmt, expected_type, arg_type);
529 if (c+1 < string + size) {
530 warningf(WARN_FORMAT, pos, "format string contains '\\0'");
536 * Check printf-style format.
538 static void check_printf_format(call_argument_t const *arg,
539 format_spec_t const *const spec)
541 /* find format arg */
543 for (; idx < spec->fmt_idx; ++idx) {
549 expression_t const *const fmt_expr = arg->expression;
551 /* find the real args */
552 for (; idx < spec->arg_idx && arg != NULL; ++idx)
555 int const num_fmt = internal_check_printf_format(fmt_expr, arg, spec);
560 for (; arg != NULL; arg = arg->next)
562 if (num_args > (size_t)num_fmt) {
563 position_t const *const pos = &fmt_expr->base.pos;
564 warningf(WARN_FORMAT, pos, "%u argument%s but only %u format specifier%s", num_args, num_args != 1 ? "s" : "", num_fmt, num_fmt != 1 ? "s" : "");
569 * Check scanf-style format.
571 static void check_scanf_format(const call_argument_t *arg,
572 const format_spec_t *spec)
574 /* find format arg */
576 for (; idx < spec->fmt_idx; ++idx) {
582 const expression_t *fmt_expr = arg->expression;
583 if (fmt_expr->kind == EXPR_UNARY_CAST) {
584 fmt_expr = fmt_expr->unary.value;
587 if (fmt_expr->kind != EXPR_STRING_LITERAL)
590 const char *string = fmt_expr->string_literal.value.begin;
591 size_t size = fmt_expr->string_literal.value.size;
592 const char *c = string;
594 /* find the real args */
595 for (; idx < spec->arg_idx && arg != NULL; ++idx)
598 const position_t *pos = &fmt_expr->base.pos;
599 unsigned num_fmt = 0;
601 for (fmt = *c; fmt != '\0'; fmt = *(++c)) {
610 bool suppress_assignment = false;
613 suppress_assignment = true;
617 if ('0' <= fmt && fmt <= '9') {
619 width = width * 10 + (fmt - '0');
621 } while ('0' <= fmt && fmt <= '9');
623 warningf(WARN_FORMAT, pos, "field width is zero at format %u", num_fmt);
627 /* look for length modifiers */
628 format_length_modifier_t fmt_mod = FMT_MOD_NONE;
634 fmt_mod = FMT_MOD_hh;
644 fmt_mod = FMT_MOD_ll;
650 case 'L': fmt = *(++c); fmt_mod = FMT_MOD_L; break;
651 case 'j': fmt = *(++c); fmt_mod = FMT_MOD_j; break;
652 case 't': fmt = *(++c); fmt_mod = FMT_MOD_t; break;
653 case 'z': fmt = *(++c); fmt_mod = FMT_MOD_z; break;
669 fmt_mod = FMT_MOD_I32;
674 } else if (fmt == '6') {
678 fmt_mod = FMT_MOD_I64;
689 warningf(WARN_FORMAT, pos, "dangling %% with conversion specififer in format string");
693 type_t *expected_type;
698 case FMT_MOD_NONE: expected_type = type_int; break;
699 case FMT_MOD_hh: expected_type = type_signed_char; break;
700 case FMT_MOD_h: expected_type = type_short; break;
701 case FMT_MOD_l: expected_type = type_long; break;
702 case FMT_MOD_ll: expected_type = type_long_long; break;
703 case FMT_MOD_j: expected_type = type_intmax_t; break;
704 case FMT_MOD_z: expected_type = type_ssize_t; break;
705 case FMT_MOD_t: expected_type = type_ptrdiff_t; break;
706 case FMT_MOD_I: expected_type = type_ptrdiff_t; break;
707 case FMT_MOD_I32: expected_type = type_int32; break;
708 case FMT_MOD_I64: expected_type = type_int64; break;
711 warn_invalid_length_modifier(pos, fmt_mod, fmt);
721 case FMT_MOD_NONE: expected_type = type_unsigned_int; break;
722 case FMT_MOD_hh: expected_type = type_unsigned_char; break;
723 case FMT_MOD_h: expected_type = type_unsigned_short; break;
724 case FMT_MOD_l: expected_type = type_unsigned_long; break;
725 case FMT_MOD_ll: expected_type = type_unsigned_long_long; break;
726 case FMT_MOD_j: expected_type = type_uintmax_t; break;
727 case FMT_MOD_z: expected_type = type_size_t; break;
728 case FMT_MOD_t: expected_type = type_uptrdiff_t; break;
729 case FMT_MOD_I: expected_type = type_size_t; break;
730 case FMT_MOD_I32: expected_type = type_unsigned_int32; break;
731 case FMT_MOD_I64: expected_type = type_unsigned_int64; break;
734 warn_invalid_length_modifier(pos, fmt_mod, fmt);
748 case FMT_MOD_l: expected_type = type_double; break;
749 case FMT_MOD_NONE: expected_type = type_float; break;
750 case FMT_MOD_L: expected_type = type_long_double; break;
753 warn_invalid_length_modifier(pos, fmt_mod, fmt);
759 if (fmt_mod != FMT_MOD_NONE) {
760 warn_invalid_length_modifier(pos, fmt_mod, fmt);
763 expected_type = type_wchar_t;
768 case FMT_MOD_NONE: expected_type = type_char; break;
769 case FMT_MOD_l: expected_type = type_wchar_t; break;
770 case FMT_MOD_w: expected_type = type_wchar_t; break;
773 warn_invalid_length_modifier(pos, fmt_mod, fmt);
780 if (!suppress_assignment && arg != NULL) {
781 type_t *const type = skip_typeref(revert_automatic_type_conversion(arg->expression));
782 if (is_type_array(type) &&
783 type->array.size_constant &&
784 width > type->array.size) {
785 warningf(WARN_FORMAT, pos, "target buffer '%T' is too small for %u characters at format %u", type, width, num_fmt);
792 if (fmt_mod != FMT_MOD_NONE) {
793 warn_invalid_length_modifier(pos, fmt_mod, fmt);
796 expected_type = type_wchar_t;
802 case FMT_MOD_NONE: expected_type = type_char; break;
803 case FMT_MOD_l: expected_type = type_wchar_t; break;
804 case FMT_MOD_w: expected_type = type_wchar_t; break;
807 warn_invalid_length_modifier(pos, fmt_mod, fmt);
811 if (!suppress_assignment &&
814 type_t *const type = skip_typeref(revert_automatic_type_conversion(arg->expression));
815 if (is_type_array(type) &&
816 type->array.size_constant &&
817 width >= type->array.size) {
818 warningf(WARN_FORMAT, pos, "target buffer '%T' is too small for %u characters and \\0 at format %u", type, width, num_fmt);
825 if (fmt_mod != FMT_MOD_NONE) {
826 warn_invalid_length_modifier(pos, fmt_mod, fmt);
829 expected_type = type_void;
833 if (suppress_assignment) {
834 warningf(WARN_FORMAT, pos, "conversion '%n' cannot be suppressed with '*' at format %u", num_fmt);
838 case FMT_MOD_NONE: expected_type = type_int; break;
839 case FMT_MOD_hh: expected_type = type_signed_char; break;
840 case FMT_MOD_h: expected_type = type_short; break;
841 case FMT_MOD_l: expected_type = type_long; break;
842 case FMT_MOD_ll: expected_type = type_long_long; break;
843 case FMT_MOD_j: expected_type = type_intmax_t; break;
844 case FMT_MOD_z: expected_type = type_ssize_t; break;
845 case FMT_MOD_t: expected_type = type_ptrdiff_t; break;
848 warn_invalid_length_modifier(pos, fmt_mod, fmt);
855 warningf(WARN_FORMAT, pos, "encountered unknown conversion specifier '%%%c' at format %u", fmt, num_fmt);
856 if (suppress_assignment)
863 if (suppress_assignment)
868 warningf(WARN_FORMAT, pos, "too few arguments for format string");
872 { /* create a scope here to prevent warning about the jump to next_arg */
873 type_t *const arg_type = arg->expression->base.type;
874 type_t *const arg_skip = skip_typeref(arg_type);
875 type_t *const expected_type_skip = skip_typeref(expected_type);
877 if (! is_type_pointer(arg_skip))
879 type_t *const ptr_skip = skip_typeref(arg_skip->pointer.points_to);
882 /* allow any pointer type for %p, not just void */
883 if (is_type_pointer(ptr_skip))
887 /* do NOT allow const or restrict, all other should be ok */
888 if (ptr_skip->base.qualifiers & (TYPE_QUALIFIER_CONST | TYPE_QUALIFIER_VOLATILE))
890 type_t *const unqual_ptr = get_unqualified_type(ptr_skip);
891 if (unqual_ptr == expected_type_skip) {
893 } else if (expected_type_skip == type_char) {
894 /* char matches with unsigned char AND signed char */
895 if (unqual_ptr == type_signed_char || unqual_ptr == type_unsigned_char)
899 if (is_type_valid(arg_skip)) {
900 position_t const *const apos = &arg->expression->base.pos;
901 char const *const mod = get_length_modifier_name(fmt_mod);
902 warningf(WARN_FORMAT, apos, "conversion '%%%s%c' at position %u specifies type '%T*' but the argument has type '%T'", mod, (char)fmt, num_fmt, expected_type, arg_type);
909 if (c+1 < string + size) {
910 warningf(WARN_FORMAT, pos, "format string contains '\\0'");
913 unsigned num_args = num_fmt;
914 while (arg != NULL) {
918 warningf(WARN_FORMAT, pos, "%u argument%s but only %u format specifier%s", num_args, num_args != 1 ? "s" : "", num_fmt, num_fmt != 1 ? "s" : "");
922 static const format_spec_t builtin_table[] = {
923 { "printf", FORMAT_PRINTF, 0, 1 },
924 { "wprintf", FORMAT_PRINTF, 0, 1 },
925 { "sprintf", FORMAT_PRINTF, 1, 2 },
926 { "swprintf", FORMAT_PRINTF, 1, 2 },
927 { "snprintf", FORMAT_PRINTF, 2, 3 },
928 { "snwprintf", FORMAT_PRINTF, 2, 3 },
929 { "fprintf", FORMAT_PRINTF, 1, 2 },
930 { "fwprintf", FORMAT_PRINTF, 1, 2 },
931 { "snwprintf", FORMAT_PRINTF, 2, 3 },
932 { "snwprintf", FORMAT_PRINTF, 2, 3 },
934 { "scanf", FORMAT_SCANF, 0, 1 },
935 { "wscanf", FORMAT_SCANF, 0, 1 },
936 { "sscanf", FORMAT_SCANF, 1, 2 },
937 { "swscanf", FORMAT_SCANF, 1, 2 },
938 { "fscanf", FORMAT_SCANF, 1, 2 },
939 { "fwscanf", FORMAT_SCANF, 1, 2 },
941 { "strftime", FORMAT_STRFTIME, 3, 4 },
942 { "wcstrftime", FORMAT_STRFTIME, 3, 4 },
944 { "strfmon", FORMAT_STRFMON, 3, 4 },
947 { "_snprintf", FORMAT_PRINTF, 2, 3 },
948 { "_snwprintf", FORMAT_PRINTF, 2, 3 },
949 { "_scrintf", FORMAT_PRINTF, 0, 1 },
950 { "_scwprintf", FORMAT_PRINTF, 0, 1 },
951 { "printf_s", FORMAT_PRINTF, 0, 1 },
952 { "wprintf_s", FORMAT_PRINTF, 0, 1 },
953 { "sprintf_s", FORMAT_PRINTF, 3, 4 },
954 { "swprintf_s", FORMAT_PRINTF, 3, 4 },
955 { "fprintf_s", FORMAT_PRINTF, 1, 2 },
956 { "fwprintf_s", FORMAT_PRINTF, 1, 2 },
957 { "_sprintf_l", FORMAT_PRINTF, 1, 3 },
958 { "_swprintf_l", FORMAT_PRINTF, 1, 3 },
959 { "_printf_l", FORMAT_PRINTF, 0, 2 },
960 { "_wprintf_l", FORMAT_PRINTF, 0, 2 },
961 { "_fprintf_l", FORMAT_PRINTF, 1, 3 },
962 { "_fwprintf_l", FORMAT_PRINTF, 1, 3 },
963 { "_printf_s_l", FORMAT_PRINTF, 0, 2 },
964 { "_wprintf_s_l", FORMAT_PRINTF, 0, 2 },
965 { "_sprintf_s_l", FORMAT_PRINTF, 3, 5 },
966 { "_swprintf_s_l", FORMAT_PRINTF, 3, 5 },
967 { "_fprintf_s_l", FORMAT_PRINTF, 1, 3 },
968 { "_fwprintf_s_l", FORMAT_PRINTF, 1, 3 },
971 void check_format(const call_expression_t *const call)
973 if (!is_warn_on(WARN_FORMAT))
976 const expression_t *const func_expr = call->function;
977 if (func_expr->kind != EXPR_REFERENCE)
980 const entity_t *const entity = func_expr->reference.entity;
981 const call_argument_t * arg = call->arguments;
984 * For some functions we always check the format, even if it was not
985 * specified. This allows to check format even in MS mode or without
988 const char *const name = entity->base.symbol->string;
989 for (size_t i = 0; i < lengthof(builtin_table); ++i) {
990 if (streq(name, builtin_table[i].name)) {
991 switch (builtin_table[i].fmt_kind) {
993 check_printf_format(arg, &builtin_table[i]);
996 check_scanf_format(arg, &builtin_table[i]);
998 case FORMAT_STRFTIME:
1000 /* TODO: implement other cases */