From b2fee21ef39e070211448d653a526fdc747f3e6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20D=C3=A4schle?= Date: Fri, 14 Aug 2020 14:57:08 +0200 Subject: [PATCH] checker: check struct casting (#5864) --- vlib/v/checker/checker.v | 35 +++++++++++++++++++ .../struct_cast_to_struct_generic_err.out | 6 ++++ .../struct_cast_to_struct_generic_err.vv | 12 +++++++ .../tests/struct_cast_to_struct_mut_err_a.out | 6 ++++ .../tests/struct_cast_to_struct_mut_err_a.vv | 13 +++++++ .../tests/struct_cast_to_struct_mut_err_b.out | 6 ++++ .../tests/struct_cast_to_struct_mut_err_b.vv | 13 +++++++ .../tests/struct_cast_to_struct_pub_err_a.out | 6 ++++ .../tests/struct_cast_to_struct_pub_err_a.vv | 13 +++++++ .../tests/struct_cast_to_struct_pub_err_b.out | 6 ++++ .../tests/struct_cast_to_struct_pub_err_b.vv | 13 +++++++ .../tests/struct_init_with_custom_len_keep.vv | 2 +- 12 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 vlib/v/checker/tests/struct_cast_to_struct_generic_err.out create mode 100644 vlib/v/checker/tests/struct_cast_to_struct_generic_err.vv create mode 100644 vlib/v/checker/tests/struct_cast_to_struct_mut_err_a.out create mode 100644 vlib/v/checker/tests/struct_cast_to_struct_mut_err_a.vv create mode 100644 vlib/v/checker/tests/struct_cast_to_struct_mut_err_b.out create mode 100644 vlib/v/checker/tests/struct_cast_to_struct_mut_err_b.vv create mode 100644 vlib/v/checker/tests/struct_cast_to_struct_pub_err_a.out create mode 100644 vlib/v/checker/tests/struct_cast_to_struct_pub_err_a.vv create mode 100644 vlib/v/checker/tests/struct_cast_to_struct_pub_err_b.out create mode 100644 vlib/v/checker/tests/struct_cast_to_struct_pub_err_b.vv diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index c645770fd3..e1e33ac1da 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -2328,6 +2328,13 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type { to_type_sym.kind == .byte { type_name := c.table.type_to_str(node.expr_type) c.error('cannot cast type `$type_name` to `byte`', node.pos) + } else if to_type_sym.kind == .struct_ && !node.typ.is_ptr() && !(to_type_sym.info as table.Struct).is_typedef { + from_type_info := from_type_sym.info as table.Struct + to_type_info := to_type_sym.info as table.Struct + if !c.check_struct_signature(from_type_info, to_type_info) { + c.error('cannot convert struct `$from_type_sym.name` to struct `$to_type_sym.name`', + node.pos) + } } if node.has_arg { c.expr(node.arg) @@ -3257,6 +3264,34 @@ pub fn (mut c Checker) error(message string, pos token.Position) { c.warn_or_error(msg, pos, false) } +// check_struct_signature checks if both structs has the same signature / fields for casting +fn (c Checker) check_struct_signature(from, to table.Struct) bool { + if from.fields.len != to.fields.len { + return false + } + for _, field in from.fields { + filtered := to.fields.filter(it.name == field.name) + if filtered.len != 1 { + // field doesn't exist + return false + } + counterpart := filtered[0] + if field.typ != counterpart.typ { + // field has different tye + return false + } + if field.is_pub != counterpart.is_pub { + // field is not public while the other one is + return false + } + if field.is_mut != counterpart.is_mut { + // field is not mutable while the other one is + return false + } + } + return true +} + fn (mut c Checker) warn_or_error(message string, pos token.Position, warn bool) { // add backtrace to issue struct, how? // if c.pref.is_verbose { diff --git a/vlib/v/checker/tests/struct_cast_to_struct_generic_err.out b/vlib/v/checker/tests/struct_cast_to_struct_generic_err.out new file mode 100644 index 0000000000..531349a5aa --- /dev/null +++ b/vlib/v/checker/tests/struct_cast_to_struct_generic_err.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/struct_cast_to_struct_generic_err.v:11:11: error: cannot convert struct `Abc` to struct `Xyz` + 9 | fn main() { + 10 | abc := Abc{} + 11 | _ := Xyz(abc) + | ~~~ + 12 | } \ No newline at end of file diff --git a/vlib/v/checker/tests/struct_cast_to_struct_generic_err.vv b/vlib/v/checker/tests/struct_cast_to_struct_generic_err.vv new file mode 100644 index 0000000000..ce81ba52f4 --- /dev/null +++ b/vlib/v/checker/tests/struct_cast_to_struct_generic_err.vv @@ -0,0 +1,12 @@ +struct Abc { + name T +} + +struct Xyz { + name string +} + +fn main() { + abc := Abc{} + _ := Xyz(abc) +} diff --git a/vlib/v/checker/tests/struct_cast_to_struct_mut_err_a.out b/vlib/v/checker/tests/struct_cast_to_struct_mut_err_a.out new file mode 100644 index 0000000000..528249ef96 --- /dev/null +++ b/vlib/v/checker/tests/struct_cast_to_struct_mut_err_a.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/struct_cast_to_struct_mut_err_a.v:12:11: error: cannot convert struct `Abc` to struct `Xyz` + 10 | fn main() { + 11 | abc := Abc{} + 12 | _ := Xyz(abc) + | ~~~ + 13 | } \ No newline at end of file diff --git a/vlib/v/checker/tests/struct_cast_to_struct_mut_err_a.vv b/vlib/v/checker/tests/struct_cast_to_struct_mut_err_a.vv new file mode 100644 index 0000000000..7a5e4a30dc --- /dev/null +++ b/vlib/v/checker/tests/struct_cast_to_struct_mut_err_a.vv @@ -0,0 +1,13 @@ +struct Abc { +mut: + name string +} + +struct Xyz { + name string +} + +fn main() { + abc := Abc{} + _ := Xyz(abc) +} diff --git a/vlib/v/checker/tests/struct_cast_to_struct_mut_err_b.out b/vlib/v/checker/tests/struct_cast_to_struct_mut_err_b.out new file mode 100644 index 0000000000..627a1731ec --- /dev/null +++ b/vlib/v/checker/tests/struct_cast_to_struct_mut_err_b.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/struct_cast_to_struct_mut_err_b.v:12:11: error: cannot convert struct `Abc` to struct `Xyz` + 10 | fn main() { + 11 | abc := Abc{} + 12 | _ := Xyz(abc) + | ~~~ + 13 | } \ No newline at end of file diff --git a/vlib/v/checker/tests/struct_cast_to_struct_mut_err_b.vv b/vlib/v/checker/tests/struct_cast_to_struct_mut_err_b.vv new file mode 100644 index 0000000000..ff34dca90c --- /dev/null +++ b/vlib/v/checker/tests/struct_cast_to_struct_mut_err_b.vv @@ -0,0 +1,13 @@ +struct Abc { + name string +} + +struct Xyz { +mut: + name string +} + +fn main() { + abc := Abc{} + _ := Xyz(abc) +} diff --git a/vlib/v/checker/tests/struct_cast_to_struct_pub_err_a.out b/vlib/v/checker/tests/struct_cast_to_struct_pub_err_a.out new file mode 100644 index 0000000000..010e62fb6d --- /dev/null +++ b/vlib/v/checker/tests/struct_cast_to_struct_pub_err_a.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/struct_cast_to_struct_pub_err_a.v:12:11: error: cannot convert struct `Abc` to struct `Xyz` + 10 | fn main() { + 11 | abc := Abc{} + 12 | _ := Xyz(abc) + | ~~~ + 13 | } \ No newline at end of file diff --git a/vlib/v/checker/tests/struct_cast_to_struct_pub_err_a.vv b/vlib/v/checker/tests/struct_cast_to_struct_pub_err_a.vv new file mode 100644 index 0000000000..7c559e4ad3 --- /dev/null +++ b/vlib/v/checker/tests/struct_cast_to_struct_pub_err_a.vv @@ -0,0 +1,13 @@ +struct Abc { +pub: + name string +} + +struct Xyz { + name string +} + +fn main() { + abc := Abc{} + _ := Xyz(abc) +} diff --git a/vlib/v/checker/tests/struct_cast_to_struct_pub_err_b.out b/vlib/v/checker/tests/struct_cast_to_struct_pub_err_b.out new file mode 100644 index 0000000000..4b06d6ef20 --- /dev/null +++ b/vlib/v/checker/tests/struct_cast_to_struct_pub_err_b.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/struct_cast_to_struct_pub_err_b.v:12:11: error: cannot convert struct `Abc` to struct `Xyz` + 10 | fn main() { + 11 | abc := Abc{} + 12 | _ := Xyz(abc) + | ~~~ + 13 | } \ No newline at end of file diff --git a/vlib/v/checker/tests/struct_cast_to_struct_pub_err_b.vv b/vlib/v/checker/tests/struct_cast_to_struct_pub_err_b.vv new file mode 100644 index 0000000000..aa92278f5e --- /dev/null +++ b/vlib/v/checker/tests/struct_cast_to_struct_pub_err_b.vv @@ -0,0 +1,13 @@ +struct Abc { + name string +} + +struct Xyz { +pub: + name string +} + +fn main() { + abc := Abc{} + _ := Xyz(abc) +} diff --git a/vlib/v/fmt/tests/struct_init_with_custom_len_keep.vv b/vlib/v/fmt/tests/struct_init_with_custom_len_keep.vv index 1daa3cb5db..e4f2e2d674 100644 --- a/vlib/v/fmt/tests/struct_init_with_custom_len_keep.vv +++ b/vlib/v/fmt/tests/struct_init_with_custom_len_keep.vv @@ -5,7 +5,7 @@ struct Foo { struct Bar { f &Foo = &Foo(0) - d Foo = Foo(0) + d Foo = Foo{0} } fn main() {