diff options
| author | Michael Goulet <michael@errs.io> | 2025-06-02 02:14:32 +0000 |
|---|---|---|
| committer | Michael Goulet <michael@errs.io> | 2025-06-02 02:19:34 +0000 |
| commit | d2d0f62f783b0640f7ededb25776a724031cebf7 (patch) | |
| tree | 686b26a4ad9e8b56eb22c17f31ae7f04400638a3 | |
| parent | ec28ae9454139023117270985f114823d6570657 (diff) | |
| download | rust-d2d0f62f783b0640f7ededb25776a724031cebf7.tar.gz rust-d2d0f62f783b0640f7ededb25776a724031cebf7.zip | |
Don't declare variables in ExprKind::Let in invalid positions
| -rw-r--r-- | compiler/rustc_resolve/src/late.rs | 19 | ||||
| -rw-r--r-- | tests/ui/destructuring-assignment/bad-let-in-destructure.rs | 14 | ||||
| -rw-r--r-- | tests/ui/destructuring-assignment/bad-let-in-destructure.stderr | 19 |
3 files changed, 51 insertions, 1 deletions
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index fb1534d0b27..aaec376e687 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -4891,11 +4891,28 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { self.resolve_expr(e, Some(expr)); } - ExprKind::Let(ref pat, ref scrutinee, _, _) => { + ExprKind::Let(ref pat, ref scrutinee, _, Recovered::No) => { self.visit_expr(scrutinee); self.resolve_pattern_top(pat, PatternSource::Let); } + ExprKind::Let(ref pat, ref scrutinee, _, Recovered::Yes(_)) => { + self.visit_expr(scrutinee); + // This is basically a tweaked, inlined `resolve_pattern_top`. + let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())]; + self.resolve_pattern(pat, PatternSource::Let, &mut bindings); + // We still collect the bindings in this `let` expression which is in + // an invalid position (and therefore shouldn't declare variables into + // its parent scope). To avoid unnecessary errors though, we do just + // reassign the resolutions to `Res::Err`. + for (_, bindings) in &mut bindings { + for (_, binding) in bindings { + *binding = Res::Err; + } + } + self.apply_pattern_bindings(bindings); + } + ExprKind::If(ref cond, ref then, ref opt_else) => { self.with_rib(ValueNS, RibKind::Normal, |this| { let old = this.diag_metadata.in_if_condition.replace(cond); diff --git a/tests/ui/destructuring-assignment/bad-let-in-destructure.rs b/tests/ui/destructuring-assignment/bad-let-in-destructure.rs new file mode 100644 index 00000000000..222557a8975 --- /dev/null +++ b/tests/ui/destructuring-assignment/bad-let-in-destructure.rs @@ -0,0 +1,14 @@ +// Regression test for <https://github.com/rust-lang/rust/issues/141844>. + +fn main() { + // The following expression gets desugared into something like: + // ``` + // let (lhs,) = x; (let x = 1) = lhs; + // ``` + // This used to ICE since we haven't yet declared the type for `x` when + // checking the first desugared statement, whose RHS resolved to `x` since + // in the AST, the `let` expression was visited first. + (let x = 1,) = x; + //~^ ERROR expected expression, found `let` statement + //~| ERROR invalid left-hand side of assignment +} diff --git a/tests/ui/destructuring-assignment/bad-let-in-destructure.stderr b/tests/ui/destructuring-assignment/bad-let-in-destructure.stderr new file mode 100644 index 00000000000..277405539d8 --- /dev/null +++ b/tests/ui/destructuring-assignment/bad-let-in-destructure.stderr @@ -0,0 +1,19 @@ +error: expected expression, found `let` statement + --> $DIR/bad-let-in-destructure.rs:10:4 + | +LL | (let x = 1,) = x; + | ^^^ + | + = note: only supported directly in conditions of `if` and `while` expressions + +error[E0070]: invalid left-hand side of assignment + --> $DIR/bad-let-in-destructure.rs:10:16 + | +LL | (let x = 1,) = x; + | --------- ^ + | | + | cannot assign to this expression + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0070`. |
