From 506c98f29160524fea86b07acc1fc37ee22318b6 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Sun, 31 Jul 2022 17:38:28 +0400 Subject: [PATCH 1/4] Suggest wrapping expressions in single-field record variants --- compiler/rustc_typeck/src/check/demand.rs | 43 ++++++++++++++++------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_typeck/src/check/demand.rs index 58b0399c5c927..7cc7e4f513ba9 100644 --- a/compiler/rustc_typeck/src/check/demand.rs +++ b/compiler/rustc_typeck/src/check/demand.rs @@ -363,11 +363,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - let compatible_variants: Vec<(String, Option)> = expected_adt + let compatible_variants: Vec<(String, _, _, Option)> = expected_adt .variants() .iter() .filter(|variant| { - variant.fields.len() == 1 && variant.ctor_kind == hir::def::CtorKind::Fn + variant.fields.len() == 1 }) .filter_map(|variant| { let sole_field = &variant.fields[0]; @@ -391,9 +391,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(path) = variant_path.strip_prefix("std::prelude::") && let Some((_, path)) = path.split_once("::") { - return Some((path.to_string(), note_about_variant_field_privacy)); + return Some((path.to_string(), variant.ctor_kind, sole_field.name, note_about_variant_field_privacy)); } - Some((variant_path, note_about_variant_field_privacy)) + Some((variant_path, variant.ctor_kind, sole_field.name, note_about_variant_field_privacy)) } else { None } @@ -405,9 +405,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None => String::new(), }; + fn brackets_for( + ctor: hir::def::CtorKind, + field_name: Symbol, + ) -> (String, &'static str) { + match ctor { + hir::def::CtorKind::Fn => ("(".to_owned(), ")"), + hir::def::CtorKind::Fictive => (format!(" {{ {field_name}: "), " }"), + hir::def::CtorKind::Const => unreachable!(), + } + } + match &compatible_variants[..] { [] => { /* No variants to format */ } - [(variant, note)] => { + [(variant, ctor_kind, field_name, note)] => { + let (open, close) = brackets_for(*ctor_kind, *field_name); + // Just a single matching variant. err.multipart_suggestion_verbose( &format!( @@ -415,8 +428,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { note = note.as_deref().unwrap_or("") ), vec![ - (expr.span.shrink_to_lo(), format!("{prefix}{variant}(")), - (expr.span.shrink_to_hi(), ")".to_string()), + (expr.span.shrink_to_lo(), format!("{prefix}{variant}{open}")), + (expr.span.shrink_to_hi(), close.to_owned()), ], Applicability::MaybeIncorrect, ); @@ -428,12 +441,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "try wrapping the expression in a variant of `{}`", self.tcx.def_path_str(expected_adt.did()) ), - compatible_variants.into_iter().map(|(variant, _)| { - vec![ - (expr.span.shrink_to_lo(), format!("{prefix}{variant}(")), - (expr.span.shrink_to_hi(), ")".to_string()), - ] - }), + compatible_variants.into_iter().map( + |(variant, ctor_kind, field_name, _)| { + let (open, close) = brackets_for(ctor_kind, field_name); + + vec![ + (expr.span.shrink_to_lo(), format!("{prefix}{variant}{open}")), + (expr.span.shrink_to_hi(), close.to_owned()), + ] + }, + ), Applicability::MaybeIncorrect, ); } From 260a840543faa2b380485a7dd101de62811e9a0d Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Sun, 31 Jul 2022 17:53:47 +0400 Subject: [PATCH 2/4] Refactor wrap suggestion code (just a bit) --- compiler/rustc_typeck/src/check/demand.rs | 40 ++++++++++------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_typeck/src/check/demand.rs index 7cc7e4f513ba9..8ed0faddf9e15 100644 --- a/compiler/rustc_typeck/src/check/demand.rs +++ b/compiler/rustc_typeck/src/check/demand.rs @@ -400,37 +400,36 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }) .collect(); - let prefix = match self.maybe_get_struct_pattern_shorthand_field(expr) { - Some(ident) => format!("{ident}: "), - None => String::new(), - }; + let suggestions_for = |variant: &_, ctor, field_name| { + let prefix = match self.maybe_get_struct_pattern_shorthand_field(expr) { + Some(ident) => format!("{ident}: "), + None => String::new(), + }; - fn brackets_for( - ctor: hir::def::CtorKind, - field_name: Symbol, - ) -> (String, &'static str) { - match ctor { + let (open, close) = match ctor { hir::def::CtorKind::Fn => ("(".to_owned(), ")"), hir::def::CtorKind::Fictive => (format!(" {{ {field_name}: "), " }"), + + // unit variants don't have fields hir::def::CtorKind::Const => unreachable!(), - } - } + }; + + vec![ + (expr.span.shrink_to_lo(), format!("{prefix}{variant}{open}")), + (expr.span.shrink_to_hi(), close.to_owned()), + ] + }; match &compatible_variants[..] { [] => { /* No variants to format */ } [(variant, ctor_kind, field_name, note)] => { - let (open, close) = brackets_for(*ctor_kind, *field_name); - // Just a single matching variant. err.multipart_suggestion_verbose( &format!( "try wrapping the expression in `{variant}`{note}", note = note.as_deref().unwrap_or("") ), - vec![ - (expr.span.shrink_to_lo(), format!("{prefix}{variant}{open}")), - (expr.span.shrink_to_hi(), close.to_owned()), - ], + suggestions_for(&**variant, *ctor_kind, *field_name), Applicability::MaybeIncorrect, ); } @@ -443,12 +442,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ), compatible_variants.into_iter().map( |(variant, ctor_kind, field_name, _)| { - let (open, close) = brackets_for(ctor_kind, field_name); - - vec![ - (expr.span.shrink_to_lo(), format!("{prefix}{variant}{open}")), - (expr.span.shrink_to_hi(), close.to_owned()), - ] + suggestions_for(&variant, ctor_kind, field_name) }, ), Applicability::MaybeIncorrect, From 5e39b35a2f02675d3dce488da1829c3abb21da07 Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Sun, 31 Jul 2022 18:09:39 +0400 Subject: [PATCH 3/4] --bless tests --- src/test/ui/did_you_mean/compatible-variants.rs | 5 ++--- src/test/ui/did_you_mean/compatible-variants.stderr | 9 +++++++-- src/test/ui/did_you_mean/issue-42764.rs | 1 + src/test/ui/did_you_mean/issue-42764.stderr | 5 +++++ 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/test/ui/did_you_mean/compatible-variants.rs b/src/test/ui/did_you_mean/compatible-variants.rs index 5d7c611980f1c..b1c7dc2a7f672 100644 --- a/src/test/ui/did_you_mean/compatible-variants.rs +++ b/src/test/ui/did_you_mean/compatible-variants.rs @@ -66,7 +66,7 @@ fn main() { } enum A { - B { b: B}, + B { b: B }, } struct A2(B); @@ -77,13 +77,12 @@ enum B { } fn foo() { - // We don't want to suggest `A::B(B::Fst)` here. let a: A = B::Fst; //~^ ERROR mismatched types + //~| HELP try wrapping } fn bar() { - // But we _do_ want to suggest `A2(B::Fst)` here! let a: A2 = B::Fst; //~^ ERROR mismatched types //~| HELP try wrapping diff --git a/src/test/ui/did_you_mean/compatible-variants.stderr b/src/test/ui/did_you_mean/compatible-variants.stderr index a16cdee44623a..fe81da19833a7 100644 --- a/src/test/ui/did_you_mean/compatible-variants.stderr +++ b/src/test/ui/did_you_mean/compatible-variants.stderr @@ -191,15 +191,20 @@ LL | let _ = Foo { bar: Some(bar) }; | ++++++++++ + error[E0308]: mismatched types - --> $DIR/compatible-variants.rs:81:16 + --> $DIR/compatible-variants.rs:80:16 | LL | let a: A = B::Fst; | - ^^^^^^ expected enum `A`, found enum `B` | | | expected due to this + | +help: try wrapping the expression in `A::B` + | +LL | let a: A = A::B { b: B::Fst }; + | +++++++++ + error[E0308]: mismatched types - --> $DIR/compatible-variants.rs:87:17 + --> $DIR/compatible-variants.rs:86:17 | LL | let a: A2 = B::Fst; | -- ^^^^^^ expected struct `A2`, found enum `B` diff --git a/src/test/ui/did_you_mean/issue-42764.rs b/src/test/ui/did_you_mean/issue-42764.rs index 6da640b2b7c76..eb96c2480630e 100644 --- a/src/test/ui/did_you_mean/issue-42764.rs +++ b/src/test/ui/did_you_mean/issue-42764.rs @@ -26,4 +26,5 @@ struct Context { wrapper: Wrapper } fn overton() { let _c = Context { wrapper: Payload{} }; //~^ ERROR mismatched types + //~| try wrapping the expression in `Wrapper` } diff --git a/src/test/ui/did_you_mean/issue-42764.stderr b/src/test/ui/did_you_mean/issue-42764.stderr index 95b572133c5f7..6a7fd8fe251dc 100644 --- a/src/test/ui/did_you_mean/issue-42764.stderr +++ b/src/test/ui/did_you_mean/issue-42764.stderr @@ -25,6 +25,11 @@ error[E0308]: mismatched types | LL | let _c = Context { wrapper: Payload{} }; | ^^^^^^^^^ expected struct `Wrapper`, found struct `Payload` + | +help: try wrapping the expression in `Wrapper` + | +LL | let _c = Context { wrapper: Wrapper { payload: Payload{} } }; + | ++++++++++++++++++ + error: aborting due to 2 previous errors From 1c2ea78f29f6c24d210728ccee177d13d235a9df Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Sun, 31 Jul 2022 19:12:53 +0400 Subject: [PATCH 4/4] Skip unstable fields when suggesting wrapping expression in structs --- compiler/rustc_typeck/src/check/demand.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_typeck/src/check/demand.rs index 8ed0faddf9e15..f0110645551af 100644 --- a/compiler/rustc_typeck/src/check/demand.rs +++ b/compiler/rustc_typeck/src/check/demand.rs @@ -1,5 +1,6 @@ use crate::check::FnCtxt; use rustc_infer::infer::InferOk; +use rustc_middle::middle::stability::EvalResult; use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::traits::ObligationCause; @@ -374,7 +375,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let field_is_local = sole_field.did.is_local(); let field_is_accessible = - sole_field.vis.is_accessible_from(expr.hir_id.owner.to_def_id(), self.tcx); + sole_field.vis.is_accessible_from(expr.hir_id.owner.to_def_id(), self.tcx) + // Skip suggestions for unstable public fields (for example `Pin::pointer`) + && matches!(self.tcx.eval_stability(sole_field.did, None, expr.span, None), EvalResult::Allow | EvalResult::Unmarked); if !field_is_local && !field_is_accessible { return None;