From d360091e799b14a81ced54a67bc67ae0b0bc3afe Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 8 Nov 2016 22:54:31 +0100 Subject: [PATCH] Add suggestion for & coercions --- src/librustc_typeck/check/demand.rs | 77 ++++++++++++++++++++++++++--- 1 file changed, 70 insertions(+), 7 deletions(-) diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index e922c7447ff8..2933f35abfb6 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -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 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { 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 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { _ => 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 { + 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, + } + } }