checker: check filter, map and sort left type (#6952)

pull/6968/head
Daniel Däschle 2020-11-26 11:28:54 +01:00 committed by GitHub
parent e03ae19372
commit 52b627feb3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 67 additions and 106 deletions

View File

@ -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

View File

@ -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`
}

View File

@ -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) {

View File

@ -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 | }

View File

@ -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 | }

View File

@ -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 | }

View File

@ -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 | }

View File

@ -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 | }

View File

@ -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 | }

View File

@ -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 | }

View File

@ -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 | }

View File

@ -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 | }

View File

@ -0,0 +1,3 @@
fn main() {
_ := 'test'.filter(it == `t`)
}

View File

@ -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 {