checker: fail if else isn't the last branch of match

pull/4413/head
Enzo Baldisserri 2020-04-14 20:31:51 +02:00 committed by GitHub
parent 0c63f5c80d
commit 86402204a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 80 additions and 55 deletions

View File

@ -1355,69 +1355,80 @@ pub fn (c mut Checker) match_expr(node mut ast.MatchExpr) table.Type {
} }
else {} else {}
} }
if !node.branches[node.branches.len - 1].is_else { mut has_else := node.branches[node.branches.len - 1].is_else
mut used_values_count := 0 if !has_else {
for bi, branch in node.branches { for i, branch in node.branches {
used_values_count += branch.exprs.len if branch.is_else && i != node.branches.len - 1 {
for bi_ei, bexpr in branch.exprs { c.error('`else` must be the last branch of `match`', branch.pos)
match bexpr { has_else = true
ast.Type { break
tidx := table.type_idx(it.typ)
stidx := tidx.str()
all_possible_left_subtypes[stidx] = all_possible_left_subtypes[stidx] +
1
}
ast.EnumVal {
all_possible_left_enum_vals[it.val] = all_possible_left_enum_vals[it.val] +
1
}
else {}
}
} }
} }
mut err := false
mut err_details := 'match must be exhaustive' if !has_else {
unhandled := []string mut used_values_count := 0
match type_sym.info { for bi, branch in node.branches {
table.SumType { used_values_count += branch.exprs.len
for k, v in all_possible_left_subtypes { for bi_ei, bexpr in branch.exprs {
if v == 0 { match bexpr {
err = true ast.Type {
unhandled << '`' + c.table.type_to_str(table.new_type(k.int())) + '`' tidx := table.type_idx(it.typ)
} stidx := tidx.str()
if v > 1 { all_possible_left_subtypes[stidx] = all_possible_left_subtypes[stidx] +
err = true 1
multiple_type_name := '`' + c.table.type_to_str(table.new_type(k.int())) + }
'`' ast.EnumVal {
c.error('a match case for $multiple_type_name is handled more than once', all_possible_left_enum_vals[it.val] = all_possible_left_enum_vals[it.val] +
node.pos) 1
}
else {}
} }
} }
} }
table.Enum { mut err := false
for k, v in all_possible_left_enum_vals { mut err_details := 'match must be exhaustive'
if v == 0 { unhandled := []string
err = true match type_sym.info {
unhandled << '`.$k`' table.SumType {
} for k, v in all_possible_left_subtypes {
if v > 1 { if v == 0 {
err = true err = true
multiple_enum_val := '`.$k`' unhandled << '`' + c.table.type_to_str(table.new_type(k.int())) + '`'
c.error('a match case for $multiple_enum_val is handled more than once', }
node.pos) if v > 1 {
err = true
multiple_type_name := '`' + c.table.type_to_str(table.new_type(k.int())) +
'`'
c.error('a match case for $multiple_type_name is handled more than once',
node.pos)
}
} }
} }
table.Enum {
for k, v in all_possible_left_enum_vals {
if v == 0 {
err = true
unhandled << '`.$k`'
}
if v > 1 {
err = true
multiple_enum_val := '`.$k`'
c.error('a match case for $multiple_enum_val is handled more than once',
node.pos)
}
}
}
else {
err = true
}
} }
else { if err {
err = true if unhandled.len > 0 {
err_details += ' (add match branches for: ' + unhandled.join(', ') + ' or an else{} branch)'
}
c.error(err_details, node.pos)
} }
} }
if err {
if unhandled.len > 0 {
err_details += ' (add match branches for: ' + unhandled.join(', ') + ' or an else{} branch)'
}
c.error(err_details, node.pos)
}
} }
c.expected_type = cond_type c.expected_type = cond_type
mut ret_type := table.void_type mut ret_type := table.void_type

View File

@ -0,0 +1,7 @@
vlib/v/checker/tests/inout/match_else_last_expr.v:4:3: error: `else` must be the last branch of `match`
2| match 1 {
3| 1 { println('1') }
4| else { println('else') }
~~~~~~
5| 4 { println('4') }
6| }

View File

@ -0,0 +1,7 @@
fn main() {
match 1 {
1 { println('1') }
else { println('else') }
4 { println('4') }
}
}

View File

@ -1919,8 +1919,8 @@ fn (p mut Parser) match_expr() ast.MatchExpr {
// p.warn('match block') // p.warn('match block')
stmts := p.parse_block() stmts := p.parse_block()
pos := token.Position{ pos := token.Position{
line_nr: match_first_pos.line_nr line_nr: branch_first_pos.line_nr
pos: match_first_pos.pos pos: branch_first_pos.pos
len: branch_last_pos.pos - branch_first_pos.pos + branch_last_pos.len len: branch_last_pos.pos - branch_first_pos.pos + branch_last_pos.len
} }
branches << ast.MatchBranch{ branches << ast.MatchBranch{