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()
|
||||
}
|
||||
}
|
||||
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]()
|
||||
rec_sym := c.table.final_sym(node.left_type)
|
||||
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 {}
|
||||
}
|
||||
mut concrete_types := node.concrete_types.map(c.unwrap_generic(it))
|
||||
if concrete_types.len > 0
|
||||
&& c.table.register_fn_concrete_types(method.fkey(), concrete_types) {
|
||||
if concrete_types.len > 0 && c.table.register_fn_concrete_types(method.fkey(), concrete_types) {
|
||||
c.need_recheck_generic_fns = true
|
||||
}
|
||||
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 {
|
||||
got_arg_typ = c.unwrap_generic(got_arg_typ)
|
||||
} else {
|
||||
if got_utyp := c.table.resolve_generic_to_concrete(got_arg_typ,
|
||||
method.generic_names, method_concrete_types)
|
||||
if got_utyp := c.table.resolve_generic_to_concrete(got_arg_typ, method.generic_names,
|
||||
method_concrete_types)
|
||||
{
|
||||
got_arg_typ = got_utyp
|
||||
} 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]
|
||||
&& !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,
|
||||
param.typ)
|
||||
c.error('cannot use `${got_typ_str}` as `${expected_typ_str}` in argument ${i +
|
||||
1} to `${method_name}`', arg.pos)
|
||||
got_typ_str, expected_typ_str := c.get_string_names_of(got_arg_typ, param.typ)
|
||||
c.error('cannot use `${got_typ_str}` as `${expected_typ_str}` in argument ${i + 1} to `${method_name}`',
|
||||
arg.pos)
|
||||
}
|
||||
continue
|
||||
}
|
||||
@ -2032,119 +2146,6 @@ fn (mut c Checker) method_call(mut node ast.CallExpr) ast.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 {
|
||||
ret_type := c.call_expr(mut node.call_expr)
|
||||
|
Loading…
Reference in New Issue
Block a user