diff options
Diffstat (limited to 'compiler/rustc_pattern_analysis/src/rustc/print.rs')
| -rw-r--r-- | compiler/rustc_pattern_analysis/src/rustc/print.rs | 268 |
1 files changed, 147 insertions, 121 deletions
diff --git a/compiler/rustc_pattern_analysis/src/rustc/print.rs b/compiler/rustc_pattern_analysis/src/rustc/print.rs index 4b76764e8b1..7d638714605 100644 --- a/compiler/rustc_pattern_analysis/src/rustc/print.rs +++ b/compiler/rustc_pattern_analysis/src/rustc/print.rs @@ -12,7 +12,7 @@ use std::fmt; use rustc_middle::thir::PatRange; -use rustc_middle::ty::{self, AdtDef, Ty}; +use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt}; use rustc_middle::{bug, mir}; use rustc_span::sym; use rustc_target::abi::{FieldIdx, VariantIdx}; @@ -33,14 +33,13 @@ pub(crate) struct Pat<'tcx> { pub(crate) enum PatKind<'tcx> { Wild, - Variant { - adt_def: AdtDef<'tcx>, - variant_index: VariantIdx, + StructLike { + enum_info: EnumInfo<'tcx>, subpatterns: Vec<FieldPat<'tcx>>, }, - Leaf { - subpatterns: Vec<FieldPat<'tcx>>, + Box { + subpattern: Box<Pat<'tcx>>, }, Deref { @@ -55,7 +54,9 @@ pub(crate) enum PatKind<'tcx> { Slice { prefix: Box<[Box<Pat<'tcx>>]>, - slice: Option<Box<Pat<'tcx>>>, + /// True if this slice-like pattern should include a `..` between the + /// prefix and suffix. + has_dot_dot: bool, suffix: Box<[Box<Pat<'tcx>>]>, }, @@ -64,130 +65,155 @@ pub(crate) enum PatKind<'tcx> { impl<'tcx> fmt::Display for Pat<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // Printing lists is a chore. - let mut first = true; - let mut start_or_continue = |s| { - if first { - first = false; - "" - } else { - s - } - }; - let mut start_or_comma = || start_or_continue(", "); - match self.kind { PatKind::Wild => write!(f, "_"), PatKind::Never => write!(f, "!"), - PatKind::Variant { ref subpatterns, .. } | PatKind::Leaf { ref subpatterns } => { - let variant_and_name = match self.kind { - PatKind::Variant { adt_def, variant_index, .. } => ty::tls::with(|tcx| { - let variant = adt_def.variant(variant_index); - let adt_did = adt_def.did(); - let name = if tcx.get_diagnostic_item(sym::Option) == Some(adt_did) - || tcx.get_diagnostic_item(sym::Result) == Some(adt_did) - { - variant.name.to_string() - } else { - format!("{}::{}", tcx.def_path_str(adt_def.did()), variant.name) - }; - Some((variant, name)) - }), - _ => self.ty.ty_adt_def().and_then(|adt_def| { - if !adt_def.is_enum() { - ty::tls::with(|tcx| { - Some((adt_def.non_enum_variant(), tcx.def_path_str(adt_def.did()))) - }) - } else { - None - } - }), - }; - - if let Some((variant, name)) = &variant_and_name { - write!(f, "{name}")?; - - // Only for Adt we can have `S {...}`, - // which we handle separately here. - if variant.ctor.is_none() { - write!(f, " {{ ")?; - - let mut printed = 0; - for p in subpatterns { - if let PatKind::Wild = p.pattern.kind { - continue; - } - let name = variant.fields[p.field].name; - write!(f, "{}{}: {}", start_or_comma(), name, p.pattern)?; - printed += 1; - } - - let is_union = self.ty.ty_adt_def().is_some_and(|adt| adt.is_union()); - if printed < variant.fields.len() && (!is_union || printed == 0) { - write!(f, "{}..", start_or_comma())?; - } - - return 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) + } + } + } +} + +/// Returns a closure that will return `""` when called the first time, +/// and then return `", "` when called any subsequent times. +/// Useful for printing comma-separated lists. +fn start_or_comma() -> impl FnMut() -> &'static str { + let mut first = true; + move || { + if first { + first = false; + "" + } else { + ", " + } + } +} + +#[derive(Clone, Debug)] +pub(crate) enum EnumInfo<'tcx> { + Enum { adt_def: AdtDef<'tcx>, variant_index: VariantIdx }, + NotEnum, +} + +fn write_struct_like<'tcx>( + f: &mut impl fmt::Write, + tcx: TyCtxt<'_>, + ty: Ty<'tcx>, + enum_info: &EnumInfo<'tcx>, + subpatterns: &[FieldPat<'tcx>], +) -> fmt::Result { + let variant_and_name = match *enum_info { + EnumInfo::Enum { adt_def, variant_index } => { + let variant = adt_def.variant(variant_index); + let adt_did = adt_def.did(); + let name = if tcx.is_diagnostic_item(sym::Option, adt_did) + || tcx.is_diagnostic_item(sym::Result, adt_did) + { + variant.name.to_string() + } else { + format!("{}::{}", tcx.def_path_str(adt_def.did()), variant.name) + }; + Some((variant, name)) + } + EnumInfo::NotEnum => ty.ty_adt_def().and_then(|adt_def| { + Some((adt_def.non_enum_variant(), tcx.def_path_str(adt_def.did()))) + }), + }; + + let mut start_or_comma = start_or_comma(); - let num_fields = - variant_and_name.as_ref().map_or(subpatterns.len(), |(v, _)| v.fields.len()); - if num_fields != 0 || variant_and_name.is_none() { - write!(f, "(")?; - for i in 0..num_fields { - write!(f, "{}", start_or_comma())?; - - // Common case: the field is where we expect it. - if let Some(p) = subpatterns.get(i) { - if p.field.index() == i { - write!(f, "{}", p.pattern)?; - continue; - } - } - - // Otherwise, we have to go looking for it. - if let Some(p) = subpatterns.iter().find(|p| p.field.index() == i) { - write!(f, "{}", p.pattern)?; - } else { - write!(f, "_")?; - } - } - write!(f, ")")?; + if let Some((variant, name)) = &variant_and_name { + write!(f, "{name}")?; + + // Only for Adt we can have `S {...}`, + // which we handle separately here. + if variant.ctor.is_none() { + write!(f, " {{ ")?; + + let mut printed = 0; + for p in subpatterns { + if let PatKind::Wild = p.pattern.kind { + continue; } + let name = variant.fields[p.field].name; + write!(f, "{}{}: {}", start_or_comma(), name, p.pattern)?; + printed += 1; + } - Ok(()) + let is_union = ty.ty_adt_def().is_some_and(|adt| adt.is_union()); + if printed < variant.fields.len() && (!is_union || printed == 0) { + write!(f, "{}..", start_or_comma())?; } - PatKind::Deref { ref subpattern } => { - match self.ty.kind() { - ty::Adt(def, _) if def.is_box() => write!(f, "box ")?, - ty::Ref(_, _, mutbl) => { - write!(f, "&{}", mutbl.prefix_str())?; - } - _ => bug!("{} is a bad Deref pattern type", self.ty), + + return write!(f, " }}"); + } + } + + let num_fields = variant_and_name.as_ref().map_or(subpatterns.len(), |(v, _)| v.fields.len()); + if num_fields != 0 || variant_and_name.is_none() { + write!(f, "(")?; + for i in 0..num_fields { + write!(f, "{}", start_or_comma())?; + + // Common case: the field is where we expect it. + if let Some(p) = subpatterns.get(i) { + if p.field.index() == i { + write!(f, "{}", p.pattern)?; + continue; } - write!(f, "{subpattern}") } - PatKind::Constant { value } => write!(f, "{value}"), - PatKind::Range(ref range) => write!(f, "{range}"), - PatKind::Slice { ref prefix, ref slice, ref suffix } => { - write!(f, "[")?; - for p in prefix.iter() { - write!(f, "{}{}", start_or_comma(), p)?; - } - if let Some(ref slice) = *slice { - write!(f, "{}", start_or_comma())?; - match slice.kind { - PatKind::Wild => {} - _ => write!(f, "{slice}")?, - } - write!(f, "..")?; - } - for p in suffix.iter() { - write!(f, "{}{}", start_or_comma(), p)?; - } - write!(f, "]") + + // Otherwise, we have to go looking for it. + if let Some(p) = subpatterns.iter().find(|p| p.field.index() == i) { + write!(f, "{}", p.pattern)?; + } else { + write!(f, "_")?; } } + write!(f, ")")?; + } + + Ok(()) +} + +fn write_ref_like<'tcx>( + f: &mut impl fmt::Write, + ty: Ty<'tcx>, + subpattern: &Pat<'tcx>, +) -> fmt::Result { + match ty.kind() { + ty::Ref(_, _, mutbl) => { + write!(f, "&{}", mutbl.prefix_str())?; + } + _ => bug!("{ty} is a bad ref pattern type"), + } + write!(f, "{subpattern}") +} + +fn write_slice_like<'tcx>( + f: &mut impl fmt::Write, + prefix: &[Box<Pat<'tcx>>], + has_dot_dot: bool, + suffix: &[Box<Pat<'tcx>>], +) -> fmt::Result { + let mut start_or_comma = start_or_comma(); + write!(f, "[")?; + for p in prefix.iter() { + write!(f, "{}{}", start_or_comma(), p)?; + } + if has_dot_dot { + write!(f, "{}..", start_or_comma())?; + } + for p in suffix.iter() { + write!(f, "{}{}", start_or_comma(), p)?; } + write!(f, "]") } |
