diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index b2cb4240cf5..dbd719db559 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -391,10 +391,26 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { arms: &[MatchArm<'p, 'tcx>], scrut_ty: Ty<'tcx>, ) -> Result, ErrorGuaranteed> { - rustc_pattern_analysis::analyze_match(&cx, &arms, scrut_ty).map_err(|err| { - self.error = Err(err); - err - }) + let report = + rustc_pattern_analysis::analyze_match(&cx, &arms, scrut_ty).map_err(|err| { + self.error = Err(err); + err + })?; + + // Warn unreachable subpatterns. + for (arm, is_useful) in report.arm_usefulness.iter() { + if let Usefulness::Useful(redundant_subpats) = is_useful + && !redundant_subpats.is_empty() + { + let mut redundant_subpats = redundant_subpats.clone(); + // Emit lints in the order in which they occur in the file. + redundant_subpats.sort_unstable_by_key(|pat| pat.data().unwrap().span); + for pat in redundant_subpats { + report_unreachable_pattern(cx, arm.arm_data, pat.data().unwrap().span, None) + } + } + } + Ok(report) } #[instrument(level = "trace", skip(self))] @@ -567,7 +583,6 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { ) -> Result { let (cx, report) = self.analyze_binding(pat, Refutable, scrut)?; // Report if the pattern is unreachable, which can only occur when the type is uninhabited. - // This also reports unreachable sub-patterns. report_arm_reachability(&cx, &report); // If the list of witnesses is empty, the match is exhaustive, i.e. the `if let` pattern is // irrefutable. @@ -837,39 +852,30 @@ fn report_irrefutable_let_patterns( } } +/// Report unreachable arms, if any. +fn report_unreachable_pattern<'p, 'tcx>( + cx: &MatchCheckCtxt<'p, 'tcx>, + hir_id: HirId, + span: Span, + catchall: Option, +) { + cx.tcx.emit_spanned_lint( + UNREACHABLE_PATTERNS, + hir_id, + span, + UnreachablePattern { span: if catchall.is_some() { Some(span) } else { None }, catchall }, + ); +} + /// Report unreachable arms, if any. fn report_arm_reachability<'p, 'tcx>( cx: &MatchCheckCtxt<'p, 'tcx>, report: &UsefulnessReport<'p, 'tcx>, ) { - let report_unreachable_pattern = |span, hir_id, catchall: Option| { - cx.tcx.emit_spanned_lint( - UNREACHABLE_PATTERNS, - hir_id, - span, - UnreachablePattern { - span: if catchall.is_some() { Some(span) } else { None }, - catchall, - }, - ); - }; - let mut catchall = None; for (arm, is_useful) in report.arm_usefulness.iter() { - match is_useful { - Usefulness::Redundant => { - report_unreachable_pattern(arm.pat.data().unwrap().span, arm.arm_data, catchall) - } - Usefulness::Useful(redundant_subpats) if redundant_subpats.is_empty() => {} - // The arm is reachable, but contains redundant subpatterns (from or-patterns). - Usefulness::Useful(redundant_subpats) => { - let mut redundant_subpats = redundant_subpats.clone(); - // Emit lints in the order in which they occur in the file. - redundant_subpats.sort_unstable_by_key(|pat| pat.data().unwrap().span); - for pat in redundant_subpats { - report_unreachable_pattern(pat.data().unwrap().span, arm.arm_data, None); - } - } + if matches!(is_useful, Usefulness::Redundant) { + report_unreachable_pattern(cx, arm.arm_data, arm.pat.data().unwrap().span, catchall) } if !arm.has_guard && catchall.is_none() && pat_is_catchall(arm.pat) { catchall = Some(arm.pat.data().unwrap().span); diff --git a/tests/ui/or-patterns/exhaustiveness-unreachable-pattern.rs b/tests/ui/or-patterns/exhaustiveness-unreachable-pattern.rs index 324ba54e0e7..1ad335bf394 100644 --- a/tests/ui/or-patterns/exhaustiveness-unreachable-pattern.rs +++ b/tests/ui/or-patterns/exhaustiveness-unreachable-pattern.rs @@ -162,12 +162,14 @@ fn main() { } fn unreachable_in_param((_ | (_, _)): (bool, bool)) {} +//~^ ERROR unreachable fn unreachable_in_binding() { let bool_pair = (true, true); let bool_option = Some(true); let (_ | (_, _)) = bool_pair; + //~^ ERROR unreachable for (_ | (_, _)) in [bool_pair] {} //~^ ERROR unreachable diff --git a/tests/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr b/tests/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr index d47645a4689..336530d1b32 100644 --- a/tests/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr +++ b/tests/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr @@ -184,29 +184,41 @@ error: unreachable pattern LL | | (y, x) => {} | ^^^^^^ +error: unreachable pattern + --> $DIR/exhaustiveness-unreachable-pattern.rs:164:30 + | +LL | fn unreachable_in_param((_ | (_, _)): (bool, bool)) {} + | ^^^^^^ + error: unreachable pattern --> $DIR/exhaustiveness-unreachable-pattern.rs:171:14 | +LL | let (_ | (_, _)) = bool_pair; + | ^^^^^^ + +error: unreachable pattern + --> $DIR/exhaustiveness-unreachable-pattern.rs:173:14 + | LL | for (_ | (_, _)) in [bool_pair] {} | ^^^^^^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:174:20 + --> $DIR/exhaustiveness-unreachable-pattern.rs:176:20 | LL | let (Some(_) | Some(true)) = bool_option else { return }; | ^^^^^^^^^^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:176:22 + --> $DIR/exhaustiveness-unreachable-pattern.rs:178:22 | LL | if let Some(_) | Some(true) = bool_option {} | ^^^^^^^^^^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:178:25 + --> $DIR/exhaustiveness-unreachable-pattern.rs:180:25 | LL | while let Some(_) | Some(true) = bool_option {} | ^^^^^^^^^^ -error: aborting due to 33 previous errors +error: aborting due to 35 previous errors diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.exh_pats.stderr b/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.exh_pats.stderr index 5a1d9ca50bd..fe2a72d2a31 100644 --- a/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.exh_pats.stderr +++ b/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.exh_pats.stderr @@ -11,22 +11,34 @@ LL | #![deny(unreachable_patterns)] | ^^^^^^^^^^^^^^^^^^^^ error: unreachable pattern - --> $DIR/unreachable.rs:21:12 + --> $DIR/unreachable.rs:20:19 + | +LL | let (Ok(_x) | Err(!)) = res_void; + | ^^^^^^ + +error: unreachable pattern + --> $DIR/unreachable.rs:22:12 | LL | if let Err(!) = res_void {} | ^^^^^^ error: unreachable pattern - --> $DIR/unreachable.rs:23:24 + --> $DIR/unreachable.rs:24:24 | LL | if let (Ok(true) | Err(!)) = res_void {} | ^^^^^^ error: unreachable pattern - --> $DIR/unreachable.rs:25:23 + --> $DIR/unreachable.rs:26:23 | LL | for (Ok(mut _x) | Err(!)) in [res_void] {} | ^^^^^^ -error: aborting due to 4 previous errors +error: unreachable pattern + --> $DIR/unreachable.rs:30:18 + | +LL | fn foo((Ok(_x) | Err(!)): Result) {} + | ^^^^^^ + +error: aborting due to 6 previous errors diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.rs b/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.rs index 7bc695fd962..df8e22abf62 100644 --- a/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.rs +++ b/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.rs @@ -18,6 +18,7 @@ fn main() { //[exh_pats]~^ ERROR unreachable } let (Ok(_x) | Err(!)) = res_void; + //[exh_pats]~^ ERROR unreachable if let Err(!) = res_void {} //[exh_pats]~^ ERROR unreachable if let (Ok(true) | Err(!)) = res_void {} @@ -27,3 +28,4 @@ fn main() { } fn foo((Ok(_x) | Err(!)): Result) {} +//[exh_pats]~^ ERROR unreachable diff --git a/tests/ui/unsafe/union_destructure.rs b/tests/ui/unsafe/union_destructure.rs index d0cf8640eaa..bb75dbf0997 100644 --- a/tests/ui/unsafe/union_destructure.rs +++ b/tests/ui/unsafe/union_destructure.rs @@ -1,4 +1,5 @@ // run-pass +#![allow(unreachable_patterns)] #[derive(Copy, Clone)] #[allow(dead_code)]