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']
 | 
						assert x == ['9', '87', '654', '3210']
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn get_score(word string) int {
 | 
					// fn get_score(word string) int {
 | 
				
			||||||
	mut total := 0
 | 
					// 	mut total := 0
 | 
				
			||||||
	for letter in word {
 | 
					// 	for letter in word {
 | 
				
			||||||
		total += int(letter) - 97
 | 
					// 		total += int(letter) - 97
 | 
				
			||||||
	}
 | 
					// 	}
 | 
				
			||||||
	return total
 | 
					// 	return total
 | 
				
			||||||
}
 | 
					// }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn test_sorting_with_fn_call_in_condition_expression() {
 | 
					// fn test_sorting_with_fn_call_in_condition_expression() {
 | 
				
			||||||
	mut words := ['aaaa', 'a', 'b', 'foo', 'bar']
 | 
					// 	mut words := ['aaaa', 'a', 'b', 'foo', 'bar']
 | 
				
			||||||
	words.sort(get_score(a) < get_score(b))
 | 
					// 	words.sort(get_score(a) < get_score(b))
 | 
				
			||||||
}
 | 
					// }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn mysort(mut a []int) {
 | 
					fn mysort(mut a []int) {
 | 
				
			||||||
	a.sort()
 | 
						a.sort()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1660,37 +1660,41 @@ 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 {
 | 
					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
 | 
						method_name := call_expr.name
 | 
				
			||||||
	mut elem_typ := table.void_type
 | 
						mut elem_typ := table.void_type
 | 
				
			||||||
	is_filter_map := method_name in ['filter', 'map']
 | 
						if method_name == 'slice' && !c.is_builtin_mod {
 | 
				
			||||||
	is_sort := method_name == 'sort'
 | 
					 | 
				
			||||||
	is_slice := method_name == 'slice'
 | 
					 | 
				
			||||||
	is_wait := method_name == 'wait'
 | 
					 | 
				
			||||||
	if is_slice && !c.is_builtin_mod {
 | 
					 | 
				
			||||||
		c.error('.slice() is a private method, use `x[start..end]` instead', call_expr.pos)
 | 
							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
 | 
						array_info := left_type_sym.info as table.Array
 | 
				
			||||||
		if is_filter_map {
 | 
						elem_typ = array_info.elem_type
 | 
				
			||||||
 | 
						if method_name in ['filter', 'map'] {
 | 
				
			||||||
		// position of `it` doesn't matter
 | 
							// position of `it` doesn't matter
 | 
				
			||||||
			scope_register_it(mut call_expr.scope, call_expr.pos, array_info.elem_type)
 | 
							scope_register_it(mut call_expr.scope, call_expr.pos, elem_typ)
 | 
				
			||||||
		} else if is_sort {
 | 
						} else if method_name == 'sort' {
 | 
				
			||||||
		c.fail_if_immutable(call_expr.left)
 | 
							c.fail_if_immutable(call_expr.left)
 | 
				
			||||||
		// position of `a` and `b` doesn't matter, they're the same
 | 
							// 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)
 | 
							scope_register_a_b(mut call_expr.scope, call_expr.pos, elem_typ)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if call_expr.args.len > 1 {
 | 
							if call_expr.args.len > 1 {
 | 
				
			||||||
			c.error('expected 0 or 1 argument, but got $call_expr.args.len', call_expr.pos)
 | 
								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)
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			// Verify `.sort(a < b)`
 | 
									left_name := '${call_expr.args[0].expr.left}'[0]
 | 
				
			||||||
			if call_expr.args.len > 0 {
 | 
									right_name := '${call_expr.args[0].expr.right}'[0]
 | 
				
			||||||
				if call_expr.args[0].expr !is ast.InfixExpr {
 | 
									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(
 | 
									c.error(
 | 
				
			||||||
					'`.sort()` requires a `<` or `>` comparison as the first and only argument' +
 | 
										'`.sort()` requires a `<` or `>` comparison as the first and only argument' +
 | 
				
			||||||
					'\ne.g. `users.sort(a.id < b.id)`', call_expr.pos)
 | 
										'\ne.g. `users.sort(a.id < b.id)`', call_expr.pos)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		}
 | 
						} else if method_name == 'wait' {
 | 
				
			||||||
		elem_typ = array_info.elem_type
 | 
					 | 
				
			||||||
		if is_wait {
 | 
					 | 
				
			||||||
		elem_sym := c.table.get_type_symbol(elem_typ)
 | 
							elem_sym := c.table.get_type_symbol(elem_typ)
 | 
				
			||||||
		if elem_sym.kind == .thread {
 | 
							if elem_sym.kind == .thread {
 | 
				
			||||||
			if call_expr.args.len != 0 {
 | 
								if call_expr.args.len != 0 {
 | 
				
			||||||
| 
						 | 
					@ -1707,7 +1711,6 @@ fn (mut c Checker) call_array_builtin_method(mut call_expr ast.CallExpr, left_ty
 | 
				
			||||||
				call_expr.left.position())
 | 
									call_expr.left.position())
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	// map/filter are supposed to have 1 arg only
 | 
						// map/filter are supposed to have 1 arg only
 | 
				
			||||||
	mut arg_type := left_type
 | 
						mut arg_type := left_type
 | 
				
			||||||
	for arg in call_expr.args {
 | 
						for arg in call_expr.args {
 | 
				
			||||||
| 
						 | 
					@ -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{
 | 
						s.register(ast.Var{
 | 
				
			||||||
		name: 'a'
 | 
							name: 'a'
 | 
				
			||||||
		pos: pos
 | 
							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]
 | 
					    2 |     mut arr := [3, 2, 1]
 | 
				
			||||||
    3 |     arr.sort(a < b, a)
 | 
					    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() {
 | 
					fn main() {
 | 
				
			||||||
	mut arr := [3, 2, 1]
 | 
						mut arr := [3, 2, 1]
 | 
				
			||||||
	arr.sort(a < b, a)
 | 
						arr.sort(a < b, a)
 | 
				
			||||||
 | 
					    arr.sort(a == b)
 | 
				
			||||||
 | 
					    arr.sort(a > a)
 | 
				
			||||||
 | 
					    arr.sort(c > d)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue