about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_typeck/src/check/demand.rs184
-rw-r--r--compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs4
-rw-r--r--src/test/ui/did_you_mean/compatible-variants.rs8
-rw-r--r--src/test/ui/did_you_mean/compatible-variants.stderr31
-rw-r--r--src/test/ui/inference/deref-suggestion.stderr2
5 files changed, 135 insertions, 94 deletions
diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_typeck/src/check/demand.rs
index c351a9f7040..241dcbc64a6 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<Symbol> {
+        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 e8a0cc946b5..473c848ad8f 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 fb6b6a5673d..a70dda8386f 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<A, B> {
     B(B),
 }
 
+struct Foo {
+    bar: Option<i32>,
+}
+
 fn f() {}
 
 fn a() -> Option<()> {
@@ -40,4 +44,8 @@ fn main() {
     let _: Hey<i32, bool> = 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 e77949687fc..0dfd8f5c128 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<i32, i32> = 1;
    |            ----------------   ^ expected enum `Result`, found integer
@@ -86,7 +86,7 @@ LL |     let _: Result<i32, i32> = Err(1);
    |                               ++++ +
 
 error[E0308]: mismatched types
-  --> $DIR/compatible-variants.rs:34:26
+  --> $DIR/compatible-variants.rs:38:26
    |
 LL |     let _: Option<i32> = 1;
    |            -----------   ^ expected enum `Option`, found integer
@@ -101,7 +101,7 @@ LL |     let _: Option<i32> = Some(1);
    |                          +++++ +
 
 error[E0308]: mismatched types
-  --> $DIR/compatible-variants.rs:37:28
+  --> $DIR/compatible-variants.rs:41:28
    |
 LL |     let _: Hey<i32, i32> = 1;
    |            -------------   ^ expected enum `Hey`, found integer
@@ -118,7 +118,7 @@ LL |     let _: Hey<i32, i32> = Hey::B(1);
    |                            +++++++ +
 
 error[E0308]: mismatched types
-  --> $DIR/compatible-variants.rs:40:29
+  --> $DIR/compatible-variants.rs:44:29
    |
 LL |     let _: Hey<i32, bool> = false;
    |            --------------   ^^^^^ expected enum `Hey`, found `bool`
@@ -132,6 +132,19 @@ help: try wrapping the expression in `Hey::B`
 LL |     let _: Hey<i32, bool> = 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<i32>`
+              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 da11ba204cb..28c9afaa52c 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