Skip to content

Commit 7262077

Browse files
committed
Consistently warn unreachable subpatterns
1 parent 753680a commit 7262077

File tree

5 files changed

+73
-39
lines changed

5 files changed

+73
-39
lines changed

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

+37-31
Original file line numberDiff line numberDiff line change
@@ -391,10 +391,26 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
391391
arms: &[MatchArm<'p, 'tcx>],
392392
scrut_ty: Ty<'tcx>,
393393
) -> Result<UsefulnessReport<'p, 'tcx>, ErrorGuaranteed> {
394-
rustc_pattern_analysis::analyze_match(&cx, &arms, scrut_ty).map_err(|err| {
395-
self.error = Err(err);
396-
err
397-
})
394+
let report =
395+
rustc_pattern_analysis::analyze_match(&cx, &arms, scrut_ty).map_err(|err| {
396+
self.error = Err(err);
397+
err
398+
})?;
399+
400+
// Warn unreachable subpatterns.
401+
for (arm, is_useful) in report.arm_usefulness.iter() {
402+
if let Usefulness::Useful(redundant_subpats) = is_useful
403+
&& !redundant_subpats.is_empty()
404+
{
405+
let mut redundant_subpats = redundant_subpats.clone();
406+
// Emit lints in the order in which they occur in the file.
407+
redundant_subpats.sort_unstable_by_key(|pat| pat.data().unwrap().span);
408+
for pat in redundant_subpats {
409+
report_unreachable_pattern(cx, arm.arm_data, pat.data().unwrap().span, None)
410+
}
411+
}
412+
}
413+
Ok(report)
398414
}
399415

400416
#[instrument(level = "trace", skip(self))]
@@ -567,7 +583,6 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
567583
) -> Result<RefutableFlag, ErrorGuaranteed> {
568584
let (cx, report) = self.analyze_binding(pat, Refutable, scrut)?;
569585
// Report if the pattern is unreachable, which can only occur when the type is uninhabited.
570-
// This also reports unreachable sub-patterns.
571586
report_arm_reachability(&cx, &report);
572587
// If the list of witnesses is empty, the match is exhaustive, i.e. the `if let` pattern is
573588
// irrefutable.
@@ -837,39 +852,30 @@ fn report_irrefutable_let_patterns(
837852
}
838853
}
839854

