From e8ddb780e406e8cd0293043ef94b819b52d64da2 Mon Sep 17 00:00:00 2001 From: Snxhit Date: Sat, 4 Jul 2026 01:49:20 +0530 Subject: [PATCH] improve diagnostic suggestion when matching struct variants with tuple syntax - update `report_unexpected_variant_res` to suggest using proper struct usage instead of stating that fields are being ignored. - add edge test cases in `tests/ui/parser/recover/recover-from-bad-variant.rs` --- compiler/rustc_hir_typeck/src/expr.rs | 1 + compiler/rustc_hir_typeck/src/lib.rs | 37 ++++++++---- compiler/rustc_hir_typeck/src/pat.rs | 20 ++++++- ...-on-tuple-and-struct-variants-63983.stderr | 2 +- .../recover/recover-from-bad-variant.rs | 8 +++ .../recover/recover-from-bad-variant.stderr | 56 +++++++++++++++++-- tests/ui/suggestions/issue-84700.stderr | 2 +- 7 files changed, 108 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 0cd80a6a83c87..186e9897b288e 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -593,6 +593,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { tcx, res, Some(expr), + &[], qpath, expr.span, E0533, diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 3b3f5caa85c09..9fcfbc2fd8648 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -42,7 +42,7 @@ pub use coercion::can_coerce; use fn_ctxt::FnCtxt; use rustc_data_structures::unord::UnordSet; use rustc_errors::codes::*; -use rustc_errors::{Applicability, Diag, ErrorGuaranteed, pluralize, struct_span_code_err}; +use rustc_errors::{Applicability, Diag, ErrorGuaranteed, struct_span_code_err}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{HirId, HirIdMap, Node}; @@ -482,6 +482,7 @@ fn report_unexpected_variant_res( tcx: TyCtxt<'_>, res: Res, expr: Option<&hir::Expr<'_>>, + sub_pats: &[hir::Pat<'_>], qpath: &hir::QPath<'_>, span: Span, err_code: ErrCode, @@ -561,19 +562,35 @@ fn report_unexpected_variant_res( let fields = &tcx.expect_variant_res(res).fields.raw; let span = qpath.span().shrink_to_hi().to(span.shrink_to_hi()); let (msg, sugg) = if fields.is_empty() { - ("use the struct variant pattern syntax".to_string(), " {}".to_string()) + ("use the struct variant pattern syntax", " {}".to_string()) } else { - let msg = format!( - "the struct variant's field{s} {are} being ignored", - s = pluralize!(fields.len()), - are = pluralize!("is", fields.len()) - ); - let fields = fields + let msg = if fields.is_empty() { + "use struct variant pattern syntax" + } else { + "add the names to match a struct variant's fields" + }; + let fields_sugg = fields .iter() - .map(|field| format!("{}: _", field.ident(tcx))) + .enumerate() + .map(|(i, field)| { + let field_name = field.ident(tcx).to_string(); + + let pat_snippet = sub_pats + .get(i) + .and_then(|sub_pat| { + tcx.sess.source_map().span_to_snippet(sub_pat.span).ok() + }) + .unwrap_or_else(|| "_".to_string()); + + if field_name == pat_snippet { + field_name + } else { + format!("{field_name}: {pat_snippet}") + } + }) .collect::>() .join(", "); - let sugg = format!(" {{ {} }}", fields); + let sugg = format!(" {{ {} }}", fields_sugg); (msg, sugg) }; diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index ad9d0df78b333..17f6ddb8e8af8 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -1585,7 +1585,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } Res::Def(DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::Variant, _) => { let expected = "unit struct, unit variant or constant"; - let e = report_unexpected_variant_res(tcx, res, None, qpath, span, E0533, expected); + let e = report_unexpected_variant_res( + tcx, + res, + None, + &[], + qpath, + span, + E0533, + expected, + ); return Err(e); } Res::SelfCtor(def_id) => { @@ -1599,6 +1608,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { tcx, res, None, + &[], qpath, span, E0533, @@ -1761,7 +1771,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let tcx = self.tcx; let report_unexpected_res = |res: Res| { let expected = "tuple struct or tuple variant"; - let e = report_unexpected_variant_res(tcx, res, None, qpath, pat.span, E0164, expected); + let sub_pats = match pat.kind { + hir::PatKind::TupleStruct(_, sub_pats, _) => sub_pats, + _ => &[], + }; + let e = report_unexpected_variant_res( + tcx, res, None, sub_pats, qpath, pat.span, E0164, expected, + ); Err(e) }; diff --git a/tests/ui/match/unit-pattern-error-on-tuple-and-struct-variants-63983.stderr b/tests/ui/match/unit-pattern-error-on-tuple-and-struct-variants-63983.stderr index 5c937a9ddab0b..65e85471494ce 100644 --- a/tests/ui/match/unit-pattern-error-on-tuple-and-struct-variants-63983.stderr +++ b/tests/ui/match/unit-pattern-error-on-tuple-and-struct-variants-63983.stderr @@ -13,7 +13,7 @@ error[E0533]: expected unit struct, unit variant or constant, found struct varia LL | MyEnum::Struct => "", | ^^^^^^^^^^^^^^ not a unit struct, unit variant or constant | -help: the struct variant's field is being ignored +help: add the names to match a struct variant's fields | LL | MyEnum::Struct { s: _ } => "", | ++++++++ diff --git a/tests/ui/parser/recover/recover-from-bad-variant.rs b/tests/ui/parser/recover/recover-from-bad-variant.rs index e8887147cbc86..b1ca827035b2e 100644 --- a/tests/ui/parser/recover/recover-from-bad-variant.rs +++ b/tests/ui/parser/recover/recover-from-bad-variant.rs @@ -9,6 +9,14 @@ fn main() { match x { Enum::Foo(a, b) => {} //~^ ERROR expected tuple struct or tuple variant, found struct variant `Enum::Foo` + Enum::Foo(x, y) => {} + //~^ ERROR expected tuple struct or tuple variant, found struct variant `Enum::Foo` + Enum::Foo(1 | 2, b @ 3) => {} + //~^ ERROR expected tuple struct or tuple variant, found struct variant `Enum::Foo` + Enum::Foo(a) => {} + //~^ ERROR expected tuple struct or tuple variant, found struct variant `Enum::Foo` + Enum::Foo(a, b, c) => {} + //~^ ERROR expected tuple struct or tuple variant, found struct variant `Enum::Foo` Enum::Bar { a, b } => {} //~^ ERROR tuple variant `Enum::Bar` written as struct variant } diff --git a/tests/ui/parser/recover/recover-from-bad-variant.stderr b/tests/ui/parser/recover/recover-from-bad-variant.stderr index 9359ede1346a7..4cd381726ae0b 100644 --- a/tests/ui/parser/recover/recover-from-bad-variant.stderr +++ b/tests/ui/parser/recover/recover-from-bad-variant.stderr @@ -21,15 +21,63 @@ error[E0164]: expected tuple struct or tuple variant, found struct variant `Enum LL | Enum::Foo(a, b) => {} | ^^^^^^^^^^^^^^^ not a tuple struct or tuple variant | -help: the struct variant's fields are being ignored +help: add the names to match a struct variant's fields | LL - Enum::Foo(a, b) => {} -LL + Enum::Foo { a: _, b: _ } => {} +LL + Enum::Foo { a, b } => {} | -error[E0769]: tuple variant `Enum::Bar` written as struct variant +error[E0164]: expected tuple struct or tuple variant, found struct variant `Enum::Foo` --> $DIR/recover-from-bad-variant.rs:12:9 | +LL | Enum::Foo(x, y) => {} + | ^^^^^^^^^^^^^^^ not a tuple struct or tuple variant + | +help: add the names to match a struct variant's fields + | +LL - Enum::Foo(x, y) => {} +LL + Enum::Foo { a: x, b: y } => {} + | + +error[E0164]: expected tuple struct or tuple variant, found struct variant `Enum::Foo` + --> $DIR/recover-from-bad-variant.rs:14:9 + | +LL | Enum::Foo(1 | 2, b @ 3) => {} + | ^^^^^^^^^^^^^^^^^^^^^^^ not a tuple struct or tuple variant + | +help: add the names to match a struct variant's fields + | +LL - Enum::Foo(1 | 2, b @ 3) => {} +LL + Enum::Foo { a: 1 | 2, b: b @ 3 } => {} + | + +error[E0164]: expected tuple struct or tuple variant, found struct variant `Enum::Foo` + --> $DIR/recover-from-bad-variant.rs:16:9 + | +LL | Enum::Foo(a) => {} + | ^^^^^^^^^^^^ not a tuple struct or tuple variant + | +help: add the names to match a struct variant's fields + | +LL - Enum::Foo(a) => {} +LL + Enum::Foo { a, b: _ } => {} + | + +error[E0164]: expected tuple struct or tuple variant, found struct variant `Enum::Foo` + --> $DIR/recover-from-bad-variant.rs:18:9 + | +LL | Enum::Foo(a, b, c) => {} + | ^^^^^^^^^^^^^^^^^^ not a tuple struct or tuple variant + | +help: add the names to match a struct variant's fields + | +LL - Enum::Foo(a, b, c) => {} +LL + Enum::Foo { a, b } => {} + | + +error[E0769]: tuple variant `Enum::Bar` written as struct variant + --> $DIR/recover-from-bad-variant.rs:20:9 + | LL | Enum::Bar { a, b } => {} | ^^^^^^^^^^^^^^^^^^ | @@ -39,7 +87,7 @@ LL - Enum::Bar { a, b } => {} LL + Enum::Bar(a, b) => {} | -error: aborting due to 3 previous errors +error: aborting due to 7 previous errors Some errors have detailed explanations: E0164, E0769. For more information about an error, try `rustc --explain E0164`. diff --git a/tests/ui/suggestions/issue-84700.stderr b/tests/ui/suggestions/issue-84700.stderr index e68b6fb544729..4c6edecd59a3d 100644 --- a/tests/ui/suggestions/issue-84700.stderr +++ b/tests/ui/suggestions/issue-84700.stderr @@ -13,7 +13,7 @@ error[E0164]: expected tuple struct or tuple variant, found struct variant `Farm LL | FarmAnimal::Chicken(_) => "cluck, cluck!".to_string(), | ^^^^^^^^^^^^^^^^^^^^^^ not a tuple struct or tuple variant | -help: the struct variant's field is being ignored +help: add the names to match a struct variant's fields | LL - FarmAnimal::Chicken(_) => "cluck, cluck!".to_string(), LL + FarmAnimal::Chicken { num_eggs: _ } => "cluck, cluck!".to_string(),