rustc_pattern_analysis/rustc/
print.rs
1use std::fmt;
13
14use rustc_abi::{FieldIdx, VariantIdx};
15use rustc_middle::bug;
16use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt};
17use rustc_span::sym;
18
19#[derive(Clone, Debug)]
20pub(crate) struct FieldPat {
21 pub(crate) field: FieldIdx,
22 pub(crate) pattern: String,
23 pub(crate) is_wildcard: bool,
24}
25
26fn start_or_comma() -> impl FnMut() -> &'static str {
30 let mut first = true;
31 move || {
32 if first {
33 first = false;
34 ""
35 } else {
36 ", "
37 }
38 }
39}
40
41#[derive(Clone, Debug)]
42pub(crate) enum EnumInfo<'tcx> {
43 Enum { adt_def: AdtDef<'tcx>, variant_index: VariantIdx },
44 NotEnum,
45}
46
47pub(crate) fn write_struct_like<'tcx>(
48 f: &mut impl fmt::Write,
49 tcx: TyCtxt<'_>,
50 ty: Ty<'tcx>,
51 enum_info: &EnumInfo<'tcx>,
52 subpatterns: &[FieldPat],
53) -> fmt::Result {
54 let variant_and_name = match *enum_info {
55 EnumInfo::Enum { adt_def, variant_index } => {
56 let variant = adt_def.variant(variant_index);
57 let adt_did = adt_def.did();
58 let name = if tcx.is_diagnostic_item(sym::Option, adt_did)
59 || tcx.is_diagnostic_item(sym::Result, adt_did)
60 {
61 variant.name.to_string()
62 } else {
63 format!("{}::{}", tcx.def_path_str(adt_def.did()), variant.name)
64 };
65 Some((variant, name))
66 }
67 EnumInfo::NotEnum => ty.ty_adt_def().and_then(|adt_def| {
68 Some((adt_def.non_enum_variant(), tcx.def_path_str(adt_def.did())))
69 }),
70 };
71
72 let mut start_or_comma = start_or_comma();
73
74 if let Some((variant, name)) = &variant_and_name {
75 write!(f, "{name}")?;
76
77 if variant.ctor.is_none() {
80 write!(f, " {{ ")?;
81
82 let mut printed = 0;
83 for &FieldPat { field, ref pattern, is_wildcard } in subpatterns {
84 if is_wildcard {
85 continue;
86 }
87 let field_name = variant.fields[field].name;
88 write!(f, "{}{field_name}: {pattern}", start_or_comma())?;
89 printed += 1;
90 }
91
92 let is_union = ty.ty_adt_def().is_some_and(|adt| adt.is_union());
93 if printed < variant.fields.len() && (!is_union || printed == 0) {
94 write!(f, "{}..", start_or_comma())?;
95 }
96
97 return write!(f, " }}");
98 }
99 }
100
101 let num_fields = variant_and_name.as_ref().map_or(subpatterns.len(), |(v, _)| v.fields.len());
102 if num_fields != 0 || variant_and_name.is_none() {
103 write!(f, "(")?;
104 for i in 0..num_fields {
105 write!(f, "{}", start_or_comma())?;
106
107 if let Some(p) = subpatterns.get(i) {
109 if p.field.index() == i {
110 write!(f, "{}", p.pattern)?;
111 continue;
112 }
113 }
114
115 if let Some(p) = subpatterns.iter().find(|p| p.field.index() == i) {
117 write!(f, "{}", p.pattern)?;
118 } else {
119 write!(f, "_")?;
120 }
121 }
122 write!(f, ")")?;
123 }
124
125 Ok(())
126}
127
128pub(crate) fn write_ref_like<'tcx>(
129 f: &mut impl fmt::Write,
130 ty: Ty<'tcx>,
131 subpattern: &str,
132) -> fmt::Result {
133 match ty.kind() {
134 ty::Ref(_, _, mutbl) => {
135 write!(f, "&{}", mutbl.prefix_str())?;
136 }
137 _ => bug!("{ty} is a bad ref pattern type"),
138 }
139 write!(f, "{subpattern}")
140}
141
142pub(crate) fn write_slice_like(
143 f: &mut impl fmt::Write,
144 prefix: &[String],
145 has_dot_dot: bool,
146 suffix: &[String],
147) -> fmt::Result {
148 let mut start_or_comma = start_or_comma();
149 write!(f, "[")?;
150 for p in prefix.iter() {
151 write!(f, "{}{}", start_or_comma(), p)?;
152 }
153 if has_dot_dot {
154 write!(f, "{}..", start_or_comma())?;
155 }
156 for p in suffix.iter() {
157 write!(f, "{}{}", start_or_comma(), p)?;
158 }
159 write!(f, "]")
160}