checker: check match for exhaustion
parent
1185f04868
commit
12e48c6fe2
|
@ -399,6 +399,7 @@ pub:
|
||||||
stmts []Stmt
|
stmts []Stmt
|
||||||
pos token.Position
|
pos token.Position
|
||||||
comment Comment // comment above `xxx {`
|
comment Comment // comment above `xxx {`
|
||||||
|
is_else bool
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CompIf {
|
pub struct CompIf {
|
||||||
|
|
|
@ -1323,6 +1323,18 @@ pub fn (c mut Checker) match_expr(node mut ast.MatchExpr) table.Type {
|
||||||
if cond_type == 0 {
|
if cond_type == 0 {
|
||||||
c.error('match 0 cond type', node.pos)
|
c.error('match 0 cond type', node.pos)
|
||||||
}
|
}
|
||||||
|
// check for exhaustion when match is expr
|
||||||
|
if node.is_sum_type && node.is_expr && !node.branches[node.branches.len - 1].is_else {
|
||||||
|
type_sym := c.table.get_type_symbol(cond_type)
|
||||||
|
info := type_sym.info as table.SumType
|
||||||
|
mut used_sum_types_count := 0
|
||||||
|
for branch in node.branches {
|
||||||
|
used_sum_types_count += branch.exprs.len
|
||||||
|
}
|
||||||
|
if used_sum_types_count < info.variants.len {
|
||||||
|
c.error('match must be exhaustive', node.pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
c.expected_type = cond_type
|
c.expected_type = cond_type
|
||||||
mut ret_type := table.void_type
|
mut ret_type := table.void_type
|
||||||
for branch in node.branches {
|
for branch in node.branches {
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
vlib/v/checker/tests/inout/match_expr_else.v:5:9: error: match must be exhaustive
|
||||||
|
3| fn main() {
|
||||||
|
4| x := A('test')
|
||||||
|
5| res := match x {
|
||||||
|
~~~~~~~~~
|
||||||
|
6| int {
|
||||||
|
7| 'int'
|
|
@ -0,0 +1,13 @@
|
||||||
|
type A = int | string | f64
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
x := A('test')
|
||||||
|
res := match x {
|
||||||
|
int {
|
||||||
|
'int'
|
||||||
|
}
|
||||||
|
string {
|
||||||
|
'string'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -577,7 +577,7 @@ fn (f mut Fmt) expr(node ast.Expr) {
|
||||||
if branch.comment.text != '' {
|
if branch.comment.text != '' {
|
||||||
f.comment(branch.comment)
|
f.comment(branch.comment)
|
||||||
}
|
}
|
||||||
if i < it.branches.len - 1 {
|
if !branch.is_else {
|
||||||
// normal branch
|
// normal branch
|
||||||
for j, expr in branch.exprs {
|
for j, expr in branch.exprs {
|
||||||
f.expr(expr)
|
f.expr(expr)
|
||||||
|
|
|
@ -1373,8 +1373,8 @@ fn (g mut Gen) match_expr(node ast.MatchExpr) {
|
||||||
// g.writeln(';') // $it.blocks.len')
|
// g.writeln(';') // $it.blocks.len')
|
||||||
// mut sum_type_str = ''
|
// mut sum_type_str = ''
|
||||||
for j, branch in node.branches {
|
for j, branch in node.branches {
|
||||||
if j == node.branches.len - 1 {
|
is_last := j == node.branches.len - 1
|
||||||
// last block is an `else{}`
|
if branch.is_else || node.is_expr && is_last {
|
||||||
if node.branches.len > 1 {
|
if node.branches.len > 1 {
|
||||||
if is_expr {
|
if is_expr {
|
||||||
// TODO too many branches. maybe separate ?: matches
|
// TODO too many branches. maybe separate ?: matches
|
||||||
|
@ -1447,7 +1447,7 @@ fn (g mut Gen) match_expr(node ast.MatchExpr) {
|
||||||
}
|
}
|
||||||
g.stmts(branch.stmts)
|
g.stmts(branch.stmts)
|
||||||
if !g.inside_ternary && node.branches.len > 1 {
|
if !g.inside_ternary && node.branches.len > 1 {
|
||||||
g.writeln('}')
|
g.write('}')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
g.inside_ternary = was_inside_ternary
|
g.inside_ternary = was_inside_ternary
|
||||||
|
|
|
@ -1834,8 +1834,8 @@ fn (p mut Parser) global_decl() ast.GlobalDecl {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (p mut Parser) match_expr() ast.MatchExpr {
|
fn (p mut Parser) match_expr() ast.MatchExpr {
|
||||||
|
match_first_pos := p.tok.position()
|
||||||
p.check(.key_match)
|
p.check(.key_match)
|
||||||
pos := p.tok.position()
|
|
||||||
is_mut := p.tok.kind == .key_mut
|
is_mut := p.tok.kind == .key_mut
|
||||||
mut is_sum_type := false
|
mut is_sum_type := false
|
||||||
if is_mut {
|
if is_mut {
|
||||||
|
@ -1844,15 +1844,15 @@ fn (p mut Parser) match_expr() ast.MatchExpr {
|
||||||
cond := p.expr(0)
|
cond := p.expr(0)
|
||||||
p.check(.lcbr)
|
p.check(.lcbr)
|
||||||
mut branches := []ast.MatchBranch
|
mut branches := []ast.MatchBranch
|
||||||
mut have_final_else := false
|
|
||||||
for {
|
for {
|
||||||
|
branch_first_pos := p.tok.position()
|
||||||
comment := p.check_comment() // comment before {}
|
comment := p.check_comment() // comment before {}
|
||||||
mut exprs := []ast.Expr
|
mut exprs := []ast.Expr
|
||||||
branch_pos := p.tok.position()
|
|
||||||
p.open_scope()
|
p.open_scope()
|
||||||
// final else
|
// final else
|
||||||
|
mut is_else := false
|
||||||
if p.tok.kind == .key_else {
|
if p.tok.kind == .key_else {
|
||||||
have_final_else = true
|
is_else = true
|
||||||
p.next()
|
p.next()
|
||||||
} else if p.tok.kind == .name && (p.tok.lit in table.builtin_type_names ||
|
} else if p.tok.kind == .name && (p.tok.lit in table.builtin_type_names ||
|
||||||
(p.tok.lit[0].is_capital() && !p.tok.lit.is_upper()) || p.peek_tok.kind == .dot) {
|
(p.tok.lit[0].is_capital() && !p.tok.lit.is_upper()) || p.peek_tok.kind == .dot) {
|
||||||
|
@ -1890,21 +1890,31 @@ fn (p mut Parser) match_expr() ast.MatchExpr {
|
||||||
p.check(.comma)
|
p.check(.comma)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
branch_last_pos := p.tok.position()
|
||||||
// p.warn('match block')
|
// p.warn('match block')
|
||||||
stmts := p.parse_block()
|
stmts := p.parse_block()
|
||||||
|
pos := token.Position{
|
||||||
|
line_nr: match_first_pos.line_nr
|
||||||
|
pos: match_first_pos.pos
|
||||||
|
len: branch_last_pos.pos - branch_first_pos.pos + branch_last_pos.len
|
||||||
|
}
|
||||||
branches << ast.MatchBranch{
|
branches << ast.MatchBranch{
|
||||||
exprs: exprs
|
exprs: exprs
|
||||||
stmts: stmts
|
stmts: stmts
|
||||||
pos: branch_pos
|
pos: pos
|
||||||
comment: comment
|
comment: comment
|
||||||
|
is_else: is_else
|
||||||
}
|
}
|
||||||
p.close_scope()
|
p.close_scope()
|
||||||
if p.tok.kind == .rcbr {
|
if p.tok.kind == .rcbr {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !have_final_else {
|
match_last_pos := p.tok.position()
|
||||||
p.error('match must be exhaustive')
|
pos := token.Position{
|
||||||
|
line_nr: match_first_pos.line_nr
|
||||||
|
pos: match_first_pos.pos
|
||||||
|
len: match_last_pos.pos - match_first_pos.pos + match_last_pos.len
|
||||||
}
|
}
|
||||||
p.check(.rcbr)
|
p.check(.rcbr)
|
||||||
return ast.MatchExpr{
|
return ast.MatchExpr{
|
||||||
|
|
|
@ -96,7 +96,9 @@ pub fn formatted_error(kind string /*error or warn*/, emsg string, filepath stri
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if pos.len > 1 {
|
if pos.len > 1 {
|
||||||
underline := '~'.repeat(pos.len)
|
max_len := sline.len - pointerline.len // rest of the line
|
||||||
|
len := if pos.len > max_len { max_len } else { pos.len }
|
||||||
|
underline := '~'.repeat(len)
|
||||||
pointerline << if emanager.support_color { term.bold(term.blue(underline)) } else { underline }
|
pointerline << if emanager.support_color { term.bold(term.blue(underline)) } else { underline }
|
||||||
}else{
|
}else{
|
||||||
pointerline << if emanager.support_color { term.bold(term.blue('^')) } else { '^' }
|
pointerline << if emanager.support_color { term.bold(term.blue('^')) } else { '^' }
|
||||||
|
|
Loading…
Reference in New Issue