diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index e966650aae..2a56c4cd7b 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -773,7 +773,8 @@ pub: stmts []Stmt // right side pos token.Position is_else bool - post_comments []Comment // comments below ´... }´ + post_comments []Comment // comments below ´... }´ + branch_pos token.Position // for checker errors about invalid branches pub mut: exprs []Expr // left side scope &Scope diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 9b76938535..2b885fb15f 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -5497,19 +5497,27 @@ pub fn (mut c Checker) match_expr(mut node ast.MatchExpr) ast.Type { node.is_sum_type = cond_type_sym.kind in [.interface_, .sum_type] c.match_exprs(mut node, cond_type_sym) c.expected_type = cond_type + mut first_iteration := true mut ret_type := ast.void_type mut nbranches_with_return := 0 mut nbranches_without_return := 0 for branch in node.branches { c.stmts(branch.stmts) - if node.is_expr && branch.stmts.len > 0 { - // ignore last statement - workaround - // currently the last statement in a match branch does not have an - // expected value set, so e.g. IfExpr.is_expr is not set. - // probably any mismatch will be caught by not producing a value instead - for st in branch.stmts[0..branch.stmts.len - 1] { - // must not contain C statements - st.check_c_expr() or { c.error('`match` expression branch has $err.msg', st.pos) } + if node.is_expr { + if branch.stmts.len > 0 { + // ignore last statement - workaround + // currently the last statement in a match branch does not have an + // expected value set, so e.g. IfExpr.is_expr is not set. + // probably any mismatch will be caught by not producing a value instead + for st in branch.stmts[0..branch.stmts.len - 1] { + // must not contain C statements + st.check_c_expr() or { + c.error('`match` expression branch has $err.msg', st.pos) + } + } + } else if ret_type != ast.void_type { + c.error('`match` expression requires an expression as the last statement of every branch', + branch.branch_pos) } } // If the last statement is an expression, return its type @@ -5521,7 +5529,7 @@ pub fn (mut c Checker) match_expr(mut node ast.MatchExpr) ast.Type { c.expected_type = node.expected_type } expr_type := c.expr(stmt.expr) - if ret_type == ast.void_type { + if first_iteration { if node.is_expr && !node.expected_type.has_flag(.optional) && c.table.get_type_symbol(node.expected_type).kind == .sum_type { ret_type = node.expected_type @@ -5540,15 +5548,14 @@ pub fn (mut c Checker) match_expr(mut node ast.MatchExpr) ast.Type { } } else { - // TODO: ask alex about this - // typ := c.expr(stmt.expr) - // type_sym := c.table.get_type_symbol(typ) - // p.warn('match expr ret $type_sym.name') - // node.typ = typ - // return typ + if node.is_expr && ret_type != ast.void_type { + c.error('`match` expression requires an expression as the last statement of every branch', + stmt.pos) + } } } } + first_iteration = false if has_return := c.has_return(branch.stmts) { if has_return { nbranches_with_return++ diff --git a/vlib/v/checker/tests/match_expr_empty_branch.out b/vlib/v/checker/tests/match_expr_empty_branch.out new file mode 100644 index 0000000000..927da5aedb --- /dev/null +++ b/vlib/v/checker/tests/match_expr_empty_branch.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/match_expr_empty_branch.vv:3:2: error: `match` expression requires an expression as the last statement of every branch + 1 | _ := match true { + 2 | true { 0 } + 3 | false {} + | ~~~~~~~~ + 4 | } diff --git a/vlib/v/checker/tests/match_expr_empty_branch.vv b/vlib/v/checker/tests/match_expr_empty_branch.vv new file mode 100644 index 0000000000..2a8650d36a --- /dev/null +++ b/vlib/v/checker/tests/match_expr_empty_branch.vv @@ -0,0 +1,4 @@ +_ := match true { + true { 0 } + false {} +} diff --git a/vlib/v/checker/tests/match_expr_non_void_stmt_last.out b/vlib/v/checker/tests/match_expr_non_void_stmt_last.out new file mode 100644 index 0000000000..6b5e1612f9 --- /dev/null +++ b/vlib/v/checker/tests/match_expr_non_void_stmt_last.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/match_expr_non_void_stmt_last.vv:9:4: error: `match` expression requires an expression as the last statement of every branch + 7 | } + 8 | []int { + 9 | return arr.str() + | ~~~~~~~~~~~~~~~~ + 10 | } + 11 | } diff --git a/vlib/v/checker/tests/match_expr_non_void_stmt_last.vv b/vlib/v/checker/tests/match_expr_non_void_stmt_last.vv new file mode 100644 index 0000000000..0737152b0d --- /dev/null +++ b/vlib/v/checker/tests/match_expr_non_void_stmt_last.vv @@ -0,0 +1,17 @@ +type Arr = []int | []string + +fn (arr Arr) str() string { + return match arr { + []string { + arr.join(' ') + } + []int { + return arr.str() + } + } +} + +fn main() { + println(Arr([0, 0])) + println(Arr(['0', '0'])) +} diff --git a/vlib/v/parser/if_match.v b/vlib/v/parser/if_match.v index caf103669a..059a6cf7f2 100644 --- a/vlib/v/parser/if_match.v +++ b/vlib/v/parser/if_match.v @@ -237,12 +237,14 @@ fn (mut p Parser) match_expr() ast.MatchExpr { p.close_scope() p.inside_match_body = false pos := branch_first_pos.extend_with_last_line(branch_last_pos, p.prev_tok.line_nr) + branch_pos := branch_first_pos.extend_with_last_line(p.tok.position(), p.tok.line_nr) post_comments := p.eat_comments({}) branches << ast.MatchBranch{ exprs: exprs ecmnts: ecmnts stmts: stmts pos: pos + branch_pos: branch_pos is_else: is_else post_comments: post_comments scope: branch_scope