checker: extract into call_array_builtin_method to cleanup (#9225)
parent
ef73e07e62
commit
f280a5c230
|
@ -1315,89 +1315,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 && method_name in checker.array_builtin_methods {
|
if left_type_sym.kind == .array && method_name in checker.array_builtin_methods {
|
||||||
mut elem_typ := table.void_type
|
return c.call_array_builtin_method(mut call_expr, left_type, left_type_sym)
|
||||||
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 {
|
|
||||||
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)
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// map/filter are supposed to have 1 arg only
|
|
||||||
mut arg_type := left_type
|
|
||||||
for arg in call_expr.args {
|
|
||||||
arg_type = c.expr(arg.expr)
|
|
||||||
}
|
|
||||||
if method_name == 'map' {
|
|
||||||
// check fn
|
|
||||||
c.check_map_and_filter(true, elem_typ, call_expr)
|
|
||||||
arg_sym := c.table.get_type_symbol(arg_type)
|
|
||||||
ret_type := match arg_sym.info {
|
|
||||||
table.FnType { arg_sym.info.func.return_type }
|
|
||||||
else { arg_type }
|
|
||||||
}
|
|
||||||
call_expr.return_type = c.table.find_or_register_array(ret_type)
|
|
||||||
} else if method_name == 'filter' {
|
|
||||||
// check fn
|
|
||||||
c.check_map_and_filter(false, elem_typ, call_expr)
|
|
||||||
} else if method_name == 'clone' {
|
|
||||||
// need to return `array_xxx` instead of `array`
|
|
||||||
// in ['clone', 'str'] {
|
|
||||||
call_expr.receiver_type = left_type.to_ptr()
|
|
||||||
if call_expr.left.is_auto_deref_var() {
|
|
||||||
call_expr.return_type = left_type.deref()
|
|
||||||
} else {
|
|
||||||
call_expr.return_type = call_expr.receiver_type.set_nr_muls(0)
|
|
||||||
}
|
|
||||||
} else if method_name == 'sort' {
|
|
||||||
call_expr.return_type = table.void_type
|
|
||||||
} else if method_name == 'contains' {
|
|
||||||
call_expr.return_type = table.bool_type
|
|
||||||
} else if method_name == 'index' {
|
|
||||||
call_expr.return_type = table.int_type
|
|
||||||
}
|
|
||||||
return call_expr.return_type
|
|
||||||
} else if left_type_sym.kind == .map && method_name in ['clone', 'keys', 'move'] {
|
} else if left_type_sym.kind == .map && method_name in ['clone', 'keys', 'move'] {
|
||||||
mut ret_type := table.void_type
|
mut ret_type := table.void_type
|
||||||
match method_name {
|
match method_name {
|
||||||
|
@ -1696,6 +1614,93 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type {
|
||||||
return table.void_type
|
return table.void_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 {
|
||||||
|
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)
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// map/filter are supposed to have 1 arg only
|
||||||
|
mut arg_type := left_type
|
||||||
|
for arg in call_expr.args {
|
||||||
|
arg_type = c.expr(arg.expr)
|
||||||
|
}
|
||||||
|
if method_name == 'map' {
|
||||||
|
// check fn
|
||||||
|
c.check_map_and_filter(true, elem_typ, call_expr)
|
||||||
|
arg_sym := c.table.get_type_symbol(arg_type)
|
||||||
|
ret_type := match arg_sym.info {
|
||||||
|
table.FnType { arg_sym.info.func.return_type }
|
||||||
|
else { arg_type }
|
||||||
|
}
|
||||||
|
call_expr.return_type = c.table.find_or_register_array(ret_type)
|
||||||
|
} else if method_name == 'filter' {
|
||||||
|
// check fn
|
||||||
|
c.check_map_and_filter(false, elem_typ, call_expr)
|
||||||
|
} else if method_name == 'clone' {
|
||||||
|
// need to return `array_xxx` instead of `array`
|
||||||
|
// in ['clone', 'str'] {
|
||||||
|
call_expr.receiver_type = left_type.to_ptr()
|
||||||
|
if call_expr.left.is_auto_deref_var() {
|
||||||
|
call_expr.return_type = left_type.deref()
|
||||||
|
} else {
|
||||||
|
call_expr.return_type = call_expr.receiver_type.set_nr_muls(0)
|
||||||
|
}
|
||||||
|
} else if method_name == 'sort' {
|
||||||
|
call_expr.return_type = table.void_type
|
||||||
|
} else if method_name == 'contains' {
|
||||||
|
call_expr.return_type = table.bool_type
|
||||||
|
} else if method_name == 'index' {
|
||||||
|
call_expr.return_type = table.int_type
|
||||||
|
}
|
||||||
|
return call_expr.return_type
|
||||||
|
}
|
||||||
|
|
||||||
pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type {
|
pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type {
|
||||||
fn_name := call_expr.name
|
fn_name := call_expr.name
|
||||||
if fn_name == 'main' {
|
if fn_name == 'main' {
|
||||||
|
|
Loading…
Reference in New Issue