Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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!(
"`{ty}` 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"));
}
}
}
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
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
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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of adding an additional note, I wonder if it's easier to just specialize the help message "ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown" below to be a bit more context specific, explaining something a bit more directly about how strings aren't exhaustive.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean something like the following?

help: as `str` cannot be matched exhaustively, ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown 

I think it would make the help message too lengthy. My understanding is that notes can be used to provide further explanation or context on what caused the error instead of cluttering the help message with too much context.

But I don't have a strong opinion on this, so I'll be happy to make the change if needed. If we do make the change, do you think it would be necessary to make the change for pointer-sized ints as well for consistency??

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would also prefer we didn't add a note, but I don't know how we'd phrase it. The tricky thing is that the non exhaustive type may be deep in a pattern somewhere (e.g. Result<Option<&str>, ...>), which prevents us from saying e.g. "ensure all possible strings are being handled".

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh oops forgot about this one before merging. I think it's ok like this, if one of you has a better idea we can make another PR. My secret plan is to suggest actual missing strings, something like patterns Some(""), Some("a"), Some("b") and many others not covered but I expect that will take some work.

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
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
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