checker: fix error for 'or expr with nested match expr' (#13658)
parent
0e5ae7126f
commit
8136157f87
|
@ -1441,34 +1441,50 @@ pub fn (mut c Checker) check_or_expr(node ast.OrExpr, ret_type ast.Type, expr_re
|
|||
return
|
||||
}
|
||||
last_stmt := node.stmts[stmts_len - 1]
|
||||
c.check_or_last_stmt(last_stmt, ret_type, expr_return_type)
|
||||
}
|
||||
|
||||
fn (mut c Checker) check_or_last_stmt(stmt ast.Stmt, ret_type ast.Type, expr_return_type ast.Type) {
|
||||
if ret_type != ast.void_type {
|
||||
match last_stmt {
|
||||
match stmt {
|
||||
ast.ExprStmt {
|
||||
c.expected_type = ret_type
|
||||
c.expected_or_type = ret_type.clear_flag(.optional)
|
||||
last_stmt_typ := c.expr(last_stmt.expr)
|
||||
last_stmt_typ := c.expr(stmt.expr)
|
||||
c.expected_or_type = ast.void_type
|
||||
type_fits := c.check_types(last_stmt_typ, ret_type)
|
||||
&& last_stmt_typ.nr_muls() == ret_type.nr_muls()
|
||||
is_noreturn := is_noreturn_callexpr(last_stmt.expr)
|
||||
is_noreturn := is_noreturn_callexpr(stmt.expr)
|
||||
if type_fits || is_noreturn {
|
||||
return
|
||||
}
|
||||
expected_type_name := c.table.type_to_str(ret_type.clear_flag(.optional))
|
||||
if last_stmt.typ == ast.void_type {
|
||||
if stmt.typ == ast.void_type {
|
||||
if stmt.expr is ast.IfExpr {
|
||||
for branch in stmt.expr.branches {
|
||||
last_stmt := branch.stmts[branch.stmts.len - 1]
|
||||
c.check_or_last_stmt(last_stmt, ret_type, expr_return_type)
|
||||
}
|
||||
return
|
||||
} else if stmt.expr is ast.MatchExpr {
|
||||
for branch in stmt.expr.branches {
|
||||
last_stmt := branch.stmts[branch.stmts.len - 1]
|
||||
c.check_or_last_stmt(last_stmt, ret_type, expr_return_type)
|
||||
}
|
||||
return
|
||||
}
|
||||
c.error('`or` block must provide a default value of type `$expected_type_name`, or return/continue/break or call a [noreturn] function like panic(err) or exit(1)',
|
||||
last_stmt.pos)
|
||||
stmt.expr.pos())
|
||||
} else {
|
||||
type_name := c.table.type_to_str(last_stmt_typ)
|
||||
c.error('wrong return type `$type_name` in the `or {}` block, expected `$expected_type_name`',
|
||||
last_stmt.pos)
|
||||
stmt.expr.pos())
|
||||
}
|
||||
return
|
||||
}
|
||||
ast.BranchStmt {
|
||||
if last_stmt.kind !in [.key_continue, .key_break] {
|
||||
if stmt.kind !in [.key_continue, .key_break] {
|
||||
c.error('only break/continue is allowed as a branch statement in the end of an `or {}` block',
|
||||
last_stmt.pos)
|
||||
stmt.pos)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -1476,27 +1492,42 @@ pub fn (mut c Checker) check_or_expr(node ast.OrExpr, ret_type ast.Type, expr_re
|
|||
else {
|
||||
expected_type_name := c.table.type_to_str(ret_type.clear_flag(.optional))
|
||||
c.error('last statement in the `or {}` block should be an expression of type `$expected_type_name` or exit parent scope',
|
||||
node.pos)
|
||||
return
|
||||
stmt.pos)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
match last_stmt {
|
||||
match stmt {
|
||||
ast.ExprStmt {
|
||||
if last_stmt.typ == ast.void_type {
|
||||
return
|
||||
match stmt.expr {
|
||||
ast.IfExpr {
|
||||
for branch in stmt.expr.branches {
|
||||
last_stmt := branch.stmts[branch.stmts.len - 1]
|
||||
c.check_or_last_stmt(last_stmt, ret_type, expr_return_type)
|
||||
}
|
||||
}
|
||||
ast.MatchExpr {
|
||||
for branch in stmt.expr.branches {
|
||||
last_stmt := branch.stmts[branch.stmts.len - 1]
|
||||
c.check_or_last_stmt(last_stmt, ret_type, expr_return_type)
|
||||
}
|
||||
}
|
||||
else {
|
||||
if stmt.typ == ast.void_type {
|
||||
return
|
||||
}
|
||||
if is_noreturn_callexpr(stmt.expr) {
|
||||
return
|
||||
}
|
||||
if c.check_types(stmt.typ, expr_return_type) {
|
||||
return
|
||||
}
|
||||
// opt_returning_string() or { ... 123 }
|
||||
type_name := c.table.type_to_str(stmt.typ)
|
||||
expr_return_type_name := c.table.type_to_str(expr_return_type)
|
||||
c.error('the default expression type in the `or` block should be `$expr_return_type_name`, instead you gave a value of type `$type_name`',
|
||||
stmt.expr.pos())
|
||||
}
|
||||
}
|
||||
if is_noreturn_callexpr(last_stmt.expr) {
|
||||
return
|
||||
}
|
||||
if c.check_types(last_stmt.typ, expr_return_type) {
|
||||
return
|
||||
}
|
||||
// opt_returning_string() or { ... 123 }
|
||||
type_name := c.table.type_to_str(last_stmt.typ)
|
||||
expr_return_type_name := c.table.type_to_str(expr_return_type)
|
||||
c.error('the default expression type in the `or` block should be `$expr_return_type_name`, instead you gave a value of type `$type_name`',
|
||||
last_stmt.expr.pos())
|
||||
}
|
||||
else {}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
vlib/v/checker/tests/or_err.vv:4:10: error: last statement in the `or {}` block should be an expression of type `&int` or exit parent scope
|
||||
2 | return none
|
||||
vlib/v/checker/tests/or_err.vv:5:2: error: last statement in the `or {}` block should be an expression of type `&int` or exit parent scope
|
||||
3 | }
|
||||
4 | a := f() or {
|
||||
| ~~~~
|
||||
5 | {}
|
||||
| ^
|
||||
6 | }
|
||||
7 | _ = f() or {
|
||||
vlib/v/checker/tests/or_err.vv:11:2: error: wrong return type `rune` in the `or {}` block, expected `&int`
|
||||
9 | }
|
||||
10 | _ = f() or {
|
||||
|
|
|
@ -5,10 +5,10 @@ vlib/v/parser/tests/or_default_missing.vv:4:3: error: `or` block must provide a
|
|||
| ~~~~~~~~~~~~~~~~
|
||||
5 | }
|
||||
6 | println(el)
|
||||
vlib/v/parser/tests/or_default_missing.vv:16:16: error: last statement in the `or {}` block should be an expression of type `int` or exit parent scope
|
||||
14 | }
|
||||
vlib/v/parser/tests/or_default_missing.vv:17:11: error: last statement in the `or {}` block should be an expression of type `int` or exit parent scope
|
||||
15 | mut testvar := 0
|
||||
16 | el := m['pp'] or {
|
||||
| ~~~~
|
||||
17 | testvar = 12
|
||||
| ^
|
||||
18 | }
|
||||
19 | println('$el $testvar')
|
||||
|
|
|
@ -13,16 +13,16 @@ vlib/v/parser/tests/prefix_first.vv:27:3: warning: move infix `&` operator befor
|
|||
28 | }
|
||||
29 | }
|
||||
vlib/v/parser/tests/prefix_first.vv:13:6: error: `if` expression requires an expression as the last statement of every branch
|
||||
11 |
|
||||
11 |
|
||||
12 | // later this should compile correctly
|
||||
13 | _ = if true {
|
||||
| ~~~~~~~
|
||||
14 | v = 1
|
||||
15 | -1
|
||||
vlib/v/parser/tests/prefix_first.vv:25:12: error: last statement in the `or {}` block should be an expression of type `&int` or exit parent scope
|
||||
23 | // later this should compile correctly
|
||||
vlib/v/parser/tests/prefix_first.vv:26:5: error: last statement in the `or {}` block should be an expression of type `&int` or exit parent scope
|
||||
24 | v := 3
|
||||
25 | _ = opt() or {
|
||||
| ~~~~
|
||||
26 | _ = 1
|
||||
| ^
|
||||
27 | &v
|
||||
28 | }
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
struct API_error {
|
||||
pub mut:
|
||||
errors []string
|
||||
}
|
||||
|
||||
fn delete_secret_v1() API_error {
|
||||
response := req_do() or {
|
||||
match err.msg {
|
||||
'dial_tcp failed' {
|
||||
return API_error{
|
||||
errors: ['Vault server not started']
|
||||
}
|
||||
}
|
||||
else {
|
||||
return API_error{
|
||||
errors: [err.msg]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
println(response)
|
||||
}
|
||||
|
||||
fn req_do() ?string {
|
||||
return error('dial_tcp failed')
|
||||
}
|
||||
|
||||
fn test_or_expr_with_nested_match_expr() {
|
||||
err := delete_secret_v1()
|
||||
println(err)
|
||||
assert err.errors.len == 1
|
||||
assert err.errors[0] == 'Vault server not started'
|
||||
}
|
Loading…
Reference in New Issue