checker: check filter, map and sort left type (#6952)
parent
e03ae19372
commit
52b627feb3
|
@ -1490,34 +1490,6 @@ pub fn (s string) fields() []string {
|
|||
return s.replace('\t', ' ').split(' ')
|
||||
}
|
||||
|
||||
pub fn (s string) map(func fn(byte) byte) string {
|
||||
unsafe {
|
||||
mut res := malloc(s.len + 1)
|
||||
for i in 0..s.len {
|
||||
res[i] = func(s[i])
|
||||
}
|
||||
return tos(res, s.len)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (s string) filter(func fn(b byte) bool) string {
|
||||
mut new_len := 0
|
||||
mut buf := malloc(s.len + 1)
|
||||
for i in 0 .. s.len {
|
||||
mut b := s[i]
|
||||
if func(b) {
|
||||
unsafe {
|
||||
buf[new_len] = b
|
||||
}
|
||||
new_len++
|
||||
}
|
||||
}
|
||||
unsafe {
|
||||
buf[new_len] = 0
|
||||
return buf.vstring_with_len(new_len)
|
||||
}
|
||||
}
|
||||
|
||||
// Allows multi-line strings to be formatted in a way that removes white-space
|
||||
// before a delimeter. by default `|` is used.
|
||||
// Note: the delimiter has to be a byte at this time. That means surrounding
|
||||
|
|
|
@ -816,41 +816,10 @@ fn test_double_quote_inter() {
|
|||
assert '${a} ${b}' == "1 2"
|
||||
}
|
||||
|
||||
fn test_string_map() {
|
||||
$if windows {
|
||||
// TODO
|
||||
return
|
||||
}
|
||||
original := 'Hello'
|
||||
println('original.len = $original.len')
|
||||
a := original.map(fn (b byte) byte {
|
||||
return b + 1
|
||||
})
|
||||
expected := 'Ifmmp'
|
||||
println('a[0] = ' + a[0].str())
|
||||
println('a[1] = ' + a[1].str())
|
||||
println('a[2] = ' + a[2].str())
|
||||
println('a[3] = ' + a[3].str())
|
||||
println('a[4] = ' + a[4].str())
|
||||
println('a.len = $a.len')
|
||||
assert a.len == expected.len
|
||||
assert a == expected
|
||||
|
||||
assert 'foo'.map(foo) == r'\ee'
|
||||
}
|
||||
|
||||
fn foo(b byte) byte {
|
||||
return b - 10
|
||||
}
|
||||
|
||||
fn test_string_filter() {
|
||||
foo := 'V is awesome!!!!'.filter(fn (b byte) bool {
|
||||
return b != `!`
|
||||
})
|
||||
assert foo == 'V is awesome'
|
||||
assert 'Alexander'.filter(filter) == 'Alexnder'
|
||||
}
|
||||
|
||||
fn filter(b byte) bool {
|
||||
return b != `a`
|
||||
}
|
||||
|
|
|
@ -1050,15 +1050,15 @@ fn (mut c Checker) check_map_and_filter(is_map bool, elem_typ table.Type, call_e
|
|||
match arg_expr {
|
||||
ast.AnonFn {
|
||||
if arg_expr.decl.params.len > 1 {
|
||||
c.error('function needs exactly 1 argument', call_expr.pos)
|
||||
c.error('function needs exactly 1 argument', arg_expr.decl.pos)
|
||||
} else if is_map &&
|
||||
(arg_expr.decl.return_type != elem_typ || arg_expr.decl.params[0].typ != elem_typ) {
|
||||
c.error('type mismatch, should use `fn(a $elem_sym.source_name) $elem_sym.source_name {...}`',
|
||||
call_expr.pos)
|
||||
arg_expr.decl.pos)
|
||||
} else if !is_map &&
|
||||
(arg_expr.decl.return_type != table.bool_type || arg_expr.decl.params[0].typ != elem_typ) {
|
||||
c.error('type mismatch, should use `fn(a $elem_sym.source_name) bool {...}`',
|
||||
call_expr.pos)
|
||||
arg_expr.decl.pos)
|
||||
}
|
||||
}
|
||||
ast.Ident {
|
||||
|
@ -1071,11 +1071,11 @@ fn (mut c Checker) check_map_and_filter(is_map bool, elem_typ table.Type, call_e
|
|||
c.error('function needs exactly 1 argument', call_expr.pos)
|
||||
} else if is_map && (func.return_type != elem_typ || func.params[0].typ != elem_typ) {
|
||||
c.error('type mismatch, should use `fn(a $elem_sym.source_name) $elem_sym.source_name {...}`',
|
||||
call_expr.pos)
|
||||
arg_expr.pos)
|
||||
} else if !is_map &&
|
||||
(func.return_type != table.bool_type || func.params[0].typ != elem_typ) {
|
||||
c.error('type mismatch, should use `fn(a $elem_sym.source_name) bool {...}`',
|
||||
call_expr.pos)
|
||||
arg_expr.pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1110,13 +1110,15 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type {
|
|||
is_sort := method_name == 'sort'
|
||||
if is_filter_map || is_sort {
|
||||
array_info := left_type_sym.info as table.Array
|
||||
mut scope := c.file.scope.innermost(call_expr.pos.pos)
|
||||
args_pos := call_expr.pos.pos + call_expr.name.len
|
||||
mut scope := c.file.scope.innermost(args_pos)
|
||||
if is_filter_map {
|
||||
scope.update_var_type('it', array_info.elem_type)
|
||||
// position of `it` doesn't matter
|
||||
scope_register_it(mut scope, call_expr.pos, array_info.elem_type)
|
||||
} else if is_sort {
|
||||
c.fail_if_immutable(call_expr.left)
|
||||
scope.update_var_type('a', array_info.elem_type)
|
||||
scope.update_var_type('b', array_info.elem_type)
|
||||
// position of `a` and `b` doesn't matter, they're the same
|
||||
scope_register_ab(mut scope, call_expr.pos, array_info.elem_type)
|
||||
// Verify `.sort(a < b)`
|
||||
if call_expr.args.len > 0 {
|
||||
if call_expr.args[0].expr !is ast.InfixExpr {
|
||||
|
@ -2251,11 +2253,28 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) {
|
|||
}
|
||||
}
|
||||
|
||||
fn (mut c Checker) open_scope(mut parent ast.Scope, start_pos int) &ast.Scope {
|
||||
mut s := ast.new_scope(parent, start_pos)
|
||||
s.end_pos = parent.end_pos
|
||||
parent.children << s
|
||||
return s
|
||||
fn scope_register_it(mut s ast.Scope, pos token.Position, typ table.Type) {
|
||||
s.register('it', ast.Var{
|
||||
name: 'it'
|
||||
pos: pos
|
||||
typ: typ
|
||||
is_used: true
|
||||
})
|
||||
}
|
||||
|
||||
fn scope_register_ab(mut s ast.Scope, pos token.Position, typ table.Type) {
|
||||
s.register('a', ast.Var{
|
||||
name: 'a'
|
||||
pos: pos
|
||||
typ: typ
|
||||
is_used: true
|
||||
})
|
||||
s.register('b', ast.Var{
|
||||
name: 'b'
|
||||
pos: pos
|
||||
typ: typ
|
||||
is_used: true
|
||||
})
|
||||
}
|
||||
|
||||
fn (mut c Checker) check_array_init_para_type(para string, expr ast.Expr, pos token.Position) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
vlib/v/checker/tests/array_filter_anon_fn_err_a.vv:2:23: error: function needs exactly 1 argument
|
||||
vlib/v/checker/tests/array_filter_anon_fn_err_a.vv:2:24: 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 | }
|
||||
4 | }
|
|
@ -1,6 +1,6 @@
|
|||
vlib/v/checker/tests/array_filter_anon_fn_err_b.vv:2:23: error: type mismatch, should use `fn(a int) bool {...}`
|
||||
vlib/v/checker/tests/array_filter_anon_fn_err_b.vv:2:24: 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 | }
|
||||
4 | }
|
|
@ -1,7 +1,7 @@
|
|||
vlib/v/checker/tests/array_filter_fn_err_a.vv:5:23: error: function needs exactly 1 argument
|
||||
vlib/v/checker/tests/array_filter_fn_err_a.vv:5:17: error: function needs exactly 1 argument
|
||||
3 | }
|
||||
4 | fn main() {
|
||||
5 | a := [1,2,3,4].filter(fil)
|
||||
| ~~~~~
|
||||
| ~~~~~~~~~~~
|
||||
6 | println(a)
|
||||
7 | }
|
||||
7 | }
|
|
@ -1,7 +1,7 @@
|
|||
vlib/v/checker/tests/array_filter_fn_err_b.vv:5:23: error: type mismatch, should use `fn(a int) bool {...}`
|
||||
vlib/v/checker/tests/array_filter_fn_err_b.vv:5:24: 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 | }
|
||||
7 | }
|
|
@ -1,6 +1,6 @@
|
|||
vlib/v/checker/tests/array_map_anon_fn_err_a.vv:2:20: error: function needs exactly 1 argument
|
||||
vlib/v/checker/tests/array_map_anon_fn_err_a.vv:2:21: 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 | }
|
||||
4 | }
|
|
@ -1,6 +1,6 @@
|
|||
vlib/v/checker/tests/array_map_anon_fn_err_b.vv:2:20: error: type mismatch, should use `fn(a int) int {...}`
|
||||
vlib/v/checker/tests/array_map_anon_fn_err_b.vv:2:21: 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 | }
|
||||
4 | }
|
|
@ -1,7 +1,7 @@
|
|||
vlib/v/checker/tests/array_map_fn_err_a.vv:5:20: error: function needs exactly 1 argument
|
||||
vlib/v/checker/tests/array_map_fn_err_a.vv:5:17: error: function needs exactly 1 argument
|
||||
3 | }
|
||||
4 | fn main() {
|
||||
5 | a := [1,2,3,4].map(add)
|
||||
| ~~~~~
|
||||
| ~~~~~~~~
|
||||
6 | println(a)
|
||||
7 | }
|
||||
7 | }
|
|
@ -1,7 +1,7 @@
|
|||
vlib/v/checker/tests/array_map_fn_err_b.vv:5:20: error: type mismatch, should use `fn(a int) int {...}`
|
||||
vlib/v/checker/tests/array_map_fn_err_b.vv:5:21: 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 | }
|
||||
7 | }
|
|
@ -0,0 +1,6 @@
|
|||
vlib/v/checker/tests/filter_on_non_arr_err.vv:2:14: error: unknown method: `string.filter`.
|
||||
Did you mean `after`?
|
||||
1 | fn main() {
|
||||
2 | _ := 'test'.filter(it == `t`)
|
||||
| ~~~~~~~~~~~~~~~~~
|
||||
3 | }
|
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
_ := 'test'.filter(it == `t`)
|
||||
}
|
|
@ -1276,21 +1276,13 @@ fn (mut p Parser) dot_expr(left ast.Expr) ast.Expr {
|
|||
if p.tok.kind == .dollar {
|
||||
return p.comptime_method_call(left)
|
||||
}
|
||||
mut name_pos := p.tok.position()
|
||||
name_pos := p.tok.position()
|
||||
field_name := p.check_name()
|
||||
is_filter := field_name in ['filter', 'map']
|
||||
if is_filter {
|
||||
p.open_scope()
|
||||
name_pos = p.tok.position()
|
||||
p.scope_register_it()
|
||||
// wrong tok position when using defer
|
||||
// defer {
|
||||
// p.close_scope()
|
||||
// }
|
||||
} else if field_name == 'sort' {
|
||||
p.open_scope()
|
||||
name_pos = p.tok.position()
|
||||
p.scope_register_ab()
|
||||
}
|
||||
// ! in mutable methods
|
||||
if p.tok.kind == .not && p.peek_tok.kind == .lpar {
|
||||
|
|
Loading…
Reference in New Issue