Skip to content

Commit

Permalink
Auto merge of #115270 - sebastiantoh:issue-105479, r=Nadrieril
Browse files Browse the repository at this point in the history
Add note on non-exhaustiveness when matching on str and nested non-exhaustive enums

Fixes #105479

r? `@Nadrieril`
  • Loading branch information
bors committed Sep 3, 2023
2 parents 2a1af89 + d87b87d commit 21305f4
Show file tree
Hide file tree
Showing 11 changed files with 119 additions and 41 deletions.
27 changes: 13 additions & 14 deletions compiler/rustc_mir_build/src/thir/pattern/check_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -720,32 +720,31 @@ fn non_exhaustive_match<'p, 'tcx>(
};
};

let is_variant_list_non_exhaustive = matches!(scrut_ty.kind(),
ty::Adt(def, _) if def.is_variant_list_non_exhaustive() && !def.did().is_local());

adt_defined_here(cx, &mut err, scrut_ty, &witnesses);
err.note(format!(
"the matched value is of type `{}`{}",
scrut_ty,
if is_variant_list_non_exhaustive { ", which is marked as non-exhaustive" } else { "" }
));
err.note(format!("the matched value is of type `{}`", scrut_ty));

if !is_empty_match && witnesses.len() == 1 {
let mut non_exhaustive_tys = FxHashSet::default();
collect_non_exhaustive_tys(&witnesses[0], &mut non_exhaustive_tys);

for ty in non_exhaustive_tys {
if ty == cx.tcx.types.usize || ty == cx.tcx.types.isize {
if ty.is_ptr_sized_integral() {
err.note(format!(
"`{ty}` does not have a fixed maximum value, so a wildcard `_` is necessary to match \
exhaustively",
));
exhaustively",
));
if cx.tcx.sess.is_nightly_build() {
err.help(format!(
"add `#![feature(precise_pointer_size_matching)]` to the crate attributes to \
enable precise `{ty}` matching",
));
"add `#![feature(precise_pointer_size_matching)]` to the crate attributes to \
enable precise `{ty}` matching",
));
}
} else if ty == cx.tcx.types.str_ {
err.note(format!(
"`&str` cannot be matched exhaustively, so a wildcard `_` is necessary",
));
} else if cx.is_foreign_non_exhaustive_enum(ty) {
err.note(format!("`{ty}` is marked as non-exhaustive, so a wildcard `_` is necessary to match exhaustively"));
}
}
}
Expand Down
49 changes: 26 additions & 23 deletions compiler/rustc_mir_build/src/thir/pattern/usefulness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -618,10 +618,15 @@ impl<'p, 'tcx> Usefulness<'p, 'tcx> {
let new_witnesses = if let Constructor::Missing { .. } = ctor {
// We got the special `Missing` constructor, so each of the missing constructors
// gives a new pattern that is not caught by the match. We list those patterns.
let new_patterns = if pcx.is_non_exhaustive {
// Here we don't want the user to try to list all variants, we want them to add
// a wildcard, so we only suggest that.
vec![DeconstructedPat::wildcard(pcx.ty, pcx.span)]
if pcx.is_non_exhaustive {
witnesses
.into_iter()
// Here we don't want the user to try to list all variants, we want them to add
// a wildcard, so we only suggest that.
.map(|witness| {
witness.apply_constructor(pcx, &Constructor::NonExhaustive)
})
.collect()
} else {
let mut split_wildcard = SplitWildcard::new(pcx);
split_wildcard.split(pcx, matrix.heads().map(DeconstructedPat::ctor));
Expand All @@ -633,7 +638,7 @@ impl<'p, 'tcx> Usefulness<'p, 'tcx> {
// constructor, that matches everything that can be built with
// it. For example, if `ctor` is a `Constructor::Variant` for
// `Option::Some`, we get the pattern `Some(_)`.
let mut new: Vec<DeconstructedPat<'_, '_>> = split_wildcard
let mut new_patterns: Vec<DeconstructedPat<'_, '_>> = split_wildcard
.iter_missing(pcx)
.filter_map(|missing_ctor| {
// Check if this variant is marked `doc(hidden)`
Expand All @@ -648,27 +653,25 @@ impl<'p, 'tcx> Usefulness<'p, 'tcx> {
.collect();

if hide_variant_show_wild {
new.push(DeconstructedPat::wildcard(pcx.ty, pcx.span));
new_patterns.push(DeconstructedPat::wildcard(pcx.ty, pcx.span));
}

new
};

witnesses
.into_iter()
.flat_map(|witness| {
new_patterns.iter().map(move |pat| {
Witness(
witness
.0
.iter()
.chain(once(pat))
.map(DeconstructedPat::clone_and_forget_reachability)
.collect(),
)
witnesses
.into_iter()
.flat_map(|witness| {
new_patterns.iter().map(move |pat| {
Witness(
witness
.0
.iter()
.chain(once(pat))
.map(DeconstructedPat::clone_and_forget_reachability)
.collect(),
)
})
})
})
.collect()
.collect()
}
} else {
witnesses
.into_iter()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ note: `E2` defined here
|
LL | pub enum E2 { A, B }
| ^^^^^^^^^^^
= note: the matched value is of type `E2`, which is marked as non-exhaustive
= note: the matched value is of type `E2`
= note: `E2` is marked as non-exhaustive, so a wildcard `_` is necessary to match exhaustively
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
LL | let _e = || { match e2 { E2::A => (), E2::B => (), _ => todo!() } };
Expand Down
3 changes: 2 additions & 1 deletion tests/ui/match/match_non_exhaustive.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ note: `E2` defined here
|
LL | pub enum E2 { A, B }
| ^^^^^^^^^^^
= note: the matched value is of type `E2`, which is marked as non-exhaustive
= note: the matched value is of type `E2`
= note: `E2` is marked as non-exhaustive, so a wildcard `_` is necessary to match exhaustively
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
LL | match e2 { E2::A => (), E2::B => (), _ => todo!() };
Expand Down
2 changes: 2 additions & 0 deletions tests/ui/pattern/usefulness/auxiliary/non-exhaustive.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#[non_exhaustive]
pub enum NonExhaustiveEnum { A, B }
12 changes: 12 additions & 0 deletions tests/ui/pattern/usefulness/issue-105479-str-non-exhaustiveness.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
fn main() {
let a = "";
let b = "";
match (a, b) {
//~^ ERROR non-exhaustive patterns: `(&_, _)` not covered [E0004]
//~| NOTE pattern `(&_, _)` not covered
//~| NOTE the matched value is of type `(&str, &str)`
//~| NOTE `&str` cannot be matched exhaustively, so a wildcard `_` is necessary
("a", "b") => {}
("c", "d") => {}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
error[E0004]: non-exhaustive patterns: `(&_, _)` not covered
--> $DIR/issue-105479-str-non-exhaustiveness.rs:4:11
|
LL | match (a, b) {
| ^^^^^^ pattern `(&_, _)` not covered
|
= note: the matched value is of type `(&str, &str)`
= note: `&str` cannot be matched exhaustively, so a wildcard `_` is necessary
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
LL ~ ("c", "d") => {},
LL + (&_, _) => todo!()
|

error: aborting due to previous error

For more information about this error, try `rustc --explain E0004`.
2 changes: 2 additions & 0 deletions tests/ui/pattern/usefulness/issue-30240.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ LL | match "world" {
| ^^^^^^^ pattern `&_` not covered
|
= note: the matched value is of type `&str`
= note: `&str` cannot be matched exhaustively, so a wildcard `_` is necessary
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
LL ~ "hello" => {},
Expand All @@ -18,6 +19,7 @@ LL | match "world" {
| ^^^^^^^ pattern `&_` not covered
|
= note: the matched value is of type `&str`
= note: `&str` cannot be matched exhaustively, so a wildcard `_` is necessary
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
LL ~ "hello" => {},
Expand Down
18 changes: 18 additions & 0 deletions tests/ui/pattern/usefulness/nested-non-exhaustive-enums.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// aux-build:non-exhaustive.rs

extern crate non_exhaustive;

use non_exhaustive::NonExhaustiveEnum;

fn main() {
match Some(NonExhaustiveEnum::A) {
//~^ ERROR non-exhaustive patterns: `Some(_)` not covered [E0004]
//~| NOTE pattern `Some(_)` not covered
//~| NOTE `Option<NonExhaustiveEnum>` defined here
//~| NOTE the matched value is of type `Option<NonExhaustiveEnum>`
//~| NOTE `NonExhaustiveEnum` is marked as non-exhaustive
Some(NonExhaustiveEnum::A) => {}
Some(NonExhaustiveEnum::B) => {}
None => {}
}
}
22 changes: 22 additions & 0 deletions tests/ui/pattern/usefulness/nested-non-exhaustive-enums.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
error[E0004]: non-exhaustive patterns: `Some(_)` not covered
--> $DIR/nested-non-exhaustive-enums.rs:8:11
|
LL | match Some(NonExhaustiveEnum::A) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `Some(_)` not covered
|
note: `Option<NonExhaustiveEnum>` defined here
--> $SRC_DIR/core/src/option.rs:LL:COL
::: $SRC_DIR/core/src/option.rs:LL:COL
|
= note: not covered
= note: the matched value is of type `Option<NonExhaustiveEnum>`
= note: `NonExhaustiveEnum` is marked as non-exhaustive, so a wildcard `_` is necessary to match exhaustively
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
LL ~ None => {},
LL + Some(_) => todo!()
|

error: aborting due to previous error

For more information about this error, try `rustc --explain E0004`.
5 changes: 3 additions & 2 deletions tests/ui/rfcs/rfc-2008-non-exhaustive/enum.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ note: `NonExhaustiveEnum` defined here
|
LL | pub enum NonExhaustiveEnum {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: the matched value is of type `NonExhaustiveEnum`, which is marked as non-exhaustive
= note: the matched value is of type `NonExhaustiveEnum`
= note: `NonExhaustiveEnum` is marked as non-exhaustive, so a wildcard `_` is necessary to match exhaustively
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
LL ~ NonExhaustiveEnum::Struct { .. } => "third",
Expand All @@ -46,7 +47,7 @@ note: `NonExhaustiveEnum` defined here
|
LL | pub enum NonExhaustiveEnum {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: the matched value is of type `NonExhaustiveEnum`, which is marked as non-exhaustive
= note: the matched value is of type `NonExhaustiveEnum`
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
LL ~ match enum_unit {
Expand Down

0 comments on commit 21305f4

Please sign in to comment.