Rollup merge of #111624 - cjgillot:private-uninhabited-pattern, r=petrochenkov
Emit diagnostic for privately uninhabited uncovered witnesses. Fixes https://github.com/rust-lang/rust/issues/104034 cc `@Nadrieril`
This commit is contained in:
commit
725cadb276
@ -340,6 +340,8 @@ mir_build_uncovered = {$count ->
|
|||||||
*[other] patterns `{$witness_1}`, `{$witness_2}`, `{$witness_3}` and {$remainder} more
|
*[other] patterns `{$witness_1}`, `{$witness_2}`, `{$witness_3}` and {$remainder} more
|
||||||
} not covered
|
} not covered
|
||||||
|
|
||||||
|
mir_build_privately_uninhabited = pattern `{$witness_1}` is currently uninhabited, but this variant contains private fields which may become inhabited in the future
|
||||||
|
|
||||||
mir_build_pattern_not_covered = refutable pattern in {$origin}
|
mir_build_pattern_not_covered = refutable pattern in {$origin}
|
||||||
.pattern_ty = the matched value is of type `{$pattern_ty}`
|
.pattern_ty = the matched value is of type `{$pattern_ty}`
|
||||||
|
|
||||||
|
@ -781,6 +781,8 @@ pub(crate) struct PatternNotCovered<'s, 'tcx> {
|
|||||||
pub interpreted_as_const: Option<InterpretedAsConst>,
|
pub interpreted_as_const: Option<InterpretedAsConst>,
|
||||||
#[subdiagnostic]
|
#[subdiagnostic]
|
||||||
pub adt_defined_here: Option<AdtDefinedHere<'tcx>>,
|
pub adt_defined_here: Option<AdtDefinedHere<'tcx>>,
|
||||||
|
#[note(mir_build_privately_uninhabited)]
|
||||||
|
pub witness_1_is_privately_uninhabited: Option<()>,
|
||||||
#[note(mir_build_pattern_ty)]
|
#[note(mir_build_pattern_ty)]
|
||||||
pub _p: (),
|
pub _p: (),
|
||||||
pub pattern_ty: Ty<'tcx>,
|
pub pattern_ty: Ty<'tcx>,
|
||||||
|
@ -479,12 +479,30 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
|
|||||||
AdtDefinedHere { adt_def_span, ty, variants }
|
AdtDefinedHere { adt_def_span, ty, variants }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Emit an extra note if the first uncovered witness is
|
||||||
|
// visibly uninhabited anywhere in the current crate.
|
||||||
|
let witness_1_is_privately_uninhabited =
|
||||||
|
if cx.tcx.features().exhaustive_patterns
|
||||||
|
&& let Some(witness_1) = witnesses.get(0)
|
||||||
|
&& let ty::Adt(adt, substs) = witness_1.ty().kind()
|
||||||
|
&& adt.is_enum()
|
||||||
|
&& let Constructor::Variant(variant_index) = witness_1.ctor()
|
||||||
|
{
|
||||||
|
let variant = adt.variant(*variant_index);
|
||||||
|
let inhabited = variant.inhabited_predicate(cx.tcx, *adt).subst(cx.tcx, substs);
|
||||||
|
assert!(inhabited.apply(cx.tcx, cx.param_env, cx.module));
|
||||||
|
!inhabited.apply_ignore_module(cx.tcx, cx.param_env)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
self.error = Err(self.tcx.sess.emit_err(PatternNotCovered {
|
self.error = Err(self.tcx.sess.emit_err(PatternNotCovered {
|
||||||
span: pat.span,
|
span: pat.span,
|
||||||
origin,
|
origin,
|
||||||
uncovered: Uncovered::new(pat.span, &cx, witnesses),
|
uncovered: Uncovered::new(pat.span, &cx, witnesses),
|
||||||
inform,
|
inform,
|
||||||
interpreted_as_const,
|
interpreted_as_const,
|
||||||
|
witness_1_is_privately_uninhabited: witness_1_is_privately_uninhabited.then_some(()),
|
||||||
_p: (),
|
_p: (),
|
||||||
pattern_ty,
|
pattern_ty,
|
||||||
let_suggestion,
|
let_suggestion,
|
||||||
|
@ -14,6 +14,7 @@ LL | enum Either<A, B> {
|
|||||||
LL | A(A),
|
LL | A(A),
|
||||||
LL | B(inner::Wrapper<B>),
|
LL | B(inner::Wrapper<B>),
|
||||||
| - not covered
|
| - not covered
|
||||||
|
= note: pattern `Either::B(_)` is currently uninhabited, but this variant contains private fields which may become inhabited in the future
|
||||||
= note: the matched value is of type `Either<(), !>`
|
= note: the matched value is of type `Either<(), !>`
|
||||||
help: you might want to use `if let` to ignore the variant that isn't matched
|
help: you might want to use `if let` to ignore the variant that isn't matched
|
||||||
|
|
|
|
||||||
|
@ -16,7 +16,9 @@ struct NotSoSecretlyEmpty {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum Foo {
|
enum Foo {
|
||||||
|
//~^ NOTE `Foo` defined here
|
||||||
A(foo::SecretlyEmpty),
|
A(foo::SecretlyEmpty),
|
||||||
|
//~^ NOTE not covered
|
||||||
B(foo::NotSoSecretlyEmpty),
|
B(foo::NotSoSecretlyEmpty),
|
||||||
C(NotSoSecretlyEmpty),
|
C(NotSoSecretlyEmpty),
|
||||||
D(u32, u32),
|
D(u32, u32),
|
||||||
@ -27,4 +29,9 @@ fn main() {
|
|||||||
let Foo::D(_y, _z) = x;
|
let Foo::D(_y, _z) = x;
|
||||||
//~^ ERROR refutable pattern in local binding
|
//~^ ERROR refutable pattern in local binding
|
||||||
//~| `Foo::A(_)` not covered
|
//~| `Foo::A(_)` not covered
|
||||||
|
//~| NOTE `let` bindings require an "irrefutable pattern"
|
||||||
|
//~| NOTE for more information
|
||||||
|
//~| NOTE pattern `Foo::A(_)` is currently uninhabited
|
||||||
|
//~| NOTE the matched value is of type `Foo`
|
||||||
|
//~| HELP you might want to use `let else`
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
error[E0005]: refutable pattern in local binding
|
error[E0005]: refutable pattern in local binding
|
||||||
--> $DIR/uninhabited-irrefutable.rs:27:9
|
--> $DIR/uninhabited-irrefutable.rs:29:9
|
||||||
|
|
|
|
||||||
LL | let Foo::D(_y, _z) = x;
|
LL | let Foo::D(_y, _z) = x;
|
||||||
| ^^^^^^^^^^^^^^ pattern `Foo::A(_)` not covered
|
| ^^^^^^^^^^^^^^ pattern `Foo::A(_)` not covered
|
||||||
@ -11,8 +11,10 @@ note: `Foo` defined here
|
|||||||
|
|
|
|
||||||
LL | enum Foo {
|
LL | enum Foo {
|
||||||
| ^^^
|
| ^^^
|
||||||
|
LL |
|
||||||
LL | A(foo::SecretlyEmpty),
|
LL | A(foo::SecretlyEmpty),
|
||||||
| - not covered
|
| - not covered
|
||||||
|
= note: pattern `Foo::A(_)` is currently uninhabited, but this variant contains private fields which may become inhabited in the future
|
||||||
= note: the matched value is of type `Foo`
|
= note: the matched value is of type `Foo`
|
||||||
help: you might want to use `let else` to handle the variant that isn't matched
|
help: you might want to use `let else` to handle the variant that isn't matched
|
||||||
|
|
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user