- allow '-' for s and S format
[cparser] / format_check.c
index 2e85c0b..5d1b6a7 100644 (file)
@@ -145,7 +145,8 @@ static bool atend(vchar_t *self) {
 /**
  * Check printf-style format.
  */
-static void check_format_arguments(const call_argument_t *arg, unsigned idx_fmt, unsigned idx_param)
+static void check_format_arguments(const call_argument_t *arg, unsigned idx_fmt,
+               unsigned idx_param)
 {
        const call_argument_t *fmt_arg;
        unsigned idx = 0;
@@ -177,7 +178,7 @@ static void check_format_arguments(const call_argument_t *arg, unsigned idx_fmt,
                return;
        }
        /* find the real args */
-       for(; idx < idx_fmt; ++idx)
+       for(; idx < idx_param; ++idx)
                arg = arg->next;
 
        const source_position_t *pos = &fmt_expr->base.source_position;
@@ -246,6 +247,7 @@ break_fmt_flags:
 
                        /* minimum field width */
                        if (fmt == '*') {
+                               fmt = vchar.next(&vchar);
                                if (arg == NULL) {
                                        warningf(pos, "missing argument for '*' field width in conversion specification");
                                        return;
@@ -266,6 +268,7 @@ break_fmt_flags:
                if (fmt == '.') {
                        fmt = vchar.next(&vchar);
                        if (fmt == '*') {
+                               fmt = vchar.next(&vchar);
                                if (arg == NULL) {
                                        warningf(pos, "missing argument for '*' precision in conversion specification");
                                        return;
@@ -355,7 +358,7 @@ break_fmt_flags:
                        break;
                }
 
-               const type_t      *expected_type;
+               type_t            *expected_type;
                type_qualifiers_t  expected_qual = TYPE_QUALIFIER_NONE;
                format_flags_t     allowed_flags;
                switch (fmt) {
@@ -460,7 +463,7 @@ eval_fmt_mod_unsigned:
                                }
                                expected_type = type_wchar_t_ptr;
                                expected_qual = TYPE_QUALIFIER_CONST;
-                               allowed_flags = FMT_FLAG_NONE;
+                               allowed_flags = FMT_FLAG_MINUS;
                                break;
 
                        case 's':
@@ -474,7 +477,7 @@ eval_fmt_mod_unsigned:
                                                goto next_arg;
                                }
                                expected_qual = TYPE_QUALIFIER_CONST;
-                               allowed_flags = FMT_FLAG_NONE;
+                               allowed_flags = FMT_FLAG_MINUS;
                                break;
 
                        case 'p':
@@ -509,9 +512,19 @@ eval_fmt_mod_unsigned:
                                goto next_arg;
                }
 
-               if ((fmt_flags & ~allowed_flags) != 0) {
-                       /* TODO better warning message text */
-                       warningf(pos, "invalid format flags in conversion specification");
+               format_flags_t wrong_flags = fmt_flags & ~allowed_flags;
+               if (wrong_flags != 0) {
+                       char wrong[8];
+                       int idx = 0;
+                       if (wrong_flags & FMT_FLAG_HASH)  wrong[idx++] = '#';
+                       if (wrong_flags & FMT_FLAG_ZERO)  wrong[idx++] = '0';
+                       if (wrong_flags & FMT_FLAG_MINUS) wrong[idx++] = '-';
+                       if (wrong_flags & FMT_FLAG_SPACE) wrong[idx++] = ' ';
+                       if (wrong_flags & FMT_FLAG_PLUS)  wrong[idx++] = '+';
+                       if (wrong_flags & FMT_FLAG_TICK)  wrong[idx++] = '\'';
+                       wrong[idx] = '\0';
+
+                       warningf(pos, "invalid format flags \"%s\" in conversion specification %%%c", wrong, fmt);
                }
 
                if (arg == NULL) {
@@ -520,11 +533,12 @@ eval_fmt_mod_unsigned:
                }
 
                {       /* create a scope here to prevent warning about the jump to next_arg */
-                       type_t *const arg_type = arg->expression->base.type;
-                       if (is_type_pointer(expected_type)) {
-                               type_t *const arg_skip = skip_typeref(arg_type);
+                       type_t *const arg_type           = arg->expression->base.type;
+                       type_t *const arg_skip           = skip_typeref(arg_type);
+                       type_t *const expected_type_skip = skip_typeref(expected_type);
+                       if (is_type_pointer(expected_type_skip)) {
                                if (is_type_pointer(arg_skip)) {
-                                       type_t *const exp_to = skip_typeref(expected_type->pointer.points_to);
+                                       type_t *const exp_to = skip_typeref(expected_type_skip->pointer.points_to);
                                        type_t *const arg_to = skip_typeref(arg_skip->pointer.points_to);
                                        if ((arg_to->base.qualifiers & ~expected_qual) == 0 &&
                                                get_unqualified_type(arg_to) == exp_to) {
@@ -532,11 +546,11 @@ eval_fmt_mod_unsigned:
                                        }
                                }
                        } else {
-                               if (get_unqualified_type(skip_typeref(arg_type)) == expected_type) {
+                               if (get_unqualified_type(arg_skip) == expected_type_skip) {
                                        goto next_arg;
                                }
                        }
-                       if (is_type_valid(arg_type)) {
+                       if (is_type_valid(arg_skip)) {
                                warningf(pos,
                                        "argument type '%T' does not match conversion specifier '%%%s%c'",
                                        arg_type, get_length_modifier_name(fmt_mod), (char)fmt);