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.rs | 107 |
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, - } -} |