Rollup merge of #128033 - Nadrieril:explain-empty-wildcards, r=compiler-errors
Explain why we require `_` for empty patterns This adds a note to the "non-exhaustive patterns" diagnostic to explain why we sometimes require extra `_` patterns on empty types. This is one of the two diagnostic improvements I wanted to do before [stabilizing `min_exhaustive_patterns`](https://github.com/rust-lang/rust/pull/122792). r? ``@compiler-errors``
This commit is contained in:
commit
d484654a5e
@ -16,8 +16,8 @@ use rustc_middle::ty::print::with_no_trimmed_paths;
|
|||||||
use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt};
|
use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt};
|
||||||
use rustc_pattern_analysis::errors::Uncovered;
|
use rustc_pattern_analysis::errors::Uncovered;
|
||||||
use rustc_pattern_analysis::rustc::{
|
use rustc_pattern_analysis::rustc::{
|
||||||
Constructor, DeconstructedPat, MatchArm, RustcPatCtxt as PatCtxt, Usefulness, UsefulnessReport,
|
Constructor, DeconstructedPat, MatchArm, RevealedTy, RustcPatCtxt as PatCtxt, Usefulness,
|
||||||
WitnessPat,
|
UsefulnessReport, WitnessPat,
|
||||||
};
|
};
|
||||||
use rustc_session::lint::builtin::{
|
use rustc_session::lint::builtin::{
|
||||||
BINDINGS_WITH_VARIANT_NAME, IRREFUTABLE_LET_PATTERNS, UNREACHABLE_PATTERNS,
|
BINDINGS_WITH_VARIANT_NAME, IRREFUTABLE_LET_PATTERNS, UNREACHABLE_PATTERNS,
|
||||||
@ -998,27 +998,31 @@ fn report_non_exhaustive_match<'p, 'tcx>(
|
|||||||
err.note(format!("the matched value is of type `{}`", scrut_ty));
|
err.note(format!("the matched value is of type `{}`", scrut_ty));
|
||||||
|
|
||||||
if !is_empty_match {
|
if !is_empty_match {
|
||||||
let mut non_exhaustive_tys = FxIndexSet::default();
|
let mut special_tys = FxIndexSet::default();
|
||||||
// Look at the first witness.
|
// Look at the first witness.
|
||||||
collect_non_exhaustive_tys(cx, &witnesses[0], &mut non_exhaustive_tys);
|
collect_special_tys(cx, &witnesses[0], &mut special_tys);
|
||||||
|
|
||||||
for ty in non_exhaustive_tys {
|
for ty in special_tys {
|
||||||
if ty.is_ptr_sized_integral() {
|
if ty.is_ptr_sized_integral() {
|
||||||
if ty == cx.tcx.types.usize {
|
if ty.inner() == cx.tcx.types.usize {
|
||||||
err.note(format!(
|
err.note(format!(
|
||||||
"`{ty}` does not have a fixed maximum value, so half-open ranges are necessary to match \
|
"`{ty}` does not have a fixed maximum value, so half-open ranges are necessary to match \
|
||||||
exhaustively",
|
exhaustively",
|
||||||
));
|
));
|
||||||
} else if ty == cx.tcx.types.isize {
|
} else if ty.inner() == cx.tcx.types.isize {
|
||||||
err.note(format!(
|
err.note(format!(
|
||||||
"`{ty}` does not have fixed minimum and maximum values, so half-open ranges are necessary to match \
|
"`{ty}` does not have fixed minimum and maximum values, so half-open ranges are necessary to match \
|
||||||
exhaustively",
|
exhaustively",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
} else if ty == cx.tcx.types.str_ {
|
} else if ty.inner() == cx.tcx.types.str_ {
|
||||||
err.note("`&str` cannot be matched exhaustively, so a wildcard `_` is necessary");
|
err.note("`&str` cannot be matched exhaustively, so a wildcard `_` is necessary");
|
||||||
} else if cx.is_foreign_non_exhaustive_enum(cx.reveal_opaque_ty(ty)) {
|
} 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"));
|
err.note(format!("`{ty}` is marked as non-exhaustive, so a wildcard `_` is necessary to match exhaustively"));
|
||||||
|
} else if cx.is_uninhabited(ty.inner()) && cx.tcx.features().min_exhaustive_patterns {
|
||||||
|
// The type is uninhabited yet there is a witness: we must be in the `MaybeInvalid`
|
||||||
|
// case.
|
||||||
|
err.note(format!("`{ty}` is uninhabited but is not being matched by value, so a wildcard `_` is required"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1168,22 +1172,22 @@ fn joined_uncovered_patterns<'p, 'tcx>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_non_exhaustive_tys<'tcx>(
|
/// Collect types that require specific explanations when they show up in witnesses.
|
||||||
|
fn collect_special_tys<'tcx>(
|
||||||
cx: &PatCtxt<'_, 'tcx>,
|
cx: &PatCtxt<'_, 'tcx>,
|
||||||
pat: &WitnessPat<'_, 'tcx>,
|
pat: &WitnessPat<'_, 'tcx>,
|
||||||
non_exhaustive_tys: &mut FxIndexSet<Ty<'tcx>>,
|
special_tys: &mut FxIndexSet<RevealedTy<'tcx>>,
|
||||||
) {
|
) {
|
||||||
if matches!(pat.ctor(), Constructor::NonExhaustive) {
|
if matches!(pat.ctor(), Constructor::NonExhaustive | Constructor::Never) {
|
||||||
non_exhaustive_tys.insert(pat.ty().inner());
|
special_tys.insert(*pat.ty());
|
||||||
}
|
}
|
||||||
if let Constructor::IntRange(range) = pat.ctor() {
|
if let Constructor::IntRange(range) = pat.ctor() {
|
||||||
if cx.is_range_beyond_boundaries(range, *pat.ty()) {
|
if cx.is_range_beyond_boundaries(range, *pat.ty()) {
|
||||||
// The range denotes the values before `isize::MIN` or the values after `usize::MAX`/`isize::MAX`.
|
// The range denotes the values before `isize::MIN` or the values after `usize::MAX`/`isize::MAX`.
|
||||||
non_exhaustive_tys.insert(pat.ty().inner());
|
special_tys.insert(*pat.ty());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pat.iter_fields()
|
pat.iter_fields().for_each(|field_pat| collect_special_tys(cx, field_pat, special_tys))
|
||||||
.for_each(|field_pat| collect_non_exhaustive_tys(cx, field_pat, non_exhaustive_tys))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn report_adt_defined_here<'tcx>(
|
fn report_adt_defined_here<'tcx>(
|
||||||
|
@ -40,9 +40,15 @@ pub type WitnessPat<'p, 'tcx> = crate::pat::WitnessPat<RustcPatCtxt<'p, 'tcx>>;
|
|||||||
///
|
///
|
||||||
/// Use `.inner()` or deref to get to the `Ty<'tcx>`.
|
/// Use `.inner()` or deref to get to the `Ty<'tcx>`.
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub struct RevealedTy<'tcx>(Ty<'tcx>);
|
pub struct RevealedTy<'tcx>(Ty<'tcx>);
|
||||||
|
|
||||||
|
impl<'tcx> fmt::Display for RevealedTy<'tcx> {
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
self.0.fmt(fmt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'tcx> fmt::Debug for RevealedTy<'tcx> {
|
impl<'tcx> fmt::Debug for RevealedTy<'tcx> {
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
self.0.fmt(fmt)
|
self.0.fmt(fmt)
|
||||||
|
@ -204,6 +204,7 @@ note: `Option<Void>` defined here
|
|||||||
|
|
|
|
||||||
= note: not covered
|
= note: not covered
|
||||||
= note: the matched value is of type `Option<Void>`
|
= note: the matched value is of type `Option<Void>`
|
||||||
|
= note: `Void` is uninhabited but is not being matched by value, so a wildcard `_` is required
|
||||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
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 ~ None => {},
|
||||||
@ -349,6 +350,7 @@ LL | match slice_never {
|
|||||||
| ^^^^^^^^^^^ pattern `&[_, ..]` not covered
|
| ^^^^^^^^^^^ pattern `&[_, ..]` not covered
|
||||||
|
|
|
|
||||||
= note: the matched value is of type `&[!]`
|
= note: the matched value is of type `&[!]`
|
||||||
|
= note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required
|
||||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
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 ~ [] => {},
|
LL ~ [] => {},
|
||||||
@ -484,6 +486,7 @@ note: `Option<!>` defined here
|
|||||||
|
|
|
|
||||||
= note: not covered
|
= note: not covered
|
||||||
= note: the matched value is of type `&Option<!>`
|
= note: the matched value is of type `&Option<!>`
|
||||||
|
= note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required
|
||||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
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 ~ &None => {},
|
||||||
@ -502,6 +505,7 @@ note: `Option<!>` defined here
|
|||||||
|
|
|
|
||||||
= note: not covered
|
= note: not covered
|
||||||
= note: the matched value is of type `Option<!>`
|
= note: the matched value is of type `Option<!>`
|
||||||
|
= note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required
|
||||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
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 ~ None => {},
|
||||||
@ -520,6 +524,7 @@ note: `Result<!, !>` defined here
|
|||||||
|
|
|
|
||||||
= note: not covered
|
= note: not covered
|
||||||
= note: the matched value is of type `Result<!, !>`
|
= note: the matched value is of type `Result<!, !>`
|
||||||
|
= note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required
|
||||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
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 ~ Ok(_) => {},
|
LL ~ Ok(_) => {},
|
||||||
@ -538,6 +543,7 @@ note: `Result<!, !>` defined here
|
|||||||
|
|
|
|
||||||
= note: not covered
|
= note: not covered
|
||||||
= note: the matched value is of type `Result<!, !>`
|
= note: the matched value is of type `Result<!, !>`
|
||||||
|
= note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required
|
||||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
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 ~ Ok(_a) => {},
|
LL ~ Ok(_a) => {},
|
||||||
@ -589,6 +595,7 @@ LL | match ref_never {
|
|||||||
| ^^^^^^^^^ pattern `&_` not covered
|
| ^^^^^^^^^ pattern `&_` not covered
|
||||||
|
|
|
|
||||||
= note: the matched value is of type `&!`
|
= note: the matched value is of type `&!`
|
||||||
|
= note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required
|
||||||
= note: references are always considered inhabited
|
= note: references are always considered inhabited
|
||||||
= note: match arms with guards don't count towards exhaustivity
|
= note: match arms with guards don't count towards exhaustivity
|
||||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
||||||
@ -609,6 +616,7 @@ note: `Result<!, !>` defined here
|
|||||||
|
|
|
|
||||||
= note: not covered
|
= note: not covered
|
||||||
= note: the matched value is of type `Result<!, !>`
|
= note: the matched value is of type `Result<!, !>`
|
||||||
|
= note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required
|
||||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
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 ~ Err(_) => {},
|
LL ~ Err(_) => {},
|
||||||
@ -627,6 +635,7 @@ note: `Option<Result<!, !>>` defined here
|
|||||||
|
|
|
|
||||||
= note: not covered
|
= note: not covered
|
||||||
= note: the matched value is of type `Option<Result<!, !>>`
|
= note: the matched value is of type `Option<Result<!, !>>`
|
||||||
|
= note: `Result<!, !>` is uninhabited but is not being matched by value, so a wildcard `_` is required
|
||||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
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 ~ None => {},
|
||||||
|
@ -5,6 +5,7 @@ LL | match nevers {
|
|||||||
| ^^^^^^ pattern `&[_, ..]` not covered
|
| ^^^^^^ pattern `&[_, ..]` not covered
|
||||||
|
|
|
|
||||||
= note: the matched value is of type `&[!]`
|
= note: the matched value is of type `&[!]`
|
||||||
|
= note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required
|
||||||
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
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 ~ &[] => (),
|
LL ~ &[] => (),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user