|
11 | 11 |
|
12 | 12 | use check::FnCtxt;
|
13 | 13 | use rustc::ty::Ty;
|
14 |
| -use rustc::infer::{InferOk}; |
| 14 | +use rustc::infer::{InferOk, TypeOrigin}; |
15 | 15 | use rustc::traits::ObligationCause;
|
| 16 | +use rustc::ty; |
16 | 17 |
|
17 | 18 | use syntax::ast;
|
18 | 19 | use syntax_pos::{self, Span};
|
@@ -80,12 +81,18 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
80 | 81 | if let Err(e) = self.try_coerce(expr, checked_ty, self.diverges.get(), expected) {
|
81 | 82 | let cause = self.misc(expr.span);
|
82 | 83 | 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 | + } |
89 | 96 | let mut err = self.report_mismatched_types(&cause, expected, expr_ty, e);
|
90 | 97 | if suggestions.len() > 0 {
|
91 | 98 | err.help(&format!("here are some functions which \
|
@@ -140,4 +147,60 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
140 | 147 | _ => false,
|
141 | 148 | }
|
142 | 149 | }
|
| 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 | + } |
143 | 206 | }
|
0 commit comments