From 778d27441d09ea17449433d34ebba5ec87c9a0ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Tue, 14 Apr 2026 10:47:37 +0200 Subject: [PATCH 1/2] also check let-else --- compiler/rustc_lint/src/internal.rs | 26 +++++++++++++++++-- .../internal-lints/must_match_exhaustively.rs | 3 +++ .../must_match_exhaustively.stderr | 20 ++++++++++++-- 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index c0b113610f67a..d637390851d9d 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -783,7 +783,7 @@ impl<'tcx> LateLintPass<'tcx> for RustcMustMatchExhaustively { } } } - hir::ExprKind::If(expr, ..) if let ExprKind::Let(expr) = expr.kind => { + hir::ExprKind::Let(expr, ..) => { if let Some(attr_span) = is_rustc_must_match_exhaustively(cx, expr.init.hir_id) { cx.emit_span_lint( RUSTC_MUST_MATCH_EXHAUSTIVELY, @@ -791,7 +791,29 @@ impl<'tcx> LateLintPass<'tcx> for RustcMustMatchExhaustively { RustcMustMatchExhaustivelyNotExhaustive { attr_span, pat_span: expr.span, - message: "using if let only matches on one variant (try using `match`)", + message: "using `if let` only matches on one variant (try using `match`)", + }, + ); + } + } + _ => {} + } + } + + fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx rustc_hir::Stmt<'tcx>) { + match stmt.kind { + rustc_hir::StmtKind::Let(let_stmt) => { + if let_stmt.els.is_some() + && let Some(attr_span) = + is_rustc_must_match_exhaustively(cx, let_stmt.pat.hir_id) + { + cx.emit_span_lint( + RUSTC_MUST_MATCH_EXHAUSTIVELY, + let_stmt.span, + RustcMustMatchExhaustivelyNotExhaustive { + attr_span, + pat_span: let_stmt.pat.span, + message: "using `let else` only matches on one variant (try using `match`)", }, ); } diff --git a/tests/ui-fulldeps/internal-lints/must_match_exhaustively.rs b/tests/ui-fulldeps/internal-lints/must_match_exhaustively.rs index cc3dcebd11cdd..e3a77471a0fe5 100644 --- a/tests/ui-fulldeps/internal-lints/must_match_exhaustively.rs +++ b/tests/ui-fulldeps/internal-lints/must_match_exhaustively.rs @@ -43,6 +43,9 @@ fn foo(f: Foo) { if let Foo::A { .. } = f {} //~^ ERROR match is not exhaustive + + let Foo::A { .. } = f else { loop {} }; + //~^ ERROR match is not exhaustive } fn main() {} diff --git a/tests/ui-fulldeps/internal-lints/must_match_exhaustively.stderr b/tests/ui-fulldeps/internal-lints/must_match_exhaustively.stderr index e17cfcefc4093..a6ef63c5aba8c 100644 --- a/tests/ui-fulldeps/internal-lints/must_match_exhaustively.stderr +++ b/tests/ui-fulldeps/internal-lints/must_match_exhaustively.stderr @@ -61,11 +61,27 @@ LL | if let Foo::A { .. } = f {} | ^^^^^^^^^^^^^^^^^^^^^ | = help: explicitly list all variants of the enum in a `match` -note: using if let only matches on one variant (try using `match`) +note: using `if let` only matches on one variant (try using `match`) --> $DIR/must_match_exhaustively.rs:44:8 | LL | if let Foo::A { .. } = f {} | ^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 4 previous errors +error: match is not exhaustive + --> $DIR/must_match_exhaustively.rs:47:5 + | +LL | #[rustc_must_match_exhaustively] + | -------------------------------- required because of this attribute +... +LL | let Foo::A { .. } = f else { loop {} }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: explicitly list all variants of the enum in a `match` +note: using `let else` only matches on one variant (try using `match`) + --> $DIR/must_match_exhaustively.rs:47:9 + | +LL | let Foo::A { .. } = f else { loop {} }; + | ^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors From 26a0fdcdcdaf41742a489026e1e540f74d7ecb82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Tue, 14 Apr 2026 10:59:42 +0200 Subject: [PATCH 2/2] fixup let-else on typing mode in the compiler --- compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs | 13 ++++++++++--- compiler/rustc_hir_typeck/src/opaque_types.rs | 16 ++++++++++++---- .../src/solve/assembly/mod.rs | 2 +- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index d3dcb65e71ee2..c863ac8e3e760 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -673,9 +673,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // being stalled on a coroutine. self.select_obligations_where_possible(|_| {}); - let ty::TypingMode::Analysis { defining_opaque_types_and_generators } = self.typing_mode() - else { - bug!(); + let defining_opaque_types_and_generators = match self.typing_mode() { + ty::TypingMode::Analysis { defining_opaque_types_and_generators } => { + defining_opaque_types_and_generators + } + ty::TypingMode::Coherence + | ty::TypingMode::Borrowck { .. } + | ty::TypingMode::PostBorrowckAnalysis { .. } + | ty::TypingMode::PostAnalysis => { + bug!() + } }; if defining_opaque_types_and_generators diff --git a/compiler/rustc_hir_typeck/src/opaque_types.rs b/compiler/rustc_hir_typeck/src/opaque_types.rs index 18c1bf39a7c13..b73bfabe92e60 100644 --- a/compiler/rustc_hir_typeck/src/opaque_types.rs +++ b/compiler/rustc_hir_typeck/src/opaque_types.rs @@ -1,8 +1,9 @@ use rustc_hir::def::DefKind; use rustc_infer::traits::ObligationCause; +use rustc_middle::bug; use rustc_middle::ty::{ self, DefiningScopeKind, DefinitionSiteHiddenType, OpaqueTypeKey, ProvisionalHiddenType, - TypeVisitableExt, TypingMode, + TypeVisitableExt, }; use rustc_trait_selection::error_reporting::infer::need_type_info::TypeAnnotationNeeded; use rustc_trait_selection::opaque_types::{ @@ -97,9 +98,16 @@ impl<'tcx> FnCtxt<'_, 'tcx> { debug!(?opaque_types); let tcx = self.tcx; - let TypingMode::Analysis { defining_opaque_types_and_generators } = self.typing_mode() - else { - unreachable!(); + let defining_opaque_types_and_generators = match self.typing_mode() { + ty::TypingMode::Analysis { defining_opaque_types_and_generators } => { + defining_opaque_types_and_generators + } + ty::TypingMode::Coherence + | ty::TypingMode::Borrowck { .. } + | ty::TypingMode::PostBorrowckAnalysis { .. } + | ty::TypingMode::PostAnalysis => { + bug!() + } }; for def_id in defining_opaque_types_and_generators { diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index 8d855be720252..2e17885b4f44d 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -442,7 +442,7 @@ where // normalizing the self type as well, since type variables are not uniquified. let goal = self.resolve_vars_if_possible(goal); - if let TypingMode::Coherence = self.typing_mode() + if self.typing_mode().is_coherence() && let Ok(candidate) = self.consider_coherence_unknowable_candidate(goal) { candidates.push(candidate);