checker: allow `map` method name (#7834)

pull/7844/head
Enzo 2021-01-03 16:57:29 +01:00 committed by GitHub
parent 86df5cd1a9
commit 9332f7cac2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 83 additions and 11 deletions

View File

@ -26,6 +26,7 @@ const (
valid_comp_if_compilers = ['gcc', 'tinyc', 'clang', 'mingw', 'msvc', 'cplusplus'] valid_comp_if_compilers = ['gcc', 'tinyc', 'clang', 'mingw', 'msvc', 'cplusplus']
valid_comp_if_platforms = ['amd64', 'aarch64', 'x64', 'x32', 'little_endian', 'big_endian'] valid_comp_if_platforms = ['amd64', 'aarch64', 'x64', 'x32', 'little_endian', 'big_endian']
valid_comp_if_other = ['js', 'debug', 'test', 'glibc', 'prealloc', 'no_bounds_checking'] valid_comp_if_other = ['js', 'debug', 'test', 'glibc', 'prealloc', 'no_bounds_checking']
array_builtin_methods = ['filter', 'clone', 'repeat', 'reverse', 'map', 'slice', 'sort', 'contains', 'index']
) )
pub struct Checker { pub struct Checker {
@ -1131,6 +1132,11 @@ pub fn (mut c Checker) call_expr(mut call_expr ast.CallExpr) table.Type {
} }
fn (mut c Checker) check_map_and_filter(is_map bool, elem_typ table.Type, call_expr ast.CallExpr) { fn (mut c Checker) check_map_and_filter(is_map bool, elem_typ table.Type, call_expr ast.CallExpr) {
if call_expr.args.len != 1 {
c.error('expected 1 arguments, but got $call_expr.args.len', call_expr.pos)
// Finish early so that it doesn't fail later
return
}
elem_sym := c.table.get_type_symbol(elem_typ) elem_sym := c.table.get_type_symbol(elem_typ)
arg_expr := call_expr.args[0].expr arg_expr := call_expr.args[0].expr
match arg_expr { match arg_expr {
@ -1200,8 +1206,7 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type {
// TODO: remove this for actual methods, use only for compiler magic // TODO: remove this for actual methods, use only for compiler magic
// FIXME: Argument count != 1 will break these // FIXME: Argument count != 1 will break these
if left_type_sym.kind == .array && if left_type_sym.kind == .array &&
method_name in method_name in array_builtin_methods {
['filter', 'clone', 'repeat', 'reverse', 'map', 'slice', 'sort', 'contains', 'index'] {
mut elem_typ := table.void_type mut elem_typ := table.void_type
is_filter_map := method_name in ['filter', 'map'] is_filter_map := method_name in ['filter', 'map']
is_sort := method_name == 'sort' is_sort := method_name == 'sort'
@ -4947,8 +4952,10 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
mut sym := c.table.get_type_symbol(node.receiver.typ) mut sym := c.table.get_type_symbol(node.receiver.typ)
if sym.kind == .interface_ { if sym.kind == .interface_ {
c.error('interfaces cannot be used as method receiver', node.receiver_pos) c.error('interfaces cannot be used as method receiver', node.receiver_pos)
} } else if sym.kind == .array && !c.is_builtin_mod && node.name == 'map' {
if sym.kind == .sum_type && node.name == 'type_name' { // TODO `node.map in array_builtin_methods`
c.error('method overrides built-in array method', node.pos)
} else if sym.kind == .sum_type && node.name == 'type_name' {
c.error('method overrides built-in sum type method', node.pos) c.error('method overrides built-in sum type method', node.pos)
} }
// if sym.has_method(node.name) { // if sym.has_method(node.name) {

View File

@ -0,0 +1,7 @@
vlib/v/checker/tests/array_builtin_redefinition.vv:7:1: error: method overrides built-in array method
5 | // fn (a []Abc) repeat() int { return 0 }
6 | // fn (a []Abc) reverse() int { return 0 }
7 | fn (a []Abc) map() int { return 0 }
| ~~~~~~~~~~~~~~~~~~~~~~
8 | // fn (a []Abc) slice() int { return 0 }
9 | // fn (a []Abc) sort() int { return 0 }

View File

@ -0,0 +1,11 @@
type Abc = int
// fn (a []Abc) filter() int { return 0 }
// fn (a []Abc) clone() int { return 0 }
// fn (a []Abc) repeat() int { return 0 }
// fn (a []Abc) reverse() int { return 0 }
fn (a []Abc) map() int { return 0 }
// fn (a []Abc) slice() int { return 0 }
// fn (a []Abc) sort() int { return 0 }
// fn (a []Abc) contains() int { return 0 }
// fn (a []Abc) index() int { return 0 }

View File

@ -0,0 +1,13 @@
vlib/v/checker/tests/array_map_arg_mismatch.vv:3:4: error: expected 1 arguments, but got 0
1 | fn main() {
2 | a := [1, 2, 3]
3 | a.map()
| ~~~~~
4 | a.map(it * 2, 3)
5 | }
vlib/v/checker/tests/array_map_arg_mismatch.vv:4:4: error: expected 1 arguments, but got 2
2 | a := [1, 2, 3]
3 | a.map()
4 | a.map(it * 2, 3)
| ~~~~~~~~~~~~~~
5 | }

View File

@ -0,0 +1,5 @@
fn main() {
a := [1, 2, 3]
a.map()
a.map(it * 2, 3)
}

View File

@ -1416,9 +1416,7 @@ fn (mut p Parser) dot_expr(left ast.Expr) ast.Expr {
p.name_error = true p.name_error = true
} }
is_filter := field_name in ['filter', 'map'] is_filter := field_name in ['filter', 'map']
if is_filter { if is_filter || field_name == 'sort' {
p.open_scope()
} else if field_name == 'sort' {
p.open_scope() p.open_scope()
} }
// ! in mutable methods // ! in mutable methods
@ -1444,10 +1442,6 @@ fn (mut p Parser) dot_expr(left ast.Expr) ast.Expr {
if p.tok.kind == .lpar { if p.tok.kind == .lpar {
p.next() p.next()
args := p.call_args() args := p.call_args()
if is_filter && args.len != 1 {
p.error('needs exactly 1 argument')
return ast.Expr{}
}
p.check(.rpar) p.check(.rpar)
mut or_stmts := []ast.Stmt{} mut or_stmts := []ast.Stmt{}
mut or_kind := ast.OrKind.absent mut or_kind := ast.OrKind.absent

View File

@ -0,0 +1,35 @@
type Foo = int
fn (a Foo) map(add int) string {
return (a + add).str()
}
fn test_map_one_arg() {
a := Foo(0)
assert a.map(1) == '1'
assert Foo(3).map(3) == '6'
}
type Bar = int
fn (b Bar) map() int {
return b + 1
}
fn test_map_no_arg() {
b := Bar(0)
assert b.map() == 1
assert Bar(1).map() == 2
}
type Baz = int
fn (b Baz) map(a int, c int) int {
return b + (a - c)
}
fn test_map_more_args() {
b := Baz(0)
assert b.map(5, 2) == 3
assert Baz(3).map(2, 5) == 0
}