diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index bcd3d3092c3..d7885e05a2f 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -774,17 +774,16 @@ fn hoist_pat_range_bdy( } } - /// Convert to a [`print::Pat`] for diagnostic purposes. - fn hoist_pat_range(&self, range: &IntRange, ty: RevealedTy<'tcx>) -> print::Pat<'tcx> { - use print::{Pat, PatKind}; + /// 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; - let kind = if matches!((range.lo, range.hi), (NegInfinity, PosInfinity)) { - PatKind::Wild + 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 = lo.as_finite().unwrap(); - PatKind::Constant { value } + value.to_string() } else { // We convert to an inclusive range for diagnostics. let mut end = rustc_hir::RangeEnd::Included; @@ -807,32 +806,24 @@ fn hoist_pat_range(&self, range: &IntRange, ty: RevealedTy<'tcx>) -> print::Pat< range.hi }; let hi = cx.hoist_pat_range_bdy(hi, ty); - PatKind::Range(Box::new(PatRange { lo, hi, end, ty: ty.inner() })) - }; - - Pat { ty: ty.inner(), kind } + 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 { - // This works by converting the witness pattern to a `print::Pat` - // and then printing that, but callers don't need to know that. - self.hoist_witness_pat(pat).to_string() - } - - /// Convert to a [`print::Pat`] for diagnostic purposes. This panics for patterns that don't - /// appear in diagnostics, like float ranges. - fn hoist_witness_pat(&self, pat: &WitnessPat<'p, 'tcx>) -> print::Pat<'tcx> { - use print::{FieldPat, Pat, PatKind}; let cx = self; - let hoist = |p| Box::new(cx.hoist_witness_pat(p)); - let kind = match pat.ctor() { - Bool(b) => PatKind::Constant { value: mir::Const::from_bool(cx.tcx, *b) }, - IntRange(range) => return self.hoist_pat_range(range, *pat.ty()), + 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 if pat.ty().is_box() => { // Outside of the `alloc` crate, the only way to create a struct pattern // of type `Box` is to use a `box` pattern via #[feature(box_patterns)]. - PatKind::Box { subpattern: hoist(&pat.fields[0]) } + format!("box {}", print(&pat.fields[0])) } Struct | Variant(_) | UnionField => { let enum_info = match *pat.ty().kind() { @@ -847,12 +838,29 @@ fn hoist_witness_pat(&self, pat: &WitnessPat<'p, 'tcx>) -> print::Pat<'tcx> { let subpatterns = pat .iter_fields() .enumerate() - .map(|(i, pat)| FieldPat { field: FieldIdx::new(i), pattern: hoist(pat) }) + .map(|(i, pat)| print::FieldPat { + field: FieldIdx::new(i), + pattern: print(pat), + is_wildcard: would_print_as_wildcard(cx.tcx, pat), + }) .collect::>(); - PatKind::StructLike { enum_info, subpatterns } + 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 } - Ref => PatKind::Deref { subpattern: hoist(&pat.fields[0]) }, Slice(slice) => { let (prefix_len, has_dot_dot) = match slice.kind { SliceKind::FixedLen(len) => (len, false), @@ -879,14 +887,15 @@ fn hoist_witness_pat(&self, pat: &WitnessPat<'p, 'tcx>) -> print::Pat<'tcx> { } } - let prefix = prefix.iter().map(hoist).collect(); - let suffix = suffix.iter().map(hoist).collect(); + let prefix = prefix.iter().map(print).collect::>(); + let suffix = suffix.iter().map(print).collect::>(); - PatKind::Slice { prefix, has_dot_dot, suffix } + let mut s = String::new(); + print::write_slice_like(&mut s, &prefix, has_dot_dot, &suffix).unwrap(); + s } - &Str(value) => PatKind::Constant { value }, - Never if self.tcx.features().never_patterns => PatKind::Never, - Never | Wildcard | NonExhaustive | Hidden | PrivateUninhabited => PatKind::Wild, + 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`" @@ -894,9 +903,7 @@ fn hoist_witness_pat(&self, pat: &WitnessPat<'p, 'tcx>) -> print::Pat<'tcx> { F16Range(..) | F32Range(..) | F64Range(..) | F128Range(..) | Opaque(..) | Or => { bug!("can't convert to pattern: {:?}", pat) } - }; - - Pat { ty: pat.ty().inner(), kind } + } } } @@ -972,7 +979,7 @@ fn lint_overlapping_range_endpoints( overlaps_on: IntRange, overlaps_with: &[&crate::pat::DeconstructedPat], ) { - let overlap_as_pat = self.hoist_pat_range(&overlaps_on, *pat.ty()); + let overlap_as_pat = self.print_pat_range(&overlaps_on, *pat.ty()); let overlaps: Vec<_> = overlaps_with .iter() .map(|pat| pat.data().span) @@ -1012,7 +1019,7 @@ fn lint_non_contiguous_range_endpoints( suggested_range.end = rustc_hir::RangeEnd::Included; suggested_range.to_string() }; - let gap_as_pat = self.hoist_pat_range(&gap, *pat.ty()); + 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( diff --git a/compiler/rustc_pattern_analysis/src/rustc/print.rs b/compiler/rustc_pattern_analysis/src/rustc/print.rs index 7d638714605..17e389df17e 100644 --- a/compiler/rustc_pattern_analysis/src/rustc/print.rs +++ b/compiler/rustc_pattern_analysis/src/rustc/print.rs @@ -11,75 +11,16 @@ use std::fmt; -use rustc_middle::thir::PatRange; +use rustc_middle::bug; use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt}; -use rustc_middle::{bug, mir}; use rustc_span::sym; use rustc_target::abi::{FieldIdx, VariantIdx}; #[derive(Clone, Debug)] -pub(crate) struct FieldPat<'tcx> { +pub(crate) struct FieldPat { pub(crate) field: FieldIdx, - pub(crate) pattern: Box>, -} - -#[derive(Clone, Debug)] -pub(crate) struct Pat<'tcx> { - pub(crate) ty: Ty<'tcx>, - pub(crate) kind: PatKind<'tcx>, -} - -#[derive(Clone, Debug)] -pub(crate) enum PatKind<'tcx> { - Wild, - - StructLike { - enum_info: EnumInfo<'tcx>, - subpatterns: Vec>, - }, - - Box { - subpattern: Box>, - }, - - Deref { - subpattern: Box>, - }, - - Constant { - value: mir::Const<'tcx>, - }, - - Range(Box>), - - Slice { - prefix: Box<[Box>]>, - /// True if this slice-like pattern should include a `..` between the - /// prefix and suffix. - has_dot_dot: bool, - suffix: Box<[Box>]>, - }, - - Never, -} - -impl<'tcx> fmt::Display for Pat<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.kind { - PatKind::Wild => write!(f, "_"), - PatKind::Never => write!(f, "!"), - PatKind::Box { ref subpattern } => write!(f, "box {subpattern}"), - PatKind::StructLike { ref enum_info, ref subpatterns } => { - ty::tls::with(|tcx| write_struct_like(f, tcx, self.ty, enum_info, subpatterns)) - } - PatKind::Deref { ref subpattern } => write_ref_like(f, self.ty, subpattern), - PatKind::Constant { value } => write!(f, "{value}"), - PatKind::Range(ref range) => write!(f, "{range}"), - PatKind::Slice { ref prefix, has_dot_dot, ref suffix } => { - write_slice_like(f, prefix, has_dot_dot, suffix) - } - } - } + pub(crate) pattern: String, + pub(crate) is_wildcard: bool, } /// Returns a closure that will return `""` when called the first time, @@ -103,12 +44,12 @@ pub(crate) enum EnumInfo<'tcx> { NotEnum, } -fn write_struct_like<'tcx>( +pub(crate) fn write_struct_like<'tcx>( f: &mut impl fmt::Write, tcx: TyCtxt<'_>, ty: Ty<'tcx>, enum_info: &EnumInfo<'tcx>, - subpatterns: &[FieldPat<'tcx>], + subpatterns: &[FieldPat], ) -> fmt::Result { let variant_and_name = match *enum_info { EnumInfo::Enum { adt_def, variant_index } => { @@ -139,12 +80,12 @@ fn write_struct_like<'tcx>( write!(f, " {{ ")?; let mut printed = 0; - for p in subpatterns { - if let PatKind::Wild = p.pattern.kind { + for &FieldPat { field, ref pattern, is_wildcard } in subpatterns { + if is_wildcard { continue; } - let name = variant.fields[p.field].name; - write!(f, "{}{}: {}", start_or_comma(), name, p.pattern)?; + let field_name = variant.fields[field].name; + write!(f, "{}{field_name}: {pattern}", start_or_comma())?; printed += 1; } @@ -184,10 +125,10 @@ fn write_struct_like<'tcx>( Ok(()) } -fn write_ref_like<'tcx>( +pub(crate) fn write_ref_like<'tcx>( f: &mut impl fmt::Write, ty: Ty<'tcx>, - subpattern: &Pat<'tcx>, + subpattern: &str, ) -> fmt::Result { match ty.kind() { ty::Ref(_, _, mutbl) => { @@ -198,11 +139,11 @@ fn write_ref_like<'tcx>( write!(f, "{subpattern}") } -fn write_slice_like<'tcx>( +pub(crate) fn write_slice_like( f: &mut impl fmt::Write, - prefix: &[Box>], + prefix: &[String], has_dot_dot: bool, - suffix: &[Box>], + suffix: &[String], ) -> fmt::Result { let mut start_or_comma = start_or_comma(); write!(f, "[")?;