diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 954d0cf97abef..b177755e9eb24 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -538,8 +538,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> { self.error = Err(report_non_exhaustive_match( &cx, self.thir, - scrut.ty, - scrut.span, + scrut, witnesses, arms, braces_span, @@ -1205,12 +1204,13 @@ fn pat_is_catchall(pat: &DeconstructedPat<'_, '_>) -> bool { fn report_non_exhaustive_match<'p, 'tcx>( cx: &PatCtxt<'p, 'tcx>, thir: &Thir<'tcx>, - scrut_ty: Ty<'tcx>, - sp: Span, + scrut: &Expr<'tcx>, witnesses: Vec>, arms: &[ArmId], braces_span: Option, ) -> ErrorGuaranteed { + let scrut_ty = scrut.ty; + let sp = scrut.span; let is_empty_match = arms.is_empty(); let non_empty_enum = match scrut_ty.kind() { ty::Adt(def, _) => def.is_enum() && !def.variants().is_empty(), @@ -1323,7 +1323,7 @@ fn report_non_exhaustive_match<'p, 'tcx>( let suggested_arm = if suggest_the_witnesses { let pattern = witnesses .iter() - .map(|witness| cx.print_witness_pat(witness)) + .map(|witness| cx.print_witness_pat_with_scrut(witness, Some(scrut))) .collect::>() .join(" | "); if witnesses.iter().all(|p| p.is_never_pattern()) && cx.tcx.features().never_patterns() { diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index 88194c737a6cc..89c438e58f51a 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -805,10 +805,18 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { } } + pub fn print_witness_pat(&self, pat: &WitnessPat<'p, 'tcx>) -> String { + self.print_witness_pat_with_scrut(pat, None) + } + /// Prints a [`WitnessPat`] to an owned string, for diagnostic purposes. /// /// This panics for patterns that don't appear in diagnostics, like float ranges. - pub fn print_witness_pat(&self, pat: &WitnessPat<'p, 'tcx>) -> String { + pub fn print_witness_pat_with_scrut( + &self, + pat: &WitnessPat<'p, 'tcx>, + scrut: Option<&thir::Expr<'tcx>>, + ) -> String { let cx = self; let print = |p| cx.print_witness_pat(p); match pat.ctor() { @@ -847,6 +855,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { pat.ty().inner(), &enum_info, &subpatterns, + scrut, ) .unwrap(); s diff --git a/compiler/rustc_pattern_analysis/src/rustc/print.rs b/compiler/rustc_pattern_analysis/src/rustc/print.rs index 7649f72f8681b..339b3b98c8627 100644 --- a/compiler/rustc_pattern_analysis/src/rustc/print.rs +++ b/compiler/rustc_pattern_analysis/src/rustc/print.rs @@ -1,7 +1,7 @@ //! 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) +//! Historically it did so by creating a synthetic [`thir::Pat`] //! and printing that, but doing so was making it hard to modify the THIR pattern //! representation for other purposes. //! @@ -12,8 +12,8 @@ use std::fmt; use rustc_abi::{FieldIdx, VariantIdx}; -use rustc_middle::bug; use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt}; +use rustc_middle::{bug, thir}; use rustc_span::sym; #[derive(Clone, Debug)] @@ -44,12 +44,33 @@ pub(crate) enum EnumInfo<'tcx> { NotEnum, } +fn erase_path_if_local<'tcx>( + tcx: TyCtxt<'_>, + adt_def: AdtDef<'tcx>, + scrut: &thir::Expr<'tcx>, +) -> bool { + let enum_parent_def_id = tcx.parent(adt_def.did()); + let scrut_parent_def_id = if let thir::ExprKind::Scope { region_scope: _, lint_level, value: _ } = + scrut.kind + && let thir::LintLevel::Explicit(hir_id) = lint_level + { + Some(hir_id.owner.to_def_id()) + } else { + None + }; + if scrut_parent_def_id == Some(enum_parent_def_id) { + return true; + } + false +} + pub(crate) fn write_struct_like<'tcx>( f: &mut impl fmt::Write, tcx: TyCtxt<'_>, ty: Ty<'tcx>, enum_info: &EnumInfo<'tcx>, subpatterns: &[FieldPat], + scrut: Option<&thir::Expr<'tcx>>, ) -> fmt::Result { let variant_and_name = match *enum_info { EnumInfo::Enum { adt_def, variant_index } => { @@ -60,7 +81,18 @@ pub(crate) fn write_struct_like<'tcx>( { variant.name.to_string() } else { - format!("{}::{}", tcx.def_path_str(adt_def.did()), variant.name) + let enum_and_variant = if let Some(scrut) = scrut + && erase_path_if_local(tcx, adt_def, scrut) + { + ty::print::with_forced_trimmed_paths!(format!( + "{}::{}", + tcx.def_path_str(adt_def.did()), + variant.name + )) + } else { + format!("{}::{}", tcx.def_path_str(adt_def.did()), variant.name) + }; + enum_and_variant }; Some((variant, name)) } diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/shadowed-non-exhaustive.rs b/tests/ui/rfcs/rfc-2008-non-exhaustive/shadowed-non-exhaustive.rs new file mode 100644 index 0000000000000..151954c248857 --- /dev/null +++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/shadowed-non-exhaustive.rs @@ -0,0 +1,13 @@ +struct Shadowed {} + +fn main() { + let v = Shadowed::Foo; + + match v { + //~^ non-exhaustive patterns: `main::Shadowed::Foo` not covered [E0004] + } + + enum Shadowed { + Foo, + } +} diff --git a/tests/ui/rfcs/rfc-2008-non-exhaustive/shadowed-non-exhaustive.stderr b/tests/ui/rfcs/rfc-2008-non-exhaustive/shadowed-non-exhaustive.stderr new file mode 100644 index 0000000000000..676b9fd7b6d8a --- /dev/null +++ b/tests/ui/rfcs/rfc-2008-non-exhaustive/shadowed-non-exhaustive.stderr @@ -0,0 +1,24 @@ +error[E0004]: non-exhaustive patterns: `main::Shadowed::Foo` not covered + --> $DIR/shadowed-non-exhaustive.rs:6:11 + | +LL | match v { + | ^ pattern `main::Shadowed::Foo` not covered + | +note: `main::Shadowed` defined here + --> $DIR/shadowed-non-exhaustive.rs:10:10 + | +LL | enum Shadowed { + | ^^^^^^^^ +LL | Foo, + | --- not covered + = note: the matched value is of type `main::Shadowed` +help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown + | +LL ~ match v { +LL + Shadowed::Foo => todo!(), +LL + } + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0004`.