diff --git a/crates/sema/src/ast_passes.rs b/crates/sema/src/ast_passes.rs index 6b1c1b27..64ea0813 100644 --- a/crates/sema/src/ast_passes.rs +++ b/crates/sema/src/ast_passes.rs @@ -50,6 +50,16 @@ impl<'sess> AstValidator<'sess, '_> { fn in_loop(&self) -> bool { self.in_loop_depth != 0 } + + fn check_single_statement_variable_declaration(&self, stmt: &ast::Stmt<'_>) { + if matches!(stmt.kind, ast::StmtKind::DeclSingle(..) | ast::StmtKind::DeclMulti(..)) { + self.dcx() + .err("variable declarations can only be used inside blocks") + .span(stmt.span) + .help("wrap the statement in a block (`{ ... }`)") + .emit(); + } + } } impl<'ast> Visit<'ast> for AstValidator<'_, 'ast> { @@ -121,14 +131,21 @@ impl<'ast> Visit<'ast> for AstValidator<'_, 'ast> { fn visit_stmt(&mut self, stmt: &'ast ast::Stmt<'ast>) -> ControlFlow { match &stmt.kind { - ast::StmtKind::While(_, body, ..) - | ast::StmtKind::DoWhile(body, ..) + ast::StmtKind::While(_, body) + | ast::StmtKind::DoWhile(body, _) | ast::StmtKind::For { body, .. } => { self.in_loop_depth += 1; - let r = self.walk_stmt(body); + self.check_single_statement_variable_declaration(body); + let r = self.walk_stmt(stmt); self.in_loop_depth -= 1; return r; } + ast::StmtKind::If(_cond, then, else_) => { + self.check_single_statement_variable_declaration(then); + if let Some(else_) = else_ { + self.check_single_statement_variable_declaration(else_); + } + } ast::StmtKind::Break | ast::StmtKind::Continue => { if !self.in_loop() { let kind = if matches!(stmt.kind, ast::StmtKind::Break) { @@ -140,14 +157,14 @@ impl<'ast> Visit<'ast> for AstValidator<'_, 'ast> { self.dcx().err(msg).span(stmt.span).emit(); } } - ast::StmtKind::UncheckedBlock(block) => { + ast::StmtKind::UncheckedBlock(_block) => { if self.in_unchecked_block { self.dcx().err("`unchecked` blocks cannot be nested").span(stmt.span).emit(); } let prev = self.in_unchecked_block; self.in_unchecked_block = true; - let r = self.walk_block(block); + let r = self.walk_stmt(stmt); self.in_unchecked_block = prev; return r; } diff --git a/tests/ui/resolve/loops.sol b/tests/ui/resolve/loops.sol index e164168a..1b67b884 100644 --- a/tests/ui/resolve/loops.sol +++ b/tests/ui/resolve/loops.sol @@ -10,20 +10,17 @@ function funky() { for (; i < 40; i++) continue; for (; i++ < 50;) continue; - // --- - // TODO: `Error: Variable declarations can only be used inside blocks.` - - while (a == 0) uint a = 0; //~ ERROR: unresolved symbol + while (a == 0) { uint a = 0; } //~ ERROR: unresolved symbol a; //~ ERROR: unresolved symbol while (b == 0) { uint b = 0; } //~ ERROR: unresolved symbol b; //~ ERROR: unresolved symbol - do uint c; while (c == 0); //~ ERROR: unresolved symbol + do { uint c; } while (c == 0); //~ ERROR: unresolved symbol c; //~ ERROR: unresolved symbol do { uint d; } while (d == 0); //~ ERROR: unresolved symbol d; //~ ERROR: unresolved symbol - for (; false; e++) uint e; //~ ERROR: unresolved symbol + for (; false; e++) { uint e; } //~ ERROR: unresolved symbol e; //~ ERROR: unresolved symbol for (; false; f++) { uint f; } //~ ERROR: unresolved symbol f; //~ ERROR: unresolved symbol @@ -32,3 +29,4 @@ function funky() { } g; //~ ERROR: unresolved symbol } + diff --git a/tests/ui/resolve/loops.stderr b/tests/ui/resolve/loops.stderr index de63bf92..c231a54f 100644 --- a/tests/ui/resolve/loops.stderr +++ b/tests/ui/resolve/loops.stderr @@ -1,7 +1,7 @@ error: unresolved symbol `a` --> ROOT/tests/ui/resolve/loops.sol:LL:CC | -LL | while (a == 0) uint a = 0; +LL | while (a == 0) { uint a = 0; } | ^ | @@ -29,8 +29,8 @@ LL | b; error: unresolved symbol `c` --> ROOT/tests/ui/resolve/loops.sol:LL:CC | -LL | do uint c; while (c == 0); - | ^ +LL | do { uint c; } while (c == 0); + | ^ | error: unresolved symbol `c` @@ -57,7 +57,7 @@ LL | d; error: unresolved symbol `e` --> ROOT/tests/ui/resolve/loops.sol:LL:CC | -LL | for (; false; e++) uint e; +LL | for (; false; e++) { uint e; } | ^ | diff --git a/tests/ui/typeck/var_decl_as_loop_body.sol b/tests/ui/typeck/var_decl_as_loop_body.sol new file mode 100644 index 00000000..0e2f5fe5 --- /dev/null +++ b/tests/ui/typeck/var_decl_as_loop_body.sol @@ -0,0 +1,28 @@ +contract C { + function var_decl_inside_loops() external { + for (uint256 i = 0; i < 100; ++i) { + uint256 m_count = i + 1 * 2; + } + + for (uint256 i = 0; i < 100; ++i) uint256 m_count = i + 1 * 2; //~ ERROR: variable declarations can only be used inside blocks + for (uint256 i = 0; i < 100; ++i) + for (uint256 j = 0; i < 100; ++j) { + uint256 k = i + j; + } + + for (uint256 i = 0; i < 100; ++i) + for (uint256 j = 0; i < 100; ++j) uint256 k = i + j; //~ ERROR: variable declarations can only be used inside blocks + + while (true) uint256 x = 4; //~ ERROR: variable declarations can only be used inside blocks + + do uint256 x = 4; while (true); //~ ERROR: variable declarations can only be used inside blocks + + unchecked { + { + { + for (uint256 i = 0; i < 10; ++i) uint256 y = 0; //~ ERROR: variable declarations can only be used inside blocks + } + } + } + } +} diff --git a/tests/ui/typeck/var_decl_as_loop_body.stderr b/tests/ui/typeck/var_decl_as_loop_body.stderr new file mode 100644 index 00000000..5a06a978 --- /dev/null +++ b/tests/ui/typeck/var_decl_as_loop_body.stderr @@ -0,0 +1,42 @@ +error: variable declarations can only be used inside blocks + --> ROOT/tests/ui/typeck/var_decl_as_loop_body.sol:LL:CC + | +LL | for (uint256 i = 0; i < 100; ++i) uint256 m_count = i + 1 * 2; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: wrap the statement in a block (`{ ... }`) + +error: variable declarations can only be used inside blocks + --> ROOT/tests/ui/typeck/var_decl_as_loop_body.sol:LL:CC + | +LL | for (uint256 j = 0; i < 100; ++j) uint256 k = i + j; + | ^^^^^^^^^^^^^^^^^^ + | + = help: wrap the statement in a block (`{ ... }`) + +error: variable declarations can only be used inside blocks + --> ROOT/tests/ui/typeck/var_decl_as_loop_body.sol:LL:CC + | +LL | while (true) uint256 x = 4; + | ^^^^^^^^^^^^^^ + | + = help: wrap the statement in a block (`{ ... }`) + +error: variable declarations can only be used inside blocks + --> ROOT/tests/ui/typeck/var_decl_as_loop_body.sol:LL:CC + | +LL | do uint256 x = 4; while (true); + | ^^^^^^^^^^^^^^ + | + = help: wrap the statement in a block (`{ ... }`) + +error: variable declarations can only be used inside blocks + --> ROOT/tests/ui/typeck/var_decl_as_loop_body.sol:LL:CC + | +LL | ... for (uint256 i = 0; i < 10; ++i) uint256 y = 0; + | ^^^^^^^^^^^^^^ + | + = help: wrap the statement in a block (`{ ... }`) + +error: aborting due to 5 previous errors +