Skip to content

Commit c75b6bd

Browse files
committed
Auto merge of rust-lang#114397 - sebastiantoh:issue-85222, r=Nadrieril
Add note when matching on tuples/ADTs containing non-exhaustive types Fixes rust-lang#85222 r? `@Nadrieril`
2 parents 4354192 + 82ce7b1 commit c75b6bd

6 files changed

+276
-14
lines changed

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

+31-14
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use crate::errors::*;
77

88
use rustc_arena::TypedArena;
99
use rustc_ast::Mutability;
10+
use rustc_data_structures::fx::FxHashSet;
1011
use rustc_data_structures::stack::ensure_sufficient_stack;
1112
use rustc_errors::{
1213
struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan,
@@ -660,6 +661,17 @@ fn report_arm_reachability<'p, 'tcx>(
660661
}
661662
}
662663

664+
fn collect_non_exhaustive_tys<'p, 'tcx>(
665+
pat: &DeconstructedPat<'p, 'tcx>,
666+
non_exhaustive_tys: &mut FxHashSet<Ty<'tcx>>,
667+
) {
668+
if matches!(pat.ctor(), Constructor::NonExhaustive) {
669+
non_exhaustive_tys.insert(pat.ty());
670+
}
671+
pat.iter_fields()
672+
.for_each(|field_pat| collect_non_exhaustive_tys(field_pat, non_exhaustive_tys))
673+
}
674+
663675
/// Report that a match is not exhaustive.
664676
fn non_exhaustive_match<'p, 'tcx>(
665677
cx: &MatchCheckCtxt<'p, 'tcx>,
@@ -717,22 +729,27 @@ fn non_exhaustive_match<'p, 'tcx>(
717729
scrut_ty,
718730
if is_variant_list_non_exhaustive { ", which is marked as non-exhaustive" } else { "" }
719731
));
720-
if (scrut_ty == cx.tcx.types.usize || scrut_ty == cx.tcx.types.isize)
721-
&& !is_empty_match
722-
&& witnesses.len() == 1
723-
&& matches!(witnesses[0].ctor(), Constructor::NonExhaustive)
724-
{
725-
err.note(format!(
726-
"`{scrut_ty}` does not have a fixed maximum value, so a wildcard `_` is necessary to match \
727-
exhaustively",
728-
));
729-
if cx.tcx.sess.is_nightly_build() {
730-
err.help(format!(
731-
"add `#![feature(precise_pointer_size_matching)]` to the crate attributes to \
732-
enable precise `{scrut_ty}` matching",
733-
));
732+
733+
if !is_empty_match && witnesses.len() == 1 {
734+
let mut non_exhaustive_tys = FxHashSet::default();
735+
collect_non_exhaustive_tys(&witnesses[0], &mut non_exhaustive_tys);
736+
737+
for ty in non_exhaustive_tys {
738+
if ty == cx.tcx.types.usize || ty == cx.tcx.types.isize {
739+
err.note(format!(
740+
"`{ty}` does not have a fixed maximum value, so a wildcard `_` is necessary to match \
741+
exhaustively",
742+
));
743+
if cx.tcx.sess.is_nightly_build() {
744+
err.help(format!(
745+
"add `#![feature(precise_pointer_size_matching)]` to the crate attributes to \
746+
enable precise `{ty}` matching",
747+
));
748+
}
749+
}
734750
}
735751
}
752+
736753
if let ty::Ref(_, sub_ty, _) = scrut_ty.kind() {
737754
if !sub_ty.is_inhabited_from(cx.tcx, cx.module, cx.param_env) {
738755
err.note("references are always considered inhabited");

tests/ui/pattern/usefulness/integer-ranges/pointer-sized-int.deny.stderr

+4
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ LL | m!((0usize, true), (0..5, true) | (5..=usize::MAX, true) | (0..=usize::
7777
| ^^^^^^^^^^^^^^ pattern `(_, _)` not covered
7878
|
7979
= note: the matched value is of type `(usize, bool)`
80+
= note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
81+
= help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
8082
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
8183
|
8284
LL | match $s { $($t)+ => {}, (_, _) => todo!() }
@@ -131,6 +133,8 @@ LL | m!((0isize, true), (isize::MIN..5, true)
131133
| ^^^^^^^^^^^^^^ pattern `(_, _)` not covered
132134
|
133135
= note: the matched value is of type `(isize, bool)`
136+
= note: `isize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
137+
= help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `isize` matching
134138
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
135139
|
136140
LL | match $s { $($t)+ => {}, (_, _) => todo!() }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
struct A<T> {
2+
a: T,
3+
}
4+
5+
struct B<T, U>(T, U);
6+
7+
fn main() {
8+
match 0 {
9+
//~^ ERROR non-exhaustive patterns: `_` not covered [E0004]
10+
0 => (),
11+
1..=usize::MAX => (),
12+
}
13+
14+
match (0usize, 0usize) {
15+
//~^ ERROR non-exhaustive patterns: `(_, _)` not covered [E0004]
16+
(0, 0) => (),
17+
(1..=usize::MAX, 1..=usize::MAX) => (),
18+
}
19+
20+
match (0isize, 0usize) {
21+
//~^ ERROR non-exhaustive patterns: `(_, _)` not covered [E0004]
22+
(isize::MIN..=isize::MAX, 0) => (),
23+
(isize::MIN..=isize::MAX, 1..=usize::MAX) => (),
24+
}
25+
26+
// Should not report note about usize not having fixed max value
27+
match Some(1usize) {
28+
//~^ ERROR non-exhaustive patterns: `Some(_)` not covered
29+
None => {}
30+
}
31+
32+
match Some(4) {
33+
//~^ ERROR non-exhaustive patterns: `Some(_)` not covered
34+
Some(0) => (),
35+
Some(1..=usize::MAX) => (),
36+
None => (),
37+
}
38+
39+
match Some(Some(Some(0))) {
40+
//~^ ERROR non-exhaustive patterns: `Some(Some(Some(_)))` not covered
41+
Some(Some(Some(0))) => (),
42+
Some(Some(Some(1..=usize::MAX))) => (),
43+
Some(Some(None)) => (),
44+
Some(None) => (),
45+
None => (),
46+
}
47+
48+
match (A { a: 0usize }) {
49+
//~^ ERROR non-exhaustive patterns: `A { .. }` not covered [E0004]
50+
A { a: 0 } => (),
51+
A { a: 1..=usize::MAX } => (),
52+
}
53+
54+
match B(0isize, 0usize) {
55+
//~^ ERROR non-exhaustive patterns: `B(_, _)` not covered [E0004]
56+
B(isize::MIN..=isize::MAX, 0) => (),
57+
B(isize::MIN..=isize::MAX, 1..=usize::MAX) => (),
58+
}
59+
60+
// Should report only the note about usize not having fixed max value and not report
61+
// report the note about isize
62+
match B(0isize, 0usize) {
63+
//~^ ERROR non-exhaustive patterns: `B(_, _)` not covered [E0004]
64+
B(_, 0) => (),
65+
B(_, 1..=usize::MAX) => (),
66+
}
67+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
error[E0004]: non-exhaustive patterns: `_` not covered
2+
--> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:8:11
3+
|
4+
LL | match 0 {
5+
| ^ pattern `_` not covered
6+
|
7+
= note: the matched value is of type `usize`
8+
= note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
9+
= help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
10+
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
11+
|
12+
LL ~ 1..=usize::MAX => (),
13+
LL ~ _ => todo!(),
14+
|
15+
16+
error[E0004]: non-exhaustive patterns: `(_, _)` not covered
17+
--> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:14:11
18+
|
19+
LL | match (0usize, 0usize) {
20+
| ^^^^^^^^^^^^^^^^ pattern `(_, _)` not covered
21+
|
22+
= note: the matched value is of type `(usize, usize)`
23+
= note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
24+
= help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
25+
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
26+
|
27+
LL ~ (1..=usize::MAX, 1..=usize::MAX) => (),
28+
LL ~ (_, _) => todo!(),
29+
|
30+
31+
error[E0004]: non-exhaustive patterns: `(_, _)` not covered
32+
--> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:20:11
33+
|
34+
LL | match (0isize, 0usize) {
35+
| ^^^^^^^^^^^^^^^^ pattern `(_, _)` not covered
36+
|
37+
= note: the matched value is of type `(isize, usize)`
38+
= note: `isize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
39+
= help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `isize` matching
40+
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
41+
|
42+
LL ~ (isize::MIN..=isize::MAX, 1..=usize::MAX) => (),
43+
LL ~ (_, _) => todo!(),
44+
|
45+
46+
error[E0004]: non-exhaustive patterns: `Some(_)` not covered
47+
--> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:27:11
48+
|
49+
LL | match Some(1usize) {
50+
| ^^^^^^^^^^^^ pattern `Some(_)` not covered
51+
|
52+
note: `Option<usize>` defined here
53+
--> $SRC_DIR/core/src/option.rs:LL:COL
54+
::: $SRC_DIR/core/src/option.rs:LL:COL
55+
|
56+
= note: not covered
57+
= note: the matched value is of type `Option<usize>`
58+
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
59+
|
60+
LL ~ None => {},
61+
LL + Some(_) => todo!()
62+
|
63+
64+
error[E0004]: non-exhaustive patterns: `Some(_)` not covered
65+
--> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:32:11
66+
|
67+
LL | match Some(4) {
68+
| ^^^^^^^ pattern `Some(_)` not covered
69+
|
70+
note: `Option<usize>` defined here
71+
--> $SRC_DIR/core/src/option.rs:LL:COL
72+
::: $SRC_DIR/core/src/option.rs:LL:COL
73+
|
74+
= note: not covered
75+
= note: the matched value is of type `Option<usize>`
76+
= note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
77+
= help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
78+
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
79+
|
80+
LL ~ None => (),
81+
LL ~ Some(_) => todo!(),
82+
|
83+
84+
error[E0004]: non-exhaustive patterns: `Some(Some(Some(_)))` not covered
85+
--> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:39:11
86+
|
87+
LL | match Some(Some(Some(0))) {
88+
| ^^^^^^^^^^^^^^^^^^^ pattern `Some(Some(Some(_)))` not covered
89+
|
90+
note: `Option<Option<Option<usize>>>` defined here
91+
--> $SRC_DIR/core/src/option.rs:LL:COL
92+
::: $SRC_DIR/core/src/option.rs:LL:COL
93+
|
94+
= note: not covered
95+
|
96+
= note: not covered
97+
|
98+
= note: not covered
99+
= note: the matched value is of type `Option<Option<Option<usize>>>`
100+
= note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
101+
= help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
102+
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
103+
|
104+
LL ~ None => (),
105+
LL ~ Some(Some(Some(_))) => todo!(),
106+
|
107+
108+
error[E0004]: non-exhaustive patterns: `A { .. }` not covered
109+
--> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:48:11
110+
|
111+
LL | match (A { a: 0usize }) {
112+
| ^^^^^^^^^^^^^^^^^ pattern `A { .. }` not covered
113+
|
114+
note: `A<usize>` defined here
115+
--> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:1:8
116+
|
117+
LL | struct A<T> {
118+
| ^
119+
= note: the matched value is of type `A<usize>`
120+
= note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
121+
= help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
122+
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
123+
|
124+
LL ~ A { a: 1..=usize::MAX } => (),
125+
LL ~ A { .. } => todo!(),
126+
|
127+
128+
error[E0004]: non-exhaustive patterns: `B(_, _)` not covered
129+
--> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:54:11
130+
|
131+
LL | match B(0isize, 0usize) {
132+
| ^^^^^^^^^^^^^^^^^ pattern `B(_, _)` not covered
133+
|
134+
note: `B<isize, usize>` defined here
135+
--> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:5:8
136+
|
137+
LL | struct B<T, U>(T, U);
138+
| ^
139+
= note: the matched value is of type `B<isize, usize>`
140+
= note: `isize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
141+
= help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `isize` matching
142+
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
143+
|
144+
LL ~ B(isize::MIN..=isize::MAX, 1..=usize::MAX) => (),
145+
LL ~ B(_, _) => todo!(),
146+
|
147+
148+
error[E0004]: non-exhaustive patterns: `B(_, _)` not covered
149+
--> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:62:11
150+
|
151+
LL | match B(0isize, 0usize) {
152+
| ^^^^^^^^^^^^^^^^^ pattern `B(_, _)` not covered
153+
|
154+
note: `B<isize, usize>` defined here
155+
--> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:5:8
156+
|
157+
LL | struct B<T, U>(T, U);
158+
| ^
159+
= note: the matched value is of type `B<isize, usize>`
160+
= note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
161+
= help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
162+
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
163+
|
164+
LL ~ B(_, 1..=usize::MAX) => (),
165+
LL ~ B(_, _) => todo!(),
166+
|
167+
168+
error: aborting due to 9 previous errors
169+
170+
For more information about this error, try `rustc --explain E0004`.

tests/ui/pattern/usefulness/non-exhaustive-pattern-witness.stderr

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ note: `Foo` defined here
1010
LL | struct Foo {
1111
| ^^^
1212
= note: the matched value is of type `Foo`
13+
= note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
14+
= help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching
1315
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
1416
|
1517
LL ~ Foo { first: false, second: Some([1, 2, 3, 4]) } => (),

tests/ui/pattern/usefulness/tuple-struct-nonexhaustive.stderr

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ note: `Foo` defined here
1010
LL | struct Foo(isize, isize);
1111
| ^^^
1212
= note: the matched value is of type `Foo`
13+
= note: `isize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively
14+
= help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `isize` matching
1315
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
1416
|
1517
LL ~ Foo(2, b) => println!("{}", b),

0 commit comments

Comments
 (0)