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
|
return
|
||||||
}
|
}
|
||||||
last_stmt := node.stmts[stmts_len - 1]
|
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 {
|
if ret_type != ast.void_type {
|
||||||
match last_stmt {
|
match stmt {
|
||||||
ast.ExprStmt {
|
ast.ExprStmt {
|
||||||
c.expected_type = ret_type
|
c.expected_type = ret_type
|
||||||
c.expected_or_type = ret_type.clear_flag(.optional)
|
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
|
c.expected_or_type = ast.void_type
|
||||||
type_fits := c.check_types(last_stmt_typ, ret_type)
|
type_fits := c.check_types(last_stmt_typ, ret_type)
|
||||||
&& last_stmt_typ.nr_muls() == ret_type.nr_muls()
|
&& 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 {
|
if type_fits || is_noreturn {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
expected_type_name := c.table.type_to_str(ret_type.clear_flag(.optional))
|
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)',
|
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 {
|
} else {
|
||||||
type_name := c.table.type_to_str(last_stmt_typ)
|
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`',
|
c.error('wrong return type `$type_name` in the `or {}` block, expected `$expected_type_name`',
|
||||||
last_stmt.pos)
|
stmt.expr.pos())
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
ast.BranchStmt {
|
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',
|
c.error('only break/continue is allowed as a branch statement in the end of an `or {}` block',
|
||||||
last_stmt.pos)
|
stmt.pos)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1476,27 +1492,42 @@ pub fn (mut c Checker) check_or_expr(node ast.OrExpr, ret_type ast.Type, expr_re
|
||||||
else {
|
else {
|
||||||
expected_type_name := c.table.type_to_str(ret_type.clear_flag(.optional))
|
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',
|
c.error('last statement in the `or {}` block should be an expression of type `$expected_type_name` or exit parent scope',
|
||||||
node.pos)
|
stmt.pos)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
match last_stmt {
|
match stmt {
|
||||||
ast.ExprStmt {
|
ast.ExprStmt {
|
||||||
if last_stmt.typ == ast.void_type {
|
match stmt.expr {
|
||||||
return
|
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 {}
|
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
|
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
|
||||||
2 | return none
|
|
||||||
3 | }
|
3 | }
|
||||||
4 | a := f() or {
|
4 | a := f() or {
|
||||||
| ~~~~
|
|
||||||
5 | {}
|
5 | {}
|
||||||
|
| ^
|
||||||
6 | }
|
6 | }
|
||||||
|
7 | _ = f() or {
|
||||||
vlib/v/checker/tests/or_err.vv:11:2: error: wrong return type `rune` in the `or {}` block, expected `&int`
|
vlib/v/checker/tests/or_err.vv:11:2: error: wrong return type `rune` in the `or {}` block, expected `&int`
|
||||||
9 | }
|
9 | }
|
||||||
10 | _ = f() or {
|
10 | _ = f() or {
|
||||||
|
|
|
@ -5,10 +5,10 @@ vlib/v/parser/tests/or_default_missing.vv:4:3: error: `or` block must provide a
|
||||||
| ~~~~~~~~~~~~~~~~
|
| ~~~~~~~~~~~~~~~~
|
||||||
5 | }
|
5 | }
|
||||||
6 | println(el)
|
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
|
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
|
||||||
14 | }
|
|
||||||
15 | mut testvar := 0
|
15 | mut testvar := 0
|
||||||
16 | el := m['pp'] or {
|
16 | el := m['pp'] or {
|
||||||
| ~~~~
|
|
||||||
17 | testvar = 12
|
17 | testvar = 12
|
||||||
|
| ^
|
||||||
18 | }
|
18 | }
|
||||||
|
19 | println('$el $testvar')
|
||||||
|
|
|
@ -13,16 +13,16 @@ vlib/v/parser/tests/prefix_first.vv:27:3: warning: move infix `&` operator befor
|
||||||
28 | }
|
28 | }
|
||||||
29 | }
|
29 | }
|
||||||
vlib/v/parser/tests/prefix_first.vv:13:6: error: `if` expression requires an expression as the last statement of every branch
|
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
|
12 | // later this should compile correctly
|
||||||
13 | _ = if true {
|
13 | _ = if true {
|
||||||
| ~~~~~~~
|
| ~~~~~~~
|
||||||
14 | v = 1
|
14 | v = 1
|
||||||
15 | -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
|
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
|
||||||
23 | // later this should compile correctly
|
|
||||||
24 | v := 3
|
24 | v := 3
|
||||||
25 | _ = opt() or {
|
25 | _ = opt() or {
|
||||||
| ~~~~
|
|
||||||
26 | _ = 1
|
26 | _ = 1
|
||||||
|
| ^
|
||||||
27 | &v
|
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