| //! Pattern analysis sometimes wants to print patterns as part of a user-visible |
| //! diagnostic. |
| //! |
| //! Historically it did so by creating a synthetic [`thir::Pat`](rustc_middle::thir::Pat) |
| //! and printing that, but doing so was making it hard to modify the THIR pattern |
| //! representation for other purposes. |
| //! |
| //! So this module contains a forked copy of `thir::Pat` that is used _only_ |
| //! for diagnostics, and has been partly simplified to remove things that aren't |
| //! needed for printing. |
| |
| use std::fmt; |
| |
| use rustc_abi::{FieldIdx, VariantIdx}; |
| use rustc_middle::bug; |
| use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt}; |
| use rustc_span::sym; |
| |
| #[derive(Clone, Debug)] |
| pub(crate) struct FieldPat { |
| pub(crate) field: FieldIdx, |
| pub(crate) pattern: String, |
| pub(crate) is_wildcard: bool, |
| } |
| |
| /// 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, |
| } |
| |
| pub(crate) fn write_struct_like<'tcx>( |
| f: &mut impl fmt::Write, |
| tcx: TyCtxt<'_>, |
| ty: Ty<'tcx>, |
| enum_info: &EnumInfo<'tcx>, |
| subpatterns: &[FieldPat], |
| ) -> 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(); |
| |
| 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 &FieldPat { field, ref pattern, is_wildcard } in subpatterns { |
| if is_wildcard { |
| continue; |
| } |
| let field_name = variant.fields[field].name; |
| write!(f, "{}{field_name}: {pattern}", start_or_comma())?; |
| printed += 1; |
| } |
| |
| 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())?; |
| } |
| |
| 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; |
| } |
| } |
| |
| // 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(()) |
| } |
| |
| pub(crate) fn write_ref_like<'tcx>( |
| f: &mut impl fmt::Write, |
| ty: Ty<'tcx>, |
| subpattern: &str, |
| ) -> fmt::Result { |
| match ty.kind() { |
| ty::Ref(_, _, mutbl) => { |
| write!(f, "&{}", mutbl.prefix_str())?; |
| } |
| _ => bug!("{ty} is a bad ref pattern type"), |
| } |
| write!(f, "{subpattern}") |
| } |
| |
| pub(crate) fn write_slice_like( |
| f: &mut impl fmt::Write, |
| prefix: &[String], |
| has_dot_dot: bool, |
| suffix: &[String], |
| ) -> 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, "]") |
| } |