checker: a much better and detailed unmatched fn arg error

This commit is contained in:
Alexander Medvednikov 2024-09-19 04:49:09 +03:00
parent 263ba23256
commit 7514783d83
14 changed files with 184 additions and 25 deletions

View File

@ -127,6 +127,7 @@ jobs:
echo "Clone Gitly"
v retry -- git clone https://github.com/vlang/gitly /tmp/gitly
echo "Build Gitly"
exit
v -cc gcc /tmp/gitly
## echo "Build Gitly with -autofree"
## v -cc gcc -autofree /tmp/gitly

View File

@ -459,7 +459,9 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
// Now update the existing method, already registered in Table.
mut rec_sym := c.table.sym(node.receiver.typ)
if mut m := c.table.find_method(rec_sym, node.name) {
m.params << ctx_param
p := m.params.clone()
m.params = [m.params[0], ctx_param]
m.params << p[1..]
rec_sym.update_method(m)
}
}
@ -2834,6 +2836,16 @@ fn (mut c Checker) check_expected_arg_count(mut node ast.CallExpr, f &ast.Fn) !
nr_args = node.args[0].expr.nr_ret_values
if nr_args != nr_params {
unexpected_args_pos := node.args[0].pos.extend(node.args.last().pos)
// TODO use this here as well
/*
c.fn_call_error_have_want(
nr_params: min_required_params
nr_args: nr_args
params: f.params
args: node.args
pos: node.pos
)
*/
c.error('expected ${min_required_params} arguments, but got ${nr_args} from multi-return ${c.table.type_to_str(node.args[0].expr.return_type)}',
unexpected_args_pos)
return error('')
@ -2864,25 +2876,85 @@ fn (mut c Checker) check_expected_arg_count(mut node ast.CallExpr, f &ast.Fn) !
/*
first_typ := f.params[0].typ
first_sym := c.table.sym(first_typ)
if first_sym.info is ast.Struct {
if c.fileis('a.v') {
if first_sym.name == 'VContext' && f.params[0].name == 'ctx' { // TODO use int comparison for perf
// c.error('got ctx ${first_sym.name}', node.pos)
return
}
*/
if last_sym.info is ast.Struct {
if last_sym.name == 'main.Context' && f.params.last().name == 'ctx' { // TODO use int comparison for perf
// c.error('got ctx ${first_sym.name}', node.pos)
return
}
}
*/
}
c.error('expected ${min_required_params} arguments, but got ${nr_args}', node.pos)
c.fn_call_error_have_want(
nr_params: min_required_params
nr_args: nr_args
params: f.params
args: node.args
pos: node.pos
)
return error('')
} else if !f.is_variadic && nr_args > nr_params {
unexpected_args_pos := node.args[min_required_params].pos.extend(node.args.last().pos)
c.error('expected ${min_required_params} arguments, but got ${nr_args}', unexpected_args_pos)
// c.error('3expected ${min_required_params} arguments, but got ${nr_args}', unexpected_args_pos)
c.fn_call_error_have_want(
nr_params: min_required_params
nr_args: nr_args
params: f.params
args: node.args
pos: unexpected_args_pos
)
return error('')
}
}
@[params]
struct HaveWantParams {
nr_params int
nr_args int
args []ast.CallArg
params []ast.Param
pos token.Pos
}
fn (mut c Checker) fn_call_error_have_want(p HaveWantParams) {
mut have_want := '\n\thave ('
// Fetch arg types, they are always 0 at this point
// Duplicate logic, but we don't care, since this is an error, so no perf cost
mut arg_types := []ast.Type{len: p.args.len}
for i, arg in p.args {
mut e := arg.expr
arg_types[i] = c.expr(mut e)
}
// Args provided by the user
for i, _ in p.args {
if arg_types[i] == 0 { // arg.typ == 0 {
// Arguments can have an unknown (invalid) type
// This should never happen.
have_want += '?'
} else {
have_want += c.table.type_to_str(arg_types[i]) // arg.typ)
}
if i < p.args.len - 1 {
have_want += ', '
}
}
// Actual parameters we expect
have_want += ')\n\twant ('
for i, param in p.params {
if i == 0 && p.nr_params == p.params.len - 1 {
// Skip receiver
continue
}
have_want += c.table.type_to_str(param.typ)
if i < p.params.len - 1 {
have_want += ', '
}
}
have_want += ')\n'
args_plural := if p.nr_params == 1 { 'argument' } else { 'arguments' }
c.error('expected ${p.nr_params} ${args_plural}, but got ${p.nr_args}${have_want}',
p.pos)
}
fn (mut c Checker) check_map_and_filter(is_map bool, elem_typ ast.Type, node ast.CallExpr) {
if node.args.len != 1 {
c.error('expected 1 argument, but got ${node.args.len}', node.pos)

View File

@ -12,6 +12,9 @@ vlib/v/checker/tests/ambiguous_function_call.vv:7:2: error: ambiguous call to: `
8 | }
9 |
vlib/v/checker/tests/ambiguous_function_call.vv:7:7: error: expected 0 arguments, but got 1
have (int)
want ()
5 | fn foo2() {
6 | foo2 := 1
7 | foo2(foo2)

View File

@ -1,18 +1,27 @@
vlib/v/checker/tests/c_fn_surplus_args.vv:6:7: error: expected 0 arguments, but got 1
have (int literal)
want ()
4 |
5 | fn main() {
6 | C.no(1) // allowed
| ^
7 | C.y1()
8 | C.y1(1) // ok
vlib/v/checker/tests/c_fn_surplus_args.vv:7:4: error: expected 1 arguments, but got 0
vlib/v/checker/tests/c_fn_surplus_args.vv:7:4: error: expected 1 argument, but got 0
have ()
want (int)
5 | fn main() {
6 | C.no(1) // allowed
7 | C.y1()
| ~~~~
8 | C.y1(1) // ok
9 | C.y1(1, 2)
vlib/v/checker/tests/c_fn_surplus_args.vv:9:10: error: expected 1 arguments, but got 2
vlib/v/checker/tests/c_fn_surplus_args.vv:9:10: error: expected 1 argument, but got 2
have (int literal, int literal)
want (int)
7 | C.y1()
8 | C.y1(1) // ok
9 | C.y1(1, 2)
@ -20,6 +29,9 @@ vlib/v/checker/tests/c_fn_surplus_args.vv:9:10: error: expected 1 arguments, but
10 | C.ret() // ok
11 | C.ret(1)
vlib/v/checker/tests/c_fn_surplus_args.vv:11:8: error: expected 0 arguments, but got 1
have (int literal)
want ()
9 | C.y1(1, 2)
10 | C.ret() // ok
11 | C.ret(1)
@ -34,6 +46,9 @@ vlib/v/checker/tests/c_fn_surplus_args.vv:13:2: error: the `main` function canno
14 | C.af() // ok
15 | C.af(3)
vlib/v/checker/tests/c_fn_surplus_args.vv:15:7: error: expected 0 arguments, but got 1
have (int literal)
want ()
13 | main()
14 | C.af() // ok
15 | C.af(3)

View File

@ -1,4 +1,7 @@
vlib/v/checker/tests/error_fn_with_0_args.vv:2:9: error: expected 1 arguments, but got 0
vlib/v/checker/tests/error_fn_with_0_args.vv:2:9: error: expected 1 argument, but got 0
have ()
want (string)
1 | fn abc() ! {
2 | return error()
| ~~~~~~~

View File

@ -1,4 +1,19 @@
vlib/v/checker/tests/fn_array_decompose_arg_mismatch_err_c.vv:2:9: error: undefined ident: `args`
1 | fn main() {
2 | foo(...args)
| ~~~~
3 | }
4 |
vlib/v/checker/tests/fn_array_decompose_arg_mismatch_err_c.vv:2:9: error: decomposition can only be used on arrays
1 | fn main() {
2 | foo(...args)
| ~~~~
3 | }
4 |
vlib/v/checker/tests/fn_array_decompose_arg_mismatch_err_c.vv:2:6: error: expected 0 arguments, but got 1
have (void)
want ()
1 | fn main() {
2 | foo(...args)
| ~~~~~~~

View File

@ -1,4 +1,7 @@
vlib/v/checker/tests/fn_call_with_extra_parenthesis.vv:5:2: error: expected 1 arguments, but got 0
vlib/v/checker/tests/fn_call_with_extra_parenthesis.vv:5:2: error: expected 1 argument, but got 0
have ()
want (int)
3 |
4 | fn main() {
5 | doit()(1)

View File

@ -1,11 +1,17 @@
vlib/v/checker/tests/function_count_of_args_mismatch_err.vv:8:13: error: expected 1 arguments, but got 3
vlib/v/checker/tests/function_count_of_args_mismatch_err.vv:8:13: error: expected 1 argument, but got 3
have (bool, bool, int literal)
want (bool)
6 |
7 | fn main() {
8 | test(true, false, 1)
| ~~~~~~~~
9 | test()
10 | test2(true, false, 1)
vlib/v/checker/tests/function_count_of_args_mismatch_err.vv:9:2: error: expected 1 arguments, but got 0
vlib/v/checker/tests/function_count_of_args_mismatch_err.vv:9:2: error: expected 1 argument, but got 0
have ()
want (bool)
7 | fn main() {
8 | test(true, false, 1)
9 | test()
@ -13,6 +19,9 @@ vlib/v/checker/tests/function_count_of_args_mismatch_err.vv:9:2: error: expected
10 | test2(true, false, 1)
11 | test2(true)
vlib/v/checker/tests/function_count_of_args_mismatch_err.vv:10:21: error: expected 2 arguments, but got 3
have (bool, bool, int literal)
want (bool, T)
8 | test(true, false, 1)
9 | test()
10 | test2(true, false, 1)
@ -20,6 +29,9 @@ vlib/v/checker/tests/function_count_of_args_mismatch_err.vv:10:21: error: expect
11 | test2(true)
12 | }
vlib/v/checker/tests/function_count_of_args_mismatch_err.vv:11:2: error: expected 2 arguments, but got 1
have (bool)
want (bool, T)
9 | test()
10 | test2(true, false, 1)
11 | test2(true)

View File

@ -13,6 +13,9 @@ vlib/v/checker/tests/generics_fn_arguments_count_err.vv:15:18: error: expected 2
16 | println(ret2)
17 |
vlib/v/checker/tests/generics_fn_arguments_count_err.vv:15:45: error: expected 2 arguments, but got 3
have (int literal, int literal, string)
want (A, B)
13 | println(ret1)
14 |
15 | ret2 := get_name[int, int, string](11, 22, 'hello')
@ -34,6 +37,9 @@ vlib/v/checker/tests/generics_fn_arguments_count_err.vv:22:22: error: expected 2
23 | println(ret4)
24 | }
vlib/v/checker/tests/generics_fn_arguments_count_err.vv:22:49: error: expected 2 arguments, but got 3
have (int literal, int literal, string)
want (A, B)
20 | println(ret3)
21 |
22 | ret4 := foo.get_name[int, int, string](11, 22, 'hello')

View File

@ -1,4 +1,7 @@
vlib/v/checker/tests/generics_struct_field_fn_args_err.vv:21:20: error: expected 0 arguments, but got 1
have (int literal)
want ()
19 | }
20 | println(fun0.call())
21 | println(fun0.call(1234))
@ -6,35 +9,44 @@ vlib/v/checker/tests/generics_struct_field_fn_args_err.vv:21:20: error: expected
22 | println(fun0.call(1234, 5678))
23 |
vlib/v/checker/tests/generics_struct_field_fn_args_err.vv:22:20: error: expected 0 arguments, but got 2
have (int literal, int literal)
want ()
20 | println(fun0.call())
21 | println(fun0.call(1234))
22 | println(fun0.call(1234, 5678))
| ~~~~~~~~~~
23 |
23 |
24 | fun1 := Fun[fn (int) int]{
vlib/v/checker/tests/generics_struct_field_fn_args_err.vv:29:15: error: expected 1 arguments, but got 0
27 |
vlib/v/checker/tests/generics_struct_field_fn_args_err.vv:29:15: error: expected 1 argument, but got 0
have ()
want (int)
27 |
28 | println(fun1.call(42))
29 | println(fun1.call())
| ~~~~~~
30 | println(fun1.call(42, 43))
31 |
vlib/v/checker/tests/generics_struct_field_fn_args_err.vv:30:24: error: expected 1 arguments, but got 2
vlib/v/checker/tests/generics_struct_field_fn_args_err.vv:30:24: error: expected 1 argument, but got 2
have (int literal, int literal)
want (int)
28 | println(fun1.call(42))
29 | println(fun1.call())
30 | println(fun1.call(42, 43))
| ~~
31 |
31 |
32 | println(fun1.call(true))
vlib/v/checker/tests/generics_struct_field_fn_args_err.vv:32:20: error: cannot use `bool` as `int` in argument 1 to `Fun[fn (int) int].call`
30 | println(fun1.call(42, 43))
31 |
31 |
32 | println(fun1.call(true))
| ~~~~
33 | println(fun1.call('text'))
34 | println(fun1.call(22.2))
vlib/v/checker/tests/generics_struct_field_fn_args_err.vv:33:20: error: cannot use `string` as `int` in argument 1 to `Fun[fn (int) int].call`
31 |
31 |
32 | println(fun1.call(true))
33 | println(fun1.call('text'))
| ~~~~~~

View File

@ -6,6 +6,9 @@ vlib/v/checker/tests/multi_return_err.vv:18:10: error: cannot use `f64` as `int`
19 | my_func3(my_func2(), 'foo')
20 | my_func4('foo', my_func2())
vlib/v/checker/tests/multi_return_err.vv:19:2: error: expected 3 arguments, but got 2
have ((int, f64), string)
want (int, int, string)
17 | fn main() {
18 | my_func(my_func2())
19 | my_func3(my_func2(), 'foo')
@ -13,6 +16,9 @@ vlib/v/checker/tests/multi_return_err.vv:19:2: error: expected 3 arguments, but
20 | my_func4('foo', my_func2())
21 | my_func(my_func5())
vlib/v/checker/tests/multi_return_err.vv:20:2: error: expected 3 arguments, but got 2
have (string, (int, f64))
want (string, int, int)
18 | my_func(my_func2())
19 | my_func3(my_func2(), 'foo')
20 | my_func4('foo', my_func2())
@ -20,6 +26,9 @@ vlib/v/checker/tests/multi_return_err.vv:20:2: error: expected 3 arguments, but
21 | my_func(my_func5())
22 | my_func(my_func6())
vlib/v/checker/tests/multi_return_err.vv:21:2: error: expected 2 arguments, but got 1
have (void)
want (int, int)
19 | my_func3(my_func2(), 'foo')
20 | my_func4('foo', my_func2())
21 | my_func(my_func5())

View File

@ -1,4 +1,7 @@
vlib/v/checker/tests/no_interface_instantiation_b.vv:6:2: error: expected 1 arguments, but got 0
vlib/v/checker/tests/no_interface_instantiation_b.vv:6:2: error: expected 1 argument, but got 0
have ()
want (Speaker)
4 |
5 | fn main() {
6 | my_fn()

View File

@ -1,3 +1,6 @@
vlib/v/checker/tests/no_main_println_err.vv:1:1: error: expected 1 arguments, but got 0
vlib/v/checker/tests/no_main_println_err.vv:1:1: error: expected 1 argument, but got 0
have ()
want (string)
1 | println()
| ~~~~~~~~~

View File

@ -243,6 +243,8 @@ fn (mut g Gen) comptime_call(mut node ast.ComptimeCall) {
// last argument; try to expand if it's []string
idx := i - node.args.len
last_arg := g.expr_string(node.args.last().expr)
// t := g.table.sym(m.params[i].typ)
// g.write('/*nr_args=${node.args.len} m.params.len=${m.params.len} i=${i} t=${t.name} ${m.params}*/')
if m.params[i].typ.is_int() || m.params[i].typ.idx() == ast.bool_type_idx {
// Gets the type name and cast the string to the type with the string_<type> function
type_name := g.table.type_symbols[int(m.params[i].typ)].str()