X-Git-Url: http://nsz.repo.hu/git/?a=blobdiff_plain;f=format_check.c;h=7c8304efccc9ae8462db42a8efbe567953e865d6;hb=3c84e06d01aaed7971067d7cc1924f6e20bcd0ba;hp=f47cc67fc3e340b20d3a7a5e004bf9540ccbace2;hpb=53a1b373a40f0c97cabed171934deacfacc276a4;p=cparser diff --git a/format_check.c b/format_check.c index f47cc67..7c8304e 100644 --- a/format_check.c +++ b/format_check.c @@ -1,6 +1,6 @@ /* * This file is part of cparser. - * Copyright (C) 2007-2008 Matthias Braun + * Copyright (C) 2007-2009 Matthias Braun * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -20,6 +20,7 @@ #include #include +#include "adt/util.h" #include "format_check.h" #include "symbol_t.h" #include "ast_t.h" @@ -86,7 +87,7 @@ static const char* get_length_modifier_name(const format_length_modifier_t mod) [FMT_MOD_I32] = "I32", [FMT_MOD_I64] = "I64" }; - assert(mod < sizeof(names) / sizeof(*names)); + assert(mod < lengthof(names)); return names[mod]; } @@ -114,79 +115,90 @@ struct vchar_t { int (*is_digit)(unsigned vchar); }; -static unsigned string_first(vchar_t *self) { +static unsigned string_first(vchar_t *self) +{ self->position = 0; const string_t *string = self->string; return string->begin[0]; } -static unsigned string_next(vchar_t *self) { +static unsigned string_next(vchar_t *self) +{ ++self->position; const string_t *string = self->string; return string->begin[self->position]; } -static int string_isdigit(unsigned vchar) { +static int string_isdigit(unsigned vchar) +{ return isdigit(vchar); } -static unsigned wstring_first(vchar_t *self) { +static unsigned wstring_first(vchar_t *self) +{ self->position = 0; const wide_string_t *wstring = self->string; return wstring->begin[0]; } -static unsigned wstring_next(vchar_t *self) { +static unsigned wstring_next(vchar_t *self) +{ ++self->position; const wide_string_t *wstring = self->string; return wstring->begin[self->position]; } -static int wstring_isdigit(unsigned vchar) { +static int wstring_isdigit(unsigned vchar) +{ return iswdigit(vchar); } -static bool atend(vchar_t *self) { +static bool atend(vchar_t *self) +{ return self->position + 1 == self->size; } /** * Check printf-style format. */ -static void check_printf_format(const call_argument_t *arg, const format_spec_t *spec) +static int internal_check_printf_format(const expression_t *fmt_expr, + const call_argument_t *arg, const format_spec_t *spec) { - /* find format arg */ - unsigned idx = 0; - for (; idx < spec->fmt_idx; ++idx) { - if (arg == NULL) - return; - arg = arg->next; - } - - const expression_t *fmt_expr = arg->expression; if (fmt_expr->kind == EXPR_UNARY_CAST_IMPLICIT) { fmt_expr = fmt_expr->unary.value; } vchar_t vchar; - if (fmt_expr->kind == EXPR_WIDE_STRING_LITERAL) { - vchar.string = &fmt_expr->wide_string.value; - vchar.size = fmt_expr->wide_string.value.size; - vchar.first = wstring_first; - vchar.next = wstring_next; - vchar.is_digit = wstring_isdigit; - } else if (fmt_expr->kind == EXPR_STRING_LITERAL) { - vchar.string = &fmt_expr->string.value; - vchar.size = fmt_expr->string.value.size; - vchar.first = string_first; - vchar.next = string_next; - vchar.is_digit = string_isdigit; - } else { - return; + switch (fmt_expr->kind) { + case EXPR_STRING_LITERAL: + vchar.string = &fmt_expr->string.value; + vchar.size = fmt_expr->string.value.size; + vchar.first = string_first; + vchar.next = string_next; + vchar.is_digit = string_isdigit; + break; + + case EXPR_WIDE_STRING_LITERAL: + vchar.string = &fmt_expr->wide_string.value; + vchar.size = fmt_expr->wide_string.value.size; + vchar.first = wstring_first; + vchar.next = wstring_next; + vchar.is_digit = wstring_isdigit; + break; + + case EXPR_CONDITIONAL: { + conditional_expression_t const *const c = &fmt_expr->conditional; + expression_t const * t = c->true_expression; + if (t == NULL) + t = c->condition; + int const nt = internal_check_printf_format(t, arg, spec); + int const nf = internal_check_printf_format(c->false_expression, arg, spec); + return nt > nf ? nt : nf; + } + + default: + return -1; } - /* find the real args */ - for(; idx < spec->arg_idx && arg != NULL; ++idx) - arg = arg->next; const source_position_t *pos = &fmt_expr->base.source_position; unsigned fmt = vchar.first(&vchar); @@ -218,7 +230,7 @@ static void check_printf_format(const call_argument_t *arg, const format_spec_t /* ... argument selector */ fmt_flags = FMT_FLAG_NONE; /* reset possibly set 0-flag */ /* TODO implement */ - return; + return -1; } /* ... minimum field width */ } else { @@ -260,7 +272,7 @@ break_fmt_flags: fmt = vchar.next(&vchar); if (arg == NULL) { warningf(pos, "missing argument for '*' field width in conversion specification %u", num_fmt); - return; + return -1; } const type_t *const arg_type = arg->expression->base.type; if (arg_type != type_int) { @@ -281,7 +293,7 @@ break_fmt_flags: fmt = vchar.next(&vchar); if (arg == NULL) { warningf(pos, "missing argument for '*' precision in conversion specification %u", num_fmt); - return; + return -1; } const type_t *const arg_type = arg->expression->base.type; if (arg_type != type_int) { @@ -520,7 +532,7 @@ eval_fmt_mod_unsigned: warningf(pos, "encountered unknown conversion specifier '%%%C' at position %u", (wint_t)fmt, num_fmt); if (arg == NULL) { warningf(pos, "too few arguments for format string"); - return; + return -1; } goto next_arg; } @@ -542,7 +554,7 @@ eval_fmt_mod_unsigned: if (arg == NULL) { warningf(pos, "too few arguments for format string"); - return; + return -1; } { /* create a scope here to prevent warning about the jump to next_arg */ @@ -582,15 +594,40 @@ next_arg: if (!atend(&vchar)) { warningf(pos, "format string contains '\\0'"); } - if (arg != NULL) { - unsigned num_args = num_fmt; - while (arg != NULL) { - ++num_args; - arg = arg->next; - } - warningf(pos, "%u argument%s but only %u format specifier%s", + return num_fmt; +} + +/** + * Check printf-style format. + */ +static void check_printf_format(call_argument_t const *arg, format_spec_t const *const spec) +{ + /* find format arg */ + size_t idx = 0; + for (; idx < spec->fmt_idx; ++idx) { + if (arg == NULL) + return; + arg = arg->next; + } + + expression_t const *const fmt_expr = arg->expression; + + /* find the real args */ + for (; idx < spec->arg_idx && arg != NULL; ++idx) + arg = arg->next; + + int const num_fmt = internal_check_printf_format(fmt_expr, arg, spec); + if (num_fmt < 0) + return; + + size_t num_args = 0; + for (; arg != NULL; arg = arg->next) + ++num_args; + if (num_args > (size_t)num_fmt) { + warningf(&fmt_expr->base.source_position, + "%u argument%s but only %u format specifier%s", num_args, num_args != 1 ? "s" : "", - num_fmt, num_fmt != 1 ? "s" : ""); + num_fmt, num_fmt != 1 ? "s" : ""); } } @@ -910,7 +947,7 @@ next_arg: ++num_args; arg = arg->next; } - warningf(pos, "%u argument%s but only %u format string%s", + warningf(pos, "%u argument%s but only %u format specifier%s", num_args, num_args != 1 ? "s" : "", num_fmt, num_fmt != 1 ? "s" : ""); } @@ -985,7 +1022,7 @@ void check_format(const call_expression_t *const call) * This allows to check format even in MS mode or without header included. */ const char *const name = entity->base.symbol->string; - for (size_t i = 0; i < sizeof(builtin_table) / sizeof(builtin_table[0]); ++i) { + for (size_t i = 0; i < lengthof(builtin_table); ++i) { if (strcmp(name, builtin_table[i].name) == 0) { switch (builtin_table[i].fmt_kind) { case FORMAT_PRINTF: