From 1ba45123ec13119af75906220900c957d8a82a6f Mon Sep 17 00:00:00 2001 From: Felipe Pena Date: Fri, 29 Aug 2025 01:43:19 -0300 Subject: [PATCH] checker: fix multi return arg passing checking (fix #25167) (fix #25180) (#25177) --- vlib/v/checker/fn.v | 33 ++++++++++++++----- .../tests/multi_return_arg_missing_err.out | 18 ++++++++++ .../tests/multi_return_arg_missing_err.vv | 15 +++++++++ vlib/v/checker/tests/multi_return_err.out | 12 +++---- 4 files changed, 61 insertions(+), 17 deletions(-) create mode 100644 vlib/v/checker/tests/multi_return_arg_missing_err.out create mode 100644 vlib/v/checker/tests/multi_return_arg_missing_err.vv diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index 69eff4fead..3b2b3aa245 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -1472,6 +1472,7 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast. node.pos) } mut has_decompose := false + mut nr_multi_values := 0 for i, mut call_arg in node.args { if func.params.len == 0 { continue @@ -1482,21 +1483,22 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast. if !func.is_variadic && has_decompose { c.error('cannot have parameter after array decompose', node.pos) } + param_i := i + nr_multi_values param := if func.is_variadic && i >= func.params.len - 1 { func.params.last() } else { - func.params[i] + func.params[param_i] } // registers if the arg must be passed by ref to disable auto deref args call_arg.should_be_ptr = param.typ.is_ptr() && !param.is_mut if func.is_variadic && call_arg.expr is ast.ArrayDecompose { - if i > func.params.len - 1 { + if param_i > func.params.len - 1 { c.error('too many arguments in call to `${func.name}`', node.pos) } } has_decompose = call_arg.expr is ast.ArrayDecompose already_checked := node.language != .js && call_arg.expr is ast.CallExpr - if func.is_variadic && i >= func.params.len - 1 { + if func.is_variadic && param_i >= func.params.len - 1 { param_sym := c.table.sym(param.typ) mut expected_type := param.typ if param_sym.info is ast.Array { @@ -1688,15 +1690,16 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast. multi_param := if func.is_variadic && i >= func.params.len - 1 { func.params.last() } else { - func.params[n + i] + func.params[n + param_i] } c.check_expected_call_arg(curr_arg, c.unwrap_generic(multi_param.typ), node.language, call_arg) or { - c.error('${err.msg()} in argument ${i + n + 1} to `${fn_name}` from ${c.table.type_to_str(arg_typ)}', + c.error('${err.msg()} in argument ${param_i + n + 1} to `${fn_name}` from ${c.table.type_to_str(arg_typ)}', call_arg.pos) continue out } } + nr_multi_values += arg_typ_sym.info.types.len - 1 continue } else if param_typ_sym.info is ast.Struct && arg_typ_sym.info is ast.Struct && param_typ_sym.info.is_anon { @@ -1788,13 +1791,14 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast. continue } */ - c.error('${err.msg()} in argument ${i + 1} to `${fn_name}`', call_arg.pos) + c.error('${err.msg()} in argument ${i + nr_multi_values + 1} to `${fn_name}`', + call_arg.pos) } if final_param_sym.kind == .struct && arg_typ !in [ast.voidptr_type, ast.nil_type] && !c.check_multiple_ptr_match(arg_typ, param.typ, param, call_arg) { got_typ_str, expected_typ_str := c.get_string_names_of(arg_typ, param.typ) - c.error('cannot use `${got_typ_str}` as `${expected_typ_str}` in argument ${i + 1} to `${fn_name}`', - call_arg.pos) + c.error('cannot use `${got_typ_str}` as `${expected_typ_str}` in argument ${i + + nr_multi_values + 1} to `${fn_name}`', call_arg.pos) } // Warn about automatic (de)referencing, which will be removed soon. if func.language != .c && !c.inside_unsafe && !(call_arg.is_mut && param.is_mut) { @@ -2924,6 +2928,17 @@ fn (mut c Checker) check_expected_arg_count(mut node ast.CallExpr, f &ast.Fn) ! return error('') } } + } else if node.args.len > 1 && node.args.any(it.expr is ast.CallExpr + && it.expr.nr_ret_values > 1) { + mut check_args := 0 + for arg in node.args { + if arg.expr is ast.CallExpr && arg.expr.nr_ret_values > 0 { + check_args += arg.expr.nr_ret_values + } else { + check_args += 1 + } + } + nr_args = check_args } if min_required_params < 0 { min_required_params = 0 @@ -2966,7 +2981,7 @@ fn (mut c Checker) check_expected_arg_count(mut node ast.CallExpr, f &ast.Fn) ! ) return error('') } else if !f.is_variadic && nr_args > nr_params { - unexpected_args_pos := node.args[min_required_params].pos.extend(node.args.last().pos) + unexpected_args_pos := node.args[int_min(min_required_params, node.args.len - 1)].pos.extend(node.args.last().pos) // c.error('3expected ${min_required_params} arguments, but got ${nr_args}', unexpected_args_pos) c.fn_call_error_have_want( nr_params: min_required_params diff --git a/vlib/v/checker/tests/multi_return_arg_missing_err.out b/vlib/v/checker/tests/multi_return_arg_missing_err.out new file mode 100644 index 0000000000..bf1936a620 --- /dev/null +++ b/vlib/v/checker/tests/multi_return_arg_missing_err.out @@ -0,0 +1,18 @@ +vlib/v/checker/tests/multi_return_arg_missing_err.vv:12:14: error: expected 2 arguments, but got 3 + 10 | + 11 | fn main() { + 12 | expect_2(1, returning_2()) + | ~~~~~~~~~~~~~ + 13 | expect_2(returning_2(), 1) + 14 | expect_4(returning_2(), returning_2()) +Details: have (int literal, (int, int)) + want (int, int) +vlib/v/checker/tests/multi_return_arg_missing_err.vv:13:26: error: expected 2 arguments, but got 3 + 11 | fn main() { + 12 | expect_2(1, returning_2()) + 13 | expect_2(returning_2(), 1) + | ^ + 14 | expect_4(returning_2(), returning_2()) + 15 | } +Details: have ((int, int), int literal) + want (int, int) diff --git a/vlib/v/checker/tests/multi_return_arg_missing_err.vv b/vlib/v/checker/tests/multi_return_arg_missing_err.vv new file mode 100644 index 0000000000..64a8db1a9f --- /dev/null +++ b/vlib/v/checker/tests/multi_return_arg_missing_err.vv @@ -0,0 +1,15 @@ +fn expect_2(a int, b int) { +} + +fn expect_4(a int, b int, c int, d int) { +} + +fn returning_2() (int, int) { + return 1, 1 +} + +fn main() { + expect_2(1, returning_2()) + expect_2(returning_2(), 1) + expect_4(returning_2(), returning_2()) +} diff --git a/vlib/v/checker/tests/multi_return_err.out b/vlib/v/checker/tests/multi_return_err.out index 86848492be..a16e11e40c 100644 --- a/vlib/v/checker/tests/multi_return_err.out +++ b/vlib/v/checker/tests/multi_return_err.out @@ -5,24 +5,20 @@ vlib/v/checker/tests/multi_return_err.vv:18:10: error: cannot use `f64` as `int` | ~~~~~~~~~~ 19 | my_func3(my_func2(), 'foo') 20 | my_func4('foo', my_func2()) -vlib/v/checker/tests/multi_return_err.vv:19:2: error: expected 3 arguments, but got 2 +vlib/v/checker/tests/multi_return_err.vv:19:11: error: cannot use `f64` as `int` in argument 2 to `my_func3` from (int, f64) 17 | fn main() { 18 | my_func(my_func2()) 19 | my_func3(my_func2(), 'foo') - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ + | ~~~~~~~~~~ 20 | my_func4('foo', my_func2()) 21 | my_func(my_func5()) -Details: have ((int, f64), string) - want (int, int, string) -vlib/v/checker/tests/multi_return_err.vv:20:2: error: expected 3 arguments, but got 2 +vlib/v/checker/tests/multi_return_err.vv:20:18: error: cannot use `f64` as `int` in argument 3 to `my_func4` from (int, f64) 18 | my_func(my_func2()) 19 | my_func3(my_func2(), 'foo') 20 | my_func4('foo', my_func2()) - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ + | ~~~~~~~~~~ 21 | my_func(my_func5()) 22 | my_func(my_func6()) -Details: have (string, (int, f64)) - want (string, int, int) vlib/v/checker/tests/multi_return_err.vv:21:2: error: expected 2 arguments, but got 1 19 | my_func3(my_func2(), 'foo') 20 | my_func4('foo', my_func2())