855+
/// Report unreachable arms, if any.
856+
fn report_unreachable_pattern<'p, 'tcx>(
857+
cx: &MatchCheckCtxt<'p, 'tcx>,
858+
hir_id: HirId,
859+
span: Span,
860+
catchall: Option<Span>,
861+
) {
862+
cx.tcx.emit_spanned_lint(
863+
UNREACHABLE_PATTERNS,
864+
hir_id,
865+
span,
866+
UnreachablePattern { span: if catchall.is_some() { Some(span) } else { None }, catchall },
867+
);
868+
}
869+
840870
/// Report unreachable arms, if any.
841871
fn report_arm_reachability<'p, 'tcx>(
842872
cx: &MatchCheckCtxt<'p, 'tcx>,
843873
report: &UsefulnessReport<'p, 'tcx>,
844874
) {
845-
let report_unreachable_pattern = |span, hir_id, catchall: Option<Span>| {
846-
cx.tcx.emit_spanned_lint(
847-
UNREACHABLE_PATTERNS,
848-
hir_id,
849-
span,
850-
UnreachablePattern {
851-
span: if catchall.is_some() { Some(span) } else { None },
852-
catchall,
853-
},
854-
);
855-
};
856-
857875
let mut catchall = None;
858876
for (arm, is_useful) in report.arm_usefulness.iter() {
859-
match is_useful {
860-
Usefulness::Redundant => {
861-
report_unreachable_pattern(arm.pat.data().unwrap().span, arm.arm_data, catchall)
862-
}
863-
Usefulness::Useful(redundant_subpats) if redundant_subpats.is_empty() => {}
864-
// The arm is reachable, but contains redundant subpatterns (from or-patterns).
865-
Usefulness::Useful(redundant_subpats) => {
866-
let mut redundant_subpats = redundant_subpats.clone();
867-
// Emit lints in the order in which they occur in the file.
868-
redundant_subpats.sort_unstable_by_key(|pat| pat.data().unwrap().span);
869-
for pat in redundant_subpats {
870-
report_unreachable_pattern(pat.data().unwrap().span, arm.arm_data, None);
871-
}
872-
}
877+
if matches!(is_useful, Usefulness::Redundant) {
878+
report_unreachable_pattern(cx, arm.arm_data, arm.pat.data().unwrap().span, catchall)
873879
}
874880
if !arm.has_guard && catchall.is_none() && pat_is_catchall(arm.pat) {
875881
catchall = Some(arm.pat.data().unwrap().span);

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

+2
Original file line numberDiff line numberDiff line change
@@ -162,12 +162,14 @@ fn main() {
162162
}
163163

164164
fn unreachable_in_param((_ | (_, _)): (bool, bool)) {}
165+
//~^ ERROR unreachable
165166

166167
fn unreachable_in_binding() {
167168
let bool_pair = (true, true);
168169
let bool_option = Some(true);
169170

170171
let (_ | (_, _)) = bool_pair;
172+
//~^ ERROR unreachable
171173
for (_ | (_, _)) in [bool_pair] {}
172174
//~^ ERROR unreachable
173175

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

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

187+
error: unreachable pattern
188+
--> $DIR/exhaustiveness-unreachable-pattern.rs:164:30
189+
|
190+
LL | fn unreachable_in_param((_ | (_, _)): (bool, bool)) {}
191+
| ^^^^^^
192+
187193
error: unreachable pattern
188194
--> $DIR/exhaustiveness-unreachable-pattern.rs:171:14
189195
|
196+
LL | let (_ | (_, _)) = bool_pair;
197+
| ^^^^^^
198+
199+
error: unreachable pattern
200+
--> $DIR/exhaustiveness-unreachable-pattern.rs:173:14
201+
|
190202
LL | for (_ | (_, _)) in [bool_pair] {}
191203
| ^^^^^^
192204

193205
error: unreachable pattern
194-
--> $DIR/exhaustiveness-unreachable-pattern.rs:174:20
206+
--> $DIR/exhaustiveness-unreachable-pattern.rs:176:20
195207
|
196208
LL | let (Some(_) | Some(true)) = bool_option else { return };
197209
| ^^^^^^^^^^
198210

199211
error: unreachable pattern
200-
--> $DIR/exhaustiveness-unreachable-pattern.rs:176:22
212+
--> $DIR/exhaustiveness-unreachable-pattern.rs:178:22
201213
|
202214
LL | if let Some(_) | Some(true) = bool_option {}
203215
| ^^^^^^^^^^
204216

205217
error: unreachable pattern
206-
--> $DIR/exhaustiveness-unreachable-pattern.rs:178:25
218+
--> $DIR/exhaustiveness-unreachable-pattern.rs:180:25
207219
|
208220
LL | while let Some(_) | Some(true) = bool_option {}
209221
| ^^^^^^^^^^
210222

211-
error: aborting due to 33 previous errors
223+
error: aborting due to 35 previous errors
212224

tests/ui/rfcs/rfc-0000-never_patterns/unreachable.exh_pats.stderr

+16-4
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,34 @@ LL | #![deny(unreachable_patterns)]
1111
| ^^^^^^^^^^^^^^^^^^^^
1212

1313
error: unreachable pattern
14-
--> $DIR/unreachable.rs:21:12
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
1521
|
1622
LL | if let Err(!) = res_void {}
1723
| ^^^^^^
1824

1925
error: unreachable pattern
20-
--> $DIR/unreachable.rs:23:24
26+
--> $DIR/unreachable.rs:24:24
2127
|
2228
LL | if let (Ok(true) | Err(!)) = res_void {}
2329
| ^^^^^^
2430

2531
error: unreachable pattern
26-
--> $DIR/unreachable.rs:25:23
32+
--> $DIR/unreachable.rs:26:23
2733
|
2834
LL | for (Ok(mut _x) | Err(!)) in [res_void] {}
2935
| ^^^^^^
3036

31-
error: aborting due to 4 previous errors
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
3244

tests/ui/rfcs/rfc-0000-never_patterns/unreachable.rs

+2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ fn main() {
1818
//[exh_pats]~^ ERROR unreachable
1919
}
2020
let (Ok(_x) | Err(!)) = res_void;
21+
//[exh_pats]~^ ERROR unreachable
2122
if let Err(!) = res_void {}
2223
//[exh_pats]~^ ERROR unreachable
2324
if let (Ok(true) | Err(!)) = res_void {}
@@ -27,3 +28,4 @@ fn main() {
2728
}
2829

2930
fn foo((Ok(_x) | Err(!)): Result<bool, Void>) {}
31+
//[exh_pats]~^ ERROR unreachable

0 commit comments

Comments
 (0)