splitting methods into smaller ones, add docs, better variable naming

This commit is contained in:
Guillaume Gomez 2016-11-02 00:03:45 +01:00
parent b5686422e1
commit 18b33579e1
3 changed files with 109 additions and 64 deletions

View File

@ -61,22 +61,50 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
} }
} }
fn check_ref(&self, expr: &hir::Expr, checked_ty: Ty<'tcx>, /// This function is used to determine potential "simple" improvements or users' errors and
expected: Ty<'tcx>) -> Option<String> { /// provide them useful help. For example:
match (&checked_ty.sty, &expected.sty) { ///
(&ty::TyRef(_, x_mutability), &ty::TyRef(_, y_mutability)) => { /// ```
/// 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(_, expected_mutability),
&ty::TyRef(_, checked_mutability)) => {
// check if there is a mutability difference // check if there is a mutability difference
if x_mutability.mutbl == hir::Mutability::MutImmutable && if checked_mutability.mutbl == hir::Mutability::MutImmutable &&
x_mutability.mutbl != y_mutability.mutbl && checked_mutability.mutbl != expected_mutability.mutbl &&
self.can_sub_types(&x_mutability.ty, y_mutability.ty).is_ok() { self.can_sub_types(&checked_mutability.ty,
expected_mutability.ty).is_ok() {
if let Ok(src) = self.tcx.sess.codemap().span_to_snippet(expr.span) { if let Ok(src) = self.tcx.sess.codemap().span_to_snippet(expr.span) {
return Some(format!("try with `&mut {}`", &src.replace("&", ""))); return Some(format!("try with `&mut {}`", &src.replace("&", "")));
} }
} }
None None
} }
(_, &ty::TyRef(_, mutability)) => { (&ty::TyRef(_, mutability), _) => {
// check if it can work when put into a ref // 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 { let ref_ty = match mutability.mutbl {
hir::Mutability::MutMutable => self.tcx.mk_mut_ref( hir::Mutability::MutMutable => self.tcx.mk_mut_ref(
self.tcx.mk_region(ty::ReStatic), self.tcx.mk_region(ty::ReStatic),
@ -110,11 +138,11 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
let mode = probe::Mode::MethodCall; let mode = probe::Mode::MethodCall;
let suggestions = if let Some(s) = self.check_ref(expr, checked_ty, expected) { let suggestions = if let Some(s) = self.check_ref(expr, checked_ty, expected) {
Some(s) Some(s)
} else if let Ok(methods) = self.probe_return(syntax_pos::DUMMY_SP, } else if let Ok(methods) = self.probe_for_return_type(syntax_pos::DUMMY_SP,
mode, mode,
expected, expected,
checked_ty, checked_ty,
ast::DUMMY_NODE_ID) { ast::DUMMY_NODE_ID) {
let suggestions: Vec<_> = let suggestions: Vec<_> =
methods.iter() methods.iter()
.map(|ref x| { .map(|ref x| {
@ -143,29 +171,38 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
} }
} }
fn format_method_suggestion(&self, method: &ImplOrTraitItem<'tcx>) -> String {
format!(".{}({})",
method.name(),
if self.has_not_input_arg(method) {
""
} else {
"..."
})
}
fn display_suggested_methods(&self, methods: &[Rc<ImplOrTraitItem<'tcx>>]) -> String {
methods.iter()
.take(5)
.map(|method| self.format_method_suggestion(&*method))
.collect::<Vec<String>>()
.join("\n - ")
}
fn get_best_match(&self, methods: &[Rc<ImplOrTraitItem<'tcx>>]) -> String { fn get_best_match(&self, methods: &[Rc<ImplOrTraitItem<'tcx>>]) -> String {
if methods.len() == 1 { let no_argument_methods: Vec<Rc<ImplOrTraitItem<'tcx>>> =
return format!(" - {}", methods[0].name());
}
let no_argument_methods: Vec<&Rc<ImplOrTraitItem<'tcx>>> =
methods.iter() methods.iter()
.filter(|ref x| self.has_not_input_arg(&*x)) .filter(|ref x| self.has_not_input_arg(&*x))
.map(|x| x.clone())
.collect(); .collect();
if no_argument_methods.len() > 0 { if no_argument_methods.len() > 0 {
no_argument_methods.iter() self.display_suggested_methods(&no_argument_methods)
.take(5)
.map(|method| format!(".{}()", method.name()))
.collect::<Vec<String>>()
.join("\n - ")
} else { } else {
methods.iter() self.display_suggested_methods(&methods)
.take(5)
.map(|method| format!(".{}()", method.name()))
.collect::<Vec<String>>()
.join("\n - ")
} }
} }
// This function checks if the method isn't static and takes other arguments than `self`.
fn has_not_input_arg(&self, method: &ImplOrTraitItem<'tcx>) -> bool { fn has_not_input_arg(&self, method: &ImplOrTraitItem<'tcx>) -> bool {
match *method { match *method {
ImplOrTraitItem::MethodTraitItem(ref x) => { ImplOrTraitItem::MethodTraitItem(ref x) => {

View File

@ -91,7 +91,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
allow_private: bool) allow_private: bool)
-> bool { -> bool {
let mode = probe::Mode::MethodCall; let mode = probe::Mode::MethodCall;
match self.probe_method(span, mode, method_name, self_ty, call_expr_id) { match self.probe_for_name(span, mode, method_name, self_ty, call_expr_id) {
Ok(..) => true, Ok(..) => true,
Err(NoMatch(..)) => false, Err(NoMatch(..)) => false,
Err(Ambiguity(..)) => true, Err(Ambiguity(..)) => true,
@ -130,7 +130,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
let mode = probe::Mode::MethodCall; let mode = probe::Mode::MethodCall;
let self_ty = self.resolve_type_vars_if_possible(&self_ty); let self_ty = self.resolve_type_vars_if_possible(&self_ty);
let pick = self.probe_method(span, mode, method_name, self_ty, call_expr.id)?.remove(0); let pick = self.probe_for_name(span, mode, method_name, self_ty, call_expr.id)?.remove(0);
if let Some(import_id) = pick.import_id { if let Some(import_id) = pick.import_id {
self.tcx.used_trait_imports.borrow_mut().insert(import_id); self.tcx.used_trait_imports.borrow_mut().insert(import_id);
@ -328,7 +328,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
expr_id: ast::NodeId) expr_id: ast::NodeId)
-> Result<Def, MethodError<'tcx>> { -> Result<Def, MethodError<'tcx>> {
let mode = probe::Mode::Path; let mode = probe::Mode::Path;
let picks = self.probe_method(span, mode, method_name, self_ty, expr_id)?; let picks = self.probe_for_name(span, mode, method_name, self_ty, expr_id)?;
let pick = &picks[0]; let pick = &picks[0];
if let Some(import_id) = pick.import_id { if let Some(import_id) = pick.import_id {

View File

@ -150,41 +150,41 @@ pub enum Mode {
} }
impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
pub fn probe_return(&self, pub fn probe_for_return_type(&self,
span: Span, span: Span,
mode: Mode, mode: Mode,
return_type: Ty<'tcx>, return_type: Ty<'tcx>,
self_ty: Ty<'tcx>, self_ty: Ty<'tcx>,
scope_expr_id: ast::NodeId) scope_expr_id: ast::NodeId)
-> PickResult<'tcx> { -> PickResult<'tcx> {
debug!("probe(self_ty={:?}, return_type={}, scope_expr_id={})", debug!("probe(self_ty={:?}, return_type={}, scope_expr_id={})",
self_ty, self_ty,
return_type, return_type,
scope_expr_id); scope_expr_id);
self._probe(span, mode, LookingFor::ReturnType(return_type), self_ty, scope_expr_id) self.probe_for(span, mode, LookingFor::ReturnType(return_type), self_ty, scope_expr_id)
} }
pub fn probe_method(&self, pub fn probe_for_name(&self,
span: Span, span: Span,
mode: Mode, mode: Mode,
item_name: ast::Name, item_name: ast::Name,
self_ty: Ty<'tcx>, self_ty: Ty<'tcx>,
scope_expr_id: ast::NodeId) scope_expr_id: ast::NodeId)
-> PickResult<'tcx> { -> PickResult<'tcx> {
debug!("probe(self_ty={:?}, item_name={}, scope_expr_id={})", debug!("probe(self_ty={:?}, item_name={}, scope_expr_id={})",
self_ty, self_ty,
item_name, item_name,
scope_expr_id); scope_expr_id);
self._probe(span, mode, LookingFor::MethodName(item_name), self_ty, scope_expr_id) self.probe_for(span, mode, LookingFor::MethodName(item_name), self_ty, scope_expr_id)
} }
fn _probe(&self, fn probe_for(&self,
span: Span, span: Span,
mode: Mode, mode: Mode,
looking_for: LookingFor<'tcx>, looking_for: LookingFor<'tcx>,
self_ty: Ty<'tcx>, self_ty: Ty<'tcx>,
scope_expr_id: ast::NodeId) scope_expr_id: ast::NodeId)
-> PickResult<'tcx> { -> PickResult<'tcx> {
// FIXME(#18741) -- right now, creating the steps involves evaluating the // FIXME(#18741) -- right now, creating the steps involves evaluating the
// `*` operator, which registers obligations that then escape into // `*` operator, which registers obligations that then escape into
@ -265,6 +265,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
let final_ty = match looking_for { let final_ty = match looking_for {
&LookingFor::MethodName(_) => autoderef.unambiguous_final_ty(), &LookingFor::MethodName(_) => autoderef.unambiguous_final_ty(),
// Since ReturnType case tries to coerce the returned type to the
// expected one, we need all the information!
&LookingFor::ReturnType(_) => self_ty, &LookingFor::ReturnType(_) => self_ty,
}; };
match final_ty.sty { match final_ty.sty {
@ -627,10 +629,10 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
match *method { match *method {
ty::ImplOrTraitItem::MethodTraitItem(ref x) => { ty::ImplOrTraitItem::MethodTraitItem(ref x) => {
self.probe(|_| { self.probe(|_| {
let output = self.replace_late_bound_regions_with_fresh_var(
self.span, infer::FnCall, &x.fty.sig.output());
let substs = self.fresh_substs_for_item(self.span, method.def_id()); let substs = self.fresh_substs_for_item(self.span, method.def_id());
let output = output.0.subst(self.tcx, substs); let output = x.fty.sig.output().subst(self.tcx, substs);
let (output, _) = self.replace_late_bound_regions_with_fresh_var(
self.span, infer::FnCall, &output);
self.can_sub_types(output, expected).is_ok() self.can_sub_types(output, expected).is_ok()
}) })
} }
@ -950,10 +952,17 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
let steps = self.steps.clone(); let steps = self.steps.clone();
match self.looking_for { match self.looking_for {
LookingFor::MethodName(_) => steps.iter() LookingFor::MethodName(_) => {
.filter_map(|step| self.pick_step(step)) // find the first step that works
.next(), steps.iter()
.filter_map(|step| self.pick_step(step))
.next()
}
LookingFor::ReturnType(_) => { LookingFor::ReturnType(_) => {
// Normally, we stop at the first step where we find a positive match.
// But when we are scanning for methods with a suitable return type,
// these methods have distinct names and hence may not shadow one another
// (also, this is just for hints, so precision is less important).
let mut ret = Vec::new(); let mut ret = Vec::new();
for step in steps.iter() { for step in steps.iter() {
@ -1050,10 +1059,9 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
match self.looking_for { match self.looking_for {
LookingFor::MethodName(_) => it.nth(0), LookingFor::MethodName(_) => it.nth(0),
LookingFor::ReturnType(_) => { LookingFor::ReturnType(_) => {
let mut ret = Vec::new(); let ret = it.filter_map(|entry| entry.ok())
it.filter_map(|entry| entry.ok()) .flat_map(|v| v)
.map(|mut v| { ret.append(&mut v); }) .collect::<Vec<_>>();
.all(|_| true);
if ret.len() < 1 { if ret.len() < 1 {
None None