From a2b3256374014947a5a8e6ded86527fe44dd79ea Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 30 Jul 2024 17:28:37 +1000 Subject: [PATCH 1/3] Print `thir::PatRange`, not its surrounding `thir::Pat` This further reduces the amount of code that relies on `thir::Pat` being printable. --- compiler/rustc_pattern_analysis/src/rustc.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index 126e5357cd8b9..135017208589c 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -1003,12 +1003,11 @@ impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> { } // `pat` is an exclusive range like `lo..gap`. `gapped_with` contains ranges that start with // `gap+1`. - let suggested_range: thir::Pat<'_> = { + let suggested_range: String = { // Suggest `lo..=gap` instead. - let mut suggested_range = thir_pat.clone(); - let thir::PatKind::Range(range) = &mut suggested_range.kind else { unreachable!() }; - range.end = rustc_hir::RangeEnd::Included; - suggested_range + let mut suggested_range = PatRange::clone(range); + suggested_range.end = rustc_hir::RangeEnd::Included; + suggested_range.to_string() }; let gap_as_pat = self.hoist_pat_range(&gap, *pat.ty()); if gapped_with.is_empty() { @@ -1023,7 +1022,7 @@ impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> { // That's the gap that isn't covered. max: gap_as_pat.to_string(), // Suggest `lo..=max` instead. - suggestion: suggested_range.to_string(), + suggestion: suggested_range, }, ); } else { @@ -1037,7 +1036,7 @@ impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> { // That's the gap that isn't covered. gap: gap_as_pat.to_string(), // Suggest `lo..=gap` instead. - suggestion: suggested_range.to_string(), + suggestion: suggested_range, // All these ranges skipped over `gap` which we think is probably a // mistake. gap_with: gapped_with @@ -1045,7 +1044,7 @@ impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> { .map(|pat| errors::GappedRange { span: pat.data().span, gap: gap_as_pat.to_string(), - first_range: thir_pat.to_string(), + first_range: range.to_string(), }) .collect(), }, From a9ea85e04422345020be7dfb7e655c911d458e6c Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 30 Jul 2024 22:36:17 +1000 Subject: [PATCH 2/3] Revert "Make `thir::Pat` not implement `fmt::Display` directly" This reverts commit ae0ec731a86ac6a2f6d8d08c4996bb8a438afdc2. The original change in #128304 was intended to be a step towards being able to print `thir::Pat` even after switching to `PatId`. But because the only patterns that need to be printed are the synthetic ones created by pattern analysis (for diagnostic purposes only), it makes more sense to completely separate the printable patterns from the real THIR patterns. --- compiler/rustc_middle/src/thir.rs | 65 +++++++++---------------------- 1 file changed, 19 insertions(+), 46 deletions(-) diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 690c0121b2b36..33bc69fbc92dd 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -1073,33 +1073,8 @@ impl<'tcx> PatRangeBoundary<'tcx> { } } -impl<'tcx> Pat<'tcx> { - /// Prints a [`Pat`] to an owned string, for user-facing diagnostics. - /// - /// If we ever switch over to storing subpatterns as `PatId`, this will also - /// need to take a context that can resolve IDs to subpatterns. - pub fn to_string(&self) -> String { - format!("{}", self.display()) - } - - /// Used internally by [`fmt::Display`] for [`PatDisplay`]. - fn display(&self) -> PatDisplay<'_, 'tcx> { - PatDisplay { pat: self } - } -} - -/// Wrapper around [`&Pat<'tcx>`][`Pat`] that implements [`fmt::Display`]. -/// -/// If we ever switch over to storing subpatterns as `PatId`, this will also -/// need to hold a context that can resolve IDs to subpatterns. -struct PatDisplay<'pat, 'tcx> { - pat: &'pat Pat<'tcx>, -} - -impl<'pat, 'tcx> fmt::Display for PatDisplay<'pat, 'tcx> { +impl<'tcx> fmt::Display for Pat<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let &Self { pat } = self; - // Printing lists is a chore. let mut first = true; let mut start_or_continue = |s| { @@ -1112,22 +1087,20 @@ impl<'pat, 'tcx> fmt::Display for PatDisplay<'pat, 'tcx> { }; let mut start_or_comma = || start_or_continue(", "); - match pat.kind { + match self.kind { PatKind::Wild => write!(f, "_"), PatKind::Never => write!(f, "!"), - PatKind::AscribeUserType { ref subpattern, .. } => { - write!(f, "{}: _", subpattern.display()) - } + PatKind::AscribeUserType { ref subpattern, .. } => write!(f, "{subpattern}: _"), PatKind::Binding { name, mode, ref subpattern, .. } => { f.write_str(mode.prefix_str())?; write!(f, "{name}")?; if let Some(ref subpattern) = *subpattern { - write!(f, " @ {}", subpattern.display())?; + write!(f, " @ {subpattern}")?; } Ok(()) } PatKind::Variant { ref subpatterns, .. } | PatKind::Leaf { ref subpatterns } => { - let variant_and_name = match pat.kind { + 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(); @@ -1140,7 +1113,7 @@ impl<'pat, 'tcx> fmt::Display for PatDisplay<'pat, 'tcx> { }; Some((variant, name)) }), - _ => pat.ty.ty_adt_def().and_then(|adt_def| { + _ => 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()))) @@ -1165,11 +1138,11 @@ impl<'pat, 'tcx> fmt::Display for PatDisplay<'pat, 'tcx> { continue; } let name = variant.fields[p.field].name; - write!(f, "{}{}: {}", start_or_comma(), name, p.pattern.display())?; + write!(f, "{}{}: {}", start_or_comma(), name, p.pattern)?; printed += 1; } - let is_union = pat.ty.ty_adt_def().is_some_and(|adt| adt.is_union()); + 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())?; } @@ -1188,14 +1161,14 @@ impl<'pat, 'tcx> fmt::Display for PatDisplay<'pat, 'tcx> { // 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.display())?; + 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.display())?; + write!(f, "{}", p.pattern)?; } else { write!(f, "_")?; } @@ -1206,45 +1179,45 @@ impl<'pat, 'tcx> fmt::Display for PatDisplay<'pat, 'tcx> { Ok(()) } PatKind::Deref { ref subpattern } => { - match pat.ty.kind() { + 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", pat.ty), + _ => bug!("{} is a bad Deref pattern type", self.ty), } - write!(f, "{}", subpattern.display()) + write!(f, "{subpattern}") } PatKind::DerefPattern { ref subpattern, .. } => { - write!(f, "deref!({})", subpattern.display()) + write!(f, "deref!({subpattern})") } PatKind::Constant { value } => write!(f, "{value}"), PatKind::InlineConstant { def: _, ref subpattern } => { - write!(f, "{} (from inline const)", subpattern.display()) + write!(f, "{} (from inline const)", subpattern) } PatKind::Range(ref range) => write!(f, "{range}"), PatKind::Slice { ref prefix, ref slice, ref suffix } | PatKind::Array { ref prefix, ref slice, ref suffix } => { write!(f, "[")?; for p in prefix.iter() { - write!(f, "{}{}", start_or_comma(), p.display())?; + 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.display())?, + _ => write!(f, "{slice}")?, } write!(f, "..")?; } for p in suffix.iter() { - write!(f, "{}{}", start_or_comma(), p.display())?; + write!(f, "{}{}", start_or_comma(), p)?; } write!(f, "]") } PatKind::Or { ref pats } => { for pat in pats.iter() { - write!(f, "{}{}", start_or_continue(" | "), pat.display())?; + write!(f, "{}{}", start_or_continue(" | "), pat)?; } Ok(()) } From dd5a8d77142562250c4780b0b8b228a0c557498e Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 30 Jul 2024 22:44:02 +1000 Subject: [PATCH 3/3] Use a separate pattern type for `rustc_pattern_analysis` diagnostics The pattern-analysis code needs to print patterns, as part of its user-visible diagnostics. But it never actually tries to print "real" patterns! Instead, it only ever prints synthetic patterns that it has reconstructed from its own internal represenations. We can therefore simultaneously remove two obstacles to changing `thir::Pat`, by having the pattern-analysis code use its own dedicated type for building printable patterns, and then making `thir::Pat` not printable at all. --- compiler/rustc_middle/src/thir.rs | 159 +-------------- compiler/rustc_pattern_analysis/src/rustc.rs | 26 ++- .../rustc_pattern_analysis/src/rustc/print.rs | 193 ++++++++++++++++++ 3 files changed, 209 insertions(+), 169 deletions(-) create mode 100644 compiler/rustc_pattern_analysis/src/rustc/print.rs diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 33bc69fbc92dd..f2ea32275f9b1 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -28,7 +28,7 @@ use rustc_middle::ty::{ TyCtxt, UpvarArgs, }; use rustc_span::def_id::LocalDefId; -use rustc_span::{sym, ErrorGuaranteed, Span, Symbol, DUMMY_SP}; +use rustc_span::{ErrorGuaranteed, Span, Symbol}; use rustc_target::abi::{FieldIdx, Integer, Size, VariantIdx}; use rustc_target::asm::InlineAsmRegOrRegClass; use tracing::instrument; @@ -597,10 +597,6 @@ pub struct Pat<'tcx> { } impl<'tcx> Pat<'tcx> { - pub fn wildcard_from_ty(ty: Ty<'tcx>) -> Self { - Pat { ty, span: DUMMY_SP, kind: PatKind::Wild } - } - pub fn simple_ident(&self) -> Option { match self.kind { PatKind::Binding { @@ -1073,159 +1069,6 @@ impl<'tcx> PatRangeBoundary<'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::AscribeUserType { ref subpattern, .. } => write!(f, "{subpattern}: _"), - PatKind::Binding { name, mode, ref subpattern, .. } => { - f.write_str(mode.prefix_str())?; - write!(f, "{name}")?; - if let Some(ref subpattern) = *subpattern { - write!(f, " @ {subpattern}")?; - } - Ok(()) - } - 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, " }}"); - } - } - - 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(()) - } - 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), - } - write!(f, "{subpattern}") - } - PatKind::DerefPattern { ref subpattern, .. } => { - write!(f, "deref!({subpattern})") - } - PatKind::Constant { value } => write!(f, "{value}"), - PatKind::InlineConstant { def: _, ref subpattern } => { - write!(f, "{} (from inline const)", subpattern) - } - PatKind::Range(ref range) => write!(f, "{range}"), - PatKind::Slice { ref prefix, ref slice, ref suffix } - | PatKind::Array { 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, "]") - } - PatKind::Or { ref pats } => { - for pat in pats.iter() { - write!(f, "{}{}", start_or_continue(" | "), pat)?; - } - Ok(()) - } - PatKind::Error(_) => write!(f, ""), - } - } -} - // Some nodes are used a lot. Make sure they don't unintentionally get bigger. #[cfg(target_pointer_width = "64")] mod size_asserts { diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index 135017208589c..6290aeb252312 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -7,7 +7,7 @@ use rustc_hir::HirId; use rustc_index::{Idx, IndexVec}; use rustc_middle::middle::stability::EvalResult; use rustc_middle::mir::{self, Const}; -use rustc_middle::thir::{self, FieldPat, Pat, PatKind, PatRange, PatRangeBoundary}; +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, @@ -26,6 +26,8 @@ use crate::pat_column::PatternColumn; use crate::usefulness::{compute_match_usefulness, PlaceValidity}; use crate::{errors, Captures, PatCx, PrivateUninhabitedField}; +mod print; + // Re-export rustc-specific versions of all these types. pub type Constructor<'p, 'tcx> = crate::constructor::Constructor>; pub type ConstructorSet<'p, 'tcx> = crate::constructor::ConstructorSet>; @@ -773,8 +775,9 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { } } - /// Convert back to a `thir::Pat` for diagnostic purposes. - fn hoist_pat_range(&self, range: &IntRange, ty: RevealedTy<'tcx>) -> Pat<'tcx> { + /// 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}; use MaybeInfiniteInt::*; let cx = self; let kind = if matches!((range.lo, range.hi), (NegInfinity, PosInfinity)) { @@ -808,19 +811,20 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { PatKind::Range(Box::new(PatRange { lo, hi, end, ty: ty.inner() })) }; - Pat { ty: ty.inner(), span: DUMMY_SP, kind } + Pat { ty: ty.inner(), kind } } /// Prints a [`WitnessPat`] to an owned string, for diagnostic purposes. pub fn print_witness_pat(&self, pat: &WitnessPat<'p, 'tcx>) -> String { - // This works by converting the witness pattern back to a `thir::Pat` + // 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 back to a `thir::Pat` for diagnostic purposes. This panics for patterns that don't + /// 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>) -> Pat<'tcx> { + fn hoist_witness_pat(&self, pat: &WitnessPat<'p, 'tcx>) -> print::Pat<'tcx> { + use print::{FieldPat, Pat, PatKind}; let cx = self; let is_wildcard = |pat: &Pat<'_>| matches!(pat.kind, PatKind::Wild); let mut subpatterns = pat.iter_fields().map(|p| Box::new(cx.hoist_witness_pat(p))); @@ -840,7 +844,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { // the pattern is a box pattern. PatKind::Deref { subpattern: subpatterns.next().unwrap() } } - ty::Adt(adt_def, args) => { + ty::Adt(adt_def, _args) => { let variant_index = RustcPatCtxt::variant_index_for_adt(&pat.ctor(), *adt_def); let subpatterns = subpatterns .enumerate() @@ -848,7 +852,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { .collect(); if adt_def.is_enum() { - PatKind::Variant { adt_def: *adt_def, args, variant_index, subpatterns } + PatKind::Variant { adt_def: *adt_def, variant_index, subpatterns } } else { PatKind::Leaf { subpatterns } } @@ -885,7 +889,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { } } let suffix: Box<[_]> = subpatterns.collect(); - let wild = Pat::wildcard_from_ty(pat.ty().inner()); + let wild = Pat { ty: pat.ty().inner(), kind: PatKind::Wild }; PatKind::Slice { prefix: prefix.into_boxed_slice(), slice: Some(Box::new(wild)), @@ -906,7 +910,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { } }; - Pat { ty: pat.ty().inner(), span: DUMMY_SP, kind } + Pat { ty: pat.ty().inner(), kind } } } diff --git a/compiler/rustc_pattern_analysis/src/rustc/print.rs b/compiler/rustc_pattern_analysis/src/rustc/print.rs new file mode 100644 index 0000000000000..4b76764e8b136 --- /dev/null +++ b/compiler/rustc_pattern_analysis/src/rustc/print.rs @@ -0,0 +1,193 @@ +//! 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_middle::thir::PatRange; +use rustc_middle::ty::{self, AdtDef, Ty}; +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) 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, + + Variant { + adt_def: AdtDef<'tcx>, + variant_index: VariantIdx, + subpatterns: Vec>, + }, + + Leaf { + subpatterns: Vec>, + }, + + Deref { + subpattern: Box>, + }, + + Constant { + value: mir::Const<'tcx>, + }, + + Range(Box>), + + Slice { + prefix: Box<[Box>]>, + slice: Option>>, + suffix: Box<[Box>]>, + }, + + Never, +} + +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, " }}"); + } + } + + 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(()) + } + 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), + } + 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, "]") + } + } + } +}