checker: minor cleanup in method_call (#12430)
parent
8a971c3bf7
commit
72a7d5a559
|
@ -1919,18 +1919,22 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
|
||||||
// or there will be hard to diagnose 0 type panics in cgen.
|
// or there will be hard to diagnose 0 type panics in cgen.
|
||||||
node.return_type = left_type
|
node.return_type = left_type
|
||||||
node.receiver_type = left_type
|
node.receiver_type = left_type
|
||||||
left_type_sym := c.table.get_type_symbol(c.unwrap_generic(left_type))
|
|
||||||
|
unwrapped_left_type := c.unwrap_generic(left_type)
|
||||||
|
left_sym := c.table.get_type_symbol(unwrapped_left_type)
|
||||||
|
final_left_sym := c.table.get_final_type_symbol(unwrapped_left_type)
|
||||||
|
|
||||||
method_name := node.name
|
method_name := node.name
|
||||||
mut unknown_method_msg := if field := c.table.find_field(left_type_sym, method_name) {
|
mut unknown_method_msg := if field := c.table.find_field(left_sym, method_name) {
|
||||||
'unknown method `$field.name` did you mean to access the field with the same name instead?'
|
'unknown method `$field.name` did you mean to access the field with the same name instead?'
|
||||||
} else {
|
} else {
|
||||||
'unknown method or field: `${left_type_sym.name}.$method_name`'
|
'unknown method or field: `${left_sym.name}.$method_name`'
|
||||||
}
|
}
|
||||||
if left_type.has_flag(.optional) {
|
if left_type.has_flag(.optional) {
|
||||||
c.error('optional type cannot be called directly', node.left.position())
|
c.error('optional type cannot be called directly', node.left.position())
|
||||||
return ast.void_type
|
return ast.void_type
|
||||||
}
|
}
|
||||||
if left_type_sym.kind in [.sum_type, .interface_] {
|
if left_sym.kind in [.sum_type, .interface_] {
|
||||||
if method_name == 'type_name' {
|
if method_name == 'type_name' {
|
||||||
return ast.string_type
|
return ast.string_type
|
||||||
}
|
}
|
||||||
|
@ -1961,18 +1965,17 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.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_sym.kind == .array && method_name in checker.array_builtin_methods {
|
||||||
return c.array_builtin_method_call(mut node, left_type, left_type_sym)
|
return c.array_builtin_method_call(mut node, left_type, left_sym)
|
||||||
} else if (left_type_sym.kind == .map || c.table.get_final_type_symbol(left_type).kind == .map)
|
} else if (left_sym.kind == .map || final_left_sym.kind == .map)
|
||||||
&& method_name in ['clone', 'keys', 'move', 'delete'] {
|
&& method_name in ['clone', 'keys', 'move', 'delete'] {
|
||||||
if left_type_sym.kind == .map {
|
if left_sym.kind == .map {
|
||||||
return c.map_builtin_method_call(mut node, left_type, left_type_sym)
|
return c.map_builtin_method_call(mut node, left_type, left_sym)
|
||||||
} else {
|
} else {
|
||||||
parent_type := (left_type_sym.info as ast.Alias).parent_type
|
parent_type := (left_sym.info as ast.Alias).parent_type
|
||||||
parent_sym := c.table.get_type_symbol(parent_type)
|
return c.map_builtin_method_call(mut node, parent_type, final_left_sym)
|
||||||
return c.map_builtin_method_call(mut node, parent_type, parent_sym)
|
|
||||||
}
|
}
|
||||||
} else if left_type_sym.kind == .array && method_name in ['insert', 'prepend'] {
|
} else if left_sym.kind == .array && method_name in ['insert', 'prepend'] {
|
||||||
if method_name == 'insert' {
|
if method_name == 'insert' {
|
||||||
if node.args.len != 2 {
|
if node.args.len != 2 {
|
||||||
c.error('`array.insert()` should have 2 arguments, e.g. `insert(1, val)`',
|
c.error('`array.insert()` should have 2 arguments, e.g. `insert(1, val)`',
|
||||||
|
@ -1993,28 +1996,26 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
|
||||||
return ast.void_type
|
return ast.void_type
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
info := left_type_sym.info as ast.Array
|
info := left_sym.info as ast.Array
|
||||||
arg_expr := if method_name == 'insert' { node.args[1].expr } else { node.args[0].expr }
|
arg_expr := if method_name == 'insert' { node.args[1].expr } else { node.args[0].expr }
|
||||||
arg_type := c.expr(arg_expr)
|
arg_type := c.expr(arg_expr)
|
||||||
arg_sym := c.table.get_type_symbol(arg_type)
|
arg_sym := c.table.get_type_symbol(arg_type)
|
||||||
if !c.check_types(arg_type, info.elem_type) && !c.check_types(left_type, arg_type) {
|
if !c.check_types(arg_type, info.elem_type) && !c.check_types(left_type, arg_type) {
|
||||||
c.error('cannot $method_name `$arg_sym.name` to `$left_type_sym.name`', arg_expr.position())
|
c.error('cannot $method_name `$arg_sym.name` to `$left_sym.name`', arg_expr.position())
|
||||||
}
|
}
|
||||||
} else if c.table.get_final_type_symbol(left_type).kind == .array
|
} else if final_left_sym.kind == .array && method_name in ['first', 'last', 'pop'] {
|
||||||
&& method_name in ['first', 'last', 'pop'] {
|
if final_left_sym.info is ast.Array {
|
||||||
info := c.table.get_final_type_symbol(left_type).info
|
node.return_type = final_left_sym.info.elem_type
|
||||||
if info is ast.Array {
|
return node.return_type
|
||||||
node.return_type = info.elem_type
|
|
||||||
return info.elem_type
|
|
||||||
}
|
}
|
||||||
} else if left_type_sym.kind == .thread && method_name == 'wait' {
|
} else if left_sym.kind == .thread && method_name == 'wait' {
|
||||||
info := left_type_sym.info as ast.Thread
|
info := left_sym.info as ast.Thread
|
||||||
if node.args.len > 0 {
|
if node.args.len > 0 {
|
||||||
c.error('wait() does not have any arguments', node.args[0].pos)
|
c.error('wait() does not have any arguments', node.args[0].pos)
|
||||||
}
|
}
|
||||||
node.return_type = info.return_type
|
node.return_type = info.return_type
|
||||||
return info.return_type
|
return info.return_type
|
||||||
} else if left_type_sym.kind == .char && left_type.nr_muls() == 0 && method_name == 'str' {
|
} else if left_sym.kind == .char && left_type.nr_muls() == 0 && method_name == 'str' {
|
||||||
c.error('calling `.str()` on type `char` is not allowed, use its address or cast it to an integer instead',
|
c.error('calling `.str()` on type `char` is not allowed, use its address or cast it to an integer instead',
|
||||||
node.left.position().extend(node.pos))
|
node.left.position().extend(node.pos))
|
||||||
return ast.void_type
|
return ast.void_type
|
||||||
|
@ -2022,18 +2023,18 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
|
||||||
mut method := ast.Fn{}
|
mut method := ast.Fn{}
|
||||||
mut has_method := false
|
mut has_method := false
|
||||||
mut is_method_from_embed := false
|
mut is_method_from_embed := false
|
||||||
if m := c.table.type_find_method(left_type_sym, method_name) {
|
if m := c.table.type_find_method(left_sym, method_name) {
|
||||||
method = m
|
method = m
|
||||||
has_method = true
|
has_method = true
|
||||||
} else {
|
} else {
|
||||||
if left_type_sym.kind in [.struct_, .sum_type, .interface_] {
|
if left_sym.kind in [.struct_, .sum_type, .interface_] {
|
||||||
mut parent_type := ast.void_type
|
mut parent_type := ast.void_type
|
||||||
if left_type_sym.info is ast.Struct {
|
if left_sym.info is ast.Struct {
|
||||||
parent_type = left_type_sym.info.parent_type
|
parent_type = left_sym.info.parent_type
|
||||||
} else if left_type_sym.info is ast.SumType {
|
} else if left_sym.info is ast.SumType {
|
||||||
parent_type = left_type_sym.info.parent_type
|
parent_type = left_sym.info.parent_type
|
||||||
} else if left_type_sym.info is ast.Interface {
|
} else if left_sym.info is ast.Interface {
|
||||||
parent_type = left_type_sym.info.parent_type
|
parent_type = left_sym.info.parent_type
|
||||||
}
|
}
|
||||||
if parent_type != 0 {
|
if parent_type != 0 {
|
||||||
type_sym := c.table.get_type_symbol(parent_type)
|
type_sym := c.table.get_type_symbol(parent_type)
|
||||||
|
@ -2047,7 +2048,7 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
|
||||||
if !has_method {
|
if !has_method {
|
||||||
has_method = true
|
has_method = true
|
||||||
mut embed_type := ast.Type(0)
|
mut embed_type := ast.Type(0)
|
||||||
method, embed_type = c.table.type_find_method_from_embeds(left_type_sym, method_name) or {
|
method, embed_type = c.table.type_find_method_from_embeds(left_sym, method_name) or {
|
||||||
if err.msg != '' {
|
if err.msg != '' {
|
||||||
c.error(err.msg, node.pos)
|
c.error(err.msg, node.pos)
|
||||||
}
|
}
|
||||||
|
@ -2059,7 +2060,7 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
|
||||||
node.from_embed_type = embed_type
|
node.from_embed_type = embed_type
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if left_type_sym.kind == .aggregate {
|
if left_sym.kind == .aggregate {
|
||||||
// the error message contains the problematic type
|
// the error message contains the problematic type
|
||||||
unknown_method_msg = err.msg
|
unknown_method_msg = err.msg
|
||||||
}
|
}
|
||||||
|
@ -2070,7 +2071,7 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
|
||||||
// If a private method is called outside of the module
|
// If a private method is called outside of the module
|
||||||
// its receiver type is defined in, show an error.
|
// its receiver type is defined in, show an error.
|
||||||
// println('warn $method_name lef.mod=$left_type_sym.mod c.mod=$c.mod')
|
// println('warn $method_name lef.mod=$left_type_sym.mod c.mod=$c.mod')
|
||||||
c.error('method `${left_type_sym.name}.$method_name` is private', node.pos)
|
c.error('method `${left_sym.name}.$method_name` is private', node.pos)
|
||||||
}
|
}
|
||||||
rec_share := method.params[0].typ.share()
|
rec_share := method.params[0].typ.share()
|
||||||
if rec_share == .shared_t && (c.locked_names.len > 0 || c.rlocked_names.len > 0) {
|
if rec_share == .shared_t && (c.locked_names.len > 0 || c.rlocked_names.len > 0) {
|
||||||
|
@ -2090,7 +2091,7 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
|
||||||
} else {
|
} else {
|
||||||
c.fail_if_unreadable(node.left, left_type, 'receiver')
|
c.fail_if_unreadable(node.left, left_type, 'receiver')
|
||||||
}
|
}
|
||||||
if left_type_sym.language != .js && (!left_type_sym.is_builtin() && method.mod != 'builtin')
|
if left_sym.language != .js && (!left_sym.is_builtin() && method.mod != 'builtin')
|
||||||
&& method.language == .v && method.no_body {
|
&& method.language == .v && method.no_body {
|
||||||
c.error('cannot call a method that does not have a body', node.pos)
|
c.error('cannot call a method that does not have a body', node.pos)
|
||||||
}
|
}
|
||||||
|
@ -2101,8 +2102,8 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
|
||||||
mut exp_arg_typ := ast.Type(0) // type of 1st arg for special builtin methods
|
mut exp_arg_typ := ast.Type(0) // type of 1st arg for special builtin methods
|
||||||
mut param_is_mut := false
|
mut param_is_mut := false
|
||||||
mut no_type_promotion := false
|
mut no_type_promotion := false
|
||||||
if left_type_sym.kind == .chan {
|
if left_sym.kind == .chan {
|
||||||
elem_typ := (left_type_sym.info as ast.Chan).elem_type
|
elem_typ := (left_sym.info as ast.Chan).elem_type
|
||||||
if method_name == 'try_push' {
|
if method_name == 'try_push' {
|
||||||
exp_arg_typ = elem_typ.ref()
|
exp_arg_typ = elem_typ.ref()
|
||||||
} else if method_name == 'try_pop' {
|
} else if method_name == 'try_pop' {
|
||||||
|
@ -2167,7 +2168,7 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
|
||||||
// continue
|
// continue
|
||||||
// }
|
// }
|
||||||
if got_arg_typ != ast.void_type {
|
if got_arg_typ != ast.void_type {
|
||||||
c.error('$err.msg in argument ${i + 1} to `${left_type_sym.name}.$method_name`',
|
c.error('$err.msg in argument ${i + 1} to `${left_sym.name}.$method_name`',
|
||||||
arg.pos)
|
arg.pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2208,12 +2209,11 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if method.is_unsafe && !c.inside_unsafe {
|
if method.is_unsafe && !c.inside_unsafe {
|
||||||
c.warn('method `${left_type_sym.name}.$method_name` must be called from an `unsafe` block',
|
c.warn('method `${left_sym.name}.$method_name` must be called from an `unsafe` block',
|
||||||
node.pos)
|
node.pos)
|
||||||
}
|
}
|
||||||
if !c.table.cur_fn.is_deprecated && method.is_deprecated {
|
if !c.table.cur_fn.is_deprecated && method.is_deprecated {
|
||||||
c.deprecate_fnmethod('method', '${left_type_sym.name}.$method.name', method,
|
c.deprecate_fnmethod('method', '${left_sym.name}.$method.name', method, node)
|
||||||
node)
|
|
||||||
}
|
}
|
||||||
// TODO: typ optimize.. this node can get processed more than once
|
// TODO: typ optimize.. this node can get processed more than once
|
||||||
if node.expected_arg_types.len == 0 {
|
if node.expected_arg_types.len == 0 {
|
||||||
|
@ -2266,8 +2266,8 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
|
||||||
}
|
}
|
||||||
// TODO: str methods
|
// TODO: str methods
|
||||||
if method_name == 'str' {
|
if method_name == 'str' {
|
||||||
if left_type_sym.kind == .interface_ {
|
if left_sym.kind == .interface_ {
|
||||||
iname := left_type_sym.name
|
iname := left_sym.name
|
||||||
c.error('interface `$iname` does not have a .str() method. Use typeof() instead',
|
c.error('interface `$iname` does not have a .str() method. Use typeof() instead',
|
||||||
node.pos)
|
node.pos)
|
||||||
}
|
}
|
||||||
|
@ -2283,12 +2283,12 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
|
||||||
}
|
}
|
||||||
// call struct field fn type
|
// call struct field fn type
|
||||||
// TODO: can we use SelectorExpr for all? this dosent really belong here
|
// TODO: can we use SelectorExpr for all? this dosent really belong here
|
||||||
if field := c.table.find_field(left_type_sym, method_name) {
|
if field := c.table.find_field(left_sym, method_name) {
|
||||||
field_type_sym := c.table.get_type_symbol(c.unwrap_generic(field.typ))
|
field_sym := c.table.get_type_symbol(c.unwrap_generic(field.typ))
|
||||||
if field_type_sym.kind == .function {
|
if field_sym.kind == .function {
|
||||||
node.is_method = false
|
node.is_method = false
|
||||||
node.is_field = true
|
node.is_field = true
|
||||||
info := field_type_sym.info as ast.FnType
|
info := field_sym.info as ast.FnType
|
||||||
|
|
||||||
c.check_expected_arg_count(mut node, info.func) or { return info.func.return_type }
|
c.check_expected_arg_count(mut node, info.func) or { return info.func.return_type }
|
||||||
node.return_type = info.func.return_type
|
node.return_type = info.func.return_type
|
||||||
|
@ -2303,7 +2303,7 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
|
||||||
exp_arg_typ := info.func.params[i].typ
|
exp_arg_typ := info.func.params[i].typ
|
||||||
c.check_expected_call_arg(targ, c.unwrap_generic(exp_arg_typ), node.language) or {
|
c.check_expected_call_arg(targ, c.unwrap_generic(exp_arg_typ), node.language) or {
|
||||||
if targ != ast.void_type {
|
if targ != ast.void_type {
|
||||||
c.error('$err.msg in argument ${i + 1} to `${left_type_sym.name}.$method_name`',
|
c.error('$err.msg in argument ${i + 1} to `${left_sym.name}.$method_name`',
|
||||||
arg.pos)
|
arg.pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2315,13 +2315,13 @@ pub fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if left_type != ast.void_type {
|
if left_type != ast.void_type {
|
||||||
suggestion := util.new_suggestion(method_name, left_type_sym.methods.map(it.name))
|
suggestion := util.new_suggestion(method_name, left_sym.methods.map(it.name))
|
||||||
c.error(suggestion.say(unknown_method_msg), node.pos)
|
c.error(suggestion.say(unknown_method_msg), node.pos)
|
||||||
}
|
}
|
||||||
return ast.void_type
|
return ast.void_type
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut c Checker) map_builtin_method_call(mut node ast.CallExpr, left_type ast.Type, left_type_sym ast.TypeSymbol) ast.Type {
|
fn (mut c Checker) map_builtin_method_call(mut node ast.CallExpr, left_type ast.Type, left_sym ast.TypeSymbol) ast.Type {
|
||||||
method_name := node.name
|
method_name := node.name
|
||||||
mut ret_type := ast.void_type
|
mut ret_type := ast.void_type
|
||||||
match method_name {
|
match method_name {
|
||||||
|
@ -2336,7 +2336,7 @@ fn (mut c Checker) map_builtin_method_call(mut node ast.CallExpr, left_type ast.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
'keys' {
|
'keys' {
|
||||||
info := left_type_sym.info as ast.Map
|
info := left_sym.info as ast.Map
|
||||||
typ := c.table.find_or_register_array(info.key_type)
|
typ := c.table.find_or_register_array(info.key_type)
|
||||||
ret_type = ast.Type(typ)
|
ret_type = ast.Type(typ)
|
||||||
}
|
}
|
||||||
|
@ -2345,7 +2345,7 @@ fn (mut c Checker) map_builtin_method_call(mut node ast.CallExpr, left_type ast.
|
||||||
if node.args.len != 1 {
|
if node.args.len != 1 {
|
||||||
c.error('expected 1 argument, but got $node.args.len', node.pos)
|
c.error('expected 1 argument, but got $node.args.len', node.pos)
|
||||||
}
|
}
|
||||||
info := left_type_sym.info as ast.Map
|
info := left_sym.info as ast.Map
|
||||||
arg_type := c.expr(node.args[0].expr)
|
arg_type := c.expr(node.args[0].expr)
|
||||||
c.check_expected_call_arg(arg_type, info.key_type, node.language) or {
|
c.check_expected_call_arg(arg_type, info.key_type, node.language) or {
|
||||||
c.error('$err.msg in argument 1 to `Map.delete`', node.args[0].pos)
|
c.error('$err.msg in argument 1 to `Map.delete`', node.args[0].pos)
|
||||||
|
@ -2358,13 +2358,13 @@ fn (mut c Checker) map_builtin_method_call(mut node ast.CallExpr, left_type ast.
|
||||||
return node.return_type
|
return node.return_type
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut c Checker) array_builtin_method_call(mut node ast.CallExpr, left_type ast.Type, left_type_sym ast.TypeSymbol) ast.Type {
|
fn (mut c Checker) array_builtin_method_call(mut node ast.CallExpr, left_type ast.Type, left_sym ast.TypeSymbol) ast.Type {
|
||||||
method_name := node.name
|
method_name := node.name
|
||||||
mut elem_typ := ast.void_type
|
mut elem_typ := ast.void_type
|
||||||
if method_name == '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', node.pos)
|
c.error('.slice() is a private method, use `x[start..end]` instead', node.pos)
|
||||||
}
|
}
|
||||||
array_info := left_type_sym.info as ast.Array
|
array_info := left_sym.info as ast.Array
|
||||||
elem_typ = array_info.elem_type
|
elem_typ = array_info.elem_type
|
||||||
if method_name in ['filter', 'map', 'any', 'all'] {
|
if method_name in ['filter', 'map', 'any', 'all'] {
|
||||||
// position of `it` doesn't matter
|
// position of `it` doesn't matter
|
||||||
|
@ -2425,7 +2425,7 @@ fn (mut c Checker) array_builtin_method_call(mut node ast.CallExpr, left_type as
|
||||||
}
|
}
|
||||||
node.return_type = c.table.find_or_register_array(thread_ret_type)
|
node.return_type = c.table.find_or_register_array(thread_ret_type)
|
||||||
} else {
|
} else {
|
||||||
c.error('`$left_type_sym.name` has no method `wait()` (only thread handles and arrays of them have)',
|
c.error('`$left_sym.name` has no method `wait()` (only thread handles and arrays of them have)',
|
||||||
node.left.position())
|
node.left.position())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue