splitting methods into smaller ones, add docs, better variable naming
This commit is contained in:
parent
b5686422e1
commit
18b33579e1
@ -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) => {
|
||||||
|
@ -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 {
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user