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/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..70a0403fc38cb --- /dev/null +++ b/tests/ui/destructuring-assignment/bad-let-in-destructure.rs @@ -0,0 +1,13 @@ +// 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 +} 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..622d714ba2fd4 --- /dev/null +++ b/tests/ui/destructuring-assignment/bad-let-in-destructure.stderr @@ -0,0 +1,10 @@ +error: expected expression, found `let` statement + --> $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: aborting due to 1 previous error +