Skip to content

Commit e444346

Browse files
committed
List missing constructors in an almost empty match
Actually empty matches are still handled by a different code path
1 parent 5a3b7d2 commit e444346

File tree

7 files changed

+115
-77
lines changed

7 files changed

+115
-77
lines changed

src/librustc_mir/hair/pattern/_match.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -770,6 +770,10 @@ impl<'tcx> Constructor<'tcx> {
770770
// Returns the set of constructors covered by `self` but not by
771771
// anything in `other_ctors`.
772772
fn subtract_ctors(&self, other_ctors: &Vec<Constructor<'tcx>>) -> Vec<Constructor<'tcx>> {
773+
if other_ctors.is_empty() {
774+
return vec![self.clone()];
775+
}
776+
773777
match self {
774778
// Those constructors can only match themselves.
775779
Single | Variant(_) | ConstantValue(..) | FloatRange(..) => {
@@ -1614,6 +1618,7 @@ pub fn is_useful<'p, 'tcx>(
16141618
v: &PatStack<'p, 'tcx>,
16151619
witness_preference: WitnessPreference,
16161620
hir_id: HirId,
1621+
is_top_level: bool,
16171622
) -> Usefulness<'tcx, 'p> {
16181623
let &Matrix(ref rows) = matrix;
16191624
debug!("is_useful({:#?}, {:#?})", matrix, v);
@@ -1641,7 +1646,7 @@ pub fn is_useful<'p, 'tcx>(
16411646
let mut unreachable_pats = Vec::new();
16421647
let mut any_is_useful = false;
16431648
for v in vs {
1644-
let res = is_useful(cx, &matrix, &v, witness_preference, hir_id);
1649+
let res = is_useful(cx, &matrix, &v, witness_preference, hir_id, false);
16451650
match res {
16461651
Useful(pats) => {
16471652
any_is_useful = true;
@@ -1741,7 +1746,7 @@ pub fn is_useful<'p, 'tcx>(
17411746
} else {
17421747
let matrix = matrix.specialize_wildcard();
17431748
let v = v.to_tail();
1744-
let usefulness = is_useful(cx, &matrix, &v, witness_preference, hir_id);
1749+
let usefulness = is_useful(cx, &matrix, &v, witness_preference, hir_id, false);
17451750

17461751
// In this case, there's at least one "free"
17471752
// constructor that is only matched against by
@@ -1770,7 +1775,9 @@ pub fn is_useful<'p, 'tcx>(
17701775
// `(<direction-1>, <direction-2>, true)` - we are
17711776
// satisfied with `(_, _, true)`. In this case,
17721777
// `used_ctors` is empty.
1773-
if missing_ctors.all_ctors_are_missing() {
1778+
// The exception is: if we are at the top-level, for example in an empty match, we
1779+
// prefer reporting the list of constructors instead of just `_`.
1780+
if missing_ctors.all_ctors_are_missing() && !is_top_level {
17741781
// All constructors are unused. Add a wild pattern
17751782
// rather than each individual constructor.
17761783
usefulness.apply_wildcard(pcx.ty)
@@ -1802,7 +1809,7 @@ fn is_useful_specialized<'p, 'tcx>(
18021809
cx.pattern_arena.alloc_from_iter(ctor.wildcard_subpatterns(cx, lty));
18031810
let matrix = matrix.specialize_constructor(cx, &ctor, ctor_wild_subpatterns);
18041811
v.specialize_constructor(cx, &ctor, ctor_wild_subpatterns)
1805-
.map(|v| is_useful(cx, &matrix, &v, witness_preference, hir_id))
1812+
.map(|v| is_useful(cx, &matrix, &v, witness_preference, hir_id, false))
18061813
.map(|u| u.apply_constructor(cx, &ctor, lty))
18071814
.unwrap_or(NotUseful)
18081815
}

src/librustc_mir/hair/pattern/check_match.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,7 @@ fn check_arms<'p, 'tcx>(
388388
for (arm_index, (pat, hir_pat, has_guard)) in arms.iter().enumerate() {
389389
let v = PatStack::from_pattern(pat);
390390

391-
match is_useful(cx, &seen, &v, LeaveOutWitness, hir_pat.hir_id) {
391+
match is_useful(cx, &seen, &v, LeaveOutWitness, hir_pat.hir_id, true) {
392392
NotUseful => {
393393
match source {
394394
hir::MatchSource::IfDesugar { .. } | hir::MatchSource::WhileDesugar => bug!(),
@@ -476,7 +476,8 @@ fn check_not_useful<'p, 'tcx>(
476476
hir_id: HirId,
477477
) -> Result<(), Vec<super::Pat<'tcx>>> {
478478
let wild_pattern = cx.pattern_arena.alloc(super::Pat::wildcard_from_ty(ty));
479-
match is_useful(cx, matrix, &PatStack::from_pattern(wild_pattern), ConstructWitness, hir_id) {
479+
let v = PatStack::from_pattern(wild_pattern);
480+
match is_useful(cx, matrix, &v, ConstructWitness, hir_id, true) {
480481
NotUseful => Ok(()), // This is good, wildcard pattern isn't reachable.
481482
UsefulWithWitness(pats) => Err(if pats.is_empty() {
482483
bug!("Exhaustiveness check returned no witnesses")

src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,26 @@
33
#![deny(unreachable_patterns)]
44
enum Foo {}
55

6-
struct NonEmptyStruct(bool);
7-
union NonEmptyUnion1 {
6+
struct NonEmptyStruct(bool); //~ `NonEmptyStruct` defined here
7+
union NonEmptyUnion1 { //~ `NonEmptyUnion1` defined here
88
foo: (),
99
}
10-
union NonEmptyUnion2 {
10+
union NonEmptyUnion2 { //~ `NonEmptyUnion2` defined here
1111
foo: (),
1212
bar: (),
1313
}
1414
enum NonEmptyEnum1 { //~ `NonEmptyEnum1` defined here
15-
Foo(bool), //~ variant not covered
15+
Foo(bool),
16+
//~^ variant not covered
17+
//~| not covered
1618
}
1719
enum NonEmptyEnum2 { //~ `NonEmptyEnum2` defined here
18-
Foo(bool), //~ variant not covered
19-
Bar, //~ variant not covered
20+
Foo(bool),
21+
//~^ variant not covered
22+
//~| not covered
23+
Bar,
24+
//~^ variant not covered
25+
//~| not covered
2026
}
2127
enum NonEmptyEnum5 { //~ `NonEmptyEnum5` defined here
2228
V1, V2, V3, V4, V5,
@@ -71,17 +77,17 @@ fn main() {
7177
//~^ ERROR multiple patterns of type `NonEmptyEnum5` are not handled
7278

7379
match_false!(0u8);
74-
//~^ ERROR `_` not covered
80+
//~^ ERROR `0u8..=std::u8::MAX` not covered
7581
match_false!(NonEmptyStruct(true));
76-
//~^ ERROR `_` not covered
82+
//~^ ERROR `NonEmptyStruct(_)` not covered
7783
match_false!((NonEmptyUnion1 { foo: () }));
78-
//~^ ERROR `_` not covered
84+
//~^ ERROR `NonEmptyUnion1 { .. }` not covered
7985
match_false!((NonEmptyUnion2 { foo: () }));
80-
//~^ ERROR `_` not covered
86+
//~^ ERROR `NonEmptyUnion2 { .. }` not covered
8187
match_false!(NonEmptyEnum1::Foo(true));
82-
//~^ ERROR `_` not covered
88+
//~^ ERROR `Foo(_)` not covered
8389
match_false!(NonEmptyEnum2::Foo(true));
84-
//~^ ERROR `_` not covered
90+
//~^ ERROR `Foo(_)` and `Bar` not covered
8591
match_false!(NonEmptyEnum5::V1);
86-
//~^ ERROR `_` not covered
92+
//~^ ERROR `V1`, `V2`, `V3` and 2 more not covered
8793
}

src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -103,26 +103,26 @@ LL | match_empty!(NonEmptyEnum5::V1);
103103
|
104104
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
105105

106-
error[E0004]: non-exhaustive patterns: `_` not covered
106+
error[E0004]: non-exhaustive patterns: `0u8..=std::u8::MAX` not covered
107107
--> $DIR/match-empty-exhaustive_patterns.rs:73:18
108108
|
109109
LL | match_false!(0u8);
110-
| ^^^ pattern `_` not covered
110+
| ^^^ pattern `0u8..=std::u8::MAX` not covered
111111
|
112112
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
113113

114-
error[E0004]: non-exhaustive patterns: `_` not covered
114+
error[E0004]: non-exhaustive patterns: `NonEmptyStruct(_)` not covered
115115
--> $DIR/match-empty-exhaustive_patterns.rs:75:18
116116
|
117117
LL | struct NonEmptyStruct(bool);
118118
| ---------------------------- `NonEmptyStruct` defined here
119119
...
120120
LL | match_false!(NonEmptyStruct(true));
121-
| ^^^^^^^^^^^^^^^^^^^^ pattern `_` not covered
121+
| ^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyStruct(_)` not covered
122122
|
123123
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
124124

125-
error[E0004]: non-exhaustive patterns: `_` not covered
125+
error[E0004]: non-exhaustive patterns: `NonEmptyUnion1 { .. }` not covered
126126
--> $DIR/match-empty-exhaustive_patterns.rs:77:18
127127
|
128128
LL | / union NonEmptyUnion1 {
@@ -131,11 +131,11 @@ LL | | }
131131
| |_- `NonEmptyUnion1` defined here
132132
...
133133
LL | match_false!((NonEmptyUnion1 { foo: () }));
134-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `_` not covered
134+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion1 { .. }` not covered
135135
|
136136
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
137137

138-
error[E0004]: non-exhaustive patterns: `_` not covered
138+
error[E0004]: non-exhaustive patterns: `NonEmptyUnion2 { .. }` not covered
139139
--> $DIR/match-empty-exhaustive_patterns.rs:79:18
140140
|
141141
LL | / union NonEmptyUnion2 {
@@ -145,38 +145,41 @@ LL | | }
145145
| |_- `NonEmptyUnion2` defined here
146146
...
147147
LL | match_false!((NonEmptyUnion2 { foo: () }));
148-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `_` not covered
148+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion2 { .. }` not covered
149149
|
150150
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
151151

152-
error[E0004]: non-exhaustive patterns: `_` not covered
152+
error[E0004]: non-exhaustive patterns: `Foo(_)` not covered
153153
--> $DIR/match-empty-exhaustive_patterns.rs:81:18
154154
|
155155
LL | / enum NonEmptyEnum1 {
156156
LL | | Foo(bool),
157+
| | --- not covered
157158
LL | | }
158159
| |_- `NonEmptyEnum1` defined here
159160
...
160161
LL | match_false!(NonEmptyEnum1::Foo(true));
161-
| ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `_` not covered
162+
| ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `Foo(_)` not covered
162163
|
163164
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
164165

165-
error[E0004]: non-exhaustive patterns: `_` not covered
166+
error[E0004]: non-exhaustive patterns: `Foo(_)` and `Bar` not covered
166167
--> $DIR/match-empty-exhaustive_patterns.rs:83:18
167168
|
168169
LL | / enum NonEmptyEnum2 {
169170
LL | | Foo(bool),
171+
| | --- not covered
170172
LL | | Bar,
173+
| | --- not covered
171174
LL | | }
172175
| |_- `NonEmptyEnum2` defined here
173176
...
174177
LL | match_false!(NonEmptyEnum2::Foo(true));
175-
| ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `_` not covered
178+
| ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `Foo(_)` and `Bar` not covered
176179
|
177180
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
178181

179-
error[E0004]: non-exhaustive patterns: `_` not covered
182+
error[E0004]: non-exhaustive patterns: `V1`, `V2`, `V3` and 2 more not covered
180183
--> $DIR/match-empty-exhaustive_patterns.rs:85:18
181184
|
182185
LL | / enum NonEmptyEnum5 {
@@ -185,7 +188,7 @@ LL | | }
185188
| |_- `NonEmptyEnum5` defined here
186189
...
187190
LL | match_false!(NonEmptyEnum5::V1);
188-
| ^^^^^^^^^^^^^^^^^ pattern `_` not covered
191+
| ^^^^^^^^^^^^^^^^^ patterns `V1`, `V2`, `V3` and 2 more not covered
189192
|
190193
= help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
191194

src/test/ui/pattern/usefulness/match-empty.rs

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,26 @@
22
#![deny(unreachable_patterns)]
33
enum Foo {}
44

5-
struct NonEmptyStruct(bool);
6-
union NonEmptyUnion1 {
5+
struct NonEmptyStruct(bool); //~ `NonEmptyStruct` defined here
6+
union NonEmptyUnion1 { //~ `NonEmptyUnion1` defined here
77
foo: (),
88
}
9-
union NonEmptyUnion2 {
9+
union NonEmptyUnion2 { //~ `NonEmptyUnion2` defined here
1010
foo: (),
1111
bar: (),
1212
}
1313
enum NonEmptyEnum1 { //~ `NonEmptyEnum1` defined here
14-
Foo(bool), //~ variant not covered
14+
Foo(bool),
15+
//~^ variant not covered
16+
//~| not covered
1517
}
1618
enum NonEmptyEnum2 { //~ `NonEmptyEnum2` defined here
17-
Foo(bool), //~ variant not covered
18-
Bar, //~ variant not covered
19+
Foo(bool),
20+
//~^ variant not covered
21+
//~| not covered
22+
Bar,
23+
//~^ variant not covered
24+
//~| not covered
1925
}
2026
enum NonEmptyEnum5 { //~ `NonEmptyEnum5` defined here
2127
V1, V2, V3, V4, V5,
@@ -70,17 +76,17 @@ fn main() {
7076
//~^ ERROR multiple patterns of type `NonEmptyEnum5` are not handled
7177

7278
match_false!(0u8);
73-
//~^ ERROR `_` not covered
79+
//~^ ERROR `0u8..=std::u8::MAX` not covered
7480
match_false!(NonEmptyStruct(true));
75-
//~^ ERROR `_` not covered
81+
//~^ ERROR `NonEmptyStruct(_)` not covered
7682
match_false!((NonEmptyUnion1 { foo: () }));
77-
//~^ ERROR `_` not covered
83+
//~^ ERROR `NonEmptyUnion1 { .. }` not covered
7884
match_false!((NonEmptyUnion2 { foo: () }));
79-
//~^ ERROR `_` not covered
85+
//~^ ERROR `NonEmptyUnion2 { .. }` not covered
8086
match_false!(NonEmptyEnum1::Foo(true));
81-
//~^ ERROR `_` not covered
87+
//~^ ERROR `Foo(_)` not covered
8288
match_false!(NonEmptyEnum2::Foo(true));
83-
//~^ ERROR `_` not covered
89+
//~^ ERROR `Foo(_)` and `Bar` not covered
8490
match_false!(NonEmptyEnum5::V1);
85-
//~^ ERROR `_` not covered
91+
//~^ ERROR `V1`, `V2`, `V3` and 2 more not covered
8692
}

0 commit comments

Comments
 (0)