From f8fb57d1aac4373855a7f9360d75b52233cbf41b Mon Sep 17 00:00:00 2001 From: Felipe Pena Date: Sat, 23 Nov 2024 16:35:44 -0300 Subject: [PATCH] checker: fix or-expr check on invalid method call (fix #22949) (#22950) --- vlib/v/checker/fn.v | 6 +- .../tests/method_call_on_undefined_err.out | 65 +++++++++++++++++++ .../tests/method_call_on_undefined_err.vv | 19 ++++++ 3 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 vlib/v/checker/tests/method_call_on_undefined_err.out create mode 100644 vlib/v/checker/tests/method_call_on_undefined_err.vv diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index 9819de59a..cc5228a9c 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -688,7 +688,7 @@ fn (mut c Checker) call_expr(mut node ast.CallExpr) ast.Type { mut continue_check := true // Now call `method_call` or `fn_call` for specific checks. typ := if node.is_method { - c.method_call(mut node) + c.method_call(mut node, mut continue_check) } else { c.fn_call(mut node, mut continue_check) } @@ -2133,7 +2133,7 @@ fn (mut c Checker) check_type_and_visibility(name string, type_idx int, expected return true } -fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type { +fn (mut c Checker) method_call(mut node ast.CallExpr, mut continue_check &bool) ast.Type { // `(if true { 'foo.bar' } else { 'foo.bar.baz' }).all_after('foo.')` mut left_expr := node.left for mut left_expr is ast.ParExpr { @@ -2150,6 +2150,7 @@ fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type { left_type := c.expr(mut node.left) if left_type == ast.void_type { // c.error('cannot call a method using an invalid expression', node.pos) + continue_check = false return ast.void_type } if c.pref.skip_unused && !c.is_builtin_mod && c.mod != 'strings' { @@ -2190,6 +2191,7 @@ fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type { // No need to print this error, since this means that the variable is unknown, // and there already was an error before. // c.error('`void` type has no methods', node.left.pos()) + continue_check = false return ast.void_type } if final_left_sym.kind == .array && array_builtin_methods_chk.matches(method_name) diff --git a/vlib/v/checker/tests/method_call_on_undefined_err.out b/vlib/v/checker/tests/method_call_on_undefined_err.out new file mode 100644 index 000000000..6b8f5d36d --- /dev/null +++ b/vlib/v/checker/tests/method_call_on_undefined_err.out @@ -0,0 +1,65 @@ +vlib/v/checker/tests/method_call_on_undefined_err.vv:6:10: error: undefined ident: `input` + 4 | fn foo() !string { + 5 | // without next error does not occurs + 6 | a, b := input.split_once('%') or { '', 'empty' } + | ~~~~~ + 7 | if b == '' { + 8 | return error('') +vlib/v/checker/tests/method_call_on_undefined_err.vv:6:32: error: unexpected `or` block, the function `split_once` does not return an Option or a Result + 4 | fn foo() !string { + 5 | // without next error does not occurs + 6 | a, b := input.split_once('%') or { '', 'empty' } + | ~~~~~~~~~~~~~~~~~~ + 7 | if b == '' { + 8 | return error('') +vlib/v/checker/tests/method_call_on_undefined_err.vv:6:7: error: assignment mismatch: 2 variables but `split_once()` returns 0 values + 4 | fn foo() !string { + 5 | // without next error does not occurs + 6 | a, b := input.split_once('%') or { '', 'empty' } + | ~~ + 7 | if b == '' { + 8 | return error('') +vlib/v/checker/tests/method_call_on_undefined_err.vv:7:5: error: invalid variable `b` + 5 | // without next error does not occurs + 6 | a, b := input.split_once('%') or { '', 'empty' } + 7 | if b == '' { + | ^ + 8 | return error('') + 9 | } +vlib/v/checker/tests/method_call_on_undefined_err.vv:7:5: error: non-bool type `void` used as if condition + 5 | // without next error does not occurs + 6 | a, b := input.split_once('%') or { '', 'empty' } + 7 | if b == '' { + | ~~~~~~~ + 8 | return error('') + 9 | } +vlib/v/checker/tests/method_call_on_undefined_err.vv:10:9: error: invalid variable `a` + 8 | return error('') + 9 | } + 10 | return a + | ^ + 11 | } + 12 | +vlib/v/checker/tests/method_call_on_undefined_err.vv:10:2: error: `a` used as value + 8 | return error('') + 9 | } + 10 | return a + | ~~~~~~~~ + 11 | } + 12 | +vlib/v/checker/tests/method_call_on_undefined_err.vv:14:13: error: expected 0 arguments, but got 1 + 12 | + 13 | fn main() { + 14 | res := foo('fe80::1234%ne0') or { + | ~~~~~~~~~~~~~~~~ + 15 | eprintln(err) + 16 | exit(1) +Details: have (string) + want () +vlib/v/checker/tests/method_call_on_undefined_err.vv:14:31: error: unexpected `or` block, the function `foo` does not return an Option or a Result + 12 | + 13 | fn main() { + 14 | res := foo('fe80::1234%ne0') or { + | ~~~~ + 15 | eprintln(err) + 16 | exit(1) diff --git a/vlib/v/checker/tests/method_call_on_undefined_err.vv b/vlib/v/checker/tests/method_call_on_undefined_err.vv new file mode 100644 index 000000000..7ce782e3d --- /dev/null +++ b/vlib/v/checker/tests/method_call_on_undefined_err.vv @@ -0,0 +1,19 @@ +module main + +// note missing `input` argument in fn signature +fn foo() !string { + // without next error does not occurs + a, b := input.split_once('%') or { '', 'empty' } + if b == '' { + return error('') + } + return a +} + +fn main() { + res := foo('fe80::1234%ne0') or { + eprintln(err) + exit(1) + } + println(res) +} -- 2.39.5