From 2261606b56afac2d43c6c08c2f329b4088b35c12 Mon Sep 17 00:00:00 2001 From: Felipe Pena Date: Wed, 7 Dec 2022 11:56:07 -0300 Subject: [PATCH] checker: disallow `Bar{...foo}` when Bar needs more fields, than what `foo` has (#16609) --- vlib/v/checker/checker.v | 16 ++++++++++ vlib/v/checker/struct.v | 3 +- .../tests/check_incompatible_struct.out | 14 ++++++++ .../tests/check_incompatible_struct.vv | 32 +++++++++++++++++++ 4 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 vlib/v/checker/tests/check_incompatible_struct.out create mode 100644 vlib/v/checker/tests/check_incompatible_struct.vv diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 01a1c4946..8e97bb9aa 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -4003,6 +4003,22 @@ fn (mut c Checker) error(message string, pos token.Pos) { c.warn_or_error(msg, pos, false) } +fn (c &Checker) check_struct_signature_init_fields(from ast.Struct, to ast.Struct, node ast.StructInit) bool { + if node.fields.len == 0 { + return from.fields.len == to.fields.len + } + + mut count_not_in_from := 0 + for field in node.fields { + filtered := from.fields.filter(it.name == field.name) + if filtered.len != 1 { + count_not_in_from++ + } + } + + return (from.fields.len + count_not_in_from) == to.fields.len +} + // check `to` has all fields of `from` fn (c &Checker) check_struct_signature(from ast.Struct, to ast.Struct) bool { // Note: `to` can have extra fields diff --git a/vlib/v/checker/struct.v b/vlib/v/checker/struct.v index 2cd30e62c..856656c7b 100644 --- a/vlib/v/checker/struct.v +++ b/vlib/v/checker/struct.v @@ -603,7 +603,8 @@ fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type { from_info := from_sym.info as ast.Struct to_info := to_sym.info as ast.Struct // TODO this check is too strict - if !c.check_struct_signature(from_info, to_info) { + if !c.check_struct_signature(from_info, to_info) + || !c.check_struct_signature_init_fields(from_info, to_info, node) { c.error('struct `${from_sym.name}` is not compatible with struct `${to_sym.name}`', node.update_expr.pos()) } diff --git a/vlib/v/checker/tests/check_incompatible_struct.out b/vlib/v/checker/tests/check_incompatible_struct.out new file mode 100644 index 000000000..af1c92e45 --- /dev/null +++ b/vlib/v/checker/tests/check_incompatible_struct.out @@ -0,0 +1,14 @@ +vlib/v/checker/tests/check_incompatible_struct.vv:17:6: error: struct `Foo` is not compatible with struct `Bar` + 15 | } + 16 | bar3 := Bar{ + 17 | ...foo + | ~~~ + 18 | b: 1 + 19 | } +vlib/v/checker/tests/check_incompatible_struct.vv:26:6: error: struct `Foo` is not compatible with struct `Bar` + 24 | } + 25 | bar := Bar{ + 26 | ...foo + | ~~~ + 27 | } + 28 | print(bar) diff --git a/vlib/v/checker/tests/check_incompatible_struct.vv b/vlib/v/checker/tests/check_incompatible_struct.vv new file mode 100644 index 000000000..30e429bb2 --- /dev/null +++ b/vlib/v/checker/tests/check_incompatible_struct.vv @@ -0,0 +1,32 @@ +struct Foo { + b int +} + +struct Bar { + a int + b int +} + +fn main() { + foo := Foo{2} + bar2 := Bar{ + ...foo + a: 1 + } + bar3 := Bar{ + ...foo + b: 1 + } + bar4 := Bar{ + ...foo + b: 1 + a: 2 + } + bar := Bar{ + ...foo + } + print(bar) + print(bar2) + print(bar3) + print(bar4) +} -- 2.39.5