diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 221544880e..434652ecd5 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -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 { // } */ diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index c19442ee8c..dc90bb085f 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -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) { diff --git a/vlib/v/checker/tests/if_match_expr.out b/vlib/v/checker/tests/if_match_expr.out new file mode 100644 index 0000000000..c0e63e31ca --- /dev/null +++ b/vlib/v/checker/tests/if_match_expr.out @@ -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 | } diff --git a/vlib/v/checker/tests/if_match_expr.vv b/vlib/v/checker/tests/if_match_expr.vv new file mode 100644 index 0000000000..025ab17727 --- /dev/null +++ b/vlib/v/checker/tests/if_match_expr.vv @@ -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 + } +} diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index d158af3434..214af5f08f 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -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 } }