diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index c6a1f6cfc0d..d21b5f739bd 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -78,6 +78,7 @@ use errors::DiagnosticBuilder; use syntax::abi; use syntax::feature_gate; use syntax::ptr::P; +use syntax_pos; use std::collections::VecDeque; use std::ops::Deref; @@ -722,6 +723,16 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { Ok(target) } + /// Same as `try_coerce()`, but without side-effects. + pub fn can_coerce(&self, expr_ty: Ty<'tcx>, target: Ty<'tcx>) -> bool { + let source = self.resolve_type_vars_with_obligations(expr_ty); + debug!("coercion::can({:?} -> {:?})", source, target); + + let cause = self.cause(syntax_pos::DUMMY_SP, ObligationCauseCode::ExprAssignable); + let coerce = Coerce::new(self, cause); + self.probe(|_| coerce.coerce::(&[], source, target)).is_ok() + } + /// Given some expressions, their known unified type and another expression, /// tries to unify the types, potentially inserting coercions on any of the /// provided expressions and returns their LUB (aka "common supertype"). diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index e922c7447ff..4cc3f2dacdf 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -10,15 +10,14 @@ use check::FnCtxt; -use rustc::ty::Ty; -use rustc::infer::{InferOk}; +use rustc::infer::InferOk; use rustc::traits::ObligationCause; use syntax::ast; use syntax_pos::{self, Span}; use rustc::hir; use rustc::hir::def::Def; -use rustc::ty::{self, AssociatedItem}; +use rustc::ty::{self, Ty, AssociatedItem}; use errors::DiagnosticBuilder; use super::method::probe; @@ -80,18 +79,24 @@ 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 mut err = self.report_mismatched_types(&cause, expected, expr_ty, e); - if suggestions.len() > 0 { - err.help(&format!("here are some functions which \ - might fulfill your needs:\n{}", - self.get_best_match(&suggestions).join("\n"))); - }; + if let Some(suggestion) = self.check_ref(expr, + checked_ty, + expected) { + err.help(&suggestion); + } else { + let mode = probe::Mode::MethodCall; + let suggestions = self.probe_for_return_type(syntax_pos::DUMMY_SP, + mode, + expected, + checked_ty, + ast::DUMMY_NODE_ID); + if suggestions.len() > 0 { + err.help(&format!("here are some functions which \ + might fulfill your needs:\n{}", + self.get_best_match(&suggestions).join("\n"))); + } + } err.emit(); } } @@ -140,4 +145,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.can_coerce(ref_ty, expected) { + 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, + } + } } diff --git a/src/test/compile-fail/coercion-slice.rs b/src/test/compile-fail/coercion-slice.rs index 6b468ff9662..7c5a4e0c3c6 100644 --- a/src/test/compile-fail/coercion-slice.rs +++ b/src/test/compile-fail/coercion-slice.rs @@ -14,6 +14,5 @@ fn main() { let _: &[i32] = [0]; //~^ ERROR mismatched types //~| expected type `&[i32]` - //~| found type `[{integer}; 1]` //~| expected &[i32], found array of 1 elements } diff --git a/src/test/compile-fail/cross-borrow-trait.rs b/src/test/compile-fail/cross-borrow-trait.rs index e5afccb9cf3..847a82c0826 100644 --- a/src/test/compile-fail/cross-borrow-trait.rs +++ b/src/test/compile-fail/cross-borrow-trait.rs @@ -17,7 +17,7 @@ impl Trait for Foo {} pub fn main() { let x: Box = Box::new(Foo); - let _y: &Trait = x; //~ ERROR mismatched types + let _y: &Trait = x; //~ ERROR E0308 //~| expected type `&Trait` //~| found type `std::boxed::Box` } diff --git a/src/test/compile-fail/issue-11374.rs b/src/test/compile-fail/issue-11374.rs index f78786a2889..1e444a6bebf 100644 --- a/src/test/compile-fail/issue-11374.rs +++ b/src/test/compile-fail/issue-11374.rs @@ -33,5 +33,5 @@ pub fn for_stdin<'a>() -> Container<'a> { fn main() { let mut c = for_stdin(); let mut v = Vec::new(); - c.read_to(v); //~ ERROR mismatched types + c.read_to(v); //~ ERROR E0308 } diff --git a/src/test/compile-fail/issue-13058.rs b/src/test/compile-fail/issue-13058.rs index 408c6d411de..ed163444149 100644 --- a/src/test/compile-fail/issue-13058.rs +++ b/src/test/compile-fail/issue-13058.rs @@ -35,4 +35,5 @@ fn check<'r, I: Iterator, T: Itble<'r, usize, I>>(cont: &T) -> bool fn main() { check((3, 5)); //~^ ERROR mismatched types +//~| HELP try with `&(3, 5)` } diff --git a/src/test/ui/span/coerce-suggestions.rs b/src/test/ui/span/coerce-suggestions.rs index 3177e858ff4..bc3122bf71c 100644 --- a/src/test/ui/span/coerce-suggestions.rs +++ b/src/test/ui/span/coerce-suggestions.rs @@ -32,7 +32,6 @@ fn main() { //~| NOTE types differ in mutability //~| NOTE expected type `&mut std::string::String` //~| NOTE found type `&std::string::String` - //~| HELP try with `&mut y` test2(&y); //~^ ERROR E0308 //~| NOTE types differ in mutability diff --git a/src/test/ui/span/coerce-suggestions.stderr b/src/test/ui/span/coerce-suggestions.stderr index 6a70b8ff851..220b2f471da 100644 --- a/src/test/ui/span/coerce-suggestions.stderr +++ b/src/test/ui/span/coerce-suggestions.stderr @@ -18,11 +18,7 @@ error[E0308]: mismatched types | = note: expected type `&str` found type `std::string::String` - = help: here are some functions which might fulfill your needs: - - .as_str() - - .trim() - - .trim_left() - - .trim_right() + = help: try with `&String::new()` error[E0308]: mismatched types --> $DIR/coerce-suggestions.rs:30:10 @@ -34,18 +30,18 @@ error[E0308]: mismatched types found type `&std::string::String` error[E0308]: mismatched types - --> $DIR/coerce-suggestions.rs:36:11 + --> $DIR/coerce-suggestions.rs:35:11 | -36 | test2(&y); +35 | test2(&y); | ^^ types differ in mutability | = note: expected type `&mut i32` found type `&std::string::String` error[E0308]: mismatched types - --> $DIR/coerce-suggestions.rs:42:9 + --> $DIR/coerce-suggestions.rs:41:9 | -42 | f = box f; +41 | f = box f; | ^^^^^ cyclic type of infinite size | = note: expected type `_`