about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2025-06-02 02:14:32 +0000
committerMichael Goulet <michael@errs.io>2025-06-02 02:19:34 +0000
commitd2d0f62f783b0640f7ededb25776a724031cebf7 (patch)
tree686b26a4ad9e8b56eb22c17f31ae7f04400638a3
parentec28ae9454139023117270985f114823d6570657 (diff)
downloadrust-d2d0f62f783b0640f7ededb25776a724031cebf7.tar.gz
rust-d2d0f62f783b0640f7ededb25776a724031cebf7.zip
Don't declare variables in ExprKind::Let in invalid positions
-rw-r--r--compiler/rustc_resolve/src/late.rs19
-rw-r--r--tests/ui/destructuring-assignment/bad-let-in-destructure.rs14
-rw-r--r--tests/ui/destructuring-assignment/bad-let-in-destructure.stderr19
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`.