Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs')
-rw-r--r--crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs107
1 files changed, 87 insertions, 20 deletions
diff --git a/crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs b/crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs
index 84ded517ba..7011ed9f10 100644
--- a/crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs
+++ b/crates/hir-ty/src/diagnostics/match_check/deconstruct_pat.rs
@@ -51,13 +51,13 @@ use std::{
use hir_def::{EnumVariantId, HasModule, LocalFieldId, VariantId};
use smallvec::{smallvec, SmallVec};
use stdx::never;
-use syntax::SmolStr;
use crate::{infer::normalize, AdtId, Interner, Scalar, Ty, TyExt, TyKind};
use super::{
+ is_box,
usefulness::{helper::Captures, MatchCheckCtx, PatCtxt},
- Pat, PatKind,
+ FieldPat, Pat, PatKind,
};
use self::Constructor::*;
@@ -144,6 +144,24 @@ impl IntRange {
}
}
+ fn to_pat(&self, _cx: &MatchCheckCtx, ty: Ty) -> Pat {
+ match ty.kind(Interner) {
+ TyKind::Scalar(Scalar::Bool) => {
+ let kind = match self.boundaries() {
+ (0, 0) => PatKind::LiteralBool { value: false },
+ (1, 1) => PatKind::LiteralBool { value: true },
+ (0, 1) => PatKind::Wild,
+ (lo, hi) => {
+ never!("bad range for bool pattern: {}..={}", lo, hi);
+ PatKind::Wild
+ }
+ };
+ Pat { ty, kind: kind.into() }
+ }
+ _ => unimplemented!(),
+ }
+ }
+
/// See `Constructor::is_covered_by`
fn is_covered_by(&self, other: &Self) -> bool {
if self.intersection(other).is_some() {
@@ -363,7 +381,7 @@ impl Constructor {
TyKind::Tuple(arity, ..) => arity,
TyKind::Ref(..) => 1,
TyKind::Adt(adt, ..) => {
- if adt_is_box(adt.0, pcx.cx) {
+ if is_box(adt.0, pcx.cx.db) {
// The only legal patterns of type `Box` (outside `std`) are `_` and box
// patterns. If we're here we can assume this is a box pattern.
1
@@ -782,7 +800,7 @@ impl<'p> Fields<'p> {
}
TyKind::Ref(.., rty) => Fields::wildcards_from_tys(cx, once(rty.clone())),
&TyKind::Adt(AdtId(adt), ref substs) => {
- if adt_is_box(adt, cx) {
+ if is_box(adt, cx.db) {
// The only legal patterns of type `Box` (outside `std`) are `_` and box
// patterns. If we're here we can assume this is a box pattern.
let subst_ty = substs.at(Interner, 0).assert_ty_ref(Interner).clone();
@@ -865,8 +883,8 @@ impl<'p> DeconstructedPat<'p> {
let ctor;
let fields;
match pat.kind.as_ref() {
- PatKind::Binding { subpattern: Some(subpat) } => return mkpat(subpat),
- PatKind::Binding { subpattern: None } | PatKind::Wild => {
+ PatKind::Binding { subpattern: Some(subpat), .. } => return mkpat(subpat),
+ PatKind::Binding { subpattern: None, .. } | PatKind::Wild => {
ctor = Wildcard;
fields = Fields::empty();
}
@@ -889,7 +907,7 @@ impl<'p> DeconstructedPat<'p> {
}
fields = Fields::from_iter(cx, wilds)
}
- TyKind::Adt(adt, substs) if adt_is_box(adt.0, cx) => {
+ TyKind::Adt(adt, substs) if is_box(adt.0, cx.db) => {
// The only legal patterns of type `Box` (outside `std`) are `_` and box
// patterns. If we're here we can assume this is a box pattern.
// FIXME(Nadrieril): A `Box` can in theory be matched either with `Box(_,
@@ -963,10 +981,67 @@ impl<'p> DeconstructedPat<'p> {
DeconstructedPat::new(ctor, fields, pat.ty.clone())
}
- // // FIXME(iDawer): implement reporting of noncovered patterns
- // pub(crate) fn to_pat(&self, _cx: &MatchCheckCtx<'_, 'p>) -> Pat {
- // Pat { ty: self.ty.clone(), kind: PatKind::Wild.into() }
- // }
+ pub(crate) fn to_pat(&self, cx: &MatchCheckCtx<'_, 'p>) -> Pat {
+ let mut subpatterns = self.iter_fields().map(|p| p.to_pat(cx));
+ let pat = match &self.ctor {
+ Single | Variant(_) => match self.ty.kind(Interner) {
+ TyKind::Tuple(..) => PatKind::Leaf {
+ subpatterns: subpatterns
+ .zip(0u32..)
+ .map(|(p, i)| FieldPat {
+ field: LocalFieldId::from_raw(i.into()),
+ pattern: p,
+ })
+ .collect(),
+ },
+ TyKind::Adt(adt, _) if is_box(adt.0, cx.db) => {
+ // Without `box_patterns`, the only legal pattern of type `Box` is `_` (outside
+ // of `std`). So this branch is only reachable when the feature is enabled and
+ // the pattern is a box pattern.
+ PatKind::Deref { subpattern: subpatterns.next().unwrap() }
+ }
+ TyKind::Adt(adt, substs) => {
+ let variant = self.ctor.variant_id_for_adt(adt.0);
+ let subpatterns = Fields::list_variant_nonhidden_fields(cx, self.ty(), variant)
+ .zip(subpatterns)
+ .map(|((field, _ty), pattern)| FieldPat { field, pattern })
+ .collect();
+
+ if let VariantId::EnumVariantId(enum_variant) = variant {
+ PatKind::Variant { substs: substs.clone(), enum_variant, subpatterns }
+ } else {
+ PatKind::Leaf { subpatterns }
+ }
+ }
+ // Note: given the expansion of `&str` patterns done in `expand_pattern`, we should
+ // be careful to reconstruct the correct constant pattern here. However a string
+ // literal pattern will never be reported as a non-exhaustiveness witness, so we
+ // ignore this issue.
+ TyKind::Ref(..) => PatKind::Deref { subpattern: subpatterns.next().unwrap() },
+ _ => {
+ never!("unexpected ctor for type {:?} {:?}", self.ctor, self.ty);
+ PatKind::Wild
+ }
+ },
+ &Slice(Slice { _unimplemented: _void }) => unimplemented!(),
+ &Str(_void) => unimplemented!(),
+ &FloatRange(_void) => unimplemented!(),
+ IntRange(range) => return range.to_pat(cx, self.ty.clone()),
+ Wildcard | NonExhaustive => PatKind::Wild,
+ Missing { .. } => {
+ never!(
+ "trying to convert a `Missing` constructor into a `Pat`; this is probably a bug,
+ `Missing` should have been processed in `apply_constructors`"
+ );
+ PatKind::Wild
+ }
+ Opaque | Or => {
+ never!("can't convert to pattern: {:?}", self.ctor);
+ PatKind::Wild
+ }
+ };
+ Pat { ty: self.ty.clone(), kind: Box::new(pat) }
+ }
pub(super) fn is_or_pat(&self) -> bool {
matches!(self.ctor, Or)
@@ -980,7 +1055,7 @@ impl<'p> DeconstructedPat<'p> {
&self.ty
}
- pub(super) fn iter_fields<'a>(&'a self) -> impl Iterator<Item = &'a DeconstructedPat<'a>> + 'a {
+ pub(super) fn iter_fields<'a>(&'a self) -> impl Iterator<Item = &'p DeconstructedPat<'p>> + 'a {
self.fields.iter_patterns()
}
@@ -1023,11 +1098,3 @@ fn is_field_list_non_exhaustive(variant_id: VariantId, cx: &MatchCheckCtx<'_, '_
};
cx.db.attrs(attr_def_id).by_key("non_exhaustive").exists()
}
-
-fn adt_is_box(adt: hir_def::AdtId, cx: &MatchCheckCtx<'_, '_>) -> bool {
- use hir_def::lang_item::LangItemTarget;
- match cx.db.lang_item(cx.module.krate(), SmolStr::new_inline("owned_box")) {
- Some(LangItemTarget::StructId(box_id)) => adt == box_id.into(),
- _ => false,
- }
-}