checker: disallow most statements in if/match expression branches (#6509)
							parent
							
								
									3a8be4d8d9
								
							
						
					
					
						commit
						18be7b115a
					
				| 
						 | 
				
			
			@ -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 {
 | 
			
		||||
		// }
 | 
			
		||||
		*/
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
	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
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue