mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
checker: clean up method_call() (#18443)
This commit is contained in:
parent
cb5d5f8ca5
commit
3fb31b971d
@ -1669,7 +1669,123 @@ fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
|
|||||||
unknown_method_msg = err.msg()
|
unknown_method_msg = err.msg()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if has_method {
|
|
||||||
|
if !has_method {
|
||||||
|
// TODO: str methods
|
||||||
|
if method_name == 'str' {
|
||||||
|
if left_sym.kind == .interface_ {
|
||||||
|
iname := left_sym.name
|
||||||
|
c.error('interface `${iname}` does not have a .str() method. Use typeof() instead',
|
||||||
|
node.pos)
|
||||||
|
}
|
||||||
|
node.receiver_type = left_type
|
||||||
|
node.return_type = ast.string_type
|
||||||
|
if node.args.len > 0 {
|
||||||
|
c.error('.str() method calls should have no arguments', node.pos)
|
||||||
|
}
|
||||||
|
c.fail_if_unreadable(node.left, left_type, 'receiver')
|
||||||
|
return ast.string_type
|
||||||
|
} else if method_name == 'free' {
|
||||||
|
if !c.is_builtin_mod && !c.inside_unsafe && !method.is_unsafe {
|
||||||
|
c.warn('manual memory management with `free()` is only allowed in unsafe code',
|
||||||
|
node.pos)
|
||||||
|
}
|
||||||
|
return ast.void_type
|
||||||
|
}
|
||||||
|
// call struct field fn type
|
||||||
|
// TODO: can we use SelectorExpr for all? this dosent really belong here
|
||||||
|
if field := c.table.find_field_with_embeds(left_sym, method_name) {
|
||||||
|
if field.typ.has_flag(.option) {
|
||||||
|
c.error('Option function field must be unwrapped first', node.pos)
|
||||||
|
}
|
||||||
|
field_sym := c.table.sym(c.unwrap_generic(field.typ))
|
||||||
|
if field_sym.kind == .function {
|
||||||
|
node.is_method = false
|
||||||
|
node.is_field = true
|
||||||
|
info := field_sym.info as ast.FnType
|
||||||
|
|
||||||
|
c.check_expected_arg_count(mut node, info.func) or { return info.func.return_type }
|
||||||
|
node.return_type = info.func.return_type
|
||||||
|
mut earg_types := []ast.Type{}
|
||||||
|
|
||||||
|
for i, mut arg in node.args {
|
||||||
|
targ := c.check_expr_opt_call(arg.expr, c.expr(arg.expr))
|
||||||
|
arg.typ = targ
|
||||||
|
earg_types << targ
|
||||||
|
|
||||||
|
param := if info.func.is_variadic && i >= info.func.params.len - 1 {
|
||||||
|
info.func.params.last()
|
||||||
|
} else {
|
||||||
|
info.func.params[i]
|
||||||
|
}
|
||||||
|
param_share := param.typ.share()
|
||||||
|
if param_share == .shared_t
|
||||||
|
&& (c.locked_names.len > 0 || c.rlocked_names.len > 0) {
|
||||||
|
c.error('method with `shared` arguments cannot be called inside `lock`/`rlock` block',
|
||||||
|
arg.pos)
|
||||||
|
}
|
||||||
|
if arg.is_mut {
|
||||||
|
to_lock, pos := c.fail_if_immutable(arg.expr)
|
||||||
|
if !param.is_mut {
|
||||||
|
tok := arg.share.str()
|
||||||
|
c.error('`${node.name}` parameter ${i + 1} is not `${tok}`, `${tok}` is not needed`',
|
||||||
|
arg.expr.pos())
|
||||||
|
} else {
|
||||||
|
if param_share != arg.share {
|
||||||
|
c.error('wrong shared type `${arg.share.str()}`, expected: `${param_share.str()}`',
|
||||||
|
arg.expr.pos())
|
||||||
|
}
|
||||||
|
if to_lock != '' && param_share != .shared_t {
|
||||||
|
c.error('${to_lock} is `shared` and must be `lock`ed to be passed as `mut`',
|
||||||
|
pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if param.is_mut {
|
||||||
|
tok := param.specifier()
|
||||||
|
c.error('method `${node.name}` parameter ${i + 1} is `${tok}`, so use `${tok} ${arg.expr}` instead',
|
||||||
|
arg.expr.pos())
|
||||||
|
} else {
|
||||||
|
c.fail_if_unreadable(arg.expr, targ, 'argument')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if i < info.func.params.len {
|
||||||
|
exp_arg_typ := info.func.params[i].typ
|
||||||
|
c.check_expected_call_arg(targ, c.unwrap_generic(exp_arg_typ),
|
||||||
|
node.language, arg) or {
|
||||||
|
if targ != ast.void_type {
|
||||||
|
c.error('${err.msg()} in argument ${i + 1} to `${left_sym.name}.${method_name}`',
|
||||||
|
arg.pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
node.expected_arg_types = earg_types
|
||||||
|
node.is_method = true
|
||||||
|
_, node.from_embed_types = c.table.find_field_from_embeds(left_sym, method_name) or {
|
||||||
|
return info.func.return_type
|
||||||
|
}
|
||||||
|
return info.func.return_type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if left_sym.kind in [.struct_, .aggregate, .interface_, .sum_type] {
|
||||||
|
if c.smartcast_mut_pos != token.Pos{} {
|
||||||
|
c.note('smartcasting requires either an immutable value, or an explicit mut keyword before the value',
|
||||||
|
c.smartcast_mut_pos)
|
||||||
|
}
|
||||||
|
if c.smartcast_cond_pos != token.Pos{} {
|
||||||
|
c.note('smartcast can only be used on the ident or selector, e.g. match foo, match foo.bar',
|
||||||
|
c.smartcast_cond_pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if left_type != ast.void_type {
|
||||||
|
suggestion := util.new_suggestion(method_name, left_sym.methods.map(it.name))
|
||||||
|
c.error(suggestion.say(unknown_method_msg), node.pos)
|
||||||
|
}
|
||||||
|
return ast.void_type
|
||||||
|
}
|
||||||
|
|
||||||
// x is Bar[T], x.foo() -> x.foo[T]()
|
// x is Bar[T], x.foo() -> x.foo[T]()
|
||||||
rec_sym := c.table.final_sym(node.left_type)
|
rec_sym := c.table.final_sym(node.left_type)
|
||||||
rec_is_generic := left_type.has_flag(.generic)
|
rec_is_generic := left_type.has_flag(.generic)
|
||||||
@ -1693,8 +1809,7 @@ fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
|
|||||||
else {}
|
else {}
|
||||||
}
|
}
|
||||||
mut concrete_types := node.concrete_types.map(c.unwrap_generic(it))
|
mut concrete_types := node.concrete_types.map(c.unwrap_generic(it))
|
||||||
if concrete_types.len > 0
|
if concrete_types.len > 0 && c.table.register_fn_concrete_types(method.fkey(), concrete_types) {
|
||||||
&& c.table.register_fn_concrete_types(method.fkey(), concrete_types) {
|
|
||||||
c.need_recheck_generic_fns = true
|
c.need_recheck_generic_fns = true
|
||||||
}
|
}
|
||||||
node.is_noreturn = method.is_noreturn
|
node.is_noreturn = method.is_noreturn
|
||||||
@ -1881,8 +1996,8 @@ fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
|
|||||||
if c.table.cur_fn != unsafe { nil } && c.table.cur_concrete_types.len > 0 {
|
if c.table.cur_fn != unsafe { nil } && c.table.cur_concrete_types.len > 0 {
|
||||||
got_arg_typ = c.unwrap_generic(got_arg_typ)
|
got_arg_typ = c.unwrap_generic(got_arg_typ)
|
||||||
} else {
|
} else {
|
||||||
if got_utyp := c.table.resolve_generic_to_concrete(got_arg_typ,
|
if got_utyp := c.table.resolve_generic_to_concrete(got_arg_typ, method.generic_names,
|
||||||
method.generic_names, method_concrete_types)
|
method_concrete_types)
|
||||||
{
|
{
|
||||||
got_arg_typ = got_utyp
|
got_arg_typ = got_utyp
|
||||||
} else {
|
} else {
|
||||||
@ -1925,10 +2040,9 @@ fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
|
|||||||
|
|
||||||
if got_arg_typ !in [ast.voidptr_type, ast.nil_type]
|
if got_arg_typ !in [ast.voidptr_type, ast.nil_type]
|
||||||
&& !c.check_multiple_ptr_match(got_arg_typ, param.typ, param, arg) {
|
&& !c.check_multiple_ptr_match(got_arg_typ, param.typ, param, arg) {
|
||||||
got_typ_str, expected_typ_str := c.get_string_names_of(got_arg_typ,
|
got_typ_str, expected_typ_str := c.get_string_names_of(got_arg_typ, param.typ)
|
||||||
param.typ)
|
c.error('cannot use `${got_typ_str}` as `${expected_typ_str}` in argument ${i + 1} to `${method_name}`',
|
||||||
c.error('cannot use `${got_typ_str}` as `${expected_typ_str}` in argument ${i +
|
arg.pos)
|
||||||
1} to `${method_name}`', arg.pos)
|
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -2032,119 +2146,6 @@ fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type {
|
|||||||
}
|
}
|
||||||
return node.return_type
|
return node.return_type
|
||||||
}
|
}
|
||||||
// TODO: str methods
|
|
||||||
if method_name == 'str' {
|
|
||||||
if left_sym.kind == .interface_ {
|
|
||||||
iname := left_sym.name
|
|
||||||
c.error('interface `${iname}` does not have a .str() method. Use typeof() instead',
|
|
||||||
node.pos)
|
|
||||||
}
|
|
||||||
node.receiver_type = left_type
|
|
||||||
node.return_type = ast.string_type
|
|
||||||
if node.args.len > 0 {
|
|
||||||
c.error('.str() method calls should have no arguments', node.pos)
|
|
||||||
}
|
|
||||||
c.fail_if_unreadable(node.left, left_type, 'receiver')
|
|
||||||
return ast.string_type
|
|
||||||
} else if method_name == 'free' {
|
|
||||||
if !c.is_builtin_mod && !c.inside_unsafe && !method.is_unsafe {
|
|
||||||
c.warn('manual memory management with `free()` is only allowed in unsafe code',
|
|
||||||
node.pos)
|
|
||||||
}
|
|
||||||
return ast.void_type
|
|
||||||
}
|
|
||||||
// call struct field fn type
|
|
||||||
// TODO: can we use SelectorExpr for all? this dosent really belong here
|
|
||||||
if field := c.table.find_field_with_embeds(left_sym, method_name) {
|
|
||||||
if field.typ.has_flag(.option) {
|
|
||||||
c.error('Option function field must be unwrapped first', node.pos)
|
|
||||||
}
|
|
||||||
field_sym := c.table.sym(c.unwrap_generic(field.typ))
|
|
||||||
if field_sym.kind == .function {
|
|
||||||
node.is_method = false
|
|
||||||
node.is_field = true
|
|
||||||
info := field_sym.info as ast.FnType
|
|
||||||
|
|
||||||
c.check_expected_arg_count(mut node, info.func) or { return info.func.return_type }
|
|
||||||
node.return_type = info.func.return_type
|
|
||||||
mut earg_types := []ast.Type{}
|
|
||||||
|
|
||||||
for i, mut arg in node.args {
|
|
||||||
targ := c.check_expr_opt_call(arg.expr, c.expr(arg.expr))
|
|
||||||
arg.typ = targ
|
|
||||||
earg_types << targ
|
|
||||||
|
|
||||||
param := if info.func.is_variadic && i >= info.func.params.len - 1 {
|
|
||||||
info.func.params.last()
|
|
||||||
} else {
|
|
||||||
info.func.params[i]
|
|
||||||
}
|
|
||||||
param_share := param.typ.share()
|
|
||||||
if param_share == .shared_t && (c.locked_names.len > 0 || c.rlocked_names.len > 0) {
|
|
||||||
c.error('method with `shared` arguments cannot be called inside `lock`/`rlock` block',
|
|
||||||
arg.pos)
|
|
||||||
}
|
|
||||||
if arg.is_mut {
|
|
||||||
to_lock, pos := c.fail_if_immutable(arg.expr)
|
|
||||||
if !param.is_mut {
|
|
||||||
tok := arg.share.str()
|
|
||||||
c.error('`${node.name}` parameter ${i + 1} is not `${tok}`, `${tok}` is not needed`',
|
|
||||||
arg.expr.pos())
|
|
||||||
} else {
|
|
||||||
if param_share != arg.share {
|
|
||||||
c.error('wrong shared type `${arg.share.str()}`, expected: `${param_share.str()}`',
|
|
||||||
arg.expr.pos())
|
|
||||||
}
|
|
||||||
if to_lock != '' && param_share != .shared_t {
|
|
||||||
c.error('${to_lock} is `shared` and must be `lock`ed to be passed as `mut`',
|
|
||||||
pos)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if param.is_mut {
|
|
||||||
tok := param.specifier()
|
|
||||||
c.error('method `${node.name}` parameter ${i + 1} is `${tok}`, so use `${tok} ${arg.expr}` instead',
|
|
||||||
arg.expr.pos())
|
|
||||||
} else {
|
|
||||||
c.fail_if_unreadable(arg.expr, targ, 'argument')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if i < info.func.params.len {
|
|
||||||
exp_arg_typ := info.func.params[i].typ
|
|
||||||
c.check_expected_call_arg(targ, c.unwrap_generic(exp_arg_typ), node.language,
|
|
||||||
arg) or {
|
|
||||||
if targ != ast.void_type {
|
|
||||||
c.error('${err.msg()} in argument ${i + 1} to `${left_sym.name}.${method_name}`',
|
|
||||||
arg.pos)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
node.expected_arg_types = earg_types
|
|
||||||
node.is_method = true
|
|
||||||
_, node.from_embed_types = c.table.find_field_from_embeds(left_sym, method_name) or {
|
|
||||||
return info.func.return_type
|
|
||||||
}
|
|
||||||
return info.func.return_type
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if left_sym.kind in [.struct_, .aggregate, .interface_, .sum_type] {
|
|
||||||
if c.smartcast_mut_pos != token.Pos{} {
|
|
||||||
c.note('smartcasting requires either an immutable value, or an explicit mut keyword before the value',
|
|
||||||
c.smartcast_mut_pos)
|
|
||||||
}
|
|
||||||
if c.smartcast_cond_pos != token.Pos{} {
|
|
||||||
c.note('smartcast can only be used on the ident or selector, e.g. match foo, match foo.bar',
|
|
||||||
c.smartcast_cond_pos)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if left_type != ast.void_type {
|
|
||||||
suggestion := util.new_suggestion(method_name, left_sym.methods.map(it.name))
|
|
||||||
c.error(suggestion.say(unknown_method_msg), node.pos)
|
|
||||||
}
|
|
||||||
return ast.void_type
|
|
||||||
}
|
|
||||||
|
|
||||||
fn (mut c Checker) spawn_expr(mut node ast.SpawnExpr) ast.Type {
|
fn (mut c Checker) spawn_expr(mut node ast.SpawnExpr) ast.Type {
|
||||||
ret_type := c.call_expr(mut node.call_expr)
|
ret_type := c.call_expr(mut node.call_expr)
|
||||||
|
Loading…
Reference in New Issue
Block a user