Skip to content

Commit f194a84

Browse files
authored
Rollup merge of #120097 - Nadrieril:consistent_unreachable_subpats, r=compiler-errors
Report unreachable subpatterns consistently We weren't reporting unreachable subpatterns in function arguments and `let` expressions. This wasn't very important, but never patterns make it more relevant: a user might write `let (Ok(x) | Err(!)) = ...` in a case where `let Ok(x) = ...` is accepted, so we should report the `Err(!)` as redundant. r? ```@compiler-errors```
2 parents d942357 + 0a9bb97 commit f194a84

File tree

6 files changed

+182
-40
lines changed

6 files changed

+182
-40
lines changed

compiler/rustc_mir_build/src/thir/pattern/check_match.rs

+49-39
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
use rustc_pattern_analysis::errors::Uncovered;
22
use rustc_pattern_analysis::rustc::{
3-
Constructor, DeconstructedPat, RustcMatchCheckCtxt as MatchCheckCtxt, Usefulness,
3+
Constructor, DeconstructedPat, MatchArm, RustcMatchCheckCtxt as MatchCheckCtxt, Usefulness,
44
UsefulnessReport, WitnessPat,
55
};
6-
use rustc_pattern_analysis::{analyze_match, MatchArm};
76

87
use crate::errors::*;
98

@@ -390,6 +389,34 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
390389
}
391390
}
392391

392+
fn analyze_patterns(
393+
&mut self,
394+
cx: &MatchCheckCtxt<'p, 'tcx>,
395+
arms: &[MatchArm<'p, 'tcx>],
396+
scrut_ty: Ty<'tcx>,
397+
) -> Result<UsefulnessReport<'p, 'tcx>, ErrorGuaranteed> {
398+
let report =
399+
rustc_pattern_analysis::analyze_match(&cx, &arms, scrut_ty).map_err(|err| {
400+
self.error = Err(err);
401+
err
402+
})?;
403+
404+
// Warn unreachable subpatterns.
405+
for (arm, is_useful) in report.arm_usefulness.iter() {
406+
if let Usefulness::Useful(redundant_subpats) = is_useful
407+
&& !redundant_subpats.is_empty()
408+
{
409+
let mut redundant_subpats = redundant_subpats.clone();
410+
// Emit lints in the order in which they occur in the file.
411+
redundant_subpats.sort_unstable_by_key(|pat| pat.data().unwrap().span);
412+
for pat in redundant_subpats {
413+
report_unreachable_pattern(cx, arm.arm_data, pat.data().unwrap().span, None)
414+
}
415+
}
416+
}
417+
Ok(report)
418+
}
419+
393420
#[instrument(level = "trace", skip(self))]
394421
fn check_let(&mut self, pat: &'p Pat<'tcx>, scrutinee: Option<ExprId>, span: Span) {
395422
assert!(self.let_source != LetSource::None);
@@ -435,14 +462,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
435462
}
436463
}
437464

438-
let scrut_ty = scrut.ty;
439-
let report = match analyze_match(&cx, &tarms, scrut_ty) {
440-
Ok(report) => report,
441-
Err(err) => {
442-
self.error = Err(err);
443-
return;
444-
}
445-
};
465+
let Ok(report) = self.analyze_patterns(&cx, &tarms, scrut.ty) else { return };
446466

447467
match source {
448468
// Don't report arm reachability of desugared `match $iter.into_iter() { iter => .. }`
@@ -474,7 +494,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
474494
);
475495
} else {
476496
self.error = Err(report_non_exhaustive_match(
477-
&cx, self.thir, scrut_ty, scrut.span, witnesses, arms, expr_span,
497+
&cx, self.thir, scrut.ty, scrut.span, witnesses, arms, expr_span,
478498
));
479499
}
480500
}
@@ -556,7 +576,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
556576
let cx = self.new_cx(refutability, None, scrut, pat.span);
557577
let pat = self.lower_pattern(&cx, pat)?;
558578
let arms = [MatchArm { pat, arm_data: self.lint_level, has_guard: false }];
559-
let report = analyze_match(&cx, &arms, pat.ty().inner())?;
579+
let report = self.analyze_patterns(&cx, &arms, pat.ty().inner())?;
560580
Ok((cx, report))
561581
}
562582

@@ -567,7 +587,6 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
567587
) -> Result<RefutableFlag, ErrorGuaranteed> {
568588
let (cx, report) = self.analyze_binding(pat, Refutable, scrut)?;
569589
// Report if the pattern is unreachable, which can only occur when the type is uninhabited.
570-
// This also reports unreachable sub-patterns.
571590
report_arm_reachability(&cx, &report);
572591
// If the list of witnesses is empty, the match is exhaustive, i.e. the `if let` pattern is
573592
// irrefutable.
@@ -850,39 +869,30 @@ fn report_irrefutable_let_patterns(
850869
}
851870
}
852871

872+
/// Report unreachable arms, if any.
873+
fn report_unreachable_pattern<'p, 'tcx>(
874+
cx: &MatchCheckCtxt<'p, 'tcx>,
875+
hir_id: HirId,
876+
span: Span,
877+
catchall: Option<Span>,
878+
) {
879+
cx.tcx.emit_spanned_lint(
880+
UNREACHABLE_PATTERNS,
881+
hir_id,
882+
span,
883+
UnreachablePattern { span: if catchall.is_some() { Some(span) } else { None }, catchall },
884+
);
885+
}
886+
853887
/// Report unreachable arms, if any.
854888
fn report_arm_reachability<'p, 'tcx>(
855889
cx: &MatchCheckCtxt<'p, 'tcx>,
856890
report: &UsefulnessReport<'p, 'tcx>,
857891
) {
858-
let report_unreachable_pattern = |span, hir_id, catchall: Option<Span>| {
859-
cx.tcx.emit_spanned_lint(
860-
UNREACHABLE_PATTERNS,
861-
hir_id,
862-
span,
863-
UnreachablePattern {
864-
span: if catchall.is_some() { Some(span) } else { None },
865-
catchall,
866-
},
867-
);
868-
};
869-
870892
let mut catchall = None;
871893
for (arm, is_useful) in report.arm_usefulness.iter() {
872-
match is_useful {
873-
Usefulness::Redundant => {
874-
report_unreachable_pattern(arm.pat.data().unwrap().span, arm.arm_data, catchall)
875-
}
876-
Usefulness::Useful(redundant_subpats) if redundant_subpats.is_empty() => {}
877-
// The arm is reachable, but contains redundant subpatterns (from or-patterns).
878-
Usefulness::Useful(redundant_subpats) => {
879-
let mut redundant_subpats = redundant_subpats.clone();
880-
// Emit lints in the order in which they occur in the file.
881-
redundant_subpats.sort_unstable_by_key(|pat| pat.data().unwrap().span);
882-
for pat in redundant_subpats {
883-
report_unreachable_pattern(pat.data().unwrap().span, arm.arm_data, None);
884-
}
885-
}
894+
if matches!(is_useful, Usefulness::Redundant) {
895+
report_unreachable_pattern(cx, arm.arm_data, arm.pat.data().unwrap().span, catchall)
886896
}
887897
if !arm.has_guard && catchall.is_none() && pat_is_catchall(arm.pat) {
888898
catchall = Some(arm.pat.data().unwrap().span);

tests/ui/or-patterns/exhaustiveness-unreachable-pattern.rs

+20
Original file line numberDiff line numberDiff line change
@@ -160,3 +160,23 @@ fn main() {
160160
| (y, x) => {} //~ ERROR unreachable
161161
}
162162
}
163+
164+
fn unreachable_in_param((_ | (_, _)): (bool, bool)) {}
165+
//~^ ERROR unreachable
166+
167+
fn unreachable_in_binding() {
168+
let bool_pair = (true, true);
169+
let bool_option = Some(true);
170+
171+
let (_ | (_, _)) = bool_pair;
172+
//~^ ERROR unreachable
173+
for (_ | (_, _)) in [bool_pair] {}
174+
//~^ ERROR unreachable
175+
176+
let (Some(_) | Some(true)) = bool_option else { return };
177+
//~^ ERROR unreachable
178+
if let Some(_) | Some(true) = bool_option {}
179+
//~^ ERROR unreachable
180+
while let Some(_) | Some(true) = bool_option {}
181+
//~^ ERROR unreachable
182+
}

tests/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr

+37-1
Original file line numberDiff line numberDiff line change
@@ -184,5 +184,41 @@ error: unreachable pattern
184184
LL | | (y, x) => {}
185185
| ^^^^^^
186186

187-
error: aborting due to 29 previous errors
187+
error: unreachable pattern
188+
--> $DIR/exhaustiveness-unreachable-pattern.rs:164:30
189+
|
190+
LL | fn unreachable_in_param((_ | (_, _)): (bool, bool)) {}
191+
| ^^^^^^
192+
193+
error: unreachable pattern
194+
--> $DIR/exhaustiveness-unreachable-pattern.rs:171:14
195+
|
196+
LL | let (_ | (_, _)) = bool_pair;
197+
| ^^^^^^
198+
199+
error: unreachable pattern
200+
--> $DIR/exhaustiveness-unreachable-pattern.rs:173:14
201+
|
202+
LL | for (_ | (_, _)) in [bool_pair] {}
203+
| ^^^^^^
204+
205+
error: unreachable pattern
206+
--> $DIR/exhaustiveness-unreachable-pattern.rs:176:20
207+
|
208+
LL | let (Some(_) | Some(true)) = bool_option else { return };
209+
| ^^^^^^^^^^
210+
211+
error: unreachable pattern
212+
--> $DIR/exhaustiveness-unreachable-pattern.rs:178:22
213+
|
214+
LL | if let Some(_) | Some(true) = bool_option {}
215+
| ^^^^^^^^^^
216+
217+
error: unreachable pattern
218+
--> $DIR/exhaustiveness-unreachable-pattern.rs:180:25
219+
|
220+
LL | while let Some(_) | Some(true) = bool_option {}
221+
| ^^^^^^^^^^
222+
223+
error: aborting due to 35 previous errors
188224

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
error: unreachable pattern
2+
--> $DIR/unreachable.rs:17:9
3+
|
4+
LL | Err(!),
5+
| ^^^^^^
6+
|
7+
note: the lint level is defined here
8+
--> $DIR/unreachable.rs:7:9
9+
|
10+
LL | #![deny(unreachable_patterns)]
11+
| ^^^^^^^^^^^^^^^^^^^^
12+
13+
error: unreachable pattern
14+
--> $DIR/unreachable.rs:20:19
15+
|
16+
LL | let (Ok(_x) | Err(!)) = res_void;
17+
| ^^^^^^
18+
19+
error: unreachable pattern
20+
--> $DIR/unreachable.rs:22:12
21+
|
22+
LL | if let Err(!) = res_void {}
23+
| ^^^^^^
24+
25+
error: unreachable pattern
26+
--> $DIR/unreachable.rs:24:24
27+
|
28+
LL | if let (Ok(true) | Err(!)) = res_void {}
29+
| ^^^^^^
30+
31+
error: unreachable pattern
32+
--> $DIR/unreachable.rs:26:23
33+
|
34+
LL | for (Ok(mut _x) | Err(!)) in [res_void] {}
35+
| ^^^^^^
36+
37+
error: unreachable pattern
38+
--> $DIR/unreachable.rs:30:18
39+
|
40+
LL | fn foo((Ok(_x) | Err(!)): Result<bool, Void>) {}
41+
| ^^^^^^
42+
43+
error: aborting due to 6 previous errors
44+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// revisions: normal exh_pats
2+
//[normal] check-pass
3+
#![feature(never_patterns)]
4+
#![allow(incomplete_features)]
5+
#![cfg_attr(exh_pats, feature(exhaustive_patterns))]
6+
#![allow(dead_code, unreachable_code)]
7+
#![deny(unreachable_patterns)]
8+
9+
#[derive(Copy, Clone)]
10+
enum Void {}
11+
12+
fn main() {
13+
let res_void: Result<bool, Void> = Ok(true);
14+
15+
match res_void {
16+
Ok(_x) => {}
17+
Err(!),
18+
//[exh_pats]~^ ERROR unreachable
19+
}
20+
let (Ok(_x) | Err(!)) = res_void;
21+
//[exh_pats]~^ ERROR unreachable
22+
if let Err(!) = res_void {}
23+
//[exh_pats]~^ ERROR unreachable
24+
if let (Ok(true) | Err(!)) = res_void {}
25+
//[exh_pats]~^ ERROR unreachable
26+
for (Ok(mut _x) | Err(!)) in [res_void] {}
27+
//[exh_pats]~^ ERROR unreachable
28+
}
29+
30+
fn foo((Ok(_x) | Err(!)): Result<bool, Void>) {}
31+
//[exh_pats]~^ ERROR unreachable

tests/ui/unsafe/union_destructure.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// run-pass
2+
#![allow(unreachable_patterns)]
23

34
#[derive(Copy, Clone)]
45
#[allow(dead_code)]

0 commit comments

Comments
 (0)