From 1fc6dbc32b0aba985af20e8eddfb05337f691dc5 Mon Sep 17 00:00:00 2001 From: GearsDatapacks Date: Tue, 12 Dec 2023 15:20:29 +0000 Subject: [PATCH] Change expr_trailing_brace to an exhaustive match to force new expression kinds to specify whether they contain a brace Add inline const and other possible curly brace expressions to expr_trailing_brace Add tests for `}` before `else` in `let...else` error Change to explicit cases for expressions with optional values when being checked for trailing braces Add tests for more complex cases of `}` before `else` in `let..else` statement Move other possible `}` cases into separate arm and add FIXME for future reference --- compiler/rustc_ast/src/util/classify.rs | 35 ++- tests/ui/parser/bad-let-else-statement.rs | 164 +++++++++++++ tests/ui/parser/bad-let-else-statement.stderr | 232 ++++++++++++++++++ 3 files changed, 428 insertions(+), 3 deletions(-) create mode 100644 tests/ui/parser/bad-let-else-statement.rs create mode 100644 tests/ui/parser/bad-let-else-statement.stderr diff --git a/compiler/rustc_ast/src/util/classify.rs b/compiler/rustc_ast/src/util/classify.rs index 821fca6656cdd..4dece07978376 100644 --- a/compiler/rustc_ast/src/util/classify.rs +++ b/compiler/rustc_ast/src/util/classify.rs @@ -40,15 +40,44 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> { | Range(_, Some(e), _) | Ret(Some(e)) | Unary(_, e) - | Yield(Some(e)) => { + | Yield(Some(e)) + | Yeet(Some(e)) + | Become(e) => { expr = e; } Closure(closure) => { expr = &closure.body; } Gen(..) | Block(..) | ForLoop(..) | If(..) | Loop(..) | Match(..) | Struct(..) - | TryBlock(..) | While(..) => break Some(expr), - _ => break None, + | TryBlock(..) | While(..) | ConstBlock(_) => break Some(expr), + + // FIXME: These can end in `}`, but changing these would break stable code. + InlineAsm(_) | OffsetOf(_, _) | MacCall(_) | IncludedBytes(_) | FormatArgs(_) => { + break None; + } + + Break(_, None) + | Range(_, None, _) + | Ret(None) + | Yield(None) + | Array(_) + | Call(_, _) + | MethodCall(_) + | Tup(_) + | Lit(_) + | Cast(_, _) + | Type(_, _) + | Await(_, _) + | Field(_, _) + | Index(_, _, _) + | Underscore + | Path(_, _) + | Continue(_) + | Repeat(_, _) + | Paren(_) + | Try(_) + | Yeet(None) + | Err => break None, } } } diff --git a/tests/ui/parser/bad-let-else-statement.rs b/tests/ui/parser/bad-let-else-statement.rs new file mode 100644 index 0000000000000..7b927c89ba074 --- /dev/null +++ b/tests/ui/parser/bad-let-else-statement.rs @@ -0,0 +1,164 @@ +#![feature(inline_const)] +#![feature(yeet_expr)] +#![allow(incomplete_features)] // Necessary for now, while explicit_tail_calls is incomplete +#![feature(explicit_tail_calls)] + +fn a() { + let foo = { + 1 + } else { + //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed + return; + }; +} + +fn b() { + let foo = for i in 1..2 { + break; + } else { + //~^ ERROR `for...else` loops are not supported + return; + }; +} + +fn c() { + let foo = if true { + 1 + } else { + 0 + } else { + //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed + return; + }; +} + +fn d() { + let foo = loop { + break; + } else { + //~^ ERROR loop...else` loops are not supported + return; + }; +} + +fn e() { + let foo = match true { + true => 1, + false => 0 + } else { + //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed + return; + }; +} + +struct X {a: i32} +fn f() { + let foo = X { + a: 1 + } else { + //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed + return; + }; +} + +fn g() { + let foo = while false { + break; + } else { + //~^ ERROR `while...else` loops are not supported + return; + }; +} + +fn h() { + let foo = const { + 1 + } else { + //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed + return; + }; +} + +fn i() { + let foo = &{ + 1 + } else { + //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed + return; + }; +} + +fn j() { + let bar = 0; + let foo = bar = { + 1 + } else { + //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed + return; + }; +} + +fn k() { + let foo = 1 + { + 1 + } else { + //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed + return; + }; +} + +fn l() { + let foo = 1..{ + 1 + } else { + //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed + return; + }; +} + +fn m() { + let foo = return { + () + } else { + //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed + return; + }; +} + +fn n() { + let foo = -{ + 1 + } else { + //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed + return; + }; +} + +fn o() -> Result<(), ()> { + let foo = do yeet { + () + } else { + //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed + return Ok(()); + }; +} + +fn p() { + let foo = become { + () + } else { + //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed + return; + }; +} + +fn q() { + let foo = |x: i32| { + x + } else { + //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed + return; + }; +} + +fn main() {} diff --git a/tests/ui/parser/bad-let-else-statement.stderr b/tests/ui/parser/bad-let-else-statement.stderr new file mode 100644 index 0000000000000..7cbda25e417f3 --- /dev/null +++ b/tests/ui/parser/bad-let-else-statement.stderr @@ -0,0 +1,232 @@ +error: right curly brace `}` before `else` in a `let...else` statement not allowed + --> $DIR/bad-let-else-statement.rs:9:5 + | +LL | } else { + | ^ + | +help: wrap the expression in parentheses + | +LL ~ let foo = ({ +LL | 1 +LL ~ }) else { + | + +error: `for...else` loops are not supported + --> $DIR/bad-let-else-statement.rs:18:7 + | +LL | let foo = for i in 1..2 { + | --- `else` is attached to this loop +LL | break; +LL | } else { + | _______^ +LL | | +LL | | return; +LL | | }; + | |_____^ + | + = note: consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run + +error: right curly brace `}` before `else` in a `let...else` statement not allowed + --> $DIR/bad-let-else-statement.rs:29:5 + | +LL | } else { + | ^ + | +help: wrap the expression in parentheses + | +LL ~ let foo = (if true { +LL | 1 +LL | } else { +LL | 0 +LL ~ }) else { + | + +error: `loop...else` loops are not supported + --> $DIR/bad-let-else-statement.rs:38:7 + | +LL | let foo = loop { + | ---- `else` is attached to this loop +LL | break; +LL | } else { + | _______^ +LL | | +LL | | return; +LL | | }; + | |_____^ + | + = note: consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run + +error: right curly brace `}` before `else` in a `let...else` statement not allowed + --> $DIR/bad-let-else-statement.rs:48:5 + | +LL | } else { + | ^ + | +help: wrap the expression in parentheses + | +LL ~ let foo = (match true { +LL | true => 1, +LL | false => 0 +LL ~ }) else { + | + +error: right curly brace `}` before `else` in a `let...else` statement not allowed + --> $DIR/bad-let-else-statement.rs:58:5 + | +LL | } else { + | ^ + | +help: wrap the expression in parentheses + | +LL ~ let foo = (X { +LL | a: 1 +LL ~ }) else { + | + +error: `while...else` loops are not supported + --> $DIR/bad-let-else-statement.rs:67:7 + | +LL | let foo = while false { + | ----- `else` is attached to this loop +LL | break; +LL | } else { + | _______^ +LL | | +LL | | return; +LL | | }; + | |_____^ + | + = note: consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run + +error: right curly brace `}` before `else` in a `let...else` statement not allowed + --> $DIR/bad-let-else-statement.rs:76:5 + | +LL | } else { + | ^ + | +help: wrap the expression in parentheses + | +LL ~ let foo = (const { +LL | 1 +LL ~ }) else { + | + +error: right curly brace `}` before `else` in a `let...else` statement not allowed + --> $DIR/bad-let-else-statement.rs:85:5 + | +LL | } else { + | ^ + | +help: wrap the expression in parentheses + | +LL ~ let foo = &({ +LL | 1 +LL ~ }) else { + | + +error: right curly brace `}` before `else` in a `let...else` statement not allowed + --> $DIR/bad-let-else-statement.rs:95:5 + | +LL | } else { + | ^ + | +help: wrap the expression in parentheses + | +LL ~ let foo = bar = ({ +LL | 1 +LL ~ }) else { + | + +error: right curly brace `}` before `else` in a `let...else` statement not allowed + --> $DIR/bad-let-else-statement.rs:104:5 + | +LL | } else { + | ^ + | +help: wrap the expression in parentheses + | +LL ~ let foo = 1 + ({ +LL | 1 +LL ~ }) else { + | + +error: right curly brace `}` before `else` in a `let...else` statement not allowed + --> $DIR/bad-let-else-statement.rs:113:5 + | +LL | } else { + | ^ + | +help: wrap the expression in parentheses + | +LL ~ let foo = 1..({ +LL | 1 +LL ~ }) else { + | + +error: right curly brace `}` before `else` in a `let...else` statement not allowed + --> $DIR/bad-let-else-statement.rs:122:5 + | +LL | } else { + | ^ + | +help: wrap the expression in parentheses + | +LL ~ let foo = return ({ +LL | () +LL ~ }) else { + | + +error: right curly brace `}` before `else` in a `let...else` statement not allowed + --> $DIR/bad-let-else-statement.rs:131:5 + | +LL | } else { + | ^ + | +help: wrap the expression in parentheses + | +LL ~ let foo = -({ +LL | 1 +LL ~ }) else { + | + +error: right curly brace `}` before `else` in a `let...else` statement not allowed + --> $DIR/bad-let-else-statement.rs:140:5 + | +LL | } else { + | ^ + | +help: wrap the expression in parentheses + | +LL ~ let foo = do yeet ({ +LL | () +LL ~ }) else { + | + +error: right curly brace `}` before `else` in a `let...else` statement not allowed + --> $DIR/bad-let-else-statement.rs:149:5 + | +LL | } else { + | ^ + | +help: wrap the expression in parentheses + | +LL ~ let foo = become ({ +LL | () +LL ~ }) else { + | + +error: right curly brace `}` before `else` in a `let...else` statement not allowed + --> $DIR/bad-let-else-statement.rs:158:5 + | +LL | } else { + | ^ + | +help: wrap the expression in parentheses + | +LL ~ let foo = |x: i32| ({ +LL | x +LL ~ }) else { + | + +error: aborting due to 17 previous errors +