Skip to content

Commit 72cbca9

Browse files
authored
checker: fix and improve return stmt error messages (#17477)
1 parent c583237 commit 72cbca9

File tree

3 files changed

+110
-5
lines changed

3 files changed

+110
-5
lines changed

vlib/v/checker/return.v

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,21 @@ module checker
55
import v.ast
66
import v.pref
77

8+
// error_type_name returns a proper type name reference for error messages
9+
// ? => Option type
10+
// ! => Result type
11+
// others => type `name`
12+
[inline]
13+
fn (mut c Checker) error_type_name(exp_type ast.Type) string {
14+
return if exp_type == ast.void_type.set_flag(.result) {
15+
'Result type'
16+
} else if exp_type == ast.void_type.set_flag(.option) {
17+
'Option type'
18+
} else {
19+
'type `${c.table.type_to_str(exp_type)}`'
20+
}
21+
}
22+
823
// TODO: non deferred
924
fn (mut c Checker) return_stmt(mut node ast.Return) {
1025
if c.table.cur_fn == unsafe { nil } {
@@ -22,6 +37,12 @@ fn (mut c Checker) return_stmt(mut node ast.Return) {
2237
if node.exprs.len > 0 && c.table.cur_fn.return_type == ast.void_type {
2338
c.error('unexpected argument, current function does not return anything', node.exprs[0].pos())
2439
return
40+
} else if node.exprs.len > 1 && c.table.cur_fn.return_type == ast.void_type.set_flag(.option) {
41+
c.error('can only return `none` from an Option-only return function', node.exprs[0].pos())
42+
return
43+
} else if node.exprs.len > 1 && c.table.cur_fn.return_type == ast.void_type.set_flag(.result) {
44+
c.error('functions with Result-only return types can only return an error', node.exprs[0].pos())
45+
return
2546
} else if node.exprs.len == 0 && !(c.expected_type == ast.void_type
2647
|| expected_type_sym.kind == .void) {
2748
stype := c.table.type_to_str(expected_type)
@@ -100,7 +121,7 @@ fn (mut c Checker) return_stmt(mut node ast.Return) {
100121
c.warn('Option and Result types have been split, use `!Foo` to return errors',
101122
node.pos)
102123
} else if exp_is_result && got_types_0_idx == ast.none_type_idx {
103-
c.warn('Option and Result types have been split, use `?Foo` to return none', node.pos)
124+
c.warn('Option and Result types have been split, use `?` to return none', node.pos)
104125
}
105126
if (exp_is_option
106127
&& got_types_0_idx in [ast.none_type_idx, ast.error_type_idx, option_type_idx])
@@ -143,13 +164,13 @@ fn (mut c Checker) return_stmt(mut node ast.Return) {
143164
got_typ := c.unwrap_generic(got_types[i])
144165
if got_typ.has_flag(.option) && got_typ.clear_flag(.option) != exp_type.clear_flag(.option) {
145166
pos := node.exprs[expr_idxs[i]].pos()
146-
c.error('cannot use `${c.table.type_to_str(got_typ)}` as type `${c.table.type_to_str(exp_type)}` in return argument',
167+
c.error('cannot use `${c.table.type_to_str(got_typ)}` as ${c.error_type_name(exp_type)} in return argument',
147168
pos)
148169
}
149170
if got_typ.has_flag(.result) && (!exp_type.has_flag(.result)
150171
|| c.table.type_to_str(got_typ) != c.table.type_to_str(exp_type)) {
151172
pos := node.exprs[expr_idxs[i]].pos()
152-
c.error('cannot use `${c.table.type_to_str(got_typ)}` as type `${c.table.type_to_str(exp_type)}` in return argument',
173+
c.error('cannot use `${c.table.type_to_str(got_typ)}` as ${c.error_type_name(exp_type)} in return argument',
153174
pos)
154175
}
155176
if node.exprs[expr_idxs[i]] !is ast.ComptimeCall {
@@ -161,7 +182,7 @@ fn (mut c Checker) return_stmt(mut node ast.Return) {
161182
if node.exprs[expr_idxs[i]] is ast.IntegerLiteral {
162183
var := (node.exprs[expr_idxs[i]] as ast.IntegerLiteral).val
163184
if var[0] == `-` {
164-
c.note('cannot use a negative value as value of type `${c.table.type_to_str(exp_type)}` in return argument',
185+
c.note('cannot use a negative value as value of ${c.error_type_name(exp_type)} in return argument',
165186
pos)
166187
}
167188
}
@@ -194,7 +215,7 @@ fn (mut c Checker) return_stmt(mut node ast.Return) {
194215
} else {
195216
got_typ_sym.name
196217
}
197-
c.error('cannot use `${got_typ_name}` as type `${c.table.type_to_str(exp_type)}` in return argument',
218+
c.error('cannot use `${got_typ_name}` as ${c.error_type_name(exp_type)} in return argument',
198219
pos)
199220
}
200221
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
vlib/v/checker/tests/void_fn_multiple_ret_err.vv:6:2: warning: Option and Result types have been split, use `?` to return none
2+
4 |
3+
5 | fn foo_result_2() ! {
4+
6 | return none
5+
| ~~~~~~~~~~~
6+
7 | }
7+
8 |
8+
vlib/v/checker/tests/void_fn_multiple_ret_err.vv:2:9: error: functions with Result-only return types can only return an error
9+
1 | fn foo_result_1() ! {
10+
2 | return none, 100
11+
| ~~~~
12+
3 | }
13+
4 |
14+
vlib/v/checker/tests/void_fn_multiple_ret_err.vv:6:9: error: cannot use `none` as Result type in return argument
15+
4 |
16+
5 | fn foo_result_2() ! {
17+
6 | return none
18+
| ~~~~
19+
7 | }
20+
8 |
21+
vlib/v/checker/tests/void_fn_multiple_ret_err.vv:14:9: error: cannot use `int literal` as Result type in return argument
22+
12 |
23+
13 | fn foo_result_4() ! {
24+
14 | return 1
25+
| ^
26+
15 | }
27+
16 |
28+
vlib/v/checker/tests/void_fn_multiple_ret_err.vv:21:9: error: cannot use `string` as type `!int` in return argument
29+
19 |
30+
20 | fn foo_result_6() !int {
31+
21 | return ''
32+
| ~~
33+
22 | }
34+
23 |
35+
vlib/v/checker/tests/void_fn_multiple_ret_err.vv:25:9: error: can only return `none` from an Option-only return function
36+
23 |
37+
24 | fn foo_option_1() ? {
38+
25 | return none, 100
39+
| ~~~~
40+
26 | }
41+
27 |
42+
vlib/v/checker/tests/void_fn_multiple_ret_err.vv:36:9: error: cannot use `int literal` as Option type in return argument
43+
34 |
44+
35 | fn foo_option_3() ? {
45+
36 | return 1
46+
| ^
47+
37 | }
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
fn foo_result_1() ! {
2+
return none, 100
3+
}
4+
5+
fn foo_result_2() ! {
6+
return none
7+
}
8+
9+
fn foo_result_3() ! {
10+
return error('')
11+
}
12+
13+
fn foo_result_4() ! {
14+
return 1
15+
}
16+
17+
fn foo_result_5() ! {
18+
}
19+
20+
fn foo_result_6() !int {
21+
return ''
22+
}
23+
24+
fn foo_option_1() ? {
25+
return none, 100
26+
}
27+
28+
fn foo_option_2() ? {
29+
return none
30+
}
31+
32+
fn foo_option_3() ? {
33+
}
34+
35+
fn foo_option_3() ? {
36+
return 1
37+
}

0 commit comments

Comments
 (0)