checker: minor cleanup in method_call (#12430)

pull/12435/head
yuyi 2021-11-11 13:12:59 +08:00 committed by GitHub
parent 8a971c3bf7
commit 72a7d5a559
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 56 additions and 56 deletions

View File

@ -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())
} }
} }