diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index f46cf0dc0ff1b..95dced644e161 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -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")); } } } diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs index 08cfe98bb68b9..21031e8ba9d7e 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs @@ -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)); @@ -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> = split_wildcard + let mut new_patterns: Vec> = split_wildcard .iter_missing(pcx) .filter_map(|missing_ctor| { // Check if this variant is marked `doc(hidden)` @@ -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() diff --git a/tests/ui/closures/2229_closure_analysis/match/non-exhaustive-match.stderr b/tests/ui/closures/2229_closure_analysis/match/non-exhaustive-match.stderr index 3a5fad15421c6..0807f4590298f 100644 --- a/tests/ui/closures/2229_closure_analysis/match/non-exhaustive-match.stderr +++ b/tests/ui/closures/2229_closure_analysis/match/non-exhaustive-match.stderr @@ -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!() } }; diff --git a/tests/ui/match/match_non_exhaustive.stderr b/tests/ui/match/match_non_exhaustive.stderr index 46ee8d5179e6b..7b8bdfe0053ae 100644 --- a/tests/ui/match/match_non_exhaustive.stderr +++ b/tests/ui/match/match_non_exhaustive.stderr @@ -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!() }; diff --git a/tests/ui/pattern/usefulness/auxiliary/non-exhaustive.rs b/tests/ui/pattern/usefulness/auxiliary/non-exhaustive.rs new file mode 100644 index 0000000000000..6f459b8268f6f --- /dev/null +++ b/tests/ui/pattern/usefulness/auxiliary/non-exhaustive.rs @@ -0,0 +1,2 @@ +#[non_exhaustive] +pub enum NonExhaustiveEnum { A, B } diff --git a/tests/ui/pattern/usefulness/issue-105479-str-non-exhaustiveness.rs b/tests/ui/pattern/usefulness/issue-105479-str-non-exhaustiveness.rs new file mode 100644 index 0000000000000..0ee7856c6803a --- /dev/null +++ b/tests/ui/pattern/usefulness/issue-105479-str-non-exhaustiveness.rs @@ -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") => {} + } +} diff --git a/tests/ui/pattern/usefulness/issue-105479-str-non-exhaustiveness.stderr b/tests/ui/pattern/usefulness/issue-105479-str-non-exhaustiveness.stderr new file mode 100644 index 0000000000000..771fc320a13f6 --- /dev/null +++ b/tests/ui/pattern/usefulness/issue-105479-str-non-exhaustiveness.stderr @@ -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`. diff --git a/tests/ui/pattern/usefulness/issue-30240.stderr b/tests/ui/pattern/usefulness/issue-30240.stderr index ff755d681ac73..da8bbdffbf6d5 100644 --- a/tests/ui/pattern/usefulness/issue-30240.stderr +++ b/tests/ui/pattern/usefulness/issue-30240.stderr @@ -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" => {}, @@ -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" => {}, diff --git a/tests/ui/pattern/usefulness/nested-non-exhaustive-enums.rs b/tests/ui/pattern/usefulness/nested-non-exhaustive-enums.rs new file mode 100644 index 0000000000000..3a8a74d1fd65f --- /dev/null +++ b/tests/ui/pattern/usefulness/nested-non-exhaustive-enums.rs @@ -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` defined here + //~| NOTE the matched value is of type `Option` + //~| NOTE `NonExhaustiveEnum` is marked as non-exhaustive + Some(NonExhaustiveEnum::A) => {} + Some(NonExhaustiveEnum::B) => {} + None => {} + } +} diff --git a/tests/ui/pattern/usefulness/nested-non-exhaustive-enums.stderr b/tests/ui/pattern/usefulness/nested-non-exhaustive-enums.stderr new file mode 100644 index 0000000000000..9fbd871db7c9d --- /dev/null +++ b/tests/ui/pattern/usefulness/nested-non-exhaustive-enums.stderr @@ -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` 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` + = 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`. diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/enum.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/enum.stderr index 872cb9b8bc68e..4e7f3098ab482 100644 --- a/tests/ui/rfcs/rfc-2008-non-exhaustive/enum.stderr +++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/enum.stderr @@ -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", @@ -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 {