Skip to main content

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                {
    let _guard =
        ::rustc_middle::ty::print::pretty::RtnModeHelper::with(RtnMode::ForSuggestion);
    ::alloc::__export::must_use({
            ::alloc::fmt::format(format_args!("{0}::{1}",
                    tcx.def_path_str(adt_def.did()), variant.name))
        })
}ty::print::with_types_for_suggestion!(format!(
64                    "{}::{}",
65                    tcx.def_path_str(adt_def.did()),
66                    variant.name
67                ))
68            };
69            Some((variant, name))
70        }
71        EnumInfo::NotEnum => ty.ty_adt_def().and_then(|adt_def| {
72            Some((adt_def.non_enum_variant(), tcx.def_path_str(adt_def.did())))
73        }),
74    };
75
76    let mut start_or_comma = start_or_comma();
77
78    if let Some((variant, name)) = &variant_and_name {
79        f.write_fmt(format_args!("{0}", name))write!(f, "{name}")?;
80
81        // Only for Adt we can have `S {...}`,
82        // which we handle separately here.
83        if variant.ctor.is_none() {
84            f.write_fmt(format_args!(" {{ "))write!(f, " {{ ")?;
85
86            let mut printed = 0;
87            for &FieldPat { field, ref pattern, is_wildcard } in subpatterns {
88                if is_wildcard {
89                    continue;
90                }
91                let field_name = variant.fields[field].name;
92                f.write_fmt(format_args!("{0}{1}: {2}", start_or_comma(), field_name,
        pattern))write!(f, "{}{field_name}: {pattern}", start_or_comma())?;
93                printed += 1;
94            }
95
96            let is_union = ty.ty_adt_def().is_some_and(|adt| adt.is_union());
97            if printed < variant.fields.len() && (!is_union || printed == 0) {
98                f.write_fmt(format_args!("{0}..", start_or_comma()))write!(f, "{}..", start_or_comma())?;
99            }
100
101            return f.write_fmt(format_args!(" }}"))write!(f, " }}");
102        }
103    }
104
105    let num_fields = variant_and_name.as_ref().map_or(subpatterns.len(), |(v, _)| v.fields.len());
106    if num_fields != 0 || variant_and_name.is_none() {
107        f.write_fmt(format_args!("("))write!(f, "(")?;
108        for FieldPat { pattern, .. } in subpatterns {
109            f.write_fmt(format_args!("{0}{1}", start_or_comma(), pattern))write!(f, "{}{pattern}", start_or_comma())?;
110        }
111        if #[allow(non_exhaustive_omitted_patterns)] match ty.kind() {
    ty::Tuple(..) => true,
    _ => false,
}matches!(ty.kind(), ty::Tuple(..)) && num_fields == 1 {
112            f.write_fmt(format_args!(","))write!(f, ",")?;
113        }
114        f.write_fmt(format_args!(")"))write!(f, ")")?;
115    }
116
117    Ok(())
118}
119
120pub(crate) fn write_ref_like<'tcx>(
121    f: &mut impl fmt::Write,
122    ty: Ty<'tcx>,
123    subpattern: &str,
124) -> fmt::Result {
125    match ty.kind() {
126        ty::Ref(_, _, mutbl) => {
127            f.write_fmt(format_args!("&{0}", mutbl.prefix_str()))write!(f, "&{}", mutbl.prefix_str())?;
128        }
129        _ => ::rustc_middle::util::bug::bug_fmt(format_args!("{0} is a bad ref pattern type",
        ty))bug!("{ty} is a bad ref pattern type"),
130    }
131    f.write_fmt(format_args!("{0}", subpattern))write!(f, "{subpattern}")
132}
133
134pub(crate) fn write_slice_like(
135    f: &mut impl fmt::Write,
136    prefix: &[String],
137    has_dot_dot: bool,
138    suffix: &[String],
139) -> fmt::Result {
140    let mut start_or_comma = start_or_comma();
141    f.write_fmt(format_args!("["))write!(f, "[")?;
142    for p in prefix.iter() {
143        f.write_fmt(format_args!("{0}{1}", start_or_comma(), p))write!(f, "{}{}", start_or_comma(), p)?;
144    }
145    if has_dot_dot {
146        f.write_fmt(format_args!("{0}..", start_or_comma()))write!(f, "{}..", start_or_comma())?;
147    }
148    for p in suffix.iter() {
149        f.write_fmt(format_args!("{0}{1}", start_or_comma(), p))write!(f, "{}{}", start_or_comma(), p)?;
150    }
151    f.write_fmt(format_args!("]"))write!(f, "]")
152}