Skip to content

Commit

Permalink
Auto merge of rust-lang#114397 - sebastiantoh:issue-85222, r=Nadrieril
Browse files Browse the repository at this point in the history
Add note when matching on tuples/ADTs containing non-exhaustive types

Fixes rust-lang#85222

r? `@Nadrieril`
  • Loading branch information
bors committed Aug 25, 2023
2 parents 4354192 + 82ce7b1 commit c75b6bd
Show file tree
Hide file tree
Showing 6 changed files with 276 additions and 14 deletions.
45 changes: 31 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 @@ -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,
Expand Down Expand Up @@ -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<Ty<'tcx>>,
) {
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>,
Expand Down Expand Up @@ -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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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!() }
Expand Down Expand Up @@ -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!() }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
struct A<T> {
a: T,
}

struct B<T, U>(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) => (),
}
}
Original file line number Diff line number Diff line change
@@ -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<usize>` 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<usize>`
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<usize>` 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<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 ~ 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<Option<Option<usize>>>` 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<Option<Option<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 ~ 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<usize>` defined here
--> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:1:8
|
LL | struct A<T> {
| ^
= note: the matched value is of type `A<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 ~ 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<isize, usize>` defined here
--> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:5:8
|
LL | struct B<T, U>(T, U);
| ^
= note: the matched value is of type `B<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 ~ 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<isize, usize>` defined here
--> $DIR/issue-85222-types-containing-non-exhaustive-types.rs:5:8
|
LL | struct B<T, U>(T, U);
| ^
= note: the matched value is of type `B<isize, 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 ~ B(_, 1..=usize::MAX) => (),
LL ~ B(_, _) => todo!(),
|

error: aborting due to 9 previous errors

For more information about this error, try `rustc --explain E0004`.
Original file line number Diff line number Diff line change
Expand Up @@ -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]) } => (),
Expand Down
2 changes: 2 additions & 0 deletions tests/ui/pattern/usefulness/tuple-struct-nonexhaustive.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down

0 comments on commit c75b6bd

Please sign in to comment.