]> git.lizzy.rs Git - rust.git/commitdiff
Add suggestion for & coercions
authorGuillaume Gomez <guillaume1.gomez@gmail.com>
Tue, 8 Nov 2016 21:54:31 +0000 (22:54 +0100)
committerGuillaume Gomez <guillaume1.gomez@gmail.com>
Fri, 21 Apr 2017 10:28:24 +0000 (12:28 +0200)
src/librustc_typeck/check/demand.rs

index e922c7447ff85384ceb3b9822e9f9058482ec33d..2933f35abfb6a9260eef9fb0cc76dc5e51ab2692 100644 (file)
@@ -11,8 +11,9 @@
 
 use check::FnCtxt;
 use rustc::ty::Ty;
-use rustc::infer::{InferOk};
+use rustc::infer::{InferOk, TypeOrigin};
 use rustc::traits::ObligationCause;
+use rustc::ty;
 
 use syntax::ast;
 use syntax_pos::{self, Span};
@@ -80,12 +81,18 @@ pub fn demand_coerce(&self,
         if let Err(e) = self.try_coerce(expr, checked_ty, self.diverges.get(), expected) {
             let cause = self.misc(expr.span);
             let expr_ty = self.resolve_type_vars_with_obligations(checked_ty);
-            let mode = probe::Mode::MethodCall;
-            let suggestions = self.probe_for_return_type(syntax_pos::DUMMY_SP,
-                                                         mode,
-                                                         expected,
-                                                         checked_ty,
-                                                         ast::DUMMY_NODE_ID);
+            let suggestions = if let Some(suggestions) = self.check_ref(expr,
+                                                                        checked_ty,
+                                                                        expected) {
+                suggestions
+            } else {
+                let mode = probe::Mode::MethodCall;
+                self.probe_for_return_type(syntax_pos::DUMMY_SP,
+                                           mode,
+                                           expected,
+                                           checked_ty,
+                                           ast::DUMMY_NODE_ID)
+            }
             let mut err = self.report_mismatched_types(&cause, expected, expr_ty, e);
             if suggestions.len() > 0 {
                 err.help(&format!("here are some functions which \
@@ -140,4 +147,60 @@ fn has_no_input_arg(&self, method: &AssociatedItem) -> bool {
             _ => false,
         }
     }
+
+    /// This function is used to determine potential "simple" improvements or users' errors and
+    /// provide them useful help. For example:
+    ///
+    /// ```
+    /// fn some_fn(s: &str) {}
+    ///
+    /// let x = "hey!".to_owned();
+    /// some_fn(x); // error
+    /// ```
+    ///
+    /// No need to find every potential function which could make a coercion to transform a
+    /// `String` into a `&str` since a `&` would do the trick!
+    ///
+    /// In addition of this check, it also checks between references mutability state. If the
+    /// expected is mutable but the provided isn't, maybe we could just say "Hey, try with
+    /// `&mut`!".
+    fn check_ref(&self,
+                 expr: &hir::Expr,
+                 checked_ty: Ty<'tcx>,
+                 expected: Ty<'tcx>)
+                 -> Option<String> {
+        match (&expected.sty, &checked_ty.sty) {
+            (&ty::TyRef(_, _), &ty::TyRef(_, _)) => None,
+            (&ty::TyRef(_, mutability), _) => {
+                // Check if it can work when put into a ref. For example:
+                //
+                // ```
+                // fn bar(x: &mut i32) {}
+                //
+                // let x = 0u32;
+                // bar(&x); // error, expected &mut
+                // ```
+                let ref_ty = match mutability.mutbl {
+                    hir::Mutability::MutMutable => self.tcx.mk_mut_ref(
+                                                       self.tcx.mk_region(ty::ReStatic),
+                                                       checked_ty),
+                    hir::Mutability::MutImmutable => self.tcx.mk_imm_ref(
+                                                       self.tcx.mk_region(ty::ReStatic),
+                                                       checked_ty),
+                };
+                if self.try_coerce(expr, ref_ty, expected).is_ok() {
+                    if let Ok(src) = self.tcx.sess.codemap().span_to_snippet(expr.span) {
+                        return Some(format!("try with `{}{}`",
+                                            match mutability.mutbl {
+                                                hir::Mutability::MutMutable => "&mut ",
+                                                hir::Mutability::MutImmutable => "&",
+                                            },
+                                            &src));
+                    }
+                }
+                None
+            }
+            _ => None,
+        }
+    }
 }