diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_typeck/src/check/demand.rs index c351a9f70404a..241dcbc64a676 100644 --- a/compiler/rustc_typeck/src/check/demand.rs +++ b/compiler/rustc_typeck/src/check/demand.rs @@ -13,7 +13,7 @@ use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, AssocItem, Ty, TypeAndMut}; -use rustc_span::symbol::sym; +use rustc_span::symbol::{sym, Symbol}; use rustc_span::{BytePos, Span}; use super::method::probe; @@ -24,7 +24,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn emit_coerce_suggestions( &self, err: &mut DiagnosticBuilder<'_>, - expr: &hir::Expr<'_>, + expr: &hir::Expr<'tcx>, expr_ty: Ty<'tcx>, expected: Ty<'tcx>, expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, @@ -109,7 +109,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn demand_coerce( &self, - expr: &hir::Expr<'_>, + expr: &hir::Expr<'tcx>, checked_ty: Ty<'tcx>, expected: Ty<'tcx>, expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, @@ -129,7 +129,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// will be permitted if the diverges flag is currently "always". pub fn demand_coerce_diag( &self, - expr: &hir::Expr<'_>, + expr: &hir::Expr<'tcx>, checked_ty: Ty<'tcx>, expected: Ty<'tcx>, expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, @@ -338,31 +338,40 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }) .collect(); - if let [variant] = &compatible_variants[..] { - // Just a single matching variant. - err.multipart_suggestion( - &format!("try wrapping the expression in `{}`", variant), - vec![ - (expr.span.shrink_to_lo(), format!("{}(", variant)), - (expr.span.shrink_to_hi(), ")".to_string()), - ], - Applicability::MaybeIncorrect, - ); - } else if compatible_variants.len() > 1 { - // More than one matching variant. - err.multipart_suggestions( - &format!( - "try wrapping the expression in a variant of `{}`", - self.tcx.def_path_str(expected_adt.did) - ), - compatible_variants.into_iter().map(|variant| { + let prefix = match self.maybe_get_struct_pattern_shorthand_field(expr) { + Some(ident) => format!("{}: ", ident), + None => format!(""), + }; + + match &compatible_variants[..] { + [] => { /* No variants to format */ } + [variant] => { + // Just a single matching variant. + err.multipart_suggestion_verbose( + &format!("try wrapping the expression in `{}`", variant), vec![ - (expr.span.shrink_to_lo(), format!("{}(", variant)), + (expr.span.shrink_to_lo(), format!("{}{}(", prefix, variant)), (expr.span.shrink_to_hi(), ")".to_string()), - ] - }), - Applicability::MaybeIncorrect, - ); + ], + Applicability::MaybeIncorrect, + ); + } + _ => { + // More than one matching variant. + err.multipart_suggestions( + &format!( + "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()), + ] + }), + Applicability::MaybeIncorrect, + ); + } } } } @@ -483,33 +492,45 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - crate fn is_hir_id_from_struct_pattern_shorthand_field( + crate fn maybe_get_struct_pattern_shorthand_field( &self, - hir_id: hir::HirId, - sp: Span, - ) -> bool { - let sm = self.sess().source_map(); - let parent_id = self.tcx.hir().get_parent_node(hir_id); - if let Some(parent) = self.tcx.hir().find(parent_id) { - // Account for fields - if let Node::Expr(hir::Expr { kind: hir::ExprKind::Struct(_, fields, ..), .. }) = parent - { - if let Ok(src) = sm.span_to_snippet(sp) { - for field in *fields { - if field.ident.as_str() == src && field.is_shorthand { - return true; - } + expr: &hir::Expr<'_>, + ) -> Option { + let hir = self.tcx.hir(); + let local = match expr { + hir::Expr { + kind: + hir::ExprKind::Path(hir::QPath::Resolved( + None, + hir::Path { + res: hir::def::Res::Local(_), + segments: [hir::PathSegment { ident, .. }], + .. + }, + )), + .. + } => Some(ident), + _ => None, + }?; + + match hir.find(hir.get_parent_node(expr.hir_id))? { + Node::Expr(hir::Expr { kind: hir::ExprKind::Struct(_, fields, ..), .. }) => { + for field in *fields { + if field.ident.name == local.name && field.is_shorthand { + return Some(local.name); } } } + _ => {} } - false + + None } /// If the given `HirId` corresponds to a block with a trailing expression, return that expression - crate fn maybe_get_block_expr(&self, hir_id: hir::HirId) -> Option<&'tcx hir::Expr<'tcx>> { - match self.tcx.hir().find(hir_id)? { - Node::Expr(hir::Expr { kind: hir::ExprKind::Block(block, ..), .. }) => block.expr, + crate fn maybe_get_block_expr(&self, expr: &hir::Expr<'tcx>) -> Option<&'tcx hir::Expr<'tcx>> { + match expr { + hir::Expr { kind: hir::ExprKind::Block(block, ..), .. } => block.expr, _ => None, } } @@ -547,7 +568,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// `&mut`!". pub fn check_ref( &self, - expr: &hir::Expr<'_>, + expr: &hir::Expr<'tcx>, checked_ty: Ty<'tcx>, expected: Ty<'tcx>, ) -> Option<(Span, &'static str, String, Applicability, bool /* verbose */)> { @@ -565,9 +586,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { s.strip_prefix(old).map(|stripped| new.to_string() + stripped) }; - let is_struct_pat_shorthand_field = - self.is_hir_id_from_struct_pattern_shorthand_field(expr.hir_id, sp); - // `ExprKind::DropTemps` is semantically irrelevant for these suggestions. let expr = expr.peel_drop_temps(); @@ -661,11 +679,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { false, )); } - let field_name = if is_struct_pat_shorthand_field { - format!("{}: ", sugg_expr) - } else { - String::new() + + let prefix = match self.maybe_get_struct_pattern_shorthand_field(expr) { + Some(ident) => format!("{}: ", ident), + None => format!(""), }; + if let Some(hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Assign(left_expr, ..), .. @@ -695,14 +714,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir::Mutability::Mut => ( sp, "consider mutably borrowing here", - format!("{}&mut {}", field_name, sugg_expr), + format!("{}&mut {}", prefix, sugg_expr), Applicability::MachineApplicable, false, ), hir::Mutability::Not => ( sp, "consider borrowing here", - format!("{}&{}", field_name, sugg_expr), + format!("{}&{}", prefix, sugg_expr), Applicability::MachineApplicable, false, ), @@ -846,32 +865,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if self.infcx.type_is_copy_modulo_regions(self.param_env, expected, sp) || checked_ty.is_box() { - if let Ok(code) = sm.span_to_snippet(expr.span) { - let message = if checked_ty.is_box() { - "consider unboxing the value" - } else if checked_ty.is_region_ptr() { - "consider dereferencing the borrow" - } else { - "consider dereferencing the type" - }; - let (span, suggestion) = if is_struct_pat_shorthand_field { - (expr.span, format!("{}: *{}", code, code)) - } else if self.is_else_if_block(expr) { - // Don't suggest nonsense like `else *if` - return None; - } else if let Some(expr) = self.maybe_get_block_expr(expr.hir_id) { - (expr.span.shrink_to_lo(), "*".to_string()) - } else { - (expr.span.shrink_to_lo(), "*".to_string()) - }; - return Some(( - span, - message, - suggestion, - Applicability::MachineApplicable, - true, - )); - } + let message = if checked_ty.is_box() { + "consider unboxing the value" + } else if checked_ty.is_region_ptr() { + "consider dereferencing the borrow" + } else { + "consider dereferencing the type" + }; + let prefix = match self.maybe_get_struct_pattern_shorthand_field(expr) { + Some(ident) => format!("{}: ", ident), + None => format!(""), + }; + let (span, suggestion) = if self.is_else_if_block(expr) { + // Don't suggest nonsense like `else *if` + return None; + } else if let Some(expr) = self.maybe_get_block_expr(expr) { + // prefix should be empty here.. + (expr.span.shrink_to_lo(), "*".to_string()) + } else { + (expr.span.shrink_to_lo(), format!("{}*", prefix)) + }; + return Some(( + span, + message, + suggestion, + Applicability::MachineApplicable, + true, + )); } } } diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs index e8a0cc946b5e1..473c848ad8f13 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs @@ -208,7 +208,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn suggest_deref_ref_or_into( &self, err: &mut DiagnosticBuilder<'_>, - expr: &hir::Expr<'_>, + expr: &hir::Expr<'tcx>, expected: Ty<'tcx>, found: Ty<'tcx>, expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, @@ -231,7 +231,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } else if !self.check_for_cast(err, expr, found, expected, expected_ty_expr) { let is_struct_pat_shorthand_field = - self.is_hir_id_from_struct_pattern_shorthand_field(expr.hir_id, expr.span); + self.maybe_get_struct_pattern_shorthand_field(expr).is_some(); let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id); if !methods.is_empty() { if let Ok(expr_text) = self.sess().source_map().span_to_snippet(expr.span) { diff --git a/src/test/ui/did_you_mean/compatible-variants.rs b/src/test/ui/did_you_mean/compatible-variants.rs index fb6b6a5673d90..a70dda8386f08 100644 --- a/src/test/ui/did_you_mean/compatible-variants.rs +++ b/src/test/ui/did_you_mean/compatible-variants.rs @@ -3,6 +3,10 @@ enum Hey { B(B), } +struct Foo { + bar: Option, +} + fn f() {} fn a() -> Option<()> { @@ -40,4 +44,8 @@ fn main() { let _: Hey = false; //~^ ERROR mismatched types //~| HELP try wrapping + let bar = 1i32; + let _ = Foo { bar }; + //~^ 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 e77949687fcb2..0dfd8f5c128f6 100644 --- a/src/test/ui/did_you_mean/compatible-variants.stderr +++ b/src/test/ui/did_you_mean/compatible-variants.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/compatible-variants.rs:9:5 + --> $DIR/compatible-variants.rs:13:5 | LL | fn a() -> Option<()> { | ---------- expected `Option<()>` because of return type @@ -21,7 +21,7 @@ LL + Some(()) | error[E0308]: mismatched types - --> $DIR/compatible-variants.rs:17:5 + --> $DIR/compatible-variants.rs:21:5 | LL | fn b() -> Result<(), ()> { | -------------- expected `Result<(), ()>` because of return type @@ -37,7 +37,7 @@ LL + Ok(()) | error[E0308]: mismatched types - --> $DIR/compatible-variants.rs:23:25 + --> $DIR/compatible-variants.rs:27:25 | LL | let _: Option<()> = while false {}; | ---------- ^^^^^^^^^^^^^^ expected enum `Option`, found `()` @@ -52,7 +52,7 @@ LL | let _: Option<()> = Some(while false {}); | +++++ + error[E0308]: mismatched types - --> $DIR/compatible-variants.rs:27:9 + --> $DIR/compatible-variants.rs:31:9 | LL | while false {} | ^^^^^^^^^^^^^^ expected enum `Option`, found `()` @@ -69,7 +69,7 @@ LL + Some(()) | error[E0308]: mismatched types - --> $DIR/compatible-variants.rs:31:31 + --> $DIR/compatible-variants.rs:35:31 | LL | let _: Result = 1; | ---------------- ^ expected enum `Result`, found integer @@ -86,7 +86,7 @@ LL | let _: Result = Err(1); | ++++ + error[E0308]: mismatched types - --> $DIR/compatible-variants.rs:34:26 + --> $DIR/compatible-variants.rs:38:26 | LL | let _: Option = 1; | ----------- ^ expected enum `Option`, found integer @@ -101,7 +101,7 @@ LL | let _: Option = Some(1); | +++++ + error[E0308]: mismatched types - --> $DIR/compatible-variants.rs:37:28 + --> $DIR/compatible-variants.rs:41:28 | LL | let _: Hey = 1; | ------------- ^ expected enum `Hey`, found integer @@ -118,7 +118,7 @@ LL | let _: Hey = Hey::B(1); | +++++++ + error[E0308]: mismatched types - --> $DIR/compatible-variants.rs:40:29 + --> $DIR/compatible-variants.rs:44:29 | LL | let _: Hey = false; | -------------- ^^^^^ expected enum `Hey`, found `bool` @@ -132,6 +132,19 @@ help: try wrapping the expression in `Hey::B` LL | let _: Hey = Hey::B(false); | +++++++ + -error: aborting due to 8 previous errors +error[E0308]: mismatched types + --> $DIR/compatible-variants.rs:48:19 + | +LL | let _ = Foo { bar }; + | ^^^ expected enum `Option`, found `i32` + | + = note: expected enum `Option` + found type `i32` +help: try wrapping the expression in `Some` + | +LL | let _ = Foo { bar: Some(bar) }; + | ++++++++++ + + +error: aborting due to 9 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/inference/deref-suggestion.stderr b/src/test/ui/inference/deref-suggestion.stderr index da11ba204cb53..28c9afaa52c22 100644 --- a/src/test/ui/inference/deref-suggestion.stderr +++ b/src/test/ui/inference/deref-suggestion.stderr @@ -87,7 +87,7 @@ LL | let r = R { i }; help: consider dereferencing the borrow | LL | let r = R { i: *i }; - | ~~~~~ + | ++++ error[E0308]: mismatched types --> $DIR/deref-suggestion.rs:46:20