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_platforms = ['amd64', 'aarch64', 'x64', 'x32', 'little_endian', 'big_endian'] | ||||
| 	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 { | ||||
|  | @ -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) { | ||||
| 	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) | ||||
| 	arg_expr := call_expr.args[0].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
 | ||||
| 	// FIXME: Argument count != 1 will break these
 | ||||
| 	if left_type_sym.kind == .array && | ||||
| 		method_name in | ||||
| 		['filter', 'clone', 'repeat', 'reverse', 'map', 'slice', 'sort', 'contains', 'index'] { | ||||
| 		method_name in array_builtin_methods { | ||||
| 		mut elem_typ := table.void_type | ||||
| 		is_filter_map := method_name in ['filter', 'map'] | ||||
| 		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) | ||||
| 		if sym.kind == .interface_ { | ||||
| 			c.error('interfaces cannot be used as method receiver', node.receiver_pos) | ||||
| 		} | ||||
| 		if sym.kind == .sum_type && node.name == 'type_name' { | ||||
| 		} else if sym.kind == .array && !c.is_builtin_mod && node.name == 'map' { | ||||
| 			// 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) | ||||
| 		} | ||||
| 		// 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 | ||||
| 	} | ||||
| 	is_filter := field_name in ['filter', 'map'] | ||||
| 	if is_filter { | ||||
| 		p.open_scope() | ||||
| 	} else if field_name == 'sort' { | ||||
| 	if is_filter || field_name == 'sort' { | ||||
| 		p.open_scope() | ||||
| 	} | ||||
| 	// ! in mutable methods
 | ||||
|  | @ -1444,10 +1442,6 @@ fn (mut p Parser) dot_expr(left ast.Expr) ast.Expr { | |||
| 	if p.tok.kind == .lpar { | ||||
| 		p.next() | ||||
| 		args := p.call_args() | ||||
| 		if is_filter && args.len != 1 { | ||||
| 			p.error('needs exactly 1 argument') | ||||
| 			return ast.Expr{} | ||||
| 		} | ||||
| 		p.check(.rpar) | ||||
| 		mut or_stmts := []ast.Stmt{} | ||||
| 		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