checker: disallow most statements in if/match expression branches (#6509)

pull/6517/head
Nick Treleaven 2020-09-30 15:06:22 +01:00 committed by GitHub
parent 3a8be4d8d9
commit 18be7b115a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 83 additions and 2 deletions

View File

@ -34,6 +34,7 @@ pub struct Block {
pub:
stmts []Stmt
is_unsafe bool
pos token.Position
}
// | IncDecStmt k
@ -1097,6 +1098,19 @@ pub fn (expr Expr) is_expr() bool {
return true
}
// check if stmt can be an expression in C
pub fn (stmt Stmt) check_c_expr()? {
match stmt {
AssignStmt {return}
ExprStmt {
if stmt.expr.is_expr() {return}
return error('unsupported statement (`${typeof(stmt.expr)}`)')
}
else {}
}
return error('unsupported statement (`${typeof(stmt)}`)')
}
pub fn (stmt Stmt) position() token.Position {
match stmt {
AssertStmt { return stmt.pos }
@ -1104,8 +1118,9 @@ pub fn (stmt Stmt) position() token.Position {
/*
// Attr {
// }
// Block {
// }
*/
Block { return stmt.pos }
/*
// BranchStmt {
// }
*/

View File

@ -3002,6 +3002,13 @@ pub fn (mut c Checker) match_expr(mut node ast.MatchExpr) table.Type {
mut branch_without_return := false
for branch in node.branches {
c.stmts(branch.stmts)
if node.is_expr {
for st in branch.stmts {
st.check_c_expr() or {
c.error('`match` expression branch has $err', st.position())
}
}
}
// If the last statement is an expression, return its type
if branch.stmts.len > 0 {
match mut branch.stmts[branch.stmts.len - 1] as stmt {
@ -3404,6 +3411,11 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type {
c.error('`$if_kind` expression requires an expression as the last statement of every branch',
branch.pos)
}
for st in branch.stmts {
st.check_c_expr() or {
c.error('`if` expression branch has $err', st.position())
}
}
}
// Also check for returns inside a comp.if's statements, even if its contents aren't parsed
if has_return := c.has_return(branch.stmts) {

View File

@ -0,0 +1,28 @@
vlib/v/checker/tests/if_match_expr.vv:8:6: error: `if` expression branch has unsupported statement (`v.ast.ForStmt`)
6 | if true {1} else {-1} // OK
7 | } else {
8 | for {break}
| ^
9 | {}
10 | -1
vlib/v/checker/tests/if_match_expr.vv:9:2: error: `if` expression branch has unsupported statement (`v.ast.Block`)
7 | } else {
8 | for {break}
9 | {}
| ^
10 | -1
11 | }
vlib/v/checker/tests/if_match_expr.vv:15:10: error: `match` expression branch has unsupported statement (`v.ast.AssertStmt`)
13 | _ = match true {
14 | true {
15 | assert true
| ~~~~
16 | 1
17 | }
vlib/v/checker/tests/if_match_expr.vv:19:3: error: `match` expression branch has unsupported statement (`v.ast.IfExpr`)
17 | }
18 | else {
19 | if true {} // statement not expression
| ~~
20 | (-1) // parens needed ATM due to bug
21 | }

View File

@ -0,0 +1,22 @@
// only C expressions are allowed for each statement
_ = if true {
if true {} // FIXME should error, if statement
_ = if true {1} else {-1} // OK
if true {1} else {-1} // OK
} else {
for {break}
{}
-1
}
_ = match true {
true {
assert true
1
}
else {
if true {} // statement not expression
(-1) // parens needed ATM due to bug
}
}

View File

@ -557,9 +557,11 @@ pub fn (mut p Parser) stmt(is_top_level bool) ast.Stmt {
p.is_stmt_ident = p.tok.kind == .name
match p.tok.kind {
.lcbr {
pos := p.tok.position()
stmts := p.parse_block()
return ast.Block{
stmts: stmts
pos: pos
}
}
.key_assert {
@ -1989,6 +1991,7 @@ fn (mut p Parser) unsafe_stmt() ast.Stmt {
p.next()
return ast.Block{
is_unsafe: true
pos: pos
}
}
p.inside_unsafe = true
@ -2025,5 +2028,6 @@ fn (mut p Parser) unsafe_stmt() ast.Stmt {
return ast.Block{
stmts: stmts
is_unsafe: true
pos: pos
}
}