diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index c39daea0811e0..28a808f473b40 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -1661,6 +1661,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { debug!("exp_found {:?} terr {:?}", exp_found, terr); if let Some(exp_found) = exp_found { self.suggest_as_ref_where_appropriate(span, &exp_found, diag); + self.suggest_accessing_field_where_appropriate(cause, &exp_found, diag); self.suggest_await_on_expect_found(cause, span, &exp_found, diag); } @@ -1819,6 +1820,53 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } + fn suggest_accessing_field_where_appropriate( + &self, + cause: &ObligationCause<'tcx>, + exp_found: &ty::error::ExpectedFound>, + diag: &mut DiagnosticBuilder<'tcx>, + ) { + debug!( + "suggest_accessing_field_where_appropriate(cause={:?}, exp_found={:?})", + cause, exp_found + ); + if let ty::Adt(expected_def, expected_substs) = exp_found.expected.kind() { + if expected_def.is_enum() { + return; + } + + if let Some((name, ty)) = expected_def + .non_enum_variant() + .fields + .iter() + .filter(|field| field.vis.is_accessible_from(field.did, self.tcx)) + .map(|field| (field.ident.name, field.ty(self.tcx, expected_substs))) + .find(|(_, ty)| ty::TyS::same_type(ty, exp_found.found)) + { + if let ObligationCauseCode::Pattern { span: Some(span), .. } = cause.code { + if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { + let suggestion = if expected_def.is_struct() { + format!("{}.{}", snippet, name) + } else if expected_def.is_union() { + format!("unsafe {{ {}.{} }}", snippet, name) + } else { + return; + }; + diag.span_suggestion( + span, + &format!( + "you might have meant to use field `{}` of type `{}`", + name, ty + ), + suggestion, + Applicability::MaybeIncorrect, + ); + } + } + } + } + } + /// When encountering a case where `.as_ref()` on a `Result` or `Option` would be appropriate, /// suggests it. fn suggest_as_ref_where_appropriate( diff --git a/src/test/ui/suggestions/field-access.fixed b/src/test/ui/suggestions/field-access.fixed new file mode 100644 index 0000000000000..05a4a0eb1266d --- /dev/null +++ b/src/test/ui/suggestions/field-access.fixed @@ -0,0 +1,35 @@ +// run-rustfix +#![allow(dead_code)] + +struct A { + b: B, +} + +enum B { + Fst, + Snd, +} + +union Foo { + bar: u32, + qux: f32, +} + +fn main() { + let a = A { b: B::Fst }; + if let B::Fst = a.b {}; //~ ERROR mismatched types [E0308] + //~^ HELP you might have meant to use field `b` of type `B` + match a.b { + //~^ HELP you might have meant to use field `b` of type `B` + //~| HELP you might have meant to use field `b` of type `B` + B::Fst => (), //~ ERROR mismatched types [E0308] + B::Snd => (), //~ ERROR mismatched types [E0308] + } + + let foo = Foo { bar: 42 }; + match unsafe { foo.bar } { + //~^ HELP you might have meant to use field `bar` of type `u32` + 1u32 => (), //~ ERROR mismatched types [E0308] + _ => (), + } +} diff --git a/src/test/ui/suggestions/field-access.rs b/src/test/ui/suggestions/field-access.rs new file mode 100644 index 0000000000000..ad23c0ffa2e74 --- /dev/null +++ b/src/test/ui/suggestions/field-access.rs @@ -0,0 +1,35 @@ +// run-rustfix +#![allow(dead_code)] + +struct A { + b: B, +} + +enum B { + Fst, + Snd, +} + +union Foo { + bar: u32, + qux: f32, +} + +fn main() { + let a = A { b: B::Fst }; + if let B::Fst = a {}; //~ ERROR mismatched types [E0308] + //~^ HELP you might have meant to use field `b` of type `B` + match a { + //~^ HELP you might have meant to use field `b` of type `B` + //~| HELP you might have meant to use field `b` of type `B` + B::Fst => (), //~ ERROR mismatched types [E0308] + B::Snd => (), //~ ERROR mismatched types [E0308] + } + + let foo = Foo { bar: 42 }; + match foo { + //~^ HELP you might have meant to use field `bar` of type `u32` + 1u32 => (), //~ ERROR mismatched types [E0308] + _ => (), + } +} diff --git a/src/test/ui/suggestions/field-access.stderr b/src/test/ui/suggestions/field-access.stderr new file mode 100644 index 0000000000000..aad9872032a2a --- /dev/null +++ b/src/test/ui/suggestions/field-access.stderr @@ -0,0 +1,67 @@ +error[E0308]: mismatched types + --> $DIR/field-access.rs:20:12 + | +LL | Fst, + | --- unit variant defined here +... +LL | if let B::Fst = a {}; + | ^^^^^^ - this expression has type `A` + | | + | expected struct `A`, found enum `B` + | +help: you might have meant to use field `b` of type `B` + | +LL | if let B::Fst = a.b {}; + | ^^^ + +error[E0308]: mismatched types + --> $DIR/field-access.rs:25:9 + | +LL | Fst, + | --- unit variant defined here +... +LL | match a { + | - this expression has type `A` +... +LL | B::Fst => (), + | ^^^^^^ expected struct `A`, found enum `B` + | +help: you might have meant to use field `b` of type `B` + | +LL | match a.b { + | ^^^ + +error[E0308]: mismatched types + --> $DIR/field-access.rs:26:9 + | +LL | Snd, + | --- unit variant defined here +... +LL | match a { + | - this expression has type `A` +... +LL | B::Snd => (), + | ^^^^^^ expected struct `A`, found enum `B` + | +help: you might have meant to use field `b` of type `B` + | +LL | match a.b { + | ^^^ + +error[E0308]: mismatched types + --> $DIR/field-access.rs:32:9 + | +LL | match foo { + | --- this expression has type `Foo` +LL | +LL | 1u32 => (), + | ^^^^ expected union `Foo`, found `u32` + | +help: you might have meant to use field `bar` of type `u32` + | +LL | match unsafe { foo.bar } { + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0308`.