From a66753871d91d512fc53f2eacb2f95f1d5ebf1af Mon Sep 17 00:00:00 2001 From: yuyi Date: Thu, 11 Jun 2020 19:14:07 +0800 Subject: [PATCH] checker: check array.map/filter fn or anon_fn --- vlib/v/checker/checker.v | 38 +++++++++++++++++++ .../tests/array_filter_anon_fn_err_a.out | 6 +++ .../tests/array_filter_anon_fn_err_a.vv | 4 ++ .../tests/array_filter_anon_fn_err_b.out | 6 +++ .../tests/array_filter_anon_fn_err_b.vv | 4 ++ .../v/checker/tests/array_filter_fn_err_a.out | 7 ++++ vlib/v/checker/tests/array_filter_fn_err_a.vv | 7 ++++ .../v/checker/tests/array_filter_fn_err_b.out | 7 ++++ vlib/v/checker/tests/array_filter_fn_err_b.vv | 7 ++++ .../checker/tests/array_map_anon_fn_err_a.out | 6 +++ .../checker/tests/array_map_anon_fn_err_a.vv | 4 ++ .../checker/tests/array_map_anon_fn_err_b.out | 6 +++ .../checker/tests/array_map_anon_fn_err_b.vv | 4 ++ vlib/v/checker/tests/array_map_fn_err_a.out | 7 ++++ vlib/v/checker/tests/array_map_fn_err_a.vv | 7 ++++ vlib/v/checker/tests/array_map_fn_err_b.out | 7 ++++ vlib/v/checker/tests/array_map_fn_err_b.vv | 7 ++++ 17 files changed, 134 insertions(+) create mode 100644 vlib/v/checker/tests/array_filter_anon_fn_err_a.out create mode 100644 vlib/v/checker/tests/array_filter_anon_fn_err_a.vv create mode 100644 vlib/v/checker/tests/array_filter_anon_fn_err_b.out create mode 100644 vlib/v/checker/tests/array_filter_anon_fn_err_b.vv create mode 100644 vlib/v/checker/tests/array_filter_fn_err_a.out create mode 100644 vlib/v/checker/tests/array_filter_fn_err_a.vv create mode 100644 vlib/v/checker/tests/array_filter_fn_err_b.out create mode 100644 vlib/v/checker/tests/array_filter_fn_err_b.vv create mode 100644 vlib/v/checker/tests/array_map_anon_fn_err_a.out create mode 100644 vlib/v/checker/tests/array_map_anon_fn_err_a.vv create mode 100644 vlib/v/checker/tests/array_map_anon_fn_err_b.out create mode 100644 vlib/v/checker/tests/array_map_anon_fn_err_b.vv create mode 100644 vlib/v/checker/tests/array_map_fn_err_a.out create mode 100644 vlib/v/checker/tests/array_map_fn_err_a.vv create mode 100644 vlib/v/checker/tests/array_map_fn_err_b.out create mode 100644 vlib/v/checker/tests/array_map_fn_err_b.vv diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index b3182d26e8..c375a5df52 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -754,6 +754,37 @@ pub fn (mut c Checker) call_expr(mut call_expr ast.CallExpr) table.Type { return c.call_fn(call_expr) } +fn (mut c Checker) check_map_and_filter(is_map bool, elem_typ table.Type, call_expr ast.CallExpr) { + elem_sym := c.table.get_type_symbol(elem_typ) + match call_expr.args[0].expr { + ast.AnonFn { + if it.decl.args.len > 1 { + c.error('function needs exactly 1 argument', call_expr.pos) + } else if is_map && (it.decl.return_type != elem_typ || it.decl.args[0].typ != elem_typ) { + c.error('type mismatch, should use `fn(a $elem_sym.name) $elem_sym.name {...}`', call_expr.pos) + } else if !is_map && (it.decl.return_type != table.bool_type || it.decl.args[0].typ != elem_typ) { + c.error('type mismatch, should use `fn(a $elem_sym.name) bool {...}`', call_expr.pos) + } + } + ast.Ident { + if it.kind == .function { + func := c.table.find_fn(it.name) or { + c.error('$it.name is not exist', it.pos) + return + } + if func.args.len > 1 { + c.error('function needs exactly 1 argument', call_expr.pos) + } else if is_map && (func.return_type != elem_typ || func.args[0].typ != elem_typ) { + c.error('type mismatch, should use `fn(a $elem_sym.name) $elem_sym.name {...}`', call_expr.pos) + } else if !is_map && (func.return_type != table.bool_type || func.args[0].typ != elem_typ) { + c.error('type mismatch, should use `fn(a $elem_sym.name) bool {...}`', call_expr.pos) + } + } + } + else {} + } +} + pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type { left_type := c.expr(call_expr.left) is_generic := left_type == table.t_type @@ -764,10 +795,12 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type { // FIXME: Argument count != 1 will break these if left_type_sym.kind == .array && method_name in ['filter', 'clone', 'repeat', 'reverse', 'map', 'slice'] { + mut elem_typ := table.void_type if method_name in ['filter', 'map'] { array_info := left_type_sym.info as table.Array mut scope := c.file.scope.innermost(call_expr.pos.pos) scope.update_var_type('it', array_info.elem_type) + elem_typ = array_info.elem_type } // map/filter are supposed to have 1 arg only mut arg_type := left_type @@ -777,6 +810,8 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type { call_expr.return_type = left_type call_expr.receiver_type = left_type if method_name == 'map' { + // check fn + c.check_map_and_filter(true, elem_typ, call_expr) arg_sym := c.table.get_type_symbol(arg_type) // FIXME: match expr failed for now mut ret_type := 0 @@ -785,6 +820,9 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type { else { ret_type = arg_type } } call_expr.return_type = c.table.find_or_register_array(ret_type, 1, c.mod) + } else if method_name == 'filter' { + // check fn + c.check_map_and_filter(false, elem_typ, call_expr) } else if method_name == 'clone' { // need to return `array_xxx` instead of `array` // in ['clone', 'str'] { diff --git a/vlib/v/checker/tests/array_filter_anon_fn_err_a.out b/vlib/v/checker/tests/array_filter_anon_fn_err_a.out new file mode 100644 index 0000000000..dd91d173d1 --- /dev/null +++ b/vlib/v/checker/tests/array_filter_anon_fn_err_a.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/array_filter_anon_fn_err_a.v:2:23: error: function needs exactly 1 argument + 1 | fn main() { + 2 | a := [1,2,3,4].filter(fn(a int, b int) bool { return a > 0 }) + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 3 | println(a) + 4 | } diff --git a/vlib/v/checker/tests/array_filter_anon_fn_err_a.vv b/vlib/v/checker/tests/array_filter_anon_fn_err_a.vv new file mode 100644 index 0000000000..edf4f8956a --- /dev/null +++ b/vlib/v/checker/tests/array_filter_anon_fn_err_a.vv @@ -0,0 +1,4 @@ +fn main() { + a := [1,2,3,4].filter(fn(a int, b int) bool { return a > 0 }) + println(a) +} diff --git a/vlib/v/checker/tests/array_filter_anon_fn_err_b.out b/vlib/v/checker/tests/array_filter_anon_fn_err_b.out new file mode 100644 index 0000000000..7fd970d8f1 --- /dev/null +++ b/vlib/v/checker/tests/array_filter_anon_fn_err_b.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/array_filter_anon_fn_err_b.v:2:23: error: type mismatch, should use `fn(a int) bool {...}` + 1 | fn main() { + 2 | a := [1,2,3,4].filter(fn(a string) bool { return a.len > 0 }) + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 3 | println(a) + 4 | } diff --git a/vlib/v/checker/tests/array_filter_anon_fn_err_b.vv b/vlib/v/checker/tests/array_filter_anon_fn_err_b.vv new file mode 100644 index 0000000000..38238b45da --- /dev/null +++ b/vlib/v/checker/tests/array_filter_anon_fn_err_b.vv @@ -0,0 +1,4 @@ +fn main() { + a := [1,2,3,4].filter(fn(a string) bool { return a.len > 0 }) + println(a) +} diff --git a/vlib/v/checker/tests/array_filter_fn_err_a.out b/vlib/v/checker/tests/array_filter_fn_err_a.out new file mode 100644 index 0000000000..ce518632db --- /dev/null +++ b/vlib/v/checker/tests/array_filter_fn_err_a.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/array_filter_fn_err_a.v:5:23: error: function needs exactly 1 argument + 3 | } + 4 | fn main() { + 5 | a := [1,2,3,4].filter(fil) + | ~~~~~ + 6 | println(a) + 7 | } diff --git a/vlib/v/checker/tests/array_filter_fn_err_a.vv b/vlib/v/checker/tests/array_filter_fn_err_a.vv new file mode 100644 index 0000000000..f829543cbc --- /dev/null +++ b/vlib/v/checker/tests/array_filter_fn_err_a.vv @@ -0,0 +1,7 @@ +fn fil(a int, b int) bool { + return a > 0 +} +fn main() { + a := [1,2,3,4].filter(fil) + println(a) +} diff --git a/vlib/v/checker/tests/array_filter_fn_err_b.out b/vlib/v/checker/tests/array_filter_fn_err_b.out new file mode 100644 index 0000000000..642b574265 --- /dev/null +++ b/vlib/v/checker/tests/array_filter_fn_err_b.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/array_filter_fn_err_b.v:5:23: error: type mismatch, should use `fn(a int) bool {...}` + 3 | } + 4 | fn main() { + 5 | a := [1,2,3,4].filter(fil) + | ~~~~~ + 6 | println(a) + 7 | } diff --git a/vlib/v/checker/tests/array_filter_fn_err_b.vv b/vlib/v/checker/tests/array_filter_fn_err_b.vv new file mode 100644 index 0000000000..2b17560c70 --- /dev/null +++ b/vlib/v/checker/tests/array_filter_fn_err_b.vv @@ -0,0 +1,7 @@ +fn fil(a string) bool { + return a.len > 0 +} +fn main() { + a := [1,2,3,4].filter(fil) + println(a) +} diff --git a/vlib/v/checker/tests/array_map_anon_fn_err_a.out b/vlib/v/checker/tests/array_map_anon_fn_err_a.out new file mode 100644 index 0000000000..764cf7866f --- /dev/null +++ b/vlib/v/checker/tests/array_map_anon_fn_err_a.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/array_map_anon_fn_err_a.v:2:20: error: function needs exactly 1 argument + 1 | fn main() { + 2 | a := [1,2,3,4].map(fn(a int, b int) int {return a + b}) + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 3 | println(a) + 4 | } diff --git a/vlib/v/checker/tests/array_map_anon_fn_err_a.vv b/vlib/v/checker/tests/array_map_anon_fn_err_a.vv new file mode 100644 index 0000000000..9b4d760a02 --- /dev/null +++ b/vlib/v/checker/tests/array_map_anon_fn_err_a.vv @@ -0,0 +1,4 @@ +fn main() { + a := [1,2,3,4].map(fn(a int, b int) int {return a + b}) + println(a) +} diff --git a/vlib/v/checker/tests/array_map_anon_fn_err_b.out b/vlib/v/checker/tests/array_map_anon_fn_err_b.out new file mode 100644 index 0000000000..7089421761 --- /dev/null +++ b/vlib/v/checker/tests/array_map_anon_fn_err_b.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/array_map_anon_fn_err_b.v:2:20: error: type mismatch, should use `fn(a int) int {...}` + 1 | fn main() { + 2 | a := [1,2,3,4].map(fn(a string) string { return a }) + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 3 | println(a) + 4 | } diff --git a/vlib/v/checker/tests/array_map_anon_fn_err_b.vv b/vlib/v/checker/tests/array_map_anon_fn_err_b.vv new file mode 100644 index 0000000000..980136eca9 --- /dev/null +++ b/vlib/v/checker/tests/array_map_anon_fn_err_b.vv @@ -0,0 +1,4 @@ +fn main() { + a := [1,2,3,4].map(fn(a string) string { return a }) + println(a) +} diff --git a/vlib/v/checker/tests/array_map_fn_err_a.out b/vlib/v/checker/tests/array_map_fn_err_a.out new file mode 100644 index 0000000000..e2b7233ea6 --- /dev/null +++ b/vlib/v/checker/tests/array_map_fn_err_a.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/array_map_fn_err_a.v:5:20: error: function needs exactly 1 argument + 3 | } + 4 | fn main() { + 5 | a := [1,2,3,4].map(add) + | ~~~~~ + 6 | println(a) + 7 | } diff --git a/vlib/v/checker/tests/array_map_fn_err_a.vv b/vlib/v/checker/tests/array_map_fn_err_a.vv new file mode 100644 index 0000000000..6707a61b45 --- /dev/null +++ b/vlib/v/checker/tests/array_map_fn_err_a.vv @@ -0,0 +1,7 @@ +fn add(a int, b int) int { + return a + b +} +fn main() { + a := [1,2,3,4].map(add) + println(a) +} diff --git a/vlib/v/checker/tests/array_map_fn_err_b.out b/vlib/v/checker/tests/array_map_fn_err_b.out new file mode 100644 index 0000000000..3bf8dcfe63 --- /dev/null +++ b/vlib/v/checker/tests/array_map_fn_err_b.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/array_map_fn_err_b.v:5:20: error: type mismatch, should use `fn(a int) int {...}` + 3 | } + 4 | fn main() { + 5 | a := [1,2,3,4].map(add) + | ~~~~~ + 6 | println(a) + 7 | } diff --git a/vlib/v/checker/tests/array_map_fn_err_b.vv b/vlib/v/checker/tests/array_map_fn_err_b.vv new file mode 100644 index 0000000000..b3dbc0bad3 --- /dev/null +++ b/vlib/v/checker/tests/array_map_fn_err_b.vv @@ -0,0 +1,7 @@ +fn add(a string) string { + return a +} +fn main() { + a := [1,2,3,4].map(add) + println(a) +}