Skip to content

Commit 8136157

Browse files
authored
checker: fix error for 'or expr with nested match expr' (#13658)
1 parent 0e5ae71 commit 8136157

File tree

5 files changed

+99
-35
lines changed

5 files changed

+99
-35
lines changed

vlib/v/checker/checker.v

Lines changed: 56 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1441,62 +1441,93 @@ pub fn (mut c Checker) check_or_expr(node ast.OrExpr, ret_type ast.Type, expr_re
14411441
return
14421442
}
14431443
last_stmt := node.stmts[stmts_len - 1]
1444+
c.check_or_last_stmt(last_stmt, ret_type, expr_return_type)
1445+
}
1446+
1447+
fn (mut c Checker) check_or_last_stmt(stmt ast.Stmt, ret_type ast.Type, expr_return_type ast.Type) {
14441448
if ret_type != ast.void_type {
1445-
match last_stmt {
1449+
match stmt {
14461450
ast.ExprStmt {
14471451
c.expected_type = ret_type
14481452
c.expected_or_type = ret_type.clear_flag(.optional)
1449-
last_stmt_typ := c.expr(last_stmt.expr)
1453+
last_stmt_typ := c.expr(stmt.expr)
14501454
c.expected_or_type = ast.void_type
14511455
type_fits := c.check_types(last_stmt_typ, ret_type)
14521456
&& last_stmt_typ.nr_muls() == ret_type.nr_muls()
1453-
is_noreturn := is_noreturn_callexpr(last_stmt.expr)
1457+
is_noreturn := is_noreturn_callexpr(stmt.expr)
14541458
if type_fits || is_noreturn {
14551459
return
14561460
}
14571461
expected_type_name := c.table.type_to_str(ret_type.clear_flag(.optional))
1458-
if last_stmt.typ == ast.void_type {
1462+
if stmt.typ == ast.void_type {
1463+
if stmt.expr is ast.IfExpr {
1464+
for branch in stmt.expr.branches {
1465+
last_stmt := branch.stmts[branch.stmts.len - 1]
1466+
c.check_or_last_stmt(last_stmt, ret_type, expr_return_type)
1467+
}
1468+
return
1469+
} else if stmt.expr is ast.MatchExpr {
1470+
for branch in stmt.expr.branches {
1471+
last_stmt := branch.stmts[branch.stmts.len - 1]
1472+
c.check_or_last_stmt(last_stmt, ret_type, expr_return_type)
1473+
}
1474+
return
1475+
}
14591476
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)',
1460-
last_stmt.pos)
1477+
stmt.expr.pos())
14611478
} else {
14621479
type_name := c.table.type_to_str(last_stmt_typ)
14631480
c.error('wrong return type `$type_name` in the `or {}` block, expected `$expected_type_name`',
1464-
last_stmt.pos)
1481+
stmt.expr.pos())
14651482
}
1466-
return
14671483
}
14681484
ast.BranchStmt {
1469-
if last_stmt.kind !in [.key_continue, .key_break] {
1485+
if stmt.kind !in [.key_continue, .key_break] {
14701486
c.error('only break/continue is allowed as a branch statement in the end of an `or {}` block',
1471-
last_stmt.pos)
1487+
stmt.pos)
14721488
return
14731489
}
14741490
}
14751491
ast.Return {}
14761492
else {
14771493
expected_type_name := c.table.type_to_str(ret_type.clear_flag(.optional))
14781494
c.error('last statement in the `or {}` block should be an expression of type `$expected_type_name` or exit parent scope',
1479-
node.pos)
1480-
return
1495+
stmt.pos)
14811496
}
14821497
}
14831498
} else {
1484-
match last_stmt {
1499+
match stmt {
14851500
ast.ExprStmt {
1486-
if last_stmt.typ == ast.void_type {
1487-
return
1488-
}
1489-
if is_noreturn_callexpr(last_stmt.expr) {
1490-
return
1491-
}
1492-
if c.check_types(last_stmt.typ, expr_return_type) {
1493-
return
1501+
match stmt.expr {
1502+
ast.IfExpr {
1503+
for branch in stmt.expr.branches {
1504+
last_stmt := branch.stmts[branch.stmts.len - 1]
1505+
c.check_or_last_stmt(last_stmt, ret_type, expr_return_type)
1506+
}
1507+
}
1508+
ast.MatchExpr {
1509+
for branch in stmt.expr.branches {
1510+
last_stmt := branch.stmts[branch.stmts.len - 1]
1511+
c.check_or_last_stmt(last_stmt, ret_type, expr_return_type)
1512+
}
1513+
}
1514+
else {
1515+
if stmt.typ == ast.void_type {
1516+
return
1517+
}
1518+
if is_noreturn_callexpr(stmt.expr) {
1519+
return
1520+
}
1521+
if c.check_types(stmt.typ, expr_return_type) {
1522+
return
1523+
}
1524+
// opt_returning_string() or { ... 123 }
1525+
type_name := c.table.type_to_str(stmt.typ)
1526+
expr_return_type_name := c.table.type_to_str(expr_return_type)
1527+
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`',
1528+
stmt.expr.pos())
1529+
}
14941530
}
1495-
// opt_returning_string() or { ... 123 }
1496-
type_name := c.table.type_to_str(last_stmt.typ)
1497-
expr_return_type_name := c.table.type_to_str(expr_return_type)
1498-
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`',
1499-
last_stmt.expr.pos())
15001531
}
15011532
else {}
15021533
}

vlib/v/checker/tests/or_err.out

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
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-
2 | return none
1+
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
32
3 | }
43
4 | a := f() or {
5-
| ~~~~
64
5 | {}
5+
| ^
76
6 | }
7+
7 | _ = f() or {
88
vlib/v/checker/tests/or_err.vv:11:2: error: wrong return type `rune` in the `or {}` block, expected `&int`
99
9 | }
1010
10 | _ = f() or {

vlib/v/parser/tests/or_default_missing.out

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ vlib/v/parser/tests/or_default_missing.vv:4:3: error: `or` block must provide a
55
| ~~~~~~~~~~~~~~~~
66
5 | }
77
6 | println(el)
8-
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
9-
14 | }
8+
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
109
15 | mut testvar := 0
1110
16 | el := m['pp'] or {
12-
| ~~~~
1311
17 | testvar = 12
12+
| ^
1413
18 | }
14+
19 | println('$el $testvar')

vlib/v/parser/tests/prefix_first.out

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,16 @@ vlib/v/parser/tests/prefix_first.vv:27:3: warning: move infix `&` operator befor
1313
28 | }
1414
29 | }
1515
vlib/v/parser/tests/prefix_first.vv:13:6: error: `if` expression requires an expression as the last statement of every branch
16-
11 |
16+
11 |
1717
12 | // later this should compile correctly
1818
13 | _ = if true {
1919
| ~~~~~~~
2020
14 | v = 1
2121
15 | -1
22-
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-
23 | // later this should compile correctly
22+
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
2423
24 | v := 3
2524
25 | _ = opt() or {
26-
| ~~~~
2725
26 | _ = 1
26+
| ^
2827
27 | &v
28+
28 | }
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
struct API_error {
2+
pub mut:
3+
errors []string
4+
}
5+
6+
fn delete_secret_v1() API_error {
7+
response := req_do() or {
8+
match err.msg {
9+
'dial_tcp failed' {
10+
return API_error{
11+
errors: ['Vault server not started']
12+
}
13+
}
14+
else {
15+
return API_error{
16+
errors: [err.msg]
17+
}
18+
}
19+
}
20+
}
21+
println(response)
22+
}
23+
24+
fn req_do() ?string {
25+
return error('dial_tcp failed')
26+
}
27+
28+
fn test_or_expr_with_nested_match_expr() {
29+
err := delete_secret_v1()
30+
println(err)
31+
assert err.errors.len == 1
32+
assert err.errors[0] == 'Vault server not started'
33+
}

0 commit comments

Comments
 (0)