From d2d0f62f783b0640f7ededb25776a724031cebf7 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 2 Jun 2025 02:14:32 +0000 Subject: [PATCH 1/2] Don't declare variables in ExprKind::Let in invalid positions --- compiler/rustc_resolve/src/late.rs | 19 ++++++++++++++++++- .../bad-let-in-destructure.rs | 14 ++++++++++++++ .../bad-let-in-destructure.stderr | 19 +++++++++++++++++++ 3 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 tests/ui/destructuring-assignment/bad-let-in-destructure.rs create mode 100644 tests/ui/destructuring-assignment/bad-let-in-destructure.stderr diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index fb1534d0b2798..aaec376e6879d 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 0000000000000..222557a8975d8 --- /dev/null +++ b/tests/ui/destructuring-assignment/bad-let-in-destructure.rs @@ -0,0 +1,14 @@ +// Regression test for . + +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 0000000000000..277405539d8f4 --- /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`. From 4a803d26ead24aa9cfdba3717fe2e9ffdc41f0e0 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 2 Jun 2025 02:19:32 +0000 Subject: [PATCH 2/2] Suppress redundant error --- compiler/rustc_hir/src/hir.rs | 7 +++++-- .../bad-let-in-destructure.rs | 1 - .../bad-let-in-destructure.stderr | 13 ++----------- 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index f63ab30368914..ea195fb2157b3 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2370,6 +2370,10 @@ impl Expr<'_> { // Lang item paths cannot currently be local variables or statics. ExprKind::Path(QPath::LangItem(..)) => false, + // Suppress errors for bad expressions. + ExprKind::Err(_guar) + | ExprKind::Let(&LetExpr { recovered: ast::Recovered::Yes(_guar), .. }) => true, + // Partially qualified paths in expressions can only legally // refer to associated items which are always rvalues. ExprKind::Path(QPath::TypeRelative(..)) @@ -2401,8 +2405,7 @@ impl Expr<'_> { | ExprKind::Binary(..) | ExprKind::Yield(..) | ExprKind::Cast(..) - | ExprKind::DropTemps(..) - | ExprKind::Err(_) => false, + | ExprKind::DropTemps(..) => false, } } diff --git a/tests/ui/destructuring-assignment/bad-let-in-destructure.rs b/tests/ui/destructuring-assignment/bad-let-in-destructure.rs index 222557a8975d8..70a0403fc38cb 100644 --- a/tests/ui/destructuring-assignment/bad-let-in-destructure.rs +++ b/tests/ui/destructuring-assignment/bad-let-in-destructure.rs @@ -10,5 +10,4 @@ fn main() { // 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 index 277405539d8f4..622d714ba2fd4 100644 --- a/tests/ui/destructuring-assignment/bad-let-in-destructure.stderr +++ b/tests/ui/destructuring-assignment/bad-let-in-destructure.stderr @@ -1,19 +1,10 @@ error: expected expression, found `let` statement - --> $DIR/bad-let-in-destructure.rs:10:4 + --> $DIR/bad-let-in-destructure.rs:11: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 +error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0070`.