checker: allow `map` method name (#7834)
							parent
							
								
									86df5cd1a9
								
							
						
					
					
						commit
						9332f7cac2
					
				|  | @ -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) {
 | ||||||
|  |  | ||||||
|  | @ -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 } | ||||||
|  | @ -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 }
 | ||||||
|  | @ -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 | } | ||||||
|  | @ -0,0 +1,5 @@ | ||||||
|  | fn main() { | ||||||
|  | 	a := [1, 2, 3] | ||||||
|  | 	a.map() | ||||||
|  | 	a.map(it * 2, 3) | ||||||
|  | } | ||||||
|  | @ -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 | ||||||
|  |  | ||||||
|  | @ -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 | ||||||
|  | } | ||||||
		Loading…
	
		Reference in New Issue