Skip to content

Commit d360091

Browse files
Add suggestion for & coercions
1 parent 968ae7b commit d360091

File tree

1 file changed

+70
-7
lines changed

1 file changed

+70
-7
lines changed

src/librustc_typeck/check/demand.rs

Lines changed: 70 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@
1111

1212
use check::FnCtxt;
1313
use rustc::ty::Ty;
14-
use rustc::infer::{InferOk};
14+
use rustc::infer::{InferOk, TypeOrigin};
1515
use rustc::traits::ObligationCause;
16+
use rustc::ty;
1617

1718
use syntax::ast;
1819
use syntax_pos::{self, Span};
@@ -80,12 +81,18 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
8081
if let Err(e) = self.try_coerce(expr, checked_ty, self.diverges.get(), expected) {
8182
let cause = self.misc(expr.span);
8283
let expr_ty = self.resolve_type_vars_with_obligations(checked_ty);
83-
let mode = probe::Mode::MethodCall;
84-
let suggestions = self.probe_for_return_type(syntax_pos::DUMMY_SP,
85-
mode,
86-
expected,
87-
checked_ty,
88-
ast::DUMMY_NODE_ID);
84+
let suggestions = if let Some(suggestions) = self.check_ref(expr,
85+
checked_ty,
86+
expected) {
87+
suggestions
88+
} else {
89+
let mode = probe::Mode::MethodCall;
90+
self.probe_for_return_type(syntax_pos::DUMMY_SP,
91+
mode,
92+
expected,
93+
checked_ty,
94+
ast::DUMMY_NODE_ID)
95+
}
8996
let mut err = self.report_mismatched_types(&cause, expected, expr_ty, e);
9097
if suggestions.len() > 0 {
9198
err.help(&format!("here are some functions which \
@@ -140,4 +147,60 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
140147
_ => false,
141148
}
142149
}
150+
151+
/// This function is used to determine potential "simple" improvements or users' errors and
152+
/// provide them useful help. For example:
153+
///
154+
/// ```
155+
/// fn some_fn(s: &str) {}
156+
///
157+
/// let x = "hey!".to_owned();
158+
/// some_fn(x); // error
159+
/// ```
160+
///
161+
/// No need to find every potential function which could make a coercion to transform a
162+
/// `String` into a `&str` since a `&` would do the trick!
163+
///
164+
/// In addition of this check, it also checks between references mutability state. If the
165+
/// expected is mutable but the provided isn't, maybe we could just say "Hey, try with
166+
/// `&mut`!".
167+
fn check_ref(&self,
168+
expr: &hir::Expr,
169+
checked_ty: Ty<'tcx>,
170+
expected: Ty<'tcx>)
171+
-> Option<String> {
172+
match (&expected.sty, &checked_ty.sty) {
173+
(&ty::TyRef(_, _), &ty::TyRef(_, _)) => None,
174+
(&ty::TyRef(_, mutability), _) => {
175+
// Check if it can work when put into a ref. For example:
176+
//
177+
// ```
178+
// fn bar(x: &mut i32) {}
179+
//
180+
// let x = 0u32;
181+
// bar(&x); // error, expected &mut
182+
// ```
183+
let ref_ty = match mutability.mutbl {
184+
hir::Mutability::MutMutable => self.tcx.mk_mut_ref(
185+
self.tcx.mk_region(ty::ReStatic),
186+
checked_ty),
187+
hir::Mutability::MutImmutable => self.tcx.mk_imm_ref(
188+
self.tcx.mk_region(ty::ReStatic),
189+
checked_ty),
190+
};
191+
if self.try_coerce(expr, ref_ty, expected).is_ok() {
192+
if let Ok(src) = self.tcx.sess.codemap().span_to_snippet(expr.span) {
193+
return Some(format!("try with `{}{}`",
194+
match mutability.mutbl {
195+
hir::Mutability::MutMutable => "&mut ",
196+
hir::Mutability::MutImmutable => "&",
197+
},
198+
&src));
199+
}
200+
}
201+
None
202+
}
203+
_ => None,
204+
}
205+
}
143206
}

0 commit comments

Comments
 (0)