1
0
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:
yuyi 2023-06-14 19:46:00 +08:00 committed by GitHub
parent cb5d5f8ca5
commit 3fb31b971d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

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