From f5d283721eafd94db9523aad7e19f14e4f5417cf Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Wed, 1 Dec 2021 11:25:53 +0200 Subject: [PATCH] checker: add more `string(x)` checks, with more detailed replacement suggestions --- vlib/v/checker/checker.v | 79 ++++++++++++------- vlib/v/checker/tests/cast_string_err.out | 48 +++++++++-- vlib/v/checker/tests/cast_string_err.vv | 38 ++++++++- vlib/v/checker/tests/struct_type_cast_err.out | 18 ++--- 4 files changed, 136 insertions(+), 47 deletions(-) diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 54d8cef089..ccd538412e 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -5690,25 +5690,6 @@ pub fn (mut c Checker) cast_expr(mut node ast.CastExpr) ast.Type { c.error('expression does not return a value so it cannot be cast', node.expr.position()) } // - if to_type == ast.string_type { - if from_type in [ast.byte_type, ast.bool_type] { - c.error('cannot cast type `$from_type_sym.name` to string, use `${node.expr.str()}.str()` instead.', - node.pos) - } - if from_type.is_real_pointer() { - c.error('cannot cast pointer type `$from_type_sym.name` to string, use `&byte($node.expr.str()).vstring()` instead.', - node.pos) - } - if from_type.is_number() { - c.error('cannot cast number to string, use `${node.expr.str()}.str()` instead.', - node.pos) - } - if from_type_sym.kind == .alias && from_type_sym_final.name != 'string' { - c.error('cannot cast type `$from_type_sym.name` to string, use `x.str()` instead', - node.pos) - } - } - // if to_type_sym.kind == .sum_type { if from_type in [ast.int_literal_type, ast.float_literal_type] { xx := if from_type == ast.int_literal_type { ast.int_type } else { ast.f64_type } @@ -5723,15 +5704,6 @@ pub fn (mut c Checker) cast_expr(mut node ast.CastExpr) ast.Type { c.error('cannot convert type `$from_type_sym.name` to `$to_type_sym.name` (alias to `$to_type_sym_final.name`)', node.pos) } - } else if to_type != ast.string_type && from_type == ast.string_type - && (!(to_type_sym.kind == .alias && to_type_sym_final.name == 'string')) { - mut error_msg := 'cannot cast a string to a type `$to_type_sym_final.name`, that is not an alias of string' - if mut node.expr is ast.StringLiteral { - if node.expr.val.len == 1 { - error_msg += ", for denoting characters use `$node.expr.val` instead of '$node.expr.val'" - } - } - c.error(error_msg, node.pos) } else if to_type_sym.kind == .byte && from_type != ast.voidptr_type && from_type_sym.kind != .enum_ && !from_type.is_int() && !from_type.is_float() && from_type != ast.bool_type && !from_type.is_ptr() && from_type_sym.kind == .alias @@ -5770,7 +5742,9 @@ pub fn (mut c Checker) cast_expr(mut node ast.CastExpr) ast.Type { if (to_type.is_ptr() || to_type_sym.kind !in [.sum_type, .interface_]) && !c.is_builtin_mod { from_type_name := c.table.type_to_str(from_type) type_name := c.table.type_to_str(to_type) - c.error('cannot cast struct `$from_type_name` to `$type_name`', node.pos) + snexpr := node.expr.str() + c.error('cannot cast struct `$from_type_name` to `$type_name`, use `${snexpr}.str()` instead.', + node.pos) } } else if from_type.has_flag(.optional) || from_type.has_flag(.variadic) { // variadic case can happen when arrays are converted into variadic @@ -5784,6 +5758,53 @@ pub fn (mut c Checker) cast_expr(mut node ast.CastExpr) ast.Type { } else if from_type_sym.kind == .array_fixed && !from_type.is_ptr() { c.warn('cannot cast a fixed array (use e.g. `&arr[0]` instead)', node.pos) } + + if to_type == ast.string_type { + if from_type in [ast.byte_type, ast.bool_type] { + snexpr := node.expr.str() + c.error('cannot cast type `$from_type_sym.name` to string, use `${snexpr}.str()` instead.', + node.pos) + } else if from_type.is_real_pointer() { + snexpr := node.expr.str() + c.error('cannot cast pointer type `$from_type_sym.name` to string, use `&byte($snexpr).vstring()` or `cstring_to_vstring($snexpr)` instead.', + node.pos) + } else if from_type.is_number() { + snexpr := node.expr.str() + c.error('cannot cast number to string, use `${snexpr}.str()` instead.', node.pos) + } else if from_type_sym.kind == .alias && from_type_sym_final.name != 'string' { + c.error('cannot cast type `$from_type_sym.name` to string, use `x.str()` instead.', + node.pos) + } else if from_type_sym_final.kind == .array { + snexpr := node.expr.str() + if from_type_sym_final.name == '[]byte' { + c.error('cannot cast []byte to string, use `${snexpr}.bytestr()` or `${snexpr}.str()` instead.', + node.pos) + } else { + first_elem_idx := '[0]' + c.error('cannot cast array to string, use `$snexpr${first_elem_idx}.str()` instead.', + node.pos) + } + } else if from_type_sym_final.kind == .enum_ { + snexpr := node.expr.str() + c.error('cannot cast enum to string, use ${snexpr}.str() instead.', node.pos) + } else if from_type_sym_final.kind == .map { + c.error('cannot cast map to string.', node.pos) + } else if from_type_sym_final.kind == .sum_type { + snexpr := node.expr.str() + c.error('cannot cast sumtype `$from_type_sym.name` to string, use `${snexpr}.str()` instead.', + node.pos) + } else if to_type != ast.string_type && from_type == ast.string_type + && (!(to_type_sym.kind == .alias && to_type_sym_final.name == 'string')) { + mut error_msg := 'cannot cast a string to a type `$to_type_sym_final.name`, that is not an alias of string' + if mut node.expr is ast.StringLiteral { + if node.expr.val.len == 1 { + error_msg += ", for denoting characters use `$node.expr.val` instead of '$node.expr.val'" + } + } + c.error(error_msg, node.pos) + } + } + if node.has_arg { c.expr(node.arg) } diff --git a/vlib/v/checker/tests/cast_string_err.out b/vlib/v/checker/tests/cast_string_err.out index 939aa1db24..7303f52447 100644 --- a/vlib/v/checker/tests/cast_string_err.out +++ b/vlib/v/checker/tests/cast_string_err.out @@ -1,6 +1,42 @@ -vlib/v/checker/tests/cast_string_err.vv:2:7: error: cannot cast number to string, use `1.str()` instead. - 1 | fn main() { - 2 | a := string(1) - | ~~~~~~~~~ - 3 | println(a) - 4 | } +vlib/v/checker/tests/cast_string_err.vv:14:9: error: cannot cast enum to string, use ev.str() instead. + 12 | fn main() { + 13 | ev := MyEnum.val1 + 14 | sev := string(ev) + | ~~~~~~~~~~ + 15 | println(sev) + 16 | // +vlib/v/checker/tests/cast_string_err.vv:18:9: error: cannot cast sumtype `Sumtype` to string, use `st.str()` instead. + 16 | // + 17 | st := Sumtype(int(456)) + 18 | sst := string(st) + | ~~~~~~~~~~ + 19 | println(sst) + 20 | // +vlib/v/checker/tests/cast_string_err.vv:22:10: error: cannot cast struct `Abc` to `string`, use `abc.str()` instead. + 20 | // + 21 | abc := Abc{} + 22 | sabc := string(abc) + | ~~~~~~~~~~~ + 23 | println(sabc) + 24 | // +vlib/v/checker/tests/cast_string_err.vv:26:8: error: cannot cast map to string. + 24 | // + 25 | mm := map[string]int{} + 26 | sm := string(mm) + | ~~~~~~~~~~ + 27 | println(sm) + 28 | // +vlib/v/checker/tests/cast_string_err.vv:30:8: error: cannot cast []byte to string, use `arr.bytestr()` or `arr.str()` instead. + 28 | // + 29 | arr := []byte{} + 30 | sa := string(arr) + | ~~~~~~~~~~~ + 31 | println(sa) + 32 | // +vlib/v/checker/tests/cast_string_err.vv:34:8: error: cannot cast number to string, use `ii.str()` instead. + 32 | // + 33 | ii := 1 + 34 | si := string(ii) + | ~~~~~~~~~~ + 35 | println(si) + 36 | } diff --git a/vlib/v/checker/tests/cast_string_err.vv b/vlib/v/checker/tests/cast_string_err.vv index 56247ce01c..85d50c359c 100644 --- a/vlib/v/checker/tests/cast_string_err.vv +++ b/vlib/v/checker/tests/cast_string_err.vv @@ -1,4 +1,36 @@ +struct Abc { + x int +} + +type Sumtype = int | u64 + +enum MyEnum { + val1 + val2 +} + fn main() { - a := string(1) - println(a) -} \ No newline at end of file + ev := MyEnum.val1 + sev := string(ev) + println(sev) + // + st := Sumtype(int(456)) + sst := string(st) + println(sst) + // + abc := Abc{} + sabc := string(abc) + println(sabc) + // + mm := map[string]int{} + sm := string(mm) + println(sm) + // + arr := []byte{} + sa := string(arr) + println(sa) + // + ii := 1 + si := string(ii) + println(si) +} diff --git a/vlib/v/checker/tests/struct_type_cast_err.out b/vlib/v/checker/tests/struct_type_cast_err.out index c693bedbb0..7678002a2a 100644 --- a/vlib/v/checker/tests/struct_type_cast_err.out +++ b/vlib/v/checker/tests/struct_type_cast_err.out @@ -1,60 +1,60 @@ -vlib/v/checker/tests/struct_type_cast_err.vv:5:10: error: cannot cast struct `Foo` to `string` +vlib/v/checker/tests/struct_type_cast_err.vv:5:10: error: cannot cast struct `Foo` to `string`, use `foo.str()` instead. 3 | fn main() { 4 | foo := Foo{} 5 | _ := string(foo) | ~~~~~~~~~~~ 6 | _ := int(foo) 7 | _ := u64(foo) -vlib/v/checker/tests/struct_type_cast_err.vv:6:10: error: cannot cast struct `Foo` to `int` +vlib/v/checker/tests/struct_type_cast_err.vv:6:10: error: cannot cast struct `Foo` to `int`, use `foo.str()` instead. 4 | foo := Foo{} 5 | _ := string(foo) 6 | _ := int(foo) | ~~~~~~~~ 7 | _ := u64(foo) 8 | _ := u32(foo) -vlib/v/checker/tests/struct_type_cast_err.vv:7:10: error: cannot cast struct `Foo` to `u64` +vlib/v/checker/tests/struct_type_cast_err.vv:7:10: error: cannot cast struct `Foo` to `u64`, use `foo.str()` instead. 5 | _ := string(foo) 6 | _ := int(foo) 7 | _ := u64(foo) | ~~~~~~~~ 8 | _ := u32(foo) 9 | _ := rune(foo) -vlib/v/checker/tests/struct_type_cast_err.vv:8:10: error: cannot cast struct `Foo` to `u32` +vlib/v/checker/tests/struct_type_cast_err.vv:8:10: error: cannot cast struct `Foo` to `u32`, use `foo.str()` instead. 6 | _ := int(foo) 7 | _ := u64(foo) 8 | _ := u32(foo) | ~~~~~~~~ 9 | _ := rune(foo) 10 | _ := byte(foo) -vlib/v/checker/tests/struct_type_cast_err.vv:9:10: error: cannot cast struct `Foo` to `rune` +vlib/v/checker/tests/struct_type_cast_err.vv:9:10: error: cannot cast struct `Foo` to `rune`, use `foo.str()` instead. 7 | _ := u64(foo) 8 | _ := u32(foo) 9 | _ := rune(foo) | ~~~~~~~~~ 10 | _ := byte(foo) 11 | _ := i8(foo) -vlib/v/checker/tests/struct_type_cast_err.vv:10:10: error: cannot cast struct `Foo` to `byte` +vlib/v/checker/tests/struct_type_cast_err.vv:10:10: error: cannot cast struct `Foo` to `byte`, use `foo.str()` instead. 8 | _ := u32(foo) 9 | _ := rune(foo) 10 | _ := byte(foo) | ~~~~~~~~~ 11 | _ := i8(foo) 12 | _ := i64(foo) -vlib/v/checker/tests/struct_type_cast_err.vv:11:10: error: cannot cast struct `Foo` to `i8` +vlib/v/checker/tests/struct_type_cast_err.vv:11:10: error: cannot cast struct `Foo` to `i8`, use `foo.str()` instead. 9 | _ := rune(foo) 10 | _ := byte(foo) 11 | _ := i8(foo) | ~~~~~~~ 12 | _ := i64(foo) 13 | _ := int(foo) -vlib/v/checker/tests/struct_type_cast_err.vv:12:10: error: cannot cast struct `Foo` to `i64` +vlib/v/checker/tests/struct_type_cast_err.vv:12:10: error: cannot cast struct `Foo` to `i64`, use `foo.str()` instead. 10 | _ := byte(foo) 11 | _ := i8(foo) 12 | _ := i64(foo) | ~~~~~~~~ 13 | _ := int(foo) 14 | _ = &I1(foo) -vlib/v/checker/tests/struct_type_cast_err.vv:13:10: error: cannot cast struct `Foo` to `int` +vlib/v/checker/tests/struct_type_cast_err.vv:13:10: error: cannot cast struct `Foo` to `int`, use `foo.str()` instead. 11 | _ := i8(foo) 12 | _ := i64(foo) 13 | _ := int(foo)