From 9e4ed337c783fab801d8a2e37feb58974205cfa3 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Fri, 29 Jan 2021 05:43:35 +0900 Subject: [PATCH 1/9] Suggest accessing field when code compiles with it --- .../src/infer/error_reporting/mod.rs | 41 +++++++++++++++++++ src/test/ui/suggestions/field-access.rs | 15 +++++++ src/test/ui/suggestions/field-access.stderr | 19 +++++++++ 3 files changed, 75 insertions(+) create mode 100644 src/test/ui/suggestions/field-access.rs create mode 100644 src/test/ui/suggestions/field-access.stderr diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index c39daea0811e0..68ffe3cd70fa8 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_field_where_appropriate(cause, &exp_found, diag); self.suggest_await_on_expect_found(cause, span, &exp_found, diag); } @@ -1819,6 +1820,46 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } + fn suggest_field_where_appropriate( + &self, + cause: &ObligationCause<'tcx>, + exp_found: &ty::error::ExpectedFound>, + diag: &mut DiagnosticBuilder<'tcx>, + ) { + debug!("suggest_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))) + .inspect(|(name, ty)| { + debug!("suggest_field_where_appropriate: name={:?}, ty={:?}", name, ty) + }) + .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) { + diag.span_suggestion( + span, + &format!( + "you might have meant to use field `{}` of type `{}`", + name, ty + ), + format!("{}.{}", snippet, name), + 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.rs b/src/test/ui/suggestions/field-access.rs new file mode 100644 index 0000000000000..822f66f2a479f --- /dev/null +++ b/src/test/ui/suggestions/field-access.rs @@ -0,0 +1,15 @@ +struct A { + b: B, +} + +enum B { + Fst, + Snd, +} + +fn main() { + let a = A { b: B::Fst }; + if let B::Fst = a {}; + //~^ ERROR mismatched types [E0308] + // note: you might have meant to use field `b` of type `B` +} diff --git a/src/test/ui/suggestions/field-access.stderr b/src/test/ui/suggestions/field-access.stderr new file mode 100644 index 0000000000000..58bc6d3f2da31 --- /dev/null +++ b/src/test/ui/suggestions/field-access.stderr @@ -0,0 +1,19 @@ +error[E0308]: mismatched types + --> $DIR/field-access.rs:12: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: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. From a9abf6f08663dce7b69cac826ca943149a6b093b Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Fri, 29 Jan 2021 16:28:53 +0900 Subject: [PATCH 2/9] Add test for match expression --- src/test/ui/suggestions/field-access.rs | 8 +++++ src/test/ui/suggestions/field-access.stderr | 35 ++++++++++++++++++++- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/test/ui/suggestions/field-access.rs b/src/test/ui/suggestions/field-access.rs index 822f66f2a479f..7bf621c21d399 100644 --- a/src/test/ui/suggestions/field-access.rs +++ b/src/test/ui/suggestions/field-access.rs @@ -12,4 +12,12 @@ fn main() { if let B::Fst = a {}; //~^ ERROR mismatched types [E0308] // note: you might have meant to use field `b` of type `B` + match a { + B::Fst => (), + B::Snd => (), + } + //~^^^ ERROR mismatched types [E0308] + // note: you might have meant to use field `b` of type `B` + //~^^^^ ERROR mismatched types [E0308] + // note: you might have meant to use field `b` of type `B` } diff --git a/src/test/ui/suggestions/field-access.stderr b/src/test/ui/suggestions/field-access.stderr index 58bc6d3f2da31..a377f8f4deaef 100644 --- a/src/test/ui/suggestions/field-access.stderr +++ b/src/test/ui/suggestions/field-access.stderr @@ -14,6 +14,39 @@ help: you might have meant to use field `b` of type `B` LL | if let B::Fst = a.b {}; | ^^^ -error: aborting due to previous error +error[E0308]: mismatched types + --> $DIR/field-access.rs:16: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:17:9 + | +LL | Snd, + | --- unit variant defined here +... +LL | match a { + | - this expression has type `A` +LL | B::Fst => (), +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: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0308`. From f641f5133bd0debee7e00e5fbb721045e7f6f87b Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Fri, 29 Jan 2021 16:33:15 +0900 Subject: [PATCH 3/9] Add rust-fix test --- src/test/ui/suggestions/field-access.fixed | 26 +++++++++++++++++++++ src/test/ui/suggestions/field-access.rs | 3 +++ src/test/ui/suggestions/field-access.stderr | 6 ++--- 3 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 src/test/ui/suggestions/field-access.fixed diff --git a/src/test/ui/suggestions/field-access.fixed b/src/test/ui/suggestions/field-access.fixed new file mode 100644 index 0000000000000..d580902f2e70b --- /dev/null +++ b/src/test/ui/suggestions/field-access.fixed @@ -0,0 +1,26 @@ +// run-rustfix +#![allow(dead_code)] + +struct A { + b: B, +} + +enum B { + Fst, + Snd, +} + +fn main() { + let a = A { b: B::Fst }; + if let B::Fst = a.b {}; + //~^ ERROR mismatched types [E0308] + // note: you might have meant to use field `b` of type `B` + match a.b { + B::Fst => (), + B::Snd => (), + } + //~^^^ ERROR mismatched types [E0308] + // note: you might have meant to use field `b` of type `B` + //~^^^^ ERROR mismatched types [E0308] + // note: you might have meant to use field `b` of type `B` +} diff --git a/src/test/ui/suggestions/field-access.rs b/src/test/ui/suggestions/field-access.rs index 7bf621c21d399..ed6f9b2112be6 100644 --- a/src/test/ui/suggestions/field-access.rs +++ b/src/test/ui/suggestions/field-access.rs @@ -1,3 +1,6 @@ +// run-rustfix +#![allow(dead_code)] + struct A { b: B, } diff --git a/src/test/ui/suggestions/field-access.stderr b/src/test/ui/suggestions/field-access.stderr index a377f8f4deaef..ba7e145c21759 100644 --- a/src/test/ui/suggestions/field-access.stderr +++ b/src/test/ui/suggestions/field-access.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/field-access.rs:12:12 + --> $DIR/field-access.rs:15:12 | LL | Fst, | --- unit variant defined here @@ -15,7 +15,7 @@ LL | if let B::Fst = a.b {}; | ^^^ error[E0308]: mismatched types - --> $DIR/field-access.rs:16:9 + --> $DIR/field-access.rs:19:9 | LL | Fst, | --- unit variant defined here @@ -31,7 +31,7 @@ LL | match a.b { | ^^^ error[E0308]: mismatched types - --> $DIR/field-access.rs:17:9 + --> $DIR/field-access.rs:20:9 | LL | Snd, | --- unit variant defined here From ad9f707bf594561f43287a9ebc9ea27ced283ef0 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sat, 30 Jan 2021 13:42:01 +0900 Subject: [PATCH 4/9] Fix test to check help message as well --- src/test/ui/suggestions/field-access.fixed | 15 ++++++--------- src/test/ui/suggestions/field-access.rs | 15 ++++++--------- src/test/ui/suggestions/field-access.stderr | 7 ++++--- 3 files changed, 16 insertions(+), 21 deletions(-) diff --git a/src/test/ui/suggestions/field-access.fixed b/src/test/ui/suggestions/field-access.fixed index d580902f2e70b..72b29d7656aed 100644 --- a/src/test/ui/suggestions/field-access.fixed +++ b/src/test/ui/suggestions/field-access.fixed @@ -12,15 +12,12 @@ enum B { fn main() { let a = A { b: B::Fst }; - if let B::Fst = a.b {}; - //~^ ERROR mismatched types [E0308] - // note: you might have meant to use field `b` of type `B` + 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 { - B::Fst => (), - B::Snd => (), + //~^ 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] } - //~^^^ ERROR mismatched types [E0308] - // note: you might have meant to use field `b` of type `B` - //~^^^^ ERROR mismatched types [E0308] - // note: you might have meant to use field `b` of type `B` } diff --git a/src/test/ui/suggestions/field-access.rs b/src/test/ui/suggestions/field-access.rs index ed6f9b2112be6..2710ead4dfbbc 100644 --- a/src/test/ui/suggestions/field-access.rs +++ b/src/test/ui/suggestions/field-access.rs @@ -12,15 +12,12 @@ enum B { fn main() { let a = A { b: B::Fst }; - if let B::Fst = a {}; - //~^ ERROR mismatched types [E0308] - // note: you might have meant to use field `b` of type `B` + if let B::Fst = a {}; //~ ERROR mismatched types [E0308] + //~^ HELP you might have meant to use field `b` of type `B` match a { - B::Fst => (), - B::Snd => (), + //~^ 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] } - //~^^^ ERROR mismatched types [E0308] - // note: you might have meant to use field `b` of type `B` - //~^^^^ ERROR mismatched types [E0308] - // note: you might have meant to use field `b` of type `B` } diff --git a/src/test/ui/suggestions/field-access.stderr b/src/test/ui/suggestions/field-access.stderr index ba7e145c21759..228510902f54b 100644 --- a/src/test/ui/suggestions/field-access.stderr +++ b/src/test/ui/suggestions/field-access.stderr @@ -15,13 +15,14 @@ LL | if let B::Fst = a.b {}; | ^^^ error[E0308]: mismatched types - --> $DIR/field-access.rs:19:9 + --> $DIR/field-access.rs:20:9 | LL | Fst, | --- unit variant defined here ... LL | match a { | - this expression has type `A` +... LL | B::Fst => (), | ^^^^^^ expected struct `A`, found enum `B` | @@ -31,14 +32,14 @@ LL | match a.b { | ^^^ error[E0308]: mismatched types - --> $DIR/field-access.rs:20:9 + --> $DIR/field-access.rs:21:9 | LL | Snd, | --- unit variant defined here ... LL | match a { | - this expression has type `A` -LL | B::Fst => (), +... LL | B::Snd => (), | ^^^^^^ expected struct `A`, found enum `B` | From 08d31e0f09f79a41e0cfc26247d41fea1489adf9 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sat, 30 Jan 2021 13:50:44 +0900 Subject: [PATCH 5/9] Remove logging iterator --- compiler/rustc_infer/src/infer/error_reporting/mod.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 68ffe3cd70fa8..268819d3094df 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -1838,9 +1838,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { .iter() .filter(|field| field.vis.is_accessible_from(field.did, self.tcx)) .map(|field| (field.ident.name, field.ty(self.tcx, expected_substs))) - .inspect(|(name, ty)| { - debug!("suggest_field_where_appropriate: name={:?}, ty={:?}", name, ty) - }) .find(|(_, ty)| ty::TyS::same_type(ty, exp_found.found)) { if let ObligationCauseCode::Pattern { span: Some(span), .. } = cause.code { From 2ce2d145c2cbcb93389dbea429d7e034ff367f48 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sat, 30 Jan 2021 14:18:50 +0900 Subject: [PATCH 6/9] Account for union --- .../src/infer/error_reporting/mod.rs | 9 +++++++- src/test/ui/suggestions/field-access.fixed | 12 ++++++++++ src/test/ui/suggestions/field-access.rs | 12 ++++++++++ src/test/ui/suggestions/field-access.stderr | 22 +++++++++++++++---- 4 files changed, 50 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 268819d3094df..447b4f6d1e5f6 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -1842,13 +1842,20 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { { 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 ), - format!("{}.{}", snippet, name), + suggestion, Applicability::MaybeIncorrect, ); } diff --git a/src/test/ui/suggestions/field-access.fixed b/src/test/ui/suggestions/field-access.fixed index 72b29d7656aed..05a4a0eb1266d 100644 --- a/src/test/ui/suggestions/field-access.fixed +++ b/src/test/ui/suggestions/field-access.fixed @@ -10,6 +10,11 @@ enum B { 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] @@ -20,4 +25,11 @@ fn main() { 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 index 2710ead4dfbbc..ad23c0ffa2e74 100644 --- a/src/test/ui/suggestions/field-access.rs +++ b/src/test/ui/suggestions/field-access.rs @@ -10,6 +10,11 @@ enum B { Snd, } +union Foo { + bar: u32, + qux: f32, +} + fn main() { let a = A { b: B::Fst }; if let B::Fst = a {}; //~ ERROR mismatched types [E0308] @@ -20,4 +25,11 @@ fn main() { 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 index 228510902f54b..aad9872032a2a 100644 --- a/src/test/ui/suggestions/field-access.stderr +++ b/src/test/ui/suggestions/field-access.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/field-access.rs:15:12 + --> $DIR/field-access.rs:20:12 | LL | Fst, | --- unit variant defined here @@ -15,7 +15,7 @@ LL | if let B::Fst = a.b {}; | ^^^ error[E0308]: mismatched types - --> $DIR/field-access.rs:20:9 + --> $DIR/field-access.rs:25:9 | LL | Fst, | --- unit variant defined here @@ -32,7 +32,7 @@ LL | match a.b { | ^^^ error[E0308]: mismatched types - --> $DIR/field-access.rs:21:9 + --> $DIR/field-access.rs:26:9 | LL | Snd, | --- unit variant defined here @@ -48,6 +48,20 @@ help: you might have meant to use field `b` of type `B` LL | match a.b { | ^^^ -error: aborting due to 3 previous errors +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`. From 6bc9634de3c7d8fd5684fb38b8fbaf7288df55bd Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sat, 30 Jan 2021 14:25:43 +0900 Subject: [PATCH 7/9] Rename function to `suggest_accessing_field_where_appropriate` --- compiler/rustc_infer/src/infer/error_reporting/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 447b4f6d1e5f6..3ccf85240a5e1 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -1661,7 +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_field_where_appropriate(cause, &exp_found, diag); + self.suggest_accessing_field_where_appropriate(cause, &exp_found, diag); self.suggest_await_on_expect_found(cause, span, &exp_found, diag); } @@ -1820,7 +1820,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } - fn suggest_field_where_appropriate( + fn suggest_accessing_field_where_appropriate( &self, cause: &ObligationCause<'tcx>, exp_found: &ty::error::ExpectedFound>, From a292ac7177f8d2c2124b28201a33f83d7ce732e8 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sun, 31 Jan 2021 16:49:33 +0900 Subject: [PATCH 8/9] Fix prefix of debug log --- compiler/rustc_infer/src/infer/error_reporting/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 3ccf85240a5e1..eae0d1e28daa1 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -1826,7 +1826,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { exp_found: &ty::error::ExpectedFound>, diag: &mut DiagnosticBuilder<'tcx>, ) { - debug!("suggest_field_where_appropriate(cause={:?}, exp_found={:?})", cause, exp_found); + 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; From f45a99361a1c8691de15b99f3d8073ba4b433ae0 Mon Sep 17 00:00:00 2001 From: Hirochika Matsumoto Date: Sun, 31 Jan 2021 16:52:46 +0900 Subject: [PATCH 9/9] Rustfmt --- compiler/rustc_infer/src/infer/error_reporting/mod.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index eae0d1e28daa1..28a808f473b40 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -1826,7 +1826,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { exp_found: &ty::error::ExpectedFound>, diag: &mut DiagnosticBuilder<'tcx>, ) { - debug!("suggest_accessing_field_where_appropriate(cause={:?}, exp_found={:?})", cause, exp_found); + 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;