rustc_pattern_analysis/rustc/
print.rs

1//! Pattern analysis sometimes wants to print patterns as part of a user-visible
2//! diagnostic.
3//!
4//! Historically it did so by creating a synthetic [`thir::Pat`](rustc_middle::thir::Pat)
5//! and printing that, but doing so was making it hard to modify the THIR pattern
6//! representation for other purposes.
7//!
8//! So this module contains a forked copy of `thir::Pat` that is used _only_
9//! for diagnostics, and has been partly simplified to remove things that aren't
10//! needed for printing.
11
12use 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(#[automatically_derived]
impl ::core::clone::Clone for FieldPat {
    #[inline]
    fn clone(&self) -> FieldPat {
        FieldPat {
            field: ::core::clone::Clone::clone(&self.field),
            pattern: ::core::clone::Clone::clone(&self.pattern),
            is_wildcard: ::core::clone::Clone::clone(&self.is_wildcard),
        }
    }
}Clone, #[automatically_derived]
impl ::core::fmt::Debug for FieldPat {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field3_finish(f, "FieldPat",
            "field", &self.field, "pattern", &self.pattern, "is_wildcard",
            &&self.is_wildcard)
    }
}Debug)]
20pub(crate) struct FieldPat {
21    pub(crate) field: FieldIdx,
22    pub(crate) pattern: String,
23    pub(crate) is_wildcard: bool,
24}
25
26/// Returns a closure that will return `""` when called the first time,
27/// and then return `", "` when called any subsequent times.
28/// Useful for printing comma-separated lists.
29fn 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(#[automatically_derived]
impl<'tcx> ::core::clone::Clone for EnumInfo<'tcx> {
    #[inline]
    fn clone(&self) -> EnumInfo<'tcx> {
        match self {
            EnumInfo::Enum { adt_def: __self_0, variant_index: __self_1 } =>
                EnumInfo::Enum {
                    adt_def: ::core::clone::Clone::clone(__self_0),
                    variant_index: ::core::clone::Clone::clone(__self_1),
                },
            EnumInfo::NotEnum => EnumInfo::NotEnum,
        }
    }
}Clone, #[automatically_derived]
impl<'tcx> ::core::fmt::Debug for EnumInfo<'tcx> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            EnumInfo::Enum { adt_def: __self_0, variant_index: __self_1 } =>
                ::core::fmt::Formatter::debug_struct_field2_finish(f, "Enum",
                    "adt_def", __self_0, "variant_index", &__self_1),
            EnumInfo::NotEnum =>
                ::core::fmt::Formatter::write_str(f, "NotEnum"),
        }
    }
}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                ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0}::{1}",
                tcx.def_path_str(adt_def.did()), variant.name))
    })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        f.write_fmt(format_args!("{0}", name))write!(f, "{name}")?;
76
77        // Only for Adt we can have `S {...}`,
78        // which we handle separately here.
79        if variant.ctor.is_none() {
80            f.write_fmt(format_args!(" {{ "))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                f.write_fmt(format_args!("{0}{1}: {2}", start_or_comma(), field_name,
        pattern))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                f.write_fmt(format_args!("{0}..", start_or_comma()))write!(f, "{}..", start_or_comma())?;
95            }
96
97            return f.write_fmt(format_args!(" }}"))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        f.write_fmt(format_args!("("))write!(f, "(")?;
104        for FieldPat { pattern, .. } in subpatterns {
105            f.write_fmt(format_args!("{0}{1}", start_or_comma(), pattern))write!(f, "{}{pattern}", start_or_comma())?;
106        }
107        if #[allow(non_exhaustive_omitted_patterns)] match ty.kind() {
    ty::Tuple(..) => true,
    _ => false,
}matches!(ty.kind(), ty::Tuple(..)) && num_fields == 1 {
108            f.write_fmt(format_args!(","))write!(f, ",")?;
109        }
110        f.write_fmt(format_args!(")"))write!(f, ")")?;
111    }
112
113    Ok(())
114}
115
116pub(crate) fn write_ref_like<'tcx>(
117    f: &mut impl fmt::Write,
118    ty: Ty<'tcx>,
119    subpattern: &str,
120) -> fmt::Result {
121    match ty.kind() {
122        ty::Ref(_, _, mutbl) => {
123            f.write_fmt(format_args!("&{0}", mutbl.prefix_str()))write!(f, "&{}", mutbl.prefix_str())?;
124        }
125        _ => ::rustc_middle::util::bug::bug_fmt(format_args!("{0} is a bad ref pattern type",
        ty))bug!("{ty} is a bad ref pattern type"),
126    }
127    f.write_fmt(format_args!("{0}", subpattern))write!(f, "{subpattern}")
128}
129
130pub(crate) fn write_slice_like(
131    f: &mut impl fmt::Write,
132    prefix: &[String],
133    has_dot_dot: bool,
134    suffix: &[String],
135) -> fmt::Result {
136    let mut start_or_comma = start_or_comma();
137    f.write_fmt(format_args!("["))write!(f, "[")?;
138    for p in prefix.iter() {
139        f.write_fmt(format_args!("{0}{1}", start_or_comma(), p))write!(f, "{}{}", start_or_comma(), p)?;
140    }
141    if has_dot_dot {
142        f.write_fmt(format_args!("{0}..", start_or_comma()))write!(f, "{}..", start_or_comma())?;
143    }
144    for p in suffix.iter() {
145        f.write_fmt(format_args!("{0}{1}", start_or_comma(), p))write!(f, "{}{}", start_or_comma(), p)?;
146    }
147    f.write_fmt(format_args!("]"))write!(f, "]")
148}