add license prefix
[cparser] / format_check.c
index 602775d..4fad47a 100644 (file)
@@ -5,6 +5,7 @@
 #include "format_check.h"
 #include "types.h"
 #include "type_t.h"
+#include "warning.h"
 
 
 typedef enum format_flag_t {
@@ -141,7 +142,7 @@ break_fmt_flags:
                                        warningf(pos, "missing argument for '*' field width in conversion specification");
                                        return;
                                }
-                               const type_t *const arg_type = arg->expression->base.datatype;
+                               const type_t *const arg_type = arg->expression->base.type;
                                if (arg_type != type_int) {
                                        warningf(pos, "argument for '*' field width in conversion specification is not an 'int', but an '%T'", arg_type);
                                }
@@ -161,7 +162,7 @@ break_fmt_flags:
                                        warningf(pos, "missing argument for '*' precision in conversion specification");
                                        return;
                                }
-                               const type_t *const arg_type = arg->expression->base.datatype;
+                               const type_t *const arg_type = arg->expression->base.type;
                                if (arg_type != type_int) {
                                        warningf(pos, "argument for '*' precision in conversion specification is not an 'int', but an '%T'", arg_type);
                                }
@@ -210,8 +211,9 @@ break_fmt_flags:
                        break;
                }
 
-               const type_t   *expected_type = NULL;
-               format_flags_t  allowed_flags;
+               const type_t      *expected_type;
+               type_qualifiers_t  expected_qual = TYPE_QUALIFIER_NONE;
+               format_flags_t     allowed_flags;
                switch (*fmt) {
                        case 'd':
                        case 'i':
@@ -227,9 +229,9 @@ break_fmt_flags:
 
                                        default:
                                                warn_invalid_length_modifier(pos, fmt_mod, *fmt);
-                                               break;
+                                               goto next_arg;
                                }
-                               allowed_flags = FMT_FLAG_MINUS | FMT_FLAG_PLUS | FMT_FLAG_ZERO;
+                               allowed_flags = FMT_FLAG_MINUS | FMT_FLAG_SPACE | FMT_FLAG_PLUS | FMT_FLAG_ZERO;
                                break;
 
                        case 'o':
@@ -254,7 +256,7 @@ eval_fmt_mod_unsigned:
 
                                        default:
                                                warn_invalid_length_modifier(pos, fmt_mod, *fmt);
-                                               break;
+                                               goto next_arg;
                                }
                                break;
 
@@ -273,14 +275,15 @@ eval_fmt_mod_unsigned:
 
                                        default:
                                                warn_invalid_length_modifier(pos, fmt_mod, *fmt);
-                                               break;
+                                               goto next_arg;
                                }
-                               allowed_flags = FMT_FLAG_MINUS | FMT_FLAG_PLUS | FMT_FLAG_HASH | FMT_FLAG_ZERO;
+                               allowed_flags = FMT_FLAG_MINUS | FMT_FLAG_SPACE | FMT_FLAG_PLUS | FMT_FLAG_HASH | FMT_FLAG_ZERO;
                                break;
 
                        case 'C':
                                if (fmt_mod != FMT_MOD_NONE) {
                                        warn_invalid_length_modifier(pos, fmt_mod, *fmt);
+                                       goto next_arg;
                                }
                                expected_type = type_wchar_t;
                                allowed_flags = FMT_FLAG_NONE;
@@ -294,7 +297,7 @@ eval_fmt_mod_unsigned:
 
                                        default:
                                                warn_invalid_length_modifier(pos, fmt_mod, *fmt);
-                                               break;
+                                               goto next_arg;
                                }
                                allowed_flags = FMT_FLAG_NONE;
                                break;
@@ -302,26 +305,30 @@ eval_fmt_mod_unsigned:
                        case 'S':
                                if (fmt_mod != FMT_MOD_NONE) {
                                        warn_invalid_length_modifier(pos, fmt_mod, *fmt);
+                                       goto next_arg;
                                }
                                expected_type = type_wchar_t_ptr;
+                               expected_qual = TYPE_QUALIFIER_CONST;
                                allowed_flags = FMT_FLAG_NONE;
                                break;
 
                        case 's':
                                switch (fmt_mod) {
-                                       case FMT_MOD_NONE: expected_type = type_string;      break;
+                                       case FMT_MOD_NONE: expected_type = type_char_ptr;    break;
                                        case FMT_MOD_l:    expected_type = type_wchar_t_ptr; break;
 
                                        default:
                                                warn_invalid_length_modifier(pos, fmt_mod, *fmt);
-                                               break;
+                                               goto next_arg;
                                }
+                               expected_qual = TYPE_QUALIFIER_CONST;
                                allowed_flags = FMT_FLAG_NONE;
                                break;
 
                        case 'p':
                                if (fmt_mod != FMT_MOD_NONE) {
                                        warn_invalid_length_modifier(pos, fmt_mod, *fmt);
+                                       goto next_arg;
                                }
                                expected_type = type_void_ptr;
                                allowed_flags = FMT_FLAG_NONE;
@@ -340,15 +347,14 @@ eval_fmt_mod_unsigned:
 
                                        default:
                                                warn_invalid_length_modifier(pos, fmt_mod, *fmt);
-                                               break;
+                                               goto next_arg;
                                }
                                allowed_flags = FMT_FLAG_NONE;
                                break;
 
                        default:
                                warningf(pos, "encountered unknown conversion specifier '%%%C'", (wint_t)*fmt);
-                               arg = arg->next;
-                               continue;
+                               goto next_arg;
                }
 
                if ((fmt_flags & ~allowed_flags) != 0) {
@@ -361,26 +367,30 @@ eval_fmt_mod_unsigned:
                        return;
                }
 
-               type_t *const arg_type = arg->expression->base.datatype;
-               if (is_type_pointer(expected_type)) {
-                       type_t *const arg_skip = skip_typeref(arg_type);
-                       if (is_type_pointer(arg_skip)) {
-                               type_t *const exp_to = skip_typeref(expected_type->pointer.points_to);
-                               type_t *const arg_to = skip_typeref(arg_skip->pointer.points_to);
-                               if (arg_to == exp_to) {
-                                       goto arg_type_ok;
+               {       /* 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);
+                               if (is_type_pointer(arg_skip)) {
+                                       type_t *const exp_to = skip_typeref(expected_type->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) {
+                                               goto next_arg;
+                                       }
+                               }
+                       } else {
+                               if (get_unqualified_type(skip_typeref(arg_type)) == expected_type) {
+                                       goto next_arg;
                                }
                        }
-               } else {
-                       if (get_unqualified_type(skip_typeref(arg_type)) == expected_type) {
-                               goto arg_type_ok;
+                       if (is_type_valid(arg_type)) {
+                               warningf(pos,
+                                       "argument type '%T' does not match conversion specifier '%%%s%c'",
+                                       arg_type, get_length_modifier_name(fmt_mod), (char)*fmt);
                        }
                }
-               warningf(pos,
-                       "argument type '%T' does not match conversion specifier '%%%s%c'\n",
-                       arg_type, get_length_modifier_name(fmt_mod), (char)*fmt);
-arg_type_ok:
-
+next_arg:
                arg = arg->next;
        }
        if (fmt + 1 != wstring->begin + wstring->size) {
@@ -393,6 +403,9 @@ arg_type_ok:
 
 void check_format(const call_expression_t *const call)
 {
+       if (!warning.check_format)
+               return;
+
        const expression_t *const func_expr = call->function;
        if (func_expr->kind != EXPR_REFERENCE)
                return;