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::ty::{self, AdtDef, Ty, TyCtxt};
16use rustc_span::{bug, sym};
17
18#[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)]
19pub(crate) struct FieldPat {
20    pub(crate) field: FieldIdx,
21    pub(crate) pattern: String,
22    pub(crate) is_wildcard: bool,
23}
24
25/// Returns a closure that will return `""` when called the first time,
26/// and then return `", "` when called any subsequent times.
27/// Useful for printing comma-separated lists.
28fn start_or_comma() -> impl FnMut() -> &'static str {
29    let mut first = true;
30    move || {
31        if first {
32            first = false;
33            ""
34        } else {
35            ", "
36        }
37    }
38}
39
40#[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)]
41pub(crate) enum EnumInfo<'tcx> {
42    Enum { adt_def: AdtDef<'tcx>, variant_index: VariantIdx },
43    NotEnum,
44}
45
46pub(crate) fn write_struct_like<'tcx>(
47    f: &mut impl fmt::Write,
48    tcx: TyCtxt<'_>,
49    ty: Ty<'tcx>,
50    enum_info: &EnumInfo<'tcx>,
51    subpatterns: &[FieldPat],
52) -> fmt::Result {
53    let variant_and_name = match *enum_info {
54        EnumInfo::Enum { adt_def, variant_index } => {
55            let variant = adt_def.variant(variant_index);
56            let adt_did = adt_def.did();
57            let name = if tcx.is_diagnostic_item(sym::Option, adt_did)
58                || tcx.is_diagnostic_item(sym::Result, adt_did)
59            {
60                variant.name.to_string()
61            } else {
62                {
    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!(
63                    "{}::{}",
64                    tcx.def_path_str(adt_def.did()),
65                    variant.name
66                ))
67            };
68            Some((variant, name))
69        }
70        EnumInfo::NotEnum => ty.ty_adt_def().and_then(|adt_def| {
71            Some((adt_def.non_enum_variant(), tcx.def_path_str(adt_def.did())))
72        }),
73    };
74
75    let mut start_or_comma = start_or_comma();
76
77    if let Some((variant, name)) = &variant_and_name {
78        f.write_fmt(format_args!("{0}", name))write!(f, "{name}")?;
79
80        // Only for Adt we can have `S {...}`,
81        // which we handle separately here.
82        if variant.ctor.is_none() {
83            f.write_fmt(format_args!(" {{ "))write!(f, " {{ ")?;
84
85            let mut printed = 0;
86            for &FieldPat { field, ref pattern, is_wildcard } in subpatterns {
87                if is_wildcard {
88                    continue;
89                }
90                let field_name = variant.fields[field].name;
91                f.write_fmt(format_args!("{0}{1}: {2}", start_or_comma(), field_name,
        pattern))write!(f, "{}{field_name}: {pattern}", start_or_comma())?;
92                printed += 1;
93            }
94
95            let is_union = ty.ty_adt_def().is_some_and(|adt| adt.is_union());
96            if printed < variant.fields.len() && (!is_union || printed == 0) {
97                f.write_fmt(format_args!("{0}..", start_or_comma()))write!(f, "{}..", start_or_comma())?;
98            }
99
100            return f.write_fmt(format_args!(" }}"))write!(f, " }}");
101        }
102    }
103
104    let num_fields = variant_and_name.as_ref().map_or(subpatterns.len(), |(v, _)| v.fields.len());
105    if num_fields != 0 || variant_and_name.is_none() {
106        f.write_fmt(format_args!("("))write!(f, "(")?;
107        for FieldPat { pattern, .. } in subpatterns {
108            f.write_fmt(format_args!("{0}{1}", start_or_comma(), pattern))write!(f, "{}{pattern}", start_or_comma())?;
109        }
110        if #[allow(non_exhaustive_omitted_patterns)] match ty.kind() {
    ty::Tuple(..) => true,
    _ => false,
}matches!(ty.kind(), ty::Tuple(..)) && num_fields == 1 {
111            f.write_fmt(format_args!(","))write!(f, ",")?;
112        }
113        f.write_fmt(format_args!(")"))write!(f, ")")?;
114    }
115
116    Ok(())
117}
118
119pub(crate) fn write_ref_like<'tcx>(
120    f: &mut impl fmt::Write,
121    ty: Ty<'tcx>,
122    subpattern: &str,
123) -> fmt::Result {
124    match ty.kind() {
125        ty::Ref(_, _, mutbl) => {
126            f.write_fmt(format_args!("&{0}", mutbl.prefix_str()))write!(f, "&{}", mutbl.prefix_str())?;
127        }
128        _ => bug_impl(None, format_args!("{0} is a bad ref pattern type", ty),
    Location::caller())bug!("{ty} is a bad ref pattern type"),
129    }
130    f.write_fmt(format_args!("{0}", subpattern))write!(f, "{subpattern}")
131}
132
133pub(crate) fn write_slice_like(
134    f: &mut impl fmt::Write,
135    prefix: &[String],
136    has_dot_dot: bool,
137    suffix: &[String],
138) -> fmt::Result {
139    let mut start_or_comma = start_or_comma();
140    f.write_fmt(format_args!("["))write!(f, "[")?;
141    for p in prefix.iter() {
142        f.write_fmt(format_args!("{0}{1}", start_or_comma(), p))write!(f, "{}{}", start_or_comma(), p)?;
143    }
144    if has_dot_dot {
145        f.write_fmt(format_args!("{0}..", start_or_comma()))write!(f, "{}..", start_or_comma())?;
146    }
147    for p in suffix.iter() {
148        f.write_fmt(format_args!("{0}{1}", start_or_comma(), p))write!(f, "{}{}", start_or_comma(), p)?;
149    }
150    f.write_fmt(format_args!("]"))write!(f, "]")
151}