checker: disallow most statements in if/match expression branches (#6509)
parent
3a8be4d8d9
commit
18be7b115a
|
@ -34,6 +34,7 @@ pub struct Block {
|
||||||
pub:
|
pub:
|
||||||
stmts []Stmt
|
stmts []Stmt
|
||||||
is_unsafe bool
|
is_unsafe bool
|
||||||
|
pos token.Position
|
||||||
}
|
}
|
||||||
|
|
||||||
// | IncDecStmt k
|
// | IncDecStmt k
|
||||||
|
@ -1097,6 +1098,19 @@ pub fn (expr Expr) is_expr() bool {
|
||||||
return true
|
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 {
|
pub fn (stmt Stmt) position() token.Position {
|
||||||
match stmt {
|
match stmt {
|
||||||
AssertStmt { return stmt.pos }
|
AssertStmt { return stmt.pos }
|
||||||
|
@ -1104,8 +1118,9 @@ pub fn (stmt Stmt) position() token.Position {
|
||||||
/*
|
/*
|
||||||
// Attr {
|
// Attr {
|
||||||
// }
|
// }
|
||||||
// Block {
|
*/
|
||||||
// }
|
Block { return stmt.pos }
|
||||||
|
/*
|
||||||
// BranchStmt {
|
// BranchStmt {
|
||||||
// }
|
// }
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -3002,6 +3002,13 @@ pub fn (mut c Checker) match_expr(mut node ast.MatchExpr) table.Type {
|
||||||
mut branch_without_return := false
|
mut branch_without_return := false
|
||||||
for branch in node.branches {
|
for branch in node.branches {
|
||||||
c.stmts(branch.stmts)
|
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 the last statement is an expression, return its type
|
||||||
if branch.stmts.len > 0 {
|
if branch.stmts.len > 0 {
|
||||||
match mut branch.stmts[branch.stmts.len - 1] as stmt {
|
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',
|
c.error('`$if_kind` expression requires an expression as the last statement of every branch',
|
||||||
branch.pos)
|
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
|
// 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) {
|
if has_return := c.has_return(branch.stmts) {
|
||||||
|
|
|
@ -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 | }
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -557,9 +557,11 @@ pub fn (mut p Parser) stmt(is_top_level bool) ast.Stmt {
|
||||||
p.is_stmt_ident = p.tok.kind == .name
|
p.is_stmt_ident = p.tok.kind == .name
|
||||||
match p.tok.kind {
|
match p.tok.kind {
|
||||||
.lcbr {
|
.lcbr {
|
||||||
|
pos := p.tok.position()
|
||||||
stmts := p.parse_block()
|
stmts := p.parse_block()
|
||||||
return ast.Block{
|
return ast.Block{
|
||||||
stmts: stmts
|
stmts: stmts
|
||||||
|
pos: pos
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.key_assert {
|
.key_assert {
|
||||||
|
@ -1989,6 +1991,7 @@ fn (mut p Parser) unsafe_stmt() ast.Stmt {
|
||||||
p.next()
|
p.next()
|
||||||
return ast.Block{
|
return ast.Block{
|
||||||
is_unsafe: true
|
is_unsafe: true
|
||||||
|
pos: pos
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.inside_unsafe = true
|
p.inside_unsafe = true
|
||||||
|
@ -2025,5 +2028,6 @@ fn (mut p Parser) unsafe_stmt() ast.Stmt {
|
||||||
return ast.Block{
|
return ast.Block{
|
||||||
stmts: stmts
|
stmts: stmts
|
||||||
is_unsafe: true
|
is_unsafe: true
|
||||||
|
pos: pos
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue