From 9e4d019fee6f8f0b452c99f883c501c341d36dba Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Mon, 28 Mar 2022 12:38:10 +0900 Subject: [PATCH 1/2] suggest replacing field when using the same type --- compiler/rustc_typeck/src/check/pat.rs | 60 +++++++----- src/test/ui/issues/issue-51102.stderr | 5 +- ...placing-field-when-specifying-same-type.rs | 28 ++++++ ...ing-field-when-specifying-same-type.stderr | 94 +++++++++++++++++++ 4 files changed, 161 insertions(+), 26 deletions(-) create mode 100644 src/test/ui/structs/suggest-replacing-field-when-specifying-same-type.rs create mode 100644 src/test/ui/structs/suggest-replacing-field-when-specifying-same-type.stderr diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs index 1c4fbbbb9bfaf..67124f2d23836 100644 --- a/compiler/rustc_typeck/src/check/pat.rs +++ b/compiler/rustc_typeck/src/check/pat.rs @@ -10,6 +10,7 @@ use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::pat_util::EnumerateAndAdjustIterator; use rustc_hir::{HirId, Pat, PatKind}; use rustc_infer::infer; +use rustc_infer::infer::error_reporting::same_type_modulo_infer; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_middle::middle::stability::EvalResult; use rustc_middle::ty::{self, Adt, BindingMode, Ty, TypeFoldable}; @@ -1258,7 +1259,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.field_ty(span, f, substs) }) .unwrap_or_else(|| { - inexistent_fields.push(field.ident); + inexistent_fields.push(field); no_field_errors = false; tcx.ty_error() }) @@ -1276,13 +1277,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .collect::>(); let inexistent_fields_err = if !(inexistent_fields.is_empty() || variant.is_recovered()) - && !inexistent_fields.iter().any(|field| field.name == kw::Underscore) + && !inexistent_fields.iter().any(|field| field.ident.name == kw::Underscore) { Some(self.error_inexistent_fields( adt.variant_descr(), &inexistent_fields, &mut unmentioned_fields, variant, + substs, )) } else { None @@ -1448,20 +1450,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn error_inexistent_fields( &self, kind_name: &str, - inexistent_fields: &[Ident], - unmentioned_fields: &mut Vec<(&ty::FieldDef, Ident)>, + inexistent_fields: &[&hir::PatField<'tcx>], + unmentioned_fields: &mut Vec<(&'tcx ty::FieldDef, Ident)>, variant: &ty::VariantDef, + substs: &'tcx ty::List>, ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { let tcx = self.tcx; let (field_names, t, plural) = if inexistent_fields.len() == 1 { - (format!("a field named `{}`", inexistent_fields[0]), "this", "") + (format!("a field named `{}`", inexistent_fields[0].ident), "this", "") } else { ( format!( "fields named {}", inexistent_fields .iter() - .map(|ident| format!("`{}`", ident)) + .map(|field| format!("`{}`", field.ident)) .collect::>() .join(", ") ), @@ -1469,7 +1472,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "s", ) }; - let spans = inexistent_fields.iter().map(|ident| ident.span).collect::>(); + let spans = inexistent_fields.iter().map(|field| field.ident.span).collect::>(); let mut err = struct_span_err!( tcx.sess, spans, @@ -1479,9 +1482,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { tcx.def_path_str(variant.def_id), field_names ); - if let Some(ident) = inexistent_fields.last() { + if let Some(pat_field) = inexistent_fields.last() { err.span_label( - ident.span, + pat_field.ident.span, format!( "{} `{}` does not have {} field{}", kind_name, @@ -1494,10 +1497,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if unmentioned_fields.len() == 1 { let input = unmentioned_fields.iter().map(|(_, field)| field.name).collect::>(); - let suggested_name = find_best_match_for_name(&input, ident.name, None); + let suggested_name = find_best_match_for_name(&input, pat_field.ident.name, None); if let Some(suggested_name) = suggested_name { err.span_suggestion( - ident.span, + pat_field.ident.span, "a field with a similar name exists", suggested_name.to_string(), Applicability::MaybeIncorrect, @@ -1513,17 +1516,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { unmentioned_fields.retain(|&(_, x)| x.name != suggested_name); } } else if inexistent_fields.len() == 1 { - let unmentioned_field = unmentioned_fields[0].1.name; - err.span_suggestion_short( - ident.span, - &format!( - "`{}` has a field named `{}`", - tcx.def_path_str(variant.def_id), - unmentioned_field - ), - unmentioned_field.to_string(), - Applicability::MaybeIncorrect, - ); + match pat_field.pat.kind { + PatKind::Lit(expr) + if !same_type_modulo_infer( + self.typeck_results.borrow().expr_ty(expr), + self.field_ty( + unmentioned_fields[0].1.span, + unmentioned_fields[0].0, + substs, + ), + ) => {} + _ => { + let unmentioned_field = unmentioned_fields[0].1.name; + err.span_suggestion_short( + pat_field.ident.span, + &format!( + "`{}` has a field named `{}`", + tcx.def_path_str(variant.def_id), + unmentioned_field + ), + unmentioned_field.to_string(), + Applicability::MaybeIncorrect, + ); + } + } } } } diff --git a/src/test/ui/issues/issue-51102.stderr b/src/test/ui/issues/issue-51102.stderr index 09c52292dccaf..eb9eb68020067 100644 --- a/src/test/ui/issues/issue-51102.stderr +++ b/src/test/ui/issues/issue-51102.stderr @@ -2,10 +2,7 @@ error[E0026]: struct `SimpleStruct` does not have a field named `state` --> $DIR/issue-51102.rs:13:17 | LL | state: 0, - | ^^^^^ - | | - | struct `SimpleStruct` does not have this field - | help: `SimpleStruct` has a field named `no_state_here` + | ^^^^^ struct `SimpleStruct` does not have this field error[E0025]: field `no_state_here` bound multiple times in the pattern --> $DIR/issue-51102.rs:24:17 diff --git a/src/test/ui/structs/suggest-replacing-field-when-specifying-same-type.rs b/src/test/ui/structs/suggest-replacing-field-when-specifying-same-type.rs new file mode 100644 index 0000000000000..dd2fe79731eac --- /dev/null +++ b/src/test/ui/structs/suggest-replacing-field-when-specifying-same-type.rs @@ -0,0 +1,28 @@ +enum Foo { + Bar { a: u8, b: i8, c: u8 }, + Baz { a: f32 }, + None, +} + +fn main() { + let foo = Foo::None; + match foo { + Foo::Bar { a, aa: 1, c } => (), + //~^ ERROR variant `Foo::Bar` does not have a field named `aa` [E0026] + //~| ERROR pattern does not mention field `b` [E0027] + Foo::Baz { bb: 1.0 } => (), + //~^ ERROR variant `Foo::Baz` does not have a field named `bb` [E0026] + //~| ERROR pattern does not mention field `a` [E0027] + _ => (), + } + + match foo { + Foo::Bar { a, aa: "", c } => (), + //~^ ERROR variant `Foo::Bar` does not have a field named `aa` [E0026] + //~| ERROR pattern does not mention field `b` [E0027] + Foo::Baz { bb: "" } => (), + //~^ ERROR variant `Foo::Baz` does not have a field named `bb` [E0026] + //~| pattern does not mention field `a` [E0027] + _ => (), + } +} diff --git a/src/test/ui/structs/suggest-replacing-field-when-specifying-same-type.stderr b/src/test/ui/structs/suggest-replacing-field-when-specifying-same-type.stderr new file mode 100644 index 0000000000000..e8503f540c288 --- /dev/null +++ b/src/test/ui/structs/suggest-replacing-field-when-specifying-same-type.stderr @@ -0,0 +1,94 @@ +error[E0026]: variant `Foo::Bar` does not have a field named `aa` + --> $DIR/suggest-replacing-field-when-specifying-same-type.rs:10:23 + | +LL | Foo::Bar { a, aa: 1, c } => (), + | ^^ + | | + | variant `Foo::Bar` does not have this field + | help: `Foo::Bar` has a field named `b` + +error[E0027]: pattern does not mention field `b` + --> $DIR/suggest-replacing-field-when-specifying-same-type.rs:10:9 + | +LL | Foo::Bar { a, aa: 1, c } => (), + | ^^^^^^^^^^^^^^^^^^^^^^^^ missing field `b` + | +help: include the missing field in the pattern + | +LL | Foo::Bar { a, aa: 1, c, b } => (), + | ~~~~~ +help: if you don't care about this missing field, you can explicitly ignore it + | +LL | Foo::Bar { a, aa: 1, c, .. } => (), + | ~~~~~~ + +error[E0026]: variant `Foo::Baz` does not have a field named `bb` + --> $DIR/suggest-replacing-field-when-specifying-same-type.rs:13:20 + | +LL | Foo::Baz { bb: 1.0 } => (), + | ^^ + | | + | variant `Foo::Baz` does not have this field + | help: `Foo::Baz` has a field named `a` + +error[E0027]: pattern does not mention field `a` + --> $DIR/suggest-replacing-field-when-specifying-same-type.rs:13:9 + | +LL | Foo::Baz { bb: 1.0 } => (), + | ^^^^^^^^^^^^^^^^^^^^ missing field `a` + | +help: include the missing field in the pattern + | +LL | Foo::Baz { bb: 1.0, a } => (), + | ~~~~~ +help: if you don't care about this missing field, you can explicitly ignore it + | +LL | Foo::Baz { bb: 1.0, .. } => (), + | ~~~~~~ + +error[E0026]: variant `Foo::Bar` does not have a field named `aa` + --> $DIR/suggest-replacing-field-when-specifying-same-type.rs:20:23 + | +LL | Foo::Bar { a, aa: "", c } => (), + | ^^ variant `Foo::Bar` does not have this field + +error[E0027]: pattern does not mention field `b` + --> $DIR/suggest-replacing-field-when-specifying-same-type.rs:20:9 + | +LL | Foo::Bar { a, aa: "", c } => (), + | ^^^^^^^^^^^^^^^^^^^^^^^^^ missing field `b` + | +help: include the missing field in the pattern + | +LL | Foo::Bar { a, aa: "", c, b } => (), + | ~~~~~ +help: if you don't care about this missing field, you can explicitly ignore it + | +LL | Foo::Bar { a, aa: "", c, .. } => (), + | ~~~~~~ + +error[E0026]: variant `Foo::Baz` does not have a field named `bb` + --> $DIR/suggest-replacing-field-when-specifying-same-type.rs:23:20 + | +LL | Foo::Baz { bb: "" } => (), + | ^^ variant `Foo::Baz` does not have this field + +error[E0027]: pattern does not mention field `a` + --> $DIR/suggest-replacing-field-when-specifying-same-type.rs:23:9 + | +LL | Foo::Baz { bb: "" } => (), + | ^^^^^^^^^^^^^^^^^^^ missing field `a` + | +help: include the missing field in the pattern + | +LL | Foo::Baz { bb: "", a } => (), + | ~~~~~ +help: if you don't care about this missing field, you can explicitly ignore it + | +LL | Foo::Baz { bb: "", .. } => (), + | ~~~~~~ + +error: aborting due to 8 previous errors + +Some errors have detailed explanations: E0026, E0027. +For more information about an error, try `rustc --explain E0026`. From c26cfd1c5347091eb72c545b71c682f972cec1f1 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Mon, 28 Mar 2022 13:43:33 +0900 Subject: [PATCH 2/2] use `can_coerce` instead of `same_type_modulo_infer` --- compiler/rustc_typeck/src/check/pat.rs | 3 +-- src/test/ui/issues/issue-51102.stderr | 5 ++++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs index 67124f2d23836..d66230acb8bda 100644 --- a/compiler/rustc_typeck/src/check/pat.rs +++ b/compiler/rustc_typeck/src/check/pat.rs @@ -10,7 +10,6 @@ use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::pat_util::EnumerateAndAdjustIterator; use rustc_hir::{HirId, Pat, PatKind}; use rustc_infer::infer; -use rustc_infer::infer::error_reporting::same_type_modulo_infer; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_middle::middle::stability::EvalResult; use rustc_middle::ty::{self, Adt, BindingMode, Ty, TypeFoldable}; @@ -1518,7 +1517,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else if inexistent_fields.len() == 1 { match pat_field.pat.kind { PatKind::Lit(expr) - if !same_type_modulo_infer( + if !self.can_coerce( self.typeck_results.borrow().expr_ty(expr), self.field_ty( unmentioned_fields[0].1.span, diff --git a/src/test/ui/issues/issue-51102.stderr b/src/test/ui/issues/issue-51102.stderr index eb9eb68020067..09c52292dccaf 100644 --- a/src/test/ui/issues/issue-51102.stderr +++ b/src/test/ui/issues/issue-51102.stderr @@ -2,7 +2,10 @@ error[E0026]: struct `SimpleStruct` does not have a field named `state` --> $DIR/issue-51102.rs:13:17 | LL | state: 0, - | ^^^^^ struct `SimpleStruct` does not have this field + | ^^^^^ + | | + | struct `SimpleStruct` does not have this field + | help: `SimpleStruct` has a field named `no_state_here` error[E0025]: field `no_state_here` bound multiple times in the pattern --> $DIR/issue-51102.rs:24:17