
Depend on rustc_abi in compiler crates that use it indirectly but have not yet taken on that dependency, and are not entangled in my other PRs. This leaves an "excise rustc_target" step after the dust settles.
160 lines
4.7 KiB
Rust
160 lines
4.7 KiB
Rust
//! 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_abi::{FieldIdx, VariantIdx};
|
|
use rustc_middle::bug;
|
|
use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt};
|
|
use rustc_span::sym;
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub(crate) struct FieldPat {
|
|
pub(crate) field: FieldIdx,
|
|
pub(crate) pattern: String,
|
|
pub(crate) is_wildcard: bool,
|
|
}
|
|
|
|
/// Returns a closure that will return `""` when called the first time,
|
|
/// and then return `", "` when called any subsequent times.
|
|
/// Useful for printing comma-separated lists.
|
|
fn start_or_comma() -> impl FnMut() -> &'static str {
|
|
let mut first = true;
|
|
move || {
|
|
if first {
|
|
first = false;
|
|
""
|
|
} else {
|
|
", "
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub(crate) enum EnumInfo<'tcx> {
|
|
Enum { adt_def: AdtDef<'tcx>, variant_index: VariantIdx },
|
|
NotEnum,
|
|
}
|
|
|
|
pub(crate) fn write_struct_like<'tcx>(
|
|
f: &mut impl fmt::Write,
|
|
tcx: TyCtxt<'_>,
|
|
ty: Ty<'tcx>,
|
|
enum_info: &EnumInfo<'tcx>,
|
|
subpatterns: &[FieldPat],
|
|
) -> fmt::Result {
|
|
let variant_and_name = match *enum_info {
|
|
EnumInfo::Enum { adt_def, variant_index } => {
|
|
let variant = adt_def.variant(variant_index);
|
|
let adt_did = adt_def.did();
|
|
let name = if tcx.is_diagnostic_item(sym::Option, adt_did)
|
|
|| tcx.is_diagnostic_item(sym::Result, adt_did)
|
|
{
|
|
variant.name.to_string()
|
|
} else {
|
|
format!("{}::{}", tcx.def_path_str(adt_def.did()), variant.name)
|
|
};
|
|
Some((variant, name))
|
|
}
|
|
EnumInfo::NotEnum => ty.ty_adt_def().and_then(|adt_def| {
|
|
Some((adt_def.non_enum_variant(), tcx.def_path_str(adt_def.did())))
|
|
}),
|
|
};
|
|
|
|
let mut start_or_comma = start_or_comma();
|
|
|
|
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 &FieldPat { field, ref pattern, is_wildcard } in subpatterns {
|
|
if is_wildcard {
|
|
continue;
|
|
}
|
|
let field_name = variant.fields[field].name;
|
|
write!(f, "{}{field_name}: {pattern}", start_or_comma())?;
|
|
printed += 1;
|
|
}
|
|
|
|
let is_union = 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(())
|
|
}
|
|
|
|
pub(crate) fn write_ref_like<'tcx>(
|
|
f: &mut impl fmt::Write,
|
|
ty: Ty<'tcx>,
|
|
subpattern: &str,
|
|
) -> fmt::Result {
|
|
match ty.kind() {
|
|
ty::Ref(_, _, mutbl) => {
|
|
write!(f, "&{}", mutbl.prefix_str())?;
|
|
}
|
|
_ => bug!("{ty} is a bad ref pattern type"),
|
|
}
|
|
write!(f, "{subpattern}")
|
|
}
|
|
|
|
pub(crate) fn write_slice_like(
|
|
f: &mut impl fmt::Write,
|
|
prefix: &[String],
|
|
has_dot_dot: bool,
|
|
suffix: &[String],
|
|
) -> fmt::Result {
|
|
let mut start_or_comma = start_or_comma();
|
|
write!(f, "[")?;
|
|
for p in prefix.iter() {
|
|
write!(f, "{}{}", start_or_comma(), p)?;
|
|
}
|
|
if has_dot_dot {
|
|
write!(f, "{}..", start_or_comma())?;
|
|
}
|
|
for p in suffix.iter() {
|
|
write!(f, "{}{}", start_or_comma(), p)?;
|
|
}
|
|
write!(f, "]")
|
|
}
|