checker: check array.sort(a < b) (#9321)
							parent
							
								
									2991cad4e8
								
							
						
					
					
						commit
						dd9f9c2718
					
				|  | @ -24,18 +24,18 @@ fn test_sorting_primitives_with_condition_expression() { | |||
| 	assert x == ['9', '87', '654', '3210'] | ||||
| } | ||||
| 
 | ||||
| fn get_score(word string) int { | ||||
| 	mut total := 0 | ||||
| 	for letter in word { | ||||
| 		total += int(letter) - 97 | ||||
| 	} | ||||
| 	return total | ||||
| } | ||||
| // fn get_score(word string) int {
 | ||||
| // 	mut total := 0
 | ||||
| // 	for letter in word {
 | ||||
| // 		total += int(letter) - 97
 | ||||
| // 	}
 | ||||
| // 	return total
 | ||||
| // }
 | ||||
| 
 | ||||
| fn test_sorting_with_fn_call_in_condition_expression() { | ||||
| 	mut words := ['aaaa', 'a', 'b', 'foo', 'bar'] | ||||
| 	words.sort(get_score(a) < get_score(b)) | ||||
| } | ||||
| // fn test_sorting_with_fn_call_in_condition_expression() {
 | ||||
| // 	mut words := ['aaaa', 'a', 'b', 'foo', 'bar']
 | ||||
| // 	words.sort(get_score(a) < get_score(b))
 | ||||
| // }
 | ||||
| 
 | ||||
| fn mysort(mut a []int) { | ||||
| 	a.sort() | ||||
|  |  | |||
|  | @ -1660,52 +1660,55 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type { | |||
| fn (mut c Checker) call_array_builtin_method(mut call_expr ast.CallExpr, left_type table.Type, left_type_sym table.TypeSymbol) table.Type { | ||||
| 	method_name := call_expr.name | ||||
| 	mut elem_typ := table.void_type | ||||
| 	is_filter_map := method_name in ['filter', 'map'] | ||||
| 	is_sort := method_name == 'sort' | ||||
| 	is_slice := method_name == 'slice' | ||||
| 	is_wait := method_name == 'wait' | ||||
| 	if is_slice && !c.is_builtin_mod { | ||||
| 	if method_name == 'slice' && !c.is_builtin_mod { | ||||
| 		c.error('.slice() is a private method, use `x[start..end]` instead', call_expr.pos) | ||||
| 	} | ||||
| 	if is_filter_map || is_sort || is_wait { | ||||
| 		array_info := left_type_sym.info as table.Array | ||||
| 		if is_filter_map { | ||||
| 			// position of `it` doesn't matter
 | ||||
| 			scope_register_it(mut call_expr.scope, call_expr.pos, array_info.elem_type) | ||||
| 		} else if is_sort { | ||||
| 			c.fail_if_immutable(call_expr.left) | ||||
| 			// position of `a` and `b` doesn't matter, they're the same
 | ||||
| 			scope_register_ab(mut call_expr.scope, call_expr.pos, array_info.elem_type) | ||||
| 	array_info := left_type_sym.info as table.Array | ||||
| 	elem_typ = array_info.elem_type | ||||
| 	if method_name in ['filter', 'map'] { | ||||
| 		// position of `it` doesn't matter
 | ||||
| 		scope_register_it(mut call_expr.scope, call_expr.pos, elem_typ) | ||||
| 	} else if method_name == 'sort' { | ||||
| 		c.fail_if_immutable(call_expr.left) | ||||
| 		// position of `a` and `b` doesn't matter, they're the same
 | ||||
| 		scope_register_a_b(mut call_expr.scope, call_expr.pos, elem_typ) | ||||
| 
 | ||||
| 			if call_expr.args.len > 1 { | ||||
| 				c.error('expected 0 or 1 argument, but got $call_expr.args.len', call_expr.pos) | ||||
| 			} | ||||
| 			// Verify `.sort(a < b)`
 | ||||
| 			if call_expr.args.len > 0 { | ||||
| 				if call_expr.args[0].expr !is ast.InfixExpr { | ||||
| 					c.error( | ||||
| 						'`.sort()` requires a `<` or `>` comparison as the first and only argument' + | ||||
| 						'\ne.g. `users.sort(a.id < b.id)`', call_expr.pos) | ||||
| 		if call_expr.args.len > 1 { | ||||
| 			c.error('expected 0 or 1 argument, but got $call_expr.args.len', call_expr.pos) | ||||
| 		} else if call_expr.args.len == 1 { | ||||
| 			if call_expr.args[0].expr is ast.InfixExpr { | ||||
| 				if call_expr.args[0].expr.op !in [.gt, .lt] { | ||||
| 					c.error('`.sort()` can only use `<` or `>` comparison', call_expr.pos) | ||||
| 				} | ||||
| 				left_name := '${call_expr.args[0].expr.left}'[0] | ||||
| 				right_name := '${call_expr.args[0].expr.right}'[0] | ||||
| 				if left_name !in [`a`, `b`] || right_name !in [`a`, `b`] { | ||||
| 					c.error('`.sort()` can only use `a` or `b` as argument, e.g. `arr.sort(a < b)`', | ||||
| 						call_expr.pos) | ||||
| 				} else if left_name == right_name { | ||||
| 					c.error('`.sort()` cannot use same argument', call_expr.pos) | ||||
| 				} | ||||
| 			} else { | ||||
| 				c.error( | ||||
| 					'`.sort()` requires a `<` or `>` comparison as the first and only argument' + | ||||
| 					'\ne.g. `users.sort(a.id < b.id)`', call_expr.pos) | ||||
| 			} | ||||
| 		} | ||||
| 		elem_typ = array_info.elem_type | ||||
| 		if is_wait { | ||||
| 			elem_sym := c.table.get_type_symbol(elem_typ) | ||||
| 			if elem_sym.kind == .thread { | ||||
| 				if call_expr.args.len != 0 { | ||||
| 					c.error('`.wait()` does not have any arguments', call_expr.args[0].pos) | ||||
| 				} | ||||
| 				thread_ret_type := elem_sym.thread_info().return_type | ||||
| 				if thread_ret_type.has_flag(.optional) { | ||||
| 					c.error('`.wait()` cannot be called for an array when thread functions return optionals. Iterate over the arrays elements instead and handle each returned optional with `or`.', | ||||
| 						call_expr.pos) | ||||
| 				} | ||||
| 				call_expr.return_type = c.table.find_or_register_array(thread_ret_type) | ||||
| 			} else { | ||||
| 				c.error('`$left_type_sym.name` has no method `wait()` (only thread handles and arrays of them have)', | ||||
| 					call_expr.left.position()) | ||||
| 	} else if method_name == 'wait' { | ||||
| 		elem_sym := c.table.get_type_symbol(elem_typ) | ||||
| 		if elem_sym.kind == .thread { | ||||
| 			if call_expr.args.len != 0 { | ||||
| 				c.error('`.wait()` does not have any arguments', call_expr.args[0].pos) | ||||
| 			} | ||||
| 			thread_ret_type := elem_sym.thread_info().return_type | ||||
| 			if thread_ret_type.has_flag(.optional) { | ||||
| 				c.error('`.wait()` cannot be called for an array when thread functions return optionals. Iterate over the arrays elements instead and handle each returned optional with `or`.', | ||||
| 					call_expr.pos) | ||||
| 			} | ||||
| 			call_expr.return_type = c.table.find_or_register_array(thread_ret_type) | ||||
| 		} else { | ||||
| 			c.error('`$left_type_sym.name` has no method `wait()` (only thread handles and arrays of them have)', | ||||
| 				call_expr.left.position()) | ||||
| 		} | ||||
| 	} | ||||
| 	// map/filter are supposed to have 1 arg only
 | ||||
|  | @ -3002,7 +3005,7 @@ fn scope_register_it(mut s ast.Scope, pos token.Position, typ table.Type) { | |||
| 	}) | ||||
| } | ||||
| 
 | ||||
| fn scope_register_ab(mut s ast.Scope, pos token.Position, typ table.Type) { | ||||
| fn scope_register_a_b(mut s ast.Scope, pos token.Position, typ table.Type) { | ||||
| 	s.register(ast.Var{ | ||||
| 		name: 'a' | ||||
| 		pos: pos | ||||
|  |  | |||
|  | @ -3,4 +3,25 @@ vlib/v/checker/tests/array_sort_err.vv:3:6: error: expected 0 or 1 argument, but | |||
|     2 |     mut arr := [3, 2, 1] | ||||
|     3 |     arr.sort(a < b, a) | ||||
|       |         ~~~~~~~~~~~~~~ | ||||
|     4 | } | ||||
|     4 |     arr.sort(a == b) | ||||
|     5 |     arr.sort(a > a) | ||||
| vlib/v/checker/tests/array_sort_err.vv:4:9: error: `.sort()` can only use `<` or `>` comparison | ||||
|     2 |     mut arr := [3, 2, 1] | ||||
|     3 |     arr.sort(a < b, a) | ||||
|     4 |     arr.sort(a == b) | ||||
|       |         ~~~~~~~~~~~~ | ||||
|     5 |     arr.sort(a > a) | ||||
|     6 |     arr.sort(c > d) | ||||
| vlib/v/checker/tests/array_sort_err.vv:5:9: error: `.sort()` cannot use same argument | ||||
|     3 |     arr.sort(a < b, a) | ||||
|     4 |     arr.sort(a == b) | ||||
|     5 |     arr.sort(a > a) | ||||
|       |         ~~~~~~~~~~~ | ||||
|     6 |     arr.sort(c > d) | ||||
|     7 | } | ||||
| vlib/v/checker/tests/array_sort_err.vv:6:9: error: `.sort()` can only use `a` or `b` as argument, e.g. `arr.sort(a < b)` | ||||
|     4 |     arr.sort(a == b) | ||||
|     5 |     arr.sort(a > a) | ||||
|     6 |     arr.sort(c > d) | ||||
|       |         ~~~~~~~~~~~ | ||||
|     7 | } | ||||
|  |  | |||
|  | @ -1,4 +1,7 @@ | |||
| fn main() { | ||||
| 	mut arr := [3, 2, 1] | ||||
| 	arr.sort(a < b, a) | ||||
|     arr.sort(a == b) | ||||
|     arr.sort(a > a) | ||||
|     arr.sort(c > d) | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue