From 372a4021fcda0db0c4f78f9baf2af522ee8f9673 Mon Sep 17 00:00:00 2001 From: yuyi Date: Fri, 27 Sep 2024 23:51:30 +0800 Subject: [PATCH] checker: check arguments mismatch of array.sorted_with_compare() (fix #22327) (#22328) --- vlib/v/checker/checker.v | 4 +- vlib/v/checker/fn.v | 72 ++++++++++++------- .../tests/array_sort_with_compare_err.out | 33 +++++---- .../tests/array_sort_with_compare_err.vv | 2 + .../tests/array_sorted_with_compare_err.out | 36 ++++++++++ .../tests/array_sorted_with_compare_err.vv | 17 +++++ 6 files changed, 123 insertions(+), 41 deletions(-) create mode 100644 vlib/v/checker/tests/array_sorted_with_compare_err.out create mode 100644 vlib/v/checker/tests/array_sorted_with_compare_err.vv diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 40bfc7a0991c38..54235e240b0a52 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -29,8 +29,8 @@ const generic_fn_postprocess_iterations_cutoff_limit = 1000_000 // are properly checked. // Note that methods that do not return anything, or that return known types, are not listed here, since they are just ordinary non generic methods. pub const array_builtin_methods = ['filter', 'clone', 'repeat', 'reverse', 'map', 'slice', 'sort', - 'sorted', 'sorted_with_compare', 'contains', 'index', 'wait', 'any', 'all', 'first', 'last', - 'pop', 'delete'] + 'sort_with_compare', 'sorted', 'sorted_with_compare', 'contains', 'index', 'wait', 'any', 'all', + 'first', 'last', 'pop', 'delete'] pub const array_builtin_methods_chk = token.new_keywords_matcher_from_array_trie(array_builtin_methods) // TODO: remove `byte` from this list when it is no longer supported pub const reserved_type_names = ['byte', 'bool', 'char', 'i8', 'i16', 'int', 'i64', 'u8', 'u16', diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index bcc693e90bf929..6e82c43c999498 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -2549,27 +2549,6 @@ fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type { } } } - if left_sym.info is ast.Array && method_name in ['sort_with_compare', 'sorted_with_compare'] { - elem_typ := left_sym.info.elem_type - arg_sym := c.table.sym(arg.typ) - if arg_sym.kind == .function { - func_info := arg_sym.info as ast.FnType - if func_info.func.params.len == 2 { - if func_info.func.params[0].typ.nr_muls() != elem_typ.nr_muls() + 1 { - arg_typ_str := c.table.type_to_str(func_info.func.params[0].typ) - expected_typ_str := c.table.type_to_str(elem_typ.ref()) - c.error('${method_name} callback function parameter `${func_info.func.params[0].name}` with type `${arg_typ_str}` should be `${expected_typ_str}`', - func_info.func.params[0].type_pos) - } - if func_info.func.params[1].typ.nr_muls() != elem_typ.nr_muls() + 1 { - arg_typ_str := c.table.type_to_str(func_info.func.params[1].typ) - expected_typ_str := c.table.type_to_str(elem_typ.ref()) - c.error('${method_name} callback function parameter `${func_info.func.params[1].name}` with type `${arg_typ_str}` should be `${expected_typ_str}`', - func_info.func.params[1].type_pos) - } - } - } - } // Handle expected interface if final_arg_sym.kind == .interface_ { if c.type_implements(got_arg_typ, final_arg_typ, arg.expr.pos()) { @@ -3184,9 +3163,48 @@ fn (mut c Checker) array_builtin_method_call(mut node ast.CallExpr, left_type as // position of `it` doesn't matter scope_register_it(mut node.scope, node.pos, elem_typ) } - } else if node.args.len == 1 && method_name == 'sorted_with_compare' { - if node.args.len > 0 && mut node.args[0].expr is ast.LambdaExpr { - c.support_lambda_expr_in_sort(elem_typ.ref(), ast.int_type, mut node.args[0].expr) + } else if method_name in ['sort_with_compare', 'sorted_with_compare'] { + if node.args.len != 1 { + c.error('`.${method_name}()` expected 1 argument, but got ${node.args.len}', + node.pos) + } else { + if mut node.args[0].expr is ast.LambdaExpr { + c.support_lambda_expr_in_sort(elem_typ.ref(), ast.int_type, mut node.args[0].expr) + } + arg_type := c.expr(mut node.args[0].expr) + arg_sym := c.table.sym(arg_type) + if arg_sym.kind == .function { + func_info := arg_sym.info as ast.FnType + if func_info.func.params.len == 2 { + if func_info.func.params[0].typ.nr_muls() != elem_typ.nr_muls() + 1 { + arg_typ_str := c.table.type_to_str(func_info.func.params[0].typ) + expected_typ_str := c.table.type_to_str(elem_typ.ref()) + c.error('${method_name} callback function parameter `${func_info.func.params[0].name}` with type `${arg_typ_str}` should be `${expected_typ_str}`', + func_info.func.params[0].type_pos) + } + if func_info.func.params[1].typ.nr_muls() != elem_typ.nr_muls() + 1 { + arg_typ_str := c.table.type_to_str(func_info.func.params[1].typ) + expected_typ_str := c.table.type_to_str(elem_typ.ref()) + c.error('${method_name} callback function parameter `${func_info.func.params[1].name}` with type `${arg_typ_str}` should be `${expected_typ_str}`', + func_info.func.params[1].type_pos) + } + } + } + node.args[0].typ = arg_type + if method := c.table.find_method(left_sym, method_name) { + c.check_expected_call_arg(arg_type, method.params[1].typ, node.language, + node.args[0]) or { + c.error('${err.msg()} in argument 1 to `${left_sym.name}.${method_name}`', + node.args[0].pos) + } + } + if method_name == 'sort_with_compare' { + node.return_type = ast.void_type + node.receiver_type = node.left_type.ref() + } else { + node.return_type = node.left_type + node.receiver_type = node.left_type + } } } else if method_name == 'sort' || method_name == 'sorted' { if method_name == 'sort' { @@ -3300,8 +3318,10 @@ fn (mut c Checker) array_builtin_method_call(mut node ast.CallExpr, left_type as c.ensure_same_array_return_type(mut node, left_type) } else if method_name == 'sorted' { c.ensure_same_array_return_type(mut node, left_type) - } else if method_name == 'sorted_with_compare' { - c.ensure_same_array_return_type(mut node, left_type) + } else if method_name in ['sort_with_compare', 'sorted_with_compare'] { + if method_name == 'sorted_with_compare' { + c.ensure_same_array_return_type(mut node, left_type) + } // Inject a (voidptr) cast for the callback argument, to pass -cstrict, otherwise: // error: incompatible function pointer types passing // 'int (string *, string *)' (aka 'int (struct string *, struct string *)') diff --git a/vlib/v/checker/tests/array_sort_with_compare_err.out b/vlib/v/checker/tests/array_sort_with_compare_err.out index e73ff4f02452cc..ee5e25ae627ec4 100644 --- a/vlib/v/checker/tests/array_sort_with_compare_err.out +++ b/vlib/v/checker/tests/array_sort_with_compare_err.out @@ -1,17 +1,17 @@ -vlib/v/checker/tests/array_sort_with_compare_err.vv:11:24: error: sort_with_compare callback function parameter `a` with type `string` should be `&string` - 9 | } - 10 | - 11 | fn sort_by_file_base(a string, b string) int { +vlib/v/checker/tests/array_sort_with_compare_err.vv:13:24: error: sort_with_compare callback function parameter `a` with type `string` should be `&string` + 11 | } + 12 | + 13 | fn sort_by_file_base(a string, b string) int { | ~~~~~~ - 12 | return int(a > b) - 13 | } -vlib/v/checker/tests/array_sort_with_compare_err.vv:11:34: error: sort_with_compare callback function parameter `b` with type `string` should be `&string` - 9 | } - 10 | - 11 | fn sort_by_file_base(a string, b string) int { + 14 | return int(a > b) + 15 | } +vlib/v/checker/tests/array_sort_with_compare_err.vv:13:34: error: sort_with_compare callback function parameter `b` with type `string` should be `&string` + 11 | } + 12 | + 13 | fn sort_by_file_base(a string, b string) int { | ~~~~~~ - 12 | return int(a > b) - 13 | } + 14 | return int(a > b) + 15 | } vlib/v/checker/tests/array_sort_with_compare_err.vv:4:26: error: cannot use `fn (string, string) int` as `fn (voidptr, voidptr) int` in argument 1 to `[]string.sort_with_compare` 2 | mut names := ['aaa', 'bbb', 'ccc'] 3 | @@ -26,4 +26,11 @@ vlib/v/checker/tests/array_sort_with_compare_err.vv:7:26: error: cannot use `int 7 | names.sort_with_compare(22) | ~~ 8 | println(names) - 9 | } + 9 | +vlib/v/checker/tests/array_sort_with_compare_err.vv:10:8: error: `.sort_with_compare()` expected 1 argument, but got 2 + 8 | println(names) + 9 | + 10 | names.sort_with_compare(sort_by_file_base, 22) + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 11 | } + 12 | diff --git a/vlib/v/checker/tests/array_sort_with_compare_err.vv b/vlib/v/checker/tests/array_sort_with_compare_err.vv index 7d9ecd74afd13e..7222a3d2e0da2d 100644 --- a/vlib/v/checker/tests/array_sort_with_compare_err.vv +++ b/vlib/v/checker/tests/array_sort_with_compare_err.vv @@ -6,6 +6,8 @@ fn main() { names.sort_with_compare(22) println(names) + + names.sort_with_compare(sort_by_file_base, 22) } fn sort_by_file_base(a string, b string) int { diff --git a/vlib/v/checker/tests/array_sorted_with_compare_err.out b/vlib/v/checker/tests/array_sorted_with_compare_err.out new file mode 100644 index 00000000000000..7412e6a4d181a6 --- /dev/null +++ b/vlib/v/checker/tests/array_sorted_with_compare_err.out @@ -0,0 +1,36 @@ +vlib/v/checker/tests/array_sorted_with_compare_err.vv:15:24: error: sorted_with_compare callback function parameter `a` with type `string` should be `&string` + 13 | } + 14 | + 15 | fn sort_by_file_base(a string, b string) int { + | ~~~~~~ + 16 | return int(a > b) + 17 | } +vlib/v/checker/tests/array_sorted_with_compare_err.vv:15:34: error: sorted_with_compare callback function parameter `b` with type `string` should be `&string` + 13 | } + 14 | + 15 | fn sort_by_file_base(a string, b string) int { + | ~~~~~~ + 16 | return int(a > b) + 17 | } +vlib/v/checker/tests/array_sorted_with_compare_err.vv:4:37: error: cannot use `fn (string, string) int` as `fn (voidptr, voidptr) int` in argument 1 to `[]string.sorted_with_compare` + 2 | names := ['aaa', 'bbb', 'ccc'] + 3 | + 4 | name1 := names.sorted_with_compare(sort_by_file_base) + | ~~~~~~~~~~~~~~~~~ + 5 | println(name1) + 6 | +Details: expected argument 1 to be a pointer, but the passed argument 1 is NOT a pointer +vlib/v/checker/tests/array_sorted_with_compare_err.vv:7:37: error: cannot use `int literal` as `fn (voidptr, voidptr) int` in argument 1 to `[]string.sorted_with_compare` + 5 | println(name1) + 6 | + 7 | name2 := names.sorted_with_compare(22) + | ~~ + 8 | println(name2) + 9 | +vlib/v/checker/tests/array_sorted_with_compare_err.vv:10:17: error: `.sorted_with_compare()` expected 1 argument, but got 2 + 8 | println(name2) + 9 | + 10 | name3 := names.sorted_with_compare(sort_by_file_base, 22) + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 11 | println(name3) + 12 | diff --git a/vlib/v/checker/tests/array_sorted_with_compare_err.vv b/vlib/v/checker/tests/array_sorted_with_compare_err.vv new file mode 100644 index 00000000000000..eb32ac2eecdb68 --- /dev/null +++ b/vlib/v/checker/tests/array_sorted_with_compare_err.vv @@ -0,0 +1,17 @@ +fn main() { + names := ['aaa', 'bbb', 'ccc'] + + name1 := names.sorted_with_compare(sort_by_file_base) + println(name1) + + name2 := names.sorted_with_compare(22) + println(name2) + + name3 := names.sorted_with_compare(sort_by_file_base, 22) + println(name3) + +} + +fn sort_by_file_base(a string, b string) int { + return int(a > b) +}