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 383e80851f0d7..f46cf0dc0ff1b 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -7,6 +7,7 @@ use crate::errors::*; use rustc_arena::TypedArena; use rustc_ast::Mutability; +use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::{ struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, @@ -660,6 +661,17 @@ fn report_arm_reachability<'p, 'tcx>( } } +fn collect_non_exhaustive_tys<'p, 'tcx>( + pat: &DeconstructedPat<'p, 'tcx>, + non_exhaustive_tys: &mut FxHashSet>, +) { + if matches!(pat.ctor(), Constructor::NonExhaustive) { + non_exhaustive_tys.insert(pat.ty()); + } + pat.iter_fields() + .for_each(|field_pat| collect_non_exhaustive_tys(field_pat, non_exhaustive_tys)) +} + /// Report that a match is not exhaustive. fn non_exhaustive_match<'p, 'tcx>( cx: &MatchCheckCtxt<'p, 'tcx>, @@ -717,22 +729,27 @@ fn non_exhaustive_match<'p, 'tcx>( scrut_ty, if is_variant_list_non_exhaustive { ", which is marked as non-exhaustive" } else { "" } )); - if (scrut_ty == cx.tcx.types.usize || scrut_ty == cx.tcx.types.isize) - && !is_empty_match - && witnesses.len() == 1 - && matches!(witnesses[0].ctor(), Constructor::NonExhaustive) - { - err.note(format!( - "`{scrut_ty}` does not have a fixed maximum value, so a wildcard `_` is necessary to match \ - exhaustively", - )); - if cx.tcx.sess.is_nightly_build() { - err.help(format!( - "add `#![feature(precise_pointer_size_matching)]` to the crate attributes to \ - enable precise `{scrut_ty}` matching", - )); + + 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 { + err.note(format!( + "`{ty}` does not have a fixed maximum value, so a wildcard `_` is necessary to match \ + 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", + )); + } + } } } + if let ty::Ref(_, sub_ty, _) = scrut_ty.kind() { if !sub_ty.is_inhabited_from(cx.tcx, cx.module, cx.param_env) { err.note("references are always considered inhabited"); diff --git a/tests/ui/pattern/usefulness/integer-ranges/pointer-sized-int.deny.stderr b/tests/ui/pattern/usefulness/integer-ranges/pointer-sized-int.deny.stderr index 0e0f0c3e11ee8..df330c60b1e81 100644 --- a/tests/ui/pattern/usefulness/integer-ranges/pointer-sized-int.deny.stderr +++ b/tests/ui/pattern/usefulness/integer-ranges/pointer-sized-int.deny.stderr @@ -77,6 +77,8 @@ LL | m!((0usize, true), (0..5, true) | (5..=usize::MAX, true) | (0..=usize:: | ^^^^^^^^^^^^^^ pattern `(_, _)` not covered | = note: the matched value is of type `(usize, bool)` + = note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively + = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching 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 $s { $($t)+ => {}, (_, _) => todo!() } @@ -131,6 +133,8 @@ LL | m!((0isize, true), (isize::MIN..5, true) | ^^^^^^^^^^^^^^ pattern `(_, _)` not covered | = note: the matched value is of type `(isize, bool)` + = note: `isize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively + = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `isize` matching 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 $s { $($t)+ => {}, (_, _) => todo!() } diff --git a/tests/ui/pattern/usefulness/issue-85222-types-containing-non-exhaustive-types.rs b/tests/ui/pattern/usefulness/issue-85222-types-containing-non-exhaustive-types.rs new file mode 100644 index 0000000000000..8f58227ee2c64 --- /dev/null +++ b/tests/ui/pattern/usefulness/issue-85222-types-containing-non-exhaustive-types.rs @@ -0,0 +1,67 @@ +struct A { + a: T, +} + +struct B(T, U); + +fn main() { + match 0 { + //~^ ERROR non-exhaustive patterns: `_` not covered [E0004] + 0 => (), + 1..=usize::MAX => (), + } + + match (0usize, 0usize) { + //~^ ERROR non-exhaustive patterns: `(_, _)` not covered [E0004] + (0, 0) => (), + (1..=usize::MAX, 1..=usize::MAX) => (), + } + + match (0isize, 0usize) { + //~^ ERROR non-exhaustive patterns: `(_, _)` not covered [E0004] + (isize::MIN..=isize::MAX, 0) => (), + (isize::MIN..=isize::MAX, 1..=usize::MAX) => (), + } + + // Should not report note about usize not having fixed max value + match Some(1usize) { + //~^ ERROR non-exhaustive patterns: `Some(_)` not covered + None => {} + } + + match Some(4) { + //~^ ERROR non-exhaustive patterns: `Some(_)` not covered + Some(0) => (), + Some(1..=usize::MAX) => (), + None => (), + } + + match Some(Some(Some(0))) { + //~^ ERROR non-exhaustive patterns: `Some(Some(Some(_)))` not covered + Some(Some(Some(0))) => (), + Some(Some(Some(1..=usize::MAX))) => (), + Some(Some(None)) => (), + Some(None) => (), + None => (), + } + + match (A { a: 0usize }) { + //~^ ERROR non-exhaustive patterns: `A { .. }` not covered [E0004] + A { a: 0 } => (), + A { a: 1..=usize::MAX } => (), + } + + match B(0isize, 0usize) { + //~^ ERROR non-exhaustive patterns: `B(_, _)` not covered [E0004] + B(isize::MIN..=isize::MAX, 0) => (), + B(isize::MIN..=isize::MAX, 1..=usize::MAX) => (), + } + + // Should report only the note about usize not having fixed max value and not report + // report the note about isize + match B(0isize, 0usize) { + //~^ ERROR non-exhaustive patterns: `B(_, _)` not covered [E0004] + B(_, 0) => (), + B(_, 1..=usize::MAX) => (), + } +} diff --git a/tests/ui/pattern/usefulness/issue-85222-types-containing-non-exhaustive-types.stderr b/tests/ui/pattern/usefulness/issue-85222-types-containing-non-exhaustive-types.stderr new file mode 100644 index 0000000000000..ea1d99e20ae1f --- /dev/null +++ b/tests/ui/pattern/usefulness/issue-85222-types-containing-non-exhaustive-types.stderr @@ -0,0 +1,170 @@ +error[E0004]: non-exhaustive patterns: `_` not covered + --> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:8:11 + | +LL | match 0 { + | ^ pattern `_` not covered + | + = note: the matched value is of type `usize` + = note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively + = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching +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 ~ 1..=usize::MAX => (), +LL ~ _ => todo!(), + | + +error[E0004]: non-exhaustive patterns: `(_, _)` not covered + --> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:14:11 + | +LL | match (0usize, 0usize) { + | ^^^^^^^^^^^^^^^^ pattern `(_, _)` not covered + | + = note: the matched value is of type `(usize, usize)` + = note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively + = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching +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 ~ (1..=usize::MAX, 1..=usize::MAX) => (), +LL ~ (_, _) => todo!(), + | + +error[E0004]: non-exhaustive patterns: `(_, _)` not covered + --> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:20:11 + | +LL | match (0isize, 0usize) { + | ^^^^^^^^^^^^^^^^ pattern `(_, _)` not covered + | + = note: the matched value is of type `(isize, usize)` + = note: `isize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively + = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `isize` matching +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 ~ (isize::MIN..=isize::MAX, 1..=usize::MAX) => (), +LL ~ (_, _) => todo!(), + | + +error[E0004]: non-exhaustive patterns: `Some(_)` not covered + --> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:27:11 + | +LL | match Some(1usize) { + | ^^^^^^^^^^^^ 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` +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[E0004]: non-exhaustive patterns: `Some(_)` not covered + --> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:32:11 + | +LL | match Some(4) { + | ^^^^^^^ 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: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively + = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching +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[E0004]: non-exhaustive patterns: `Some(Some(Some(_)))` not covered + --> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:39:11 + | +LL | match Some(Some(Some(0))) { + | ^^^^^^^^^^^^^^^^^^^ pattern `Some(Some(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: not covered + | + = note: not covered + = note: the matched value is of type `Option>>` + = note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively + = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching +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(Some(Some(_))) => todo!(), + | + +error[E0004]: non-exhaustive patterns: `A { .. }` not covered + --> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:48:11 + | +LL | match (A { a: 0usize }) { + | ^^^^^^^^^^^^^^^^^ pattern `A { .. }` not covered + | +note: `A` defined here + --> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:1:8 + | +LL | struct A { + | ^ + = note: the matched value is of type `A` + = note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively + = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching +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 ~ A { a: 1..=usize::MAX } => (), +LL ~ A { .. } => todo!(), + | + +error[E0004]: non-exhaustive patterns: `B(_, _)` not covered + --> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:54:11 + | +LL | match B(0isize, 0usize) { + | ^^^^^^^^^^^^^^^^^ pattern `B(_, _)` not covered + | +note: `B` defined here + --> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:5:8 + | +LL | struct B(T, U); + | ^ + = note: the matched value is of type `B` + = note: `isize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively + = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `isize` matching +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 ~ B(isize::MIN..=isize::MAX, 1..=usize::MAX) => (), +LL ~ B(_, _) => todo!(), + | + +error[E0004]: non-exhaustive patterns: `B(_, _)` not covered + --> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:62:11 + | +LL | match B(0isize, 0usize) { + | ^^^^^^^^^^^^^^^^^ pattern `B(_, _)` not covered + | +note: `B` defined here + --> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:5:8 + | +LL | struct B(T, U); + | ^ + = note: the matched value is of type `B` + = note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively + = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching +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 ~ B(_, 1..=usize::MAX) => (), +LL ~ B(_, _) => todo!(), + | + +error: aborting due to 9 previous errors + +For more information about this error, try `rustc --explain E0004`. diff --git a/tests/ui/pattern/usefulness/non-exhaustive-pattern-witness.stderr b/tests/ui/pattern/usefulness/non-exhaustive-pattern-witness.stderr index b8af566de7c68..d798ec722ddd2 100644 --- a/tests/ui/pattern/usefulness/non-exhaustive-pattern-witness.stderr +++ b/tests/ui/pattern/usefulness/non-exhaustive-pattern-witness.stderr @@ -10,6 +10,8 @@ note: `Foo` defined here LL | struct Foo { | ^^^ = note: the matched value is of type `Foo` + = note: `usize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively + = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `usize` matching 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 ~ Foo { first: false, second: Some([1, 2, 3, 4]) } => (), diff --git a/tests/ui/pattern/usefulness/tuple-struct-nonexhaustive.stderr b/tests/ui/pattern/usefulness/tuple-struct-nonexhaustive.stderr index e2a65ff852404..50c7fc889f421 100644 --- a/tests/ui/pattern/usefulness/tuple-struct-nonexhaustive.stderr +++ b/tests/ui/pattern/usefulness/tuple-struct-nonexhaustive.stderr @@ -10,6 +10,8 @@ note: `Foo` defined here LL | struct Foo(isize, isize); | ^^^ = note: the matched value is of type `Foo` + = note: `isize` does not have a fixed maximum value, so a wildcard `_` is necessary to match exhaustively + = help: add `#![feature(precise_pointer_size_matching)]` to the crate attributes to enable precise `isize` matching 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 ~ Foo(2, b) => println!("{}", b),