]> git.lizzy.rs Git - rust.git/blobdiff - src/librustc_typeck/check/demand.rs
add some comments
[rust.git] / src / librustc_typeck / check / demand.rs
index 1edb141c2afef50176179c3d264c09d04518fd3b..7110a1ba81d8f4888891ca60ef464136e54e662c 100644 (file)
@@ -16,6 +16,7 @@
 use syntax::ast;
 use syntax_pos::{self, Span};
 use rustc::hir;
+use rustc::hir::print;
 use rustc::hir::def::Def;
 use rustc::ty::{self, Ty, AssociatedItem};
 use errors::{DiagnosticBuilder, CodeMapper};
@@ -66,7 +67,7 @@ pub fn demand_eqtype_with_origin(&self,
             Ok(InferOk { obligations, value: () }) => {
                 self.register_predicates(obligations);
                 None
-            },
+            }
             Err(e) => {
                 Some(self.report_mismatched_types(cause, expected, actual, e))
             }
@@ -81,7 +82,7 @@ pub fn demand_coerce(&self, expr: &hir::Expr, checked_ty: Ty<'tcx>, expected: Ty
 
     // Checks that the type of `expr` can be coerced to `expected`.
     //
-    // NB: This code relies on `self.diverges` to be accurate.  In
+    // NB: This code relies on `self.diverges` to be accurate. In
     // particular, assignments to `!` will be permitted if the
     // diverges flag is currently "always".
     pub fn demand_coerce_diag(&self,
@@ -94,6 +95,34 @@ pub fn demand_coerce_diag(&self,
             let cause = self.misc(expr.span);
             let expr_ty = self.resolve_type_vars_with_obligations(checked_ty);
             let mut err = self.report_mismatched_types(&cause, expected, expr_ty, e);
+
+            // If the expected type is an enum with any variants whose sole
+            // field is of the found type, suggest such variants. See Issue
+            // #42764.
+            if let ty::TyAdt(expected_adt, substs) = expected.sty {
+                let mut compatible_variants = vec![];
+                for variant in &expected_adt.variants {
+                    if variant.fields.len() == 1 {
+                        let sole_field = &variant.fields[0];
+                        let sole_field_ty = sole_field.ty(self.tcx, substs);
+                        if self.can_coerce(expr_ty, sole_field_ty) {
+                            let mut variant_path = self.tcx.item_path_str(variant.did);
+                            variant_path = variant_path.trim_left_matches("std::prelude::v1::")
+                                .to_string();
+                            compatible_variants.push(variant_path);
+                        }
+                    }
+                }
+                if !compatible_variants.is_empty() {
+                    let expr_text = print::to_string(print::NO_ANN, |s| s.print_expr(expr));
+                    let suggestions = compatible_variants.iter()
+                        .map(|v| format!("{}({})", v, expr_text)).collect::<Vec<_>>();
+                    err.span_suggestions(expr.span,
+                                         "try using a variant of the expected type",
+                                         suggestions);
+                }
+            }
+
             if let Some(suggestion) = self.check_ref(expr,
                                                      checked_ty,
                                                      expected) {
@@ -150,12 +179,7 @@ fn get_best_match(&self, methods: &[AssociatedItem]) -> Vec<String> {
     fn has_no_input_arg(&self, method: &AssociatedItem) -> bool {
         match method.def() {
             Def::Method(def_id) => {
-                match self.tcx.type_of(def_id).sty {
-                    ty::TypeVariants::TyFnDef(_, _, sig) => {
-                        sig.inputs().skip_binder().len() == 1
-                    }
-                    _ => false,
-                }
+                self.tcx.fn_sig(def_id).inputs().skip_binder().len() == 1
             }
             _ => false,
         }
@@ -183,7 +207,29 @@ fn check_ref(&self,
                  expected: Ty<'tcx>)
                  -> Option<String> {
         match (&expected.sty, &checked_ty.sty) {
-            (&ty::TyRef(_, _), &ty::TyRef(_, _)) => None,
+            (&ty::TyRef(_, exp), &ty::TyRef(_, check)) => match (&exp.ty.sty, &check.ty.sty) {
+                (&ty::TyStr, &ty::TyArray(arr, _)) |
+                (&ty::TyStr, &ty::TySlice(arr)) if arr == self.tcx.types.u8 => {
+                    if let hir::ExprLit(_) = expr.node {
+                        let sp = self.sess().codemap().call_span_if_macro(expr.span);
+                        if let Ok(src) = self.tcx.sess.codemap().span_to_snippet(sp) {
+                            return Some(format!("try `{}`", &src[1..]));
+                        }
+                    }
+                    None
+                },
+                (&ty::TyArray(arr, _), &ty::TyStr) |
+                (&ty::TySlice(arr), &ty::TyStr) if arr == self.tcx.types.u8 => {
+                    if let hir::ExprLit(_) = expr.node {
+                        let sp = self.sess().codemap().call_span_if_macro(expr.span);
+                        if let Ok(src) = self.tcx.sess.codemap().span_to_snippet(sp) {
+                            return Some(format!("try `b{}`", src));
+                        }
+                    }
+                    None
+                }
+                _ => None,
+            },
             (&ty::TyRef(_, mutability), _) => {
                 // Check if it can work when put into a ref. For example:
                 //
@@ -215,6 +261,39 @@ fn check_ref(&self,
                 }
                 None
             }
+            (_, &ty::TyRef(_, checked)) => {
+                // We have `&T`, check if what was expected was `T`. If so,
+                // we may want to suggest adding a `*`, or removing
+                // a `&`.
+                //
+                // (But, also check check the `expn_info()` to see if this is
+                // a macro; if so, it's hard to extract the text and make a good
+                // suggestion, so don't bother.)
+                if self.infcx.can_sub(self.param_env, checked.ty, &expected).is_ok() &&
+                   expr.span.ctxt().outer().expn_info().is_none() {
+                    match expr.node {
+                        // Maybe remove `&`?
+                        hir::ExprAddrOf(_, ref expr) => {
+                            if let Ok(code) = self.tcx.sess.codemap().span_to_snippet(expr.span) {
+                                return Some(format!("try with `{}`", code));
+                            }
+                        }
+
+                        // Maybe add `*`? Only if `T: Copy`.
+                        _ => {
+                            if !self.infcx.type_moves_by_default(self.param_env,
+                                                                checked.ty,
+                                                                expr.span) {
+                                let sp = self.sess().codemap().call_span_if_macro(expr.span);
+                                if let Ok(code) = self.tcx.sess.codemap().span_to_snippet(sp) {
+                                    return Some(format!("try with `*{}`", code));
+                                }
+                            }
+                        },
+                    }
+                }
+                None
+            }
             _ => None,
         }
     }