checker: check match for exhaustion

pull/4389/head
Daniel Däschle 2020-04-14 01:03:31 +02:00 committed by GitHub
parent 1185f04868
commit 12e48c6fe2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 57 additions and 12 deletions

View File

@ -399,6 +399,7 @@ pub:
stmts []Stmt
pos token.Position
comment Comment // comment above `xxx {`
is_else bool
}
pub struct CompIf {

View File

@ -1323,6 +1323,18 @@ pub fn (c mut Checker) match_expr(node mut ast.MatchExpr) table.Type {
if cond_type == 0 {
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
mut ret_type := table.void_type
for branch in node.branches {

View File

@ -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'

View File

@ -0,0 +1,13 @@
type A = int | string | f64
fn main() {
x := A('test')
res := match x {
int {
'int'
}
string {
'string'
}
}
}

View File

@ -577,7 +577,7 @@ fn (f mut Fmt) expr(node ast.Expr) {
if branch.comment.text != '' {
f.comment(branch.comment)
}
if i < it.branches.len - 1 {
if !branch.is_else {
// normal branch
for j, expr in branch.exprs {
f.expr(expr)

View File

@ -1373,8 +1373,8 @@ fn (g mut Gen) match_expr(node ast.MatchExpr) {
// g.writeln(';') // $it.blocks.len')
// mut sum_type_str = ''
for j, branch in node.branches {
if j == node.branches.len - 1 {
// last block is an `else{}`
is_last := j == node.branches.len - 1
if branch.is_else || node.is_expr && is_last {
if node.branches.len > 1 {
if is_expr {
// TODO too many branches. maybe separate ?: matches
@ -1447,7 +1447,7 @@ fn (g mut Gen) match_expr(node ast.MatchExpr) {
}
g.stmts(branch.stmts)
if !g.inside_ternary && node.branches.len > 1 {
g.writeln('}')
g.write('}')
}
}
g.inside_ternary = was_inside_ternary

View File

@ -1834,8 +1834,8 @@ fn (p mut Parser) global_decl() ast.GlobalDecl {
}
fn (p mut Parser) match_expr() ast.MatchExpr {
match_first_pos := p.tok.position()
p.check(.key_match)
pos := p.tok.position()
is_mut := p.tok.kind == .key_mut
mut is_sum_type := false
if is_mut {
@ -1844,15 +1844,15 @@ fn (p mut Parser) match_expr() ast.MatchExpr {
cond := p.expr(0)
p.check(.lcbr)
mut branches := []ast.MatchBranch
mut have_final_else := false
for {
branch_first_pos := p.tok.position()
comment := p.check_comment() // comment before {}
mut exprs := []ast.Expr
branch_pos := p.tok.position()
p.open_scope()
// final else
mut is_else := false
if p.tok.kind == .key_else {
have_final_else = true
is_else = true
p.next()
} 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) {
@ -1890,21 +1890,31 @@ fn (p mut Parser) match_expr() ast.MatchExpr {
p.check(.comma)
}
}
branch_last_pos := p.tok.position()
// p.warn('match 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{
exprs: exprs
stmts: stmts
pos: branch_pos
pos: pos
comment: comment
is_else: is_else
}
p.close_scope()
if p.tok.kind == .rcbr {
break
}
}
if !have_final_else {
p.error('match must be exhaustive')
match_last_pos := p.tok.position()
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)
return ast.MatchExpr{

View File

@ -96,7 +96,9 @@ pub fn formatted_error(kind string /*error or warn*/, emsg string, filepath stri
continue
}
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 }
}else{
pointerline << if emanager.support_color { term.bold(term.blue('^')) } else { '^' }