| use std::cell::Cell; | |
| use std::fmt; | |
| use std::iter::once; | |
| use rustc_abi::{FIRST_VARIANT, FieldIdx, Integer, VariantIdx}; | |
| use rustc_arena::DroplessArena; | |
| use rustc_hir::HirId; | |
| use rustc_index::{Idx, IndexVec}; | |
| use rustc_lint_defs::builtin::{NON_CONTIGUOUS_RANGE_ENDPOINTS, OVERLAPPING_RANGE_ENDPOINTS}; | |
| use rustc_middle::middle::stability::EvalResult; | |
| use rustc_middle::thir::{self, Pat, PatKind, PatRange, PatRangeBoundary}; | |
| use rustc_middle::ty::layout::IntegerExt; | |
| use rustc_middle::ty::{ | |
| self, FieldDef, OpaqueTypeKey, ScalarInt, Ty, TyCtxt, TypeVisitableExt, VariantDef, | |
| }; | |
| use rustc_middle::{bug, span_bug}; | |
| use rustc_span::def_id::LocalModId; | |
| use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; | |
| use crate::constructor::Constructor::*; | |
| use crate::constructor::{ | |
| IntRange, MaybeInfiniteInt, OpaqueId, RangeEnd, Slice, SliceKind, VariantVisibility, | |
| }; | |
| use crate::lints::lint_nonexhaustive_missing_variants; | |
| use crate::pat_column::PatternColumn; | |
| use crate::rustc::print::EnumInfo; | |
| use crate::usefulness::{PlaceValidity, compute_match_usefulness}; | |
| use crate::{PatCx, PrivateUninhabitedField, diagnostics}; | |
| mod print; | |
| // Re-export rustc-specific versions of all these types. | |
| pub type Constructor<'p, 'tcx> = crate::constructor::Constructor<RustcPatCtxt<'p, 'tcx>>; | |
| pub type ConstructorSet<'p, 'tcx> = crate::constructor::ConstructorSet<RustcPatCtxt<'p, 'tcx>>; | |
| pub type DeconstructedPat<'p, 'tcx> = crate::pat::DeconstructedPat<RustcPatCtxt<'p, 'tcx>>; | |
| pub type MatchArm<'p, 'tcx> = crate::MatchArm<'p, RustcPatCtxt<'p, 'tcx>>; | |
| pub type RedundancyExplanation<'p, 'tcx> = | |
| crate::usefulness::RedundancyExplanation<'p, RustcPatCtxt<'p, 'tcx>>; | |
| pub type Usefulness<'p, 'tcx> = crate::usefulness::Usefulness<'p, RustcPatCtxt<'p, 'tcx>>; | |
| pub type UsefulnessReport<'p, 'tcx> = | |
| crate::usefulness::UsefulnessReport<'p, RustcPatCtxt<'p, 'tcx>>; | |
| pub type WitnessPat<'p, 'tcx> = crate::pat::WitnessPat<RustcPatCtxt<'p, 'tcx>>; | |
| /// A type which has gone through `cx.reveal_opaque_ty`, i.e. if it was opaque it was replaced by | |
| /// the hidden type if allowed in the current body. This ensures we consistently inspect the hidden | |
| /// types when we should. | |
| /// | |
| /// Use `.inner()` or deref to get to the `Ty<'tcx>`. | |
| #[repr(transparent)] | |
| #[derive(Clone, Copy, PartialEq, Eq, Hash)] | |
| 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> { | |
| fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { | |
| self.0.fmt(fmt) | |
| } | |
| } | |
| impl<'tcx> std::ops::Deref for RevealedTy<'tcx> { | |
| type Target = Ty<'tcx>; | |
| fn deref(&self) -> &Self::Target { | |
| &self.0 | |
| } | |
| } | |
| impl<'tcx> RevealedTy<'tcx> { | |
| pub fn inner(self) -> Ty<'tcx> { | |
| self.0 | |
| } | |
| } | |
| #[derive(Clone)] | |
| pub struct RustcPatCtxt<'p, 'tcx: 'p> { | |
| pub tcx: TyCtxt<'tcx>, | |
| pub typeck_results: &'tcx ty::TypeckResults<'tcx>, | |
| /// The module in which the match occurs. This is necessary for | |
| /// checking inhabited-ness of types because whether a type is (visibly) | |
| /// inhabited can depend on whether it was defined in the current module or | |
| /// not. E.g., `struct Foo { _private: ! }` cannot be seen to be empty | |
| /// outside its module and should not be matchable with an empty match statement. | |
| pub module: LocalModId, | |
| pub typing_env: ty::TypingEnv<'tcx>, | |
| /// To allocate the result of `self.ctor_sub_tys()` | |
| pub dropless_arena: &'p DroplessArena, | |
| /// Lint level at the match. | |
| pub match_lint_level: HirId, | |
| /// The span of the whole match, if applicable. | |
| pub whole_match_span: Option<Span>, | |
| /// Span of the scrutinee. | |
| pub scrut_span: Span, | |
| /// Only produce `NON_EXHAUSTIVE_OMITTED_PATTERNS` lint on refutable patterns. | |
| pub refutable: bool, | |
| /// Whether the data at the scrutinee is known to be valid. This is false if the scrutinee comes | |
| /// from a union field, a pointer deref, or a reference deref (pending opsem decisions). | |
| pub known_valid_scrutinee: bool, | |
| pub internal_state: RustcPatCtxtState, | |
| } | |
| /// Private fields of [`RustcPatCtxt`], separated out to permit record initialization syntax. | |
| #[derive(Clone, Default)] | |
| pub struct RustcPatCtxtState { | |
| /// Has a deref pattern been lowered? This is initialized to `false` and is updated by | |
| /// [`RustcPatCtxt::lower_pat`] in order to avoid performing deref-pattern-specific validation | |
| /// for everything containing patterns. | |
| has_lowered_deref_pat: Cell<bool>, | |
| } | |
| impl<'p, 'tcx: 'p> fmt::Debug for RustcPatCtxt<'p, 'tcx> { | |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
| f.debug_struct("RustcPatCtxt").finish() | |
| } | |
| } | |
| impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { | |
| /// Type inference occasionally gives us opaque types in places where corresponding patterns | |
| /// have more specific types. To avoid inconsistencies as well as detect opaque uninhabited | |
| /// types, we use the corresponding hidden type if possible. | |
| // FIXME(#132279): This will be unnecessary once we have a TypingMode which supports revealing | |
| // opaque types defined in a body. | |
| #[inline] | |
| pub fn reveal_opaque_ty(&self, ty: Ty<'tcx>) -> RevealedTy<'tcx> { | |
| fn reveal_inner<'tcx>(cx: &RustcPatCtxt<'_, 'tcx>, ty: Ty<'tcx>) -> RevealedTy<'tcx> { | |
| debug_assert!(!cx.tcx.next_trait_solver_globally()); | |
| let ty::Alias(_, ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) = *ty.kind() | |
| else { | |
| bug!() | |
| }; | |
| if let Some(local_def_id) = def_id.as_local() { | |
| let key = ty::OpaqueTypeKey { def_id: local_def_id, args }; | |
| if let Some(ty) = cx.reveal_opaque_key(key) { | |
| return RevealedTy(ty); | |
| } | |
| } | |
| RevealedTy(ty) | |
| } | |
| if let ty::Alias(ty::IsRigid::No, ty::AliasTy { kind: ty::Opaque { .. }, .. }) = ty.kind() { | |
| reveal_inner(self, ty) | |
| } else { | |
| RevealedTy(ty) | |
| } | |
| } | |
| /// Returns the hidden type corresponding to this key if the body under analysis is allowed to | |
| /// know it. | |
| fn reveal_opaque_key(&self, key: OpaqueTypeKey<'tcx>) -> Option<Ty<'tcx>> { | |
| self.typeck_results | |
| .hidden_types | |
| .get(&key.def_id) | |
| .map(|x| x.ty.instantiate(self.tcx, key.args).skip_norm_wip()) | |
| } | |
| // This can take a non-revealed `Ty` because it reveals opaques itself. | |
| pub fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool { | |
| !ty.inhabited_predicate(self.tcx).apply_revealing_opaque( | |
| self.tcx, | |
| self.typing_env, | |
| self.module, | |
| &|key| self.reveal_opaque_key(key), | |
| ) | |
| } | |
| /// Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`. | |
| pub fn is_foreign_non_exhaustive_enum(&self, ty: RevealedTy<'tcx>) -> bool { | |
| match ty.kind() { | |
| ty::Adt(def, ..) => def.variant_list_has_applicable_non_exhaustive(), | |
| _ => false, | |
| } | |
| } | |
| /// Whether the range denotes the fictitious values before `isize::MIN` or after | |
| /// `usize::MAX`/`isize::MAX` (see doc of [`IntRange::split`] for why these exist). | |
| pub fn is_range_beyond_boundaries(&self, range: &IntRange, ty: RevealedTy<'tcx>) -> bool { | |
| ty.is_ptr_sized_integral() && { | |
| // The two invalid ranges are `NegInfinity..isize::MIN` (represented as | |
| // `NegInfinity..0`), and `{u,i}size::MAX+1..PosInfinity`. `hoist_pat_range_bdy` | |
| // converts `MAX+1` to `PosInfinity`, and we couldn't have `PosInfinity` in `range.lo` | |
| // otherwise. | |
| let lo = self.hoist_pat_range_bdy(range.lo, ty); | |
| matches!(lo, PatRangeBoundary::PosInfinity) | |
| || matches!(range.hi, MaybeInfiniteInt::Finite(0)) | |
| } | |
| } | |
| pub(crate) fn variant_sub_tys( | |
| &self, | |
| ty: RevealedTy<'tcx>, | |
| variant: &'tcx VariantDef, | |
| ) -> impl Iterator<Item = (&'tcx FieldDef, RevealedTy<'tcx>)> { | |
| let ty::Adt(_, args) = ty.kind() else { bug!() }; | |
| variant.fields.iter().map(move |field| { | |
| let ty = field.ty(self.tcx, args); | |
| // `field.ty()` doesn't normalize after instantiating. | |
| let ty = | |
| self.tcx.try_normalize_erasing_regions(self.typing_env, ty).unwrap_or_else(|e| { | |
| self.tcx.dcx().span_delayed_bug( | |
| self.scrut_span, | |
| format!( | |
| "Failed to normalize {:?} in typing_env={:?} while getting variant sub tys for {ty:?}", | |
| e.get_type_for_failure(), | |
| self.typing_env, | |
| ), | |
| ); | |
| ty.skip_norm_wip() | |
| }); | |
| let ty = self.reveal_opaque_ty(ty); | |
| (field, ty) | |
| }) | |
| } | |
| pub(crate) fn variant_index_for_adt( | |
| ctor: &Constructor<'p, 'tcx>, | |
| adt: ty::AdtDef<'tcx>, | |
| ) -> VariantIdx { | |
| match *ctor { | |
| Variant(idx) => idx, | |
| Struct | UnionField => { | |
| assert!(!adt.is_enum()); | |
| FIRST_VARIANT | |
| } | |
| _ => bug!("bad constructor {:?} for adt {:?}", ctor, adt), | |
| } | |
| } | |
| /// Returns the types of the fields for a given constructor. The result must have a length of | |
| /// `ctor.arity()`. | |
| pub(crate) fn ctor_sub_tys( | |
| &self, | |
| ctor: &Constructor<'p, 'tcx>, | |
| ty: RevealedTy<'tcx>, | |
| ) -> impl Iterator<Item = (RevealedTy<'tcx>, PrivateUninhabitedField)> + ExactSizeIterator { | |
| fn reveal_and_alloc<'a, 'tcx>( | |
| cx: &'a RustcPatCtxt<'_, 'tcx>, | |
| iter: impl Iterator<Item = Ty<'tcx>>, | |
| ) -> &'a [(RevealedTy<'tcx>, PrivateUninhabitedField)] { | |
| cx.dropless_arena.alloc_from_iter( | |
| iter.map(|ty| cx.reveal_opaque_ty(ty)) | |
| .map(|ty| (ty, PrivateUninhabitedField(false))), | |
| ) | |
| } | |
| let cx = self; | |
| let slice = match ctor { | |
| Struct | Variant(_) | UnionField => match ty.kind() { | |
| ty::Tuple(fs) => reveal_and_alloc(cx, fs.iter()), | |
| ty::Adt(adt, _) => { | |
| let variant = &adt.variant(RustcPatCtxt::variant_index_for_adt(&ctor, *adt)); | |
| let tys = cx.variant_sub_tys(ty, variant).map(|(field, ty)| { | |
| let is_visible = | |
| adt.is_enum() || field.vis.is_accessible_from(cx.module, cx.tcx); | |
| let is_uninhabited = cx.is_uninhabited(*ty); | |
| let skip = is_uninhabited && !is_visible; | |
| (ty, PrivateUninhabitedField(skip)) | |
| }); | |
| cx.dropless_arena.alloc_from_iter(tys) | |
| } | |
| _ => bug!("Unexpected type for constructor `{ctor:?}`: {ty:?}"), | |
| }, | |
| Ref => match ty.kind() { | |
| ty::Ref(_, rty, _) => reveal_and_alloc(cx, once(*rty)), | |
| _ => bug!("Unexpected type for `Ref` constructor: {ty:?}"), | |
| }, | |
| Slice(slice) => match ty.builtin_index() { | |
| Some(ty) => { | |
| let arity = slice.arity(); | |
| reveal_and_alloc(cx, (0..arity).map(|_| ty)) | |
| } | |
| None => bug!("bad slice pattern {:?} {:?}", ctor, ty), | |
| }, | |
| DerefPattern(pointee_ty) => reveal_and_alloc(cx, once(pointee_ty.inner())), | |
| Bool(..) | IntRange(..) | F16Range(..) | F32Range(..) | F64Range(..) | |
| | F128Range(..) | Str(..) | Opaque(..) | Never | NonExhaustive | Hidden | Missing | |
| | PrivateUninhabited | Wildcard => &[], | |
| Or => { | |
| bug!("called `Fields::wildcards` on an `Or` ctor") | |
| } | |
| }; | |
| slice.iter().copied() | |
| } | |
| /// The number of fields for this constructor. | |
| pub(crate) fn ctor_arity(&self, ctor: &Constructor<'p, 'tcx>, ty: RevealedTy<'tcx>) -> usize { | |
| match ctor { | |
| Struct | Variant(_) | UnionField => match ty.kind() { | |
| ty::Tuple(fs) => fs.len(), | |
| ty::Adt(adt, ..) => { | |
| let variant_idx = RustcPatCtxt::variant_index_for_adt(&ctor, *adt); | |
| adt.variant(variant_idx).fields.len() | |
| } | |
| _ => bug!("Unexpected type for constructor `{ctor:?}`: {ty:?}"), | |
| }, | |
| Ref | DerefPattern(_) => 1, | |
| Slice(slice) => slice.arity(), | |
| Bool(..) | IntRange(..) | F16Range(..) | F32Range(..) | F64Range(..) | |
| | F128Range(..) | Str(..) | Opaque(..) | Never | NonExhaustive | Hidden | Missing | |
| | PrivateUninhabited | Wildcard => 0, | |
| Or => bug!("The `Or` constructor doesn't have a fixed arity"), | |
| } | |
| } | |
| /// Creates a set that represents all the constructors of `ty`. | |
| /// | |
| /// See [`crate::constructor`] for considerations of emptiness. | |
| pub fn ctors_for_ty( | |
| &self, | |
| ty: RevealedTy<'tcx>, | |
| ) -> Result<ConstructorSet<'p, 'tcx>, ErrorGuaranteed> { | |
| let cx = self; | |
| let make_uint_range = |start, end| { | |
| IntRange::from_range( | |
| MaybeInfiniteInt::new_finite_uint(start), | |
| MaybeInfiniteInt::new_finite_uint(end), | |
| RangeEnd::Included, | |
| ) | |
| }; | |
| // Abort on type error. | |
| ty.error_reported()?; | |
| // This determines the set of all possible constructors for the type `ty`. For numbers, | |
| // arrays and slices we use ranges and variable-length slices when appropriate. | |
| Ok(match ty.kind() { | |
| ty::Bool => ConstructorSet::Bool, | |
| ty::Char => { | |
| // The valid Unicode Scalar Value ranges. | |
| ConstructorSet::Integers { | |
| range_1: make_uint_range('\u{0000}' as u128, '\u{D7FF}' as u128), | |
| range_2: Some(make_uint_range('\u{E000}' as u128, '\u{10FFFF}' as u128)), | |
| } | |
| } | |
| &ty::Int(ity) => { | |
| let range = if ty.is_ptr_sized_integral() { | |
| // The min/max values of `isize` are not allowed to be observed. | |
| IntRange { | |
| lo: MaybeInfiniteInt::NegInfinity, | |
| hi: MaybeInfiniteInt::PosInfinity, | |
| } | |
| } else { | |
| let size = Integer::from_int_ty(&cx.tcx, ity).size().bits(); | |
| let min = 1u128 << (size - 1); | |
| let max = min - 1; | |
| let min = MaybeInfiniteInt::new_finite_int(min, size); | |
| let max = MaybeInfiniteInt::new_finite_int(max, size); | |
| IntRange::from_range(min, max, RangeEnd::Included) | |
| }; | |
| ConstructorSet::Integers { range_1: range, range_2: None } | |
| } | |
| &ty::Uint(uty) => { | |
| let range = if ty.is_ptr_sized_integral() { | |
| // The max value of `usize` is not allowed to be observed. | |
| let lo = MaybeInfiniteInt::new_finite_uint(0); | |
| IntRange { lo, hi: MaybeInfiniteInt::PosInfinity } | |
| } else { | |
| let size = Integer::from_uint_ty(&cx.tcx, uty).size(); | |
| let max = size.truncate(u128::MAX); | |
| make_uint_range(0, max) | |
| }; | |
| ConstructorSet::Integers { range_1: range, range_2: None } | |
| } | |
| ty::Slice(sub_ty) => ConstructorSet::Slice { | |
| array_len: None, | |
| subtype_is_empty: cx.is_uninhabited(*sub_ty), | |
| }, | |
| ty::Array(sub_ty, len) => { | |
| // We treat arrays of a constant but unknown length like slices. | |
| ConstructorSet::Slice { | |
| array_len: len.try_to_target_usize(cx.tcx).map(|l| l as usize), | |
| subtype_is_empty: cx.is_uninhabited(*sub_ty), | |
| } | |
| } | |
| ty::Adt(def, args) if def.is_enum() => { | |
| let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive_enum(ty); | |
| if def.variants().is_empty() && !is_declared_nonexhaustive { | |
| ConstructorSet::NoConstructors | |
| } else { | |
| let mut variants = | |
| IndexVec::from_elem(VariantVisibility::Visible, def.variants()); | |
| for (idx, v) in def.variants().iter_enumerated() { | |
| let variant_def_id = def.variant(idx).def_id; | |
| // Visibly uninhabited variants. | |
| let is_inhabited = v | |
| .inhabited_predicate(cx.tcx) | |
| .instantiate(cx.tcx, args) | |
| .apply_revealing_opaque(cx.tcx, cx.typing_env, cx.module, &|key| { | |
| cx.reveal_opaque_key(key) | |
| }); | |
| // Variants that depend on a disabled unstable feature. | |
| let is_unstable = matches!( | |
| cx.tcx.eval_stability(variant_def_id, None, DUMMY_SP, None), | |
| EvalResult::Deny { .. } | |
| ); | |
| // Foreign `#[doc(hidden)]` variants. | |
| let is_doc_hidden = | |
| cx.tcx.is_doc_hidden(variant_def_id) && !variant_def_id.is_local(); | |
| let visibility = if !is_inhabited { | |
| // FIXME: handle empty+hidden | |
| VariantVisibility::Empty | |
| } else if is_unstable || is_doc_hidden { | |
| VariantVisibility::Hidden | |
| } else { | |
| VariantVisibility::Visible | |
| }; | |
| variants[idx] = visibility; | |
| } | |
| ConstructorSet::Variants { variants, non_exhaustive: is_declared_nonexhaustive } | |
| } | |
| } | |
| ty::Adt(def, _) if def.is_union() => ConstructorSet::Union, | |
| ty::Adt(..) | ty::Tuple(..) => { | |
| ConstructorSet::Struct { empty: cx.is_uninhabited(ty.inner()) } | |
| } | |
| ty::Ref(..) => ConstructorSet::Ref, | |
| ty::Never => ConstructorSet::NoConstructors, | |
| // This type is one for which we cannot list constructors, like `str` or `f64`. | |
| // FIXME(Nadrieril): which of these are actually allowed? | |
| ty::Float(_) | |
| | ty::Str | |
| | ty::Foreign(_) | |
| | ty::RawPtr(_, _) | |
| | ty::FnDef(_, _) | |
| | ty::FnPtr(..) | |
| | ty::Pat(_, _) | |
| | ty::Dynamic(_, _) | |
| | ty::Closure(..) | |
| | ty::CoroutineClosure(..) | |
| | ty::Coroutine(_, _) | |
| | ty::UnsafeBinder(_) | |
| | ty::Alias(_, _) | |
| | ty::Param(_) | |
| | ty::Error(_) => ConstructorSet::Unlistable, | |
| ty::CoroutineWitness(_, _) | ty::Bound(_, _) | ty::Placeholder(_) | ty::Infer(_) => { | |
| bug!("Encountered unexpected type in `ConstructorSet::for_ty`: {ty:?}") | |
| } | |
| }) | |
| } | |
| pub(crate) fn lower_pat_range_bdy( | |
| &self, | |
| bdy: PatRangeBoundary<'tcx>, | |
| ty: RevealedTy<'tcx>, | |
| ) -> MaybeInfiniteInt { | |
| match bdy { | |
| PatRangeBoundary::NegInfinity => MaybeInfiniteInt::NegInfinity, | |
| PatRangeBoundary::Finite(value) => { | |
| let bits = value.to_leaf().to_bits_unchecked(); | |
| match *ty.kind() { | |
| ty::Int(ity) => { | |
| let size = Integer::from_int_ty(&self.tcx, ity).size().bits(); | |
| MaybeInfiniteInt::new_finite_int(bits, size) | |
| } | |
| _ => MaybeInfiniteInt::new_finite_uint(bits), | |
| } | |
| } | |
| PatRangeBoundary::PosInfinity => MaybeInfiniteInt::PosInfinity, | |
| } | |
| } | |
| /// Note: the input patterns must have been lowered through | |
| /// `rustc_mir_build::thir::pattern::check_match::MatchVisitor::lower_pattern`. | |
| pub fn lower_pat(&self, pat: &'p Pat<'tcx>) -> DeconstructedPat<'p, 'tcx> { | |
| let cx = self; | |
| let ty = cx.reveal_opaque_ty(pat.ty); | |
| let ctor; | |
| let arity; | |
| let fields: Vec<_>; | |
| match &pat.kind { | |
| PatKind::Binding { subpattern: Some(subpat), .. } | |
| | PatKind::Guard { subpattern: subpat, .. } => return self.lower_pat(subpat), | |
| PatKind::Missing | PatKind::Binding { subpattern: None, .. } | PatKind::Wild => { | |
| ctor = Wildcard; | |
| fields = vec![]; | |
| arity = 0; | |
| } | |
| PatKind::Deref { pin, subpattern } => { | |
| fields = vec![self.lower_pat(subpattern).at_index(0)]; | |
| arity = 1; | |
| ctor = match (pin, ty.maybe_pinned_ref()) { | |
| (ty::Pinnedness::Not, Some((_, ty::Pinnedness::Not, _, _))) => Ref, | |
| (ty::Pinnedness::Pinned, Some((inner_ty, ty::Pinnedness::Pinned, _, _))) => { | |
| self.internal_state.has_lowered_deref_pat.set(true); | |
| DerefPattern(RevealedTy(inner_ty)) | |
| } | |
| _ => span_bug!( | |
| pat.span, | |
| "pattern has unexpected type: pat: {:?}, ty: {:?}", | |
| pat.kind, | |
| ty.inner() | |
| ), | |
| }; | |
| } | |
| PatKind::DerefPattern { subpattern, .. } => { | |
| // NB(deref_patterns): This assumes the deref pattern is matching on a trusted | |
| // `DerefPure` type. If the `Deref` impl isn't trusted, exhaustiveness must take | |
| // into account that multiple calls to deref may return different results. Hence | |
| // multiple deref! patterns cannot be exhaustive together unless each is exhaustive | |
| // by itself. | |
| fields = vec![self.lower_pat(subpattern).at_index(0)]; | |
| arity = 1; | |
| ctor = DerefPattern(cx.reveal_opaque_ty(subpattern.ty)); | |
| self.internal_state.has_lowered_deref_pat.set(true); | |
| } | |
| PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => { | |
| match ty.kind() { | |
| ty::Tuple(fs) => { | |
| ctor = Struct; | |
| arity = fs.len(); | |
| fields = subpatterns | |
| .iter() | |
| .map(|ipat| self.lower_pat(&ipat.pattern).at_index(ipat.field.index())) | |
| .collect(); | |
| } | |
| ty::Adt(adt, _) => { | |
| ctor = match pat.kind { | |
| PatKind::Leaf { .. } if adt.is_union() => UnionField, | |
| PatKind::Leaf { .. } => Struct, | |
| PatKind::Variant { variant_index, .. } => Variant(variant_index), | |
| _ => bug!(), | |
| }; | |
| let variant = | |
| &adt.variant(RustcPatCtxt::variant_index_for_adt(&ctor, *adt)); | |
| arity = variant.fields.len(); | |
| fields = subpatterns | |
| .iter() | |
| .map(|ipat| self.lower_pat(&ipat.pattern).at_index(ipat.field.index())) | |
| .collect(); | |
| } | |
| _ => span_bug!( | |
| pat.span, | |
| "pattern has unexpected type: pat: {:?}, ty: {}", | |
| pat.kind, | |
| ty.inner() | |
| ), | |
| } | |
| } | |
| PatKind::Constant { value } => { | |
| match ty.kind() { | |
| ty::Bool => { | |
| ctor = Bool(value.try_to_bool().unwrap()); | |
| fields = vec![]; | |
| arity = 0; | |
| } | |
| ty::Char | ty::Int(_) | ty::Uint(_) => { | |
| ctor = { | |
| let bits = value.to_leaf().to_bits_unchecked(); | |
| let x = match *ty.kind() { | |
| ty::Int(ity) => { | |
| let size = Integer::from_int_ty(&cx.tcx, ity).size().bits(); | |
| MaybeInfiniteInt::new_finite_int(bits, size) | |
| } | |
| _ => MaybeInfiniteInt::new_finite_uint(bits), | |
| }; | |
| IntRange(IntRange::from_singleton(x)) | |
| }; | |
| fields = vec![]; | |
| arity = 0; | |
| } | |
| ty::Float(ty::FloatTy::F16) => { | |
| use rustc_apfloat::Float; | |
| let bits = value.to_leaf().to_u16(); | |
| let value = rustc_apfloat::ieee::Half::from_bits(bits.into()); | |
| ctor = F16Range(value, value, RangeEnd::Included); | |
| fields = vec![]; | |
| arity = 0; | |
| } | |
| ty::Float(ty::FloatTy::F32) => { | |
| use rustc_apfloat::Float; | |
| let bits = value.to_leaf().to_u32(); | |
| let value = rustc_apfloat::ieee::Single::from_bits(bits.into()); | |
| ctor = F32Range(value, value, RangeEnd::Included); | |
| fields = vec![]; | |
| arity = 0; | |
| } | |
| ty::Float(ty::FloatTy::F64) => { | |
| use rustc_apfloat::Float; | |
| let bits = value.to_leaf().to_u64(); | |
| let value = rustc_apfloat::ieee::Double::from_bits(bits.into()); | |
| ctor = F64Range(value, value, RangeEnd::Included); | |
| fields = vec![]; | |
| arity = 0; | |
| } | |
| ty::Float(ty::FloatTy::F128) => { | |
| use rustc_apfloat::Float; | |
| let bits = value.to_leaf().to_u128(); | |
| let value = rustc_apfloat::ieee::Quad::from_bits(bits); | |
| ctor = F128Range(value, value, RangeEnd::Included); | |
| fields = vec![]; | |
| arity = 0; | |
| } | |
| ty::Str => { | |
| // For constant/literal patterns of type `&str`, the THIR | |
| // pattern is a `PatKind::Deref` of type `&str` wrapping a | |
| // `PatKind::Const` of type `str`. | |
| ctor = Str(*value); | |
| fields = vec![]; | |
| arity = 0; | |
| } | |
| // All constants that can be structurally matched have already been expanded | |
| // into the corresponding `Pat`s by `const_to_pat`. Constants that remain are | |
| // opaque. | |
| _ => { | |
| ctor = Opaque(OpaqueId::new()); | |
| fields = vec![]; | |
| arity = 0; | |
| } | |
| } | |
| } | |
| PatKind::Range(patrange) => { | |
| let PatRange { lo, hi, end, .. } = patrange.as_ref(); | |
| let end = match end { | |
| rustc_hir::RangeEnd::Included => RangeEnd::Included, | |
| rustc_hir::RangeEnd::Excluded => RangeEnd::Excluded, | |
| }; | |
| ctor = match ty.kind() { | |
| ty::Char | ty::Int(_) | ty::Uint(_) => { | |
| let lo = cx.lower_pat_range_bdy(*lo, ty); | |
| let hi = cx.lower_pat_range_bdy(*hi, ty); | |
| IntRange(IntRange::from_range(lo, hi, end)) | |
| } | |
| ty::Float(fty) => { | |
| use rustc_apfloat::Float; | |
| let lo = lo.as_finite().map(|c| c.to_leaf().to_bits_unchecked()); | |
| let hi = hi.as_finite().map(|c| c.to_leaf().to_bits_unchecked()); | |
| match fty { | |
| ty::FloatTy::F16 => { | |
| use rustc_apfloat::ieee::Half; | |
| let lo = lo.map(Half::from_bits).unwrap_or(-Half::INFINITY); | |
| let hi = hi.map(Half::from_bits).unwrap_or(Half::INFINITY); | |
| F16Range(lo, hi, end) | |
| } | |
| ty::FloatTy::F32 => { | |
| use rustc_apfloat::ieee::Single; | |
| let lo = lo.map(Single::from_bits).unwrap_or(-Single::INFINITY); | |
| let hi = hi.map(Single::from_bits).unwrap_or(Single::INFINITY); | |
| F32Range(lo, hi, end) | |
| } | |
| ty::FloatTy::F64 => { | |
| use rustc_apfloat::ieee::Double; | |
| let lo = lo.map(Double::from_bits).unwrap_or(-Double::INFINITY); | |
| let hi = hi.map(Double::from_bits).unwrap_or(Double::INFINITY); | |
| F64Range(lo, hi, end) | |
| } | |
| ty::FloatTy::F128 => { | |
| use rustc_apfloat::ieee::Quad; | |
| let lo = lo.map(Quad::from_bits).unwrap_or(-Quad::INFINITY); | |
| let hi = hi.map(Quad::from_bits).unwrap_or(Quad::INFINITY); | |
| F128Range(lo, hi, end) | |
| } | |
| } | |
| } | |
| _ => span_bug!(pat.span, "invalid type for range pattern: {}", ty.inner()), | |
| }; | |
| fields = vec![]; | |
| arity = 0; | |
| } | |
| PatKind::Array { prefix, slice, suffix } | PatKind::Slice { prefix, slice, suffix } => { | |
| let array_len = match ty.kind() { | |
| ty::Array(_, length) => Some( | |
| length | |
| .try_to_target_usize(cx.tcx) | |
| .expect("expected len of array pat to be definite") | |
| as usize, | |
| ), | |
| ty::Slice(_) => None, | |
| _ => span_bug!(pat.span, "bad ty {} for slice pattern", ty.inner()), | |
| }; | |
| let kind = if slice.is_some() { | |
| SliceKind::VarLen(prefix.len(), suffix.len()) | |
| } else { | |
| SliceKind::FixedLen(prefix.len() + suffix.len()) | |
| }; | |
| ctor = Slice(Slice::new(array_len, kind)); | |
| fields = prefix | |
| .iter() | |
| .chain(suffix.iter()) | |
| .map(|p| self.lower_pat(&*p)) | |
| .enumerate() | |
| .map(|(i, p)| p.at_index(i)) | |
| .collect(); | |
| arity = kind.arity(); | |
| } | |
| PatKind::Or { .. } => { | |
| ctor = Or; | |
| let pats = expand_or_pat(pat); | |
| fields = pats | |
| .into_iter() | |
| .map(|p| self.lower_pat(p)) | |
| .enumerate() | |
| .map(|(i, p)| p.at_index(i)) | |
| .collect(); | |
| arity = fields.len(); | |
| } | |
| PatKind::Never => { | |
| // A never pattern matches all the values of its type (namely none). Moreover it | |
| // must be compatible with other constructors, since we can use `!` on a type like | |
| // `Result<!, !>` which has other constructors. Hence we lower it as a wildcard. | |
| ctor = Wildcard; | |
| fields = vec![]; | |
| arity = 0; | |
| } | |
| PatKind::Error(_) => { | |
| ctor = Opaque(OpaqueId::new()); | |
| fields = vec![]; | |
| arity = 0; | |
| } | |
| } | |
| DeconstructedPat::new(ctor, fields, arity, ty, pat) | |
| } | |
| /// Convert back to a `thir::PatRangeBoundary` for diagnostic purposes. | |
| /// Note: it is possible to get `isize/usize::MAX+1` here, as explained in the doc for | |
| /// [`IntRange::split`]. This cannot be represented as a `Const`, so we represent it with | |
| /// `PosInfinity`. | |
| fn hoist_pat_range_bdy( | |
| &self, | |
| miint: MaybeInfiniteInt, | |
| ty: RevealedTy<'tcx>, | |
| ) -> PatRangeBoundary<'tcx> { | |
| use MaybeInfiniteInt::*; | |
| let tcx = self.tcx; | |
| match miint { | |
| NegInfinity => PatRangeBoundary::NegInfinity, | |
| Finite(_) => { | |
| let size = ty.primitive_size(tcx); | |
| let bits = match *ty.kind() { | |
| ty::Int(_) => miint.as_finite_int(size.bits()).unwrap(), | |
| _ => miint.as_finite_uint().unwrap(), | |
| }; | |
| match ScalarInt::try_from_uint(bits, size) { | |
| Some(scalar) => { | |
| let valtree = ty::ValTree::from_scalar_int(tcx, scalar); | |
| PatRangeBoundary::Finite(valtree) | |
| } | |
| // The value doesn't fit. Since `x >= 0` and 0 always encodes the minimum value | |
| // for a type, the problem isn't that the value is too small. So it must be too | |
| // large. | |
| None => PatRangeBoundary::PosInfinity, | |
| } | |
| } | |
| PosInfinity => PatRangeBoundary::PosInfinity, | |
| } | |
| } | |
| /// Prints an [`IntRange`] to a string for diagnostic purposes. | |
| fn print_pat_range(&self, range: &IntRange, ty: RevealedTy<'tcx>) -> String { | |
| use MaybeInfiniteInt::*; | |
| let cx = self; | |
| if matches!((range.lo, range.hi), (NegInfinity, PosInfinity)) { | |
| "_".to_string() | |
| } else if range.is_singleton() { | |
| let lo = cx.hoist_pat_range_bdy(range.lo, ty); | |
| let value = ty::Value { ty: ty.inner(), valtree: lo.as_finite().unwrap() }; | |
| value.to_string() | |
| } else { | |
| // We convert to an inclusive range for diagnostics. | |
| let mut end = rustc_hir::RangeEnd::Included; | |
| let mut lo = cx.hoist_pat_range_bdy(range.lo, ty); | |
| if matches!(lo, PatRangeBoundary::PosInfinity) { | |
| // The only reason to get `PosInfinity` here is the special case where | |
| // `hoist_pat_range_bdy` found `{u,i}size::MAX+1`. So the range denotes the | |
| // fictitious values after `{u,i}size::MAX` (see [`IntRange::split`] for why we do | |
| // this). We show this to the user as `usize::MAX..` which is slightly incorrect but | |
| // probably clear enough. | |
| let max = ty.numeric_max_val(cx.tcx).unwrap(); | |
| let max = ty::ValTree::from_scalar_int(cx.tcx, max.try_to_scalar_int().unwrap()); | |
| lo = PatRangeBoundary::Finite(max); | |
| } | |
| let hi = if let Some(hi) = range.hi.minus_one() { | |
| hi | |
| } else { | |
| // The range encodes `..ty::MIN`, so we can't convert it to an inclusive range. | |
| end = rustc_hir::RangeEnd::Excluded; | |
| range.hi | |
| }; | |
| let hi = cx.hoist_pat_range_bdy(hi, ty); | |
| PatRange { lo, hi, end, ty: ty.inner() }.to_string() | |
| } | |
| } | |
| /// Prints a [`WitnessPat`] to an owned string, for diagnostic purposes. | |
| /// | |
| /// This panics for patterns that don't appear in diagnostics, like float ranges. | |
| pub fn print_witness_pat(&self, pat: &WitnessPat<'p, 'tcx>) -> String { | |
| let cx = self; | |
| let print = |p| cx.print_witness_pat(p); | |
| match pat.ctor() { | |
| Bool(b) => b.to_string(), | |
| Str(s) => s.to_string(), | |
| IntRange(range) => return self.print_pat_range(range, *pat.ty()), | |
| Struct | Variant(_) | UnionField => { | |
| let enum_info = match *pat.ty().kind() { | |
| ty::Adt(adt_def, _) if adt_def.is_enum() => EnumInfo::Enum { | |
| adt_def, | |
| variant_index: RustcPatCtxt::variant_index_for_adt(pat.ctor(), adt_def), | |
| }, | |
| ty::Adt(..) | ty::Tuple(..) => EnumInfo::NotEnum, | |
| _ => bug!("unexpected ctor for type {:?} {:?}", pat.ctor(), *pat.ty()), | |
| }; | |
| let subpatterns = pat | |
| .iter_fields() | |
| .enumerate() | |
| .map(|(i, pat)| print::FieldPat { | |
| field: FieldIdx::new(i), | |
| pattern: print(pat), | |
| is_wildcard: would_print_as_wildcard(cx.tcx, pat), | |
| }) | |
| .collect::<Vec<_>>(); | |
| let mut s = String::new(); | |
| print::write_struct_like( | |
| &mut s, | |
| self.tcx, | |
| pat.ty().inner(), | |
| &enum_info, | |
| &subpatterns, | |
| ) | |
| .unwrap(); | |
| s | |
| } | |
| Ref => { | |
| let mut s = String::new(); | |
| print::write_ref_like(&mut s, pat.ty().inner(), &print(&pat.fields[0])).unwrap(); | |
| s | |
| } | |
| DerefPattern(_) => format!("deref!({})", print(&pat.fields[0])), | |
| Slice(slice) => { | |
| let (prefix_len, has_dot_dot) = match slice.kind { | |
| SliceKind::FixedLen(len) => (len, false), | |
| SliceKind::VarLen(prefix_len, _) => (prefix_len, true), | |
| }; | |
| let (mut prefix, mut suffix) = pat.fields.split_at(prefix_len); | |
| // If the pattern contains a `..`, but is applied to values of statically-known | |
| // length (arrays), then we can slightly simplify diagnostics by merging any | |
| // adjacent wildcard patterns into the `..`: `[x, _, .., _, y]` => `[x, .., y]`. | |
| // (This simplification isn't allowed for slice values, because in that case | |
| // `[x, .., y]` would match some slices that `[x, _, .., _, y]` would not.) | |
| if has_dot_dot && slice.array_len.is_some() { | |
| while let [rest @ .., last] = prefix | |
| && would_print_as_wildcard(cx.tcx, last) | |
| { | |
| prefix = rest; | |
| } | |
| while let [first, rest @ ..] = suffix | |
| && would_print_as_wildcard(cx.tcx, first) | |
| { | |
| suffix = rest; | |
| } | |
| } | |
| let prefix = prefix.iter().map(print).collect::<Vec<_>>(); | |
| let suffix = suffix.iter().map(print).collect::<Vec<_>>(); | |
| let mut s = String::new(); | |
| print::write_slice_like(&mut s, &prefix, has_dot_dot, &suffix).unwrap(); | |
| s | |
| } | |
| Never if self.tcx.features().never_patterns() => "!".to_string(), | |
| Never | Wildcard | NonExhaustive | Hidden | PrivateUninhabited => "_".to_string(), | |
| Missing { .. } => bug!( | |
| "trying to convert a `Missing` constructor into a `Pat`; this is probably a bug, | |
| `Missing` should have been processed in `apply_constructors`" | |
| ), | |
| F16Range(..) | F32Range(..) | F64Range(..) | F128Range(..) | Opaque(..) | Or => { | |
| bug!("can't convert to pattern: {:?}", pat) | |
| } | |
| } | |
| } | |
| } | |
| /// Returns `true` if the given pattern would be printed as a wildcard (`_`). | |
| fn would_print_as_wildcard(tcx: TyCtxt<'_>, p: &WitnessPat<'_, '_>) -> bool { | |
| match p.ctor() { | |
| Constructor::IntRange(IntRange { | |
| lo: MaybeInfiniteInt::NegInfinity, | |
| hi: MaybeInfiniteInt::PosInfinity, | |
| }) | |
| | Constructor::Wildcard | |
| | Constructor::NonExhaustive | |
| | Constructor::Hidden | |
| | Constructor::PrivateUninhabited => true, | |
| Constructor::Never if !tcx.features().never_patterns() => true, | |
| _ => false, | |
| } | |
| } | |
| impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> { | |
| type Ty = RevealedTy<'tcx>; | |
| type Error = ErrorGuaranteed; | |
| type VariantIdx = VariantIdx; | |
| type StrLit = ty::Value<'tcx>; | |
| type ArmData = HirId; | |
| type PatData = &'p Pat<'tcx>; | |
| fn is_exhaustive_patterns_feature_on(&self) -> bool { | |
| self.tcx.features().exhaustive_patterns() | |
| } | |
| fn ctor_arity(&self, ctor: &crate::constructor::Constructor<Self>, ty: &Self::Ty) -> usize { | |
| self.ctor_arity(ctor, *ty) | |
| } | |
| fn ctor_sub_tys( | |
| &self, | |
| ctor: &crate::constructor::Constructor<Self>, | |
| ty: &Self::Ty, | |
| ) -> impl Iterator<Item = (Self::Ty, PrivateUninhabitedField)> + ExactSizeIterator { | |
| self.ctor_sub_tys(ctor, *ty) | |
| } | |
| fn ctors_for_ty( | |
| &self, | |
| ty: &Self::Ty, | |
| ) -> Result<crate::constructor::ConstructorSet<Self>, Self::Error> { | |
| self.ctors_for_ty(*ty) | |
| } | |
| fn write_variant_name( | |
| f: &mut fmt::Formatter<'_>, | |
| ctor: &crate::constructor::Constructor<Self>, | |
| ty: &Self::Ty, | |
| ) -> fmt::Result { | |
| if let ty::Adt(adt, _) = ty.kind() { | |
| let variant = adt.variant(Self::variant_index_for_adt(ctor, *adt)); | |
| write!(f, "{}", variant.name)?; | |
| } | |
| Ok(()) | |
| } | |
| fn bug(&self, fmt: fmt::Arguments<'_>) -> Self::Error { | |
| span_bug!(self.scrut_span, "{}", fmt) | |
| } | |
| fn delayed_bug(&self, fmt: fmt::Arguments<'_>) -> Self::Error { | |
| self.tcx.dcx().span_delayed_bug(self.scrut_span, format!("{fmt}")) | |
| } | |
| fn lint_overlapping_range_endpoints( | |
| &self, | |
| pat: &crate::pat::DeconstructedPat<Self>, | |
| overlaps_on: IntRange, | |
| overlaps_with: &[&crate::pat::DeconstructedPat<Self>], | |
| ) { | |
| let overlap_as_pat = self.print_pat_range(&overlaps_on, *pat.ty()); | |
| let overlaps: Vec<_> = overlaps_with | |
| .iter() | |
| .map(|pat| pat.data().span) | |
| .map(|span| diagnostics::Overlap { range: overlap_as_pat.to_string(), span }) | |
| .collect(); | |
| let pat_span = pat.data().span; | |
| self.tcx.emit_node_span_lint( | |
| OVERLAPPING_RANGE_ENDPOINTS, | |
| self.match_lint_level, | |
| pat_span, | |
| diagnostics::OverlappingRangeEndpoints { overlap: overlaps, range: pat_span }, | |
| ); | |
| } | |
| fn complexity_exceeded(&self) -> Result<(), Self::Error> { | |
| let span = self.whole_match_span.unwrap_or(self.scrut_span); | |
| Err(self.tcx.dcx().span_err(span, "reached pattern complexity limit")) | |
| } | |
| fn lint_non_contiguous_range_endpoints( | |
| &self, | |
| pat: &crate::pat::DeconstructedPat<Self>, | |
| gap: IntRange, | |
| gapped_with: &[&crate::pat::DeconstructedPat<Self>], | |
| ) { | |
| let &thir_pat = pat.data(); | |
| let thir::PatKind::Range(range) = &thir_pat.kind else { return }; | |
| // Only lint when the left range is an exclusive range. | |
| if range.end != rustc_hir::RangeEnd::Excluded { | |
| return; | |
| } | |
| // `pat` is an exclusive range like `lo..gap`. `gapped_with` contains ranges that start with | |
| // `gap+1`. | |
| let suggested_range: String = { | |
| // Suggest `lo..=gap` instead. | |
| let mut suggested_range = PatRange::clone(range); | |
| suggested_range.end = rustc_hir::RangeEnd::Included; | |
| suggested_range.to_string() | |
| }; | |
| let gap_as_pat = self.print_pat_range(&gap, *pat.ty()); | |
| if gapped_with.is_empty() { | |
| // If `gapped_with` is empty, `gap == T::MAX`. | |
| self.tcx.emit_node_span_lint( | |
| NON_CONTIGUOUS_RANGE_ENDPOINTS, | |
| self.match_lint_level, | |
| thir_pat.span, | |
| diagnostics::ExclusiveRangeMissingMax { | |
| // Point at this range. | |
| first_range: thir_pat.span, | |
| // That's the gap that isn't covered. | |
| max: gap_as_pat, | |
| // Suggest `lo..=max` instead. | |
| suggestion: suggested_range, | |
| }, | |
| ); | |
| } else { | |
| self.tcx.emit_node_span_lint( | |
| NON_CONTIGUOUS_RANGE_ENDPOINTS, | |
| self.match_lint_level, | |
| thir_pat.span, | |
| diagnostics::ExclusiveRangeMissingGap { | |
| // Point at this range. | |
| first_range: thir_pat.span, | |
| // That's the gap that isn't covered. | |
| gap: gap_as_pat.to_string(), | |
| // Suggest `lo..=gap` instead. | |
| suggestion: suggested_range, | |
| // All these ranges skipped over `gap` which we think is probably a | |
| // mistake. | |
| gap_with: gapped_with | |
| .iter() | |
| .map(|pat| diagnostics::GappedRange { | |
| span: pat.data().span, | |
| gap: gap_as_pat.to_string(), | |
| first_range: range.to_string(), | |
| }) | |
| .collect(), | |
| }, | |
| ); | |
| } | |
| } | |
| fn match_may_contain_deref_pats(&self) -> bool { | |
| self.internal_state.has_lowered_deref_pat.get() | |
| } | |
| fn report_mixed_deref_pat_ctors( | |
| &self, | |
| deref_pat: &crate::pat::DeconstructedPat<Self>, | |
| normal_pat: &crate::pat::DeconstructedPat<Self>, | |
| ) -> Self::Error { | |
| let deref_pattern_label = deref_pat.data().span; | |
| let normal_constructor_label = normal_pat.data().span; | |
| self.tcx.dcx().emit_err(diagnostics::MixedDerefPatternConstructors { | |
| spans: vec![deref_pattern_label, normal_constructor_label], | |
| smart_pointer_ty: deref_pat.ty().inner(), | |
| deref_pattern_label, | |
| normal_constructor_label, | |
| }) | |
| } | |
| } | |
| /// Recursively expand this pattern into its subpatterns. Only useful for or-patterns. | |
| fn expand_or_pat<'p, 'tcx>(pat: &'p Pat<'tcx>) -> Vec<&'p Pat<'tcx>> { | |
| fn expand<'p, 'tcx>(pat: &'p Pat<'tcx>, vec: &mut Vec<&'p Pat<'tcx>>) { | |
| if let PatKind::Or { pats } = &pat.kind { | |
| for pat in pats.iter() { | |
| expand(pat, vec); | |
| } | |
| } else { | |
| vec.push(pat) | |
| } | |
| } | |
| let mut pats = Vec::new(); | |
| expand(pat, &mut pats); | |
| pats | |
| } | |
| /// The entrypoint for this crate. Computes whether a match is exhaustive and which of its arms are | |
| /// useful, and runs some lints. | |
| pub fn analyze_match<'p, 'tcx>( | |
| tycx: &RustcPatCtxt<'p, 'tcx>, | |
| arms: &[MatchArm<'p, 'tcx>], | |
| scrut_ty: Ty<'tcx>, | |
| ) -> Result<UsefulnessReport<'p, 'tcx>, ErrorGuaranteed> { | |
| let scrut_ty = tycx.reveal_opaque_ty(scrut_ty); | |
| let scrut_validity = PlaceValidity::from_bool(tycx.known_valid_scrutinee); | |
| let report = compute_match_usefulness( | |
| tycx, | |
| arms, | |
| scrut_ty, | |
| scrut_validity, | |
| tycx.tcx.pattern_complexity_limit().0, | |
| )?; | |
| // Run the non_exhaustive_omitted_patterns lint. Only run on refutable patterns to avoid hitting | |
| // `if let`s. Only run if the match is exhaustive otherwise the error is redundant. | |
| if tycx.refutable && report.non_exhaustiveness_witnesses.is_empty() { | |
| let pat_column = PatternColumn::new(arms); | |
| lint_nonexhaustive_missing_variants(tycx, arms, &pat_column, scrut_ty)?; | |
| } | |
| Ok(report) | |
| } |