Allow multiple suggestions

This commit is contained in:
Guillaume Gomez 2016-12-02 16:36:03 -08:00
parent dac8883a94
commit 25a2d13cde
3 changed files with 107 additions and 54 deletions

View file

@ -1367,7 +1367,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
cause: &ObligationCause<'tcx>,
expected: Ty<'tcx>,
actual: Ty<'tcx>,
err: TypeError<'tcx>) -> DiagnosticBuilder<'tcx> {
err: TypeError<'tcx>)
-> DiagnosticBuilder<'tcx> {
let trace = TypeTrace::types(cause, true, expected, actual);
self.report_and_explain_type_error(trace, &err)
}

View file

@ -81,7 +81,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
fn format_method_suggestion(&self, method: &AssociatedItem) -> String {
format!(".{}({})",
method.name,
if self.has_not_input_arg(method) {
if self.has_no_input_arg(method) {
""
} else {
"..."
@ -99,7 +99,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
fn get_best_match(&self, methods: &[AssociatedItem]) -> String {
let no_argument_methods: Vec<_> =
methods.iter()
.filter(|ref x| self.has_not_input_arg(&*x))
.filter(|ref x| self.has_no_input_arg(&*x))
.map(|x| x.clone())
.collect();
if no_argument_methods.len() > 0 {
@ -110,7 +110,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
}
// This function checks if the method isn't static and takes other arguments than `self`.
fn has_not_input_arg(&self, method: &AssociatedItem) -> bool {
fn has_no_input_arg(&self, method: &AssociatedItem) -> bool {
match method.def() {
Def::Method(def_id) => {
match self.tcx.item_type(def_id).sty {

View file

@ -37,6 +37,22 @@ pub enum LookingFor<'tcx> {
ReturnType(Ty<'tcx>),
}
impl<'tcx> LookingFor<'tcx> {
pub fn is_method_name(&self) -> bool {
match *self {
LookingFor::MethodName(_) => true,
_ => false,
}
}
pub fn is_return_type(&self) -> bool {
match *self {
LookingFor::ReturnType(_) => true,
_ => false,
}
}
}
struct ProbeContext<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
fcx: &'a FnCtxt<'a, 'gcx, 'tcx>,
span: Span,
@ -468,44 +484,81 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
debug!("assemble_inherent_impl_probe {:?}", impl_def_id);
let item = match self.impl_or_trait_item(impl_def_id) {
Some(m) => m,
None => {
let items = self.impl_or_trait_item(impl_def_id);
if items.len() < 1 {
return // No method with correct name on this impl
}
if self.looking_for.is_method_name() {
let item = items[0];
if !self.has_applicable_self(&item) {
// No receiver declared. Not a candidate.
return self.record_static_candidate(ImplSource(impl_def_id));
}
if !item.vis.is_accessible_from(self.body_id, &self.tcx.map) {
self.private_candidate = Some(item.def());
return;
} // No method with correct name on this impl
};
}
if !self.has_applicable_self(&item) {
// No receiver declared. Not a candidate.
return self.record_static_candidate(ImplSource(impl_def_id));
let (impl_ty, impl_substs) = self.impl_ty_and_substs(impl_def_id);
let impl_ty = impl_ty.subst(self.tcx, impl_substs);
// Determine the receiver type that the method itself expects.
let xform_self_ty = self.xform_self_ty(&item, impl_ty, impl_substs);
// We can't use normalize_associated_types_in as it will pollute the
// fcx's fulfillment context after this probe is over.
let cause = traits::ObligationCause::misc(self.span, self.body_id);
let mut selcx = &mut traits::SelectionContext::new(self.fcx);
let traits::Normalized { value: xform_self_ty, obligations } =
traits::normalize(selcx, cause, &xform_self_ty);
debug!("assemble_inherent_impl_probe: xform_self_ty = {:?}",
xform_self_ty);
self.inherent_candidates.push(Candidate {
xform_self_ty: xform_self_ty,
item: item,
kind: InherentImplCandidate(impl_substs, obligations),
import_id: self.import_id,
});
} else {
for item in items {
if !self.has_applicable_self(&item) {
// No receiver declared. Not a candidate.
self.record_static_candidate(ImplSource(impl_def_id));
continue
}
if !item.vis.is_accessible_from(self.body_id, &self.tcx.map) {
self.private_candidate = Some(item.def());
continue
}
let (impl_ty, impl_substs) = self.impl_ty_and_substs(impl_def_id);
let impl_ty = impl_ty.subst(self.tcx, impl_substs);
// Determine the receiver type that the method itself expects.
let xform_self_ty = self.xform_self_ty(&item, impl_ty, impl_substs);
// We can't use normalize_associated_types_in as it will pollute the
// fcx's fulfillment context after this probe is over.
let cause = traits::ObligationCause::misc(self.span, self.body_id);
let mut selcx = &mut traits::SelectionContext::new(self.fcx);
let traits::Normalized { value: xform_self_ty, obligations } =
traits::normalize(selcx, cause, &xform_self_ty);
debug!("assemble_inherent_impl_probe: xform_self_ty = {:?}",
xform_self_ty);
self.inherent_candidates.push(Candidate {
xform_self_ty: xform_self_ty,
item: item,
kind: InherentImplCandidate(impl_substs, obligations),
import_id: self.import_id,
});
}
}
if !item.vis.is_accessible_from(self.body_id, &self.tcx.map) {
self.private_candidate = Some(item.def());
return;
}
let (impl_ty, impl_substs) = self.impl_ty_and_substs(impl_def_id);
let impl_ty = impl_ty.subst(self.tcx, impl_substs);
// Determine the receiver type that the method itself expects.
let xform_self_ty = self.xform_self_ty(&item, impl_ty, impl_substs);
// We can't use normalize_associated_types_in as it will pollute the
// fcx's fulfillment context after this probe is over.
let cause = traits::ObligationCause::misc(self.span, self.body_id);
let mut selcx = &mut traits::SelectionContext::new(self.fcx);
let traits::Normalized { value: xform_self_ty, obligations } =
traits::normalize(selcx, cause, &xform_self_ty);
debug!("assemble_inherent_impl_probe: xform_self_ty = {:?}",
xform_self_ty);
self.inherent_candidates.push(Candidate {
xform_self_ty: xform_self_ty,
item: item,
kind: InherentImplCandidate(impl_substs, obligations),
import_id: self.import_id,
});
}
fn assemble_inherent_candidates_from_object(&mut self,
@ -598,12 +651,11 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
let tcx = self.tcx;
for bound_trait_ref in traits::transitive_bounds(tcx, bounds) {
let item = match self.impl_or_trait_item(bound_trait_ref.def_id()) {
Some(v) => v,
None => {
continue;
}
};
let items = self.impl_or_trait_item(bound_trait_ref.def_id());
if items.len() < 1 {
continue
}
let item = items[0];
if !self.has_applicable_self(&item) {
self.record_static_candidate(TraitSource(bound_trait_ref.def_id()));
@ -665,12 +717,11 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
debug!("assemble_extension_candidates_for_trait(trait_def_id={:?})",
trait_def_id);
let item = match self.impl_or_trait_item(trait_def_id) {
Some(i) => i,
None => {
return Ok(());
}
};
let items = self.impl_or_trait_item(trait_def_id);
if items.len() < 1 {
return Ok(());
}
let item = items[0];
// Check whether `trait_def_id` defines a method with suitable name:
if !self.has_applicable_self(&item) {
@ -1351,16 +1402,17 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> {
}
/// Find the method with the appropriate name (or return type, as the case may be).
fn impl_or_trait_item(&self, def_id: DefId) -> Option<ty::AssociatedItem> {
fn impl_or_trait_item(&self, def_id: DefId) -> Vec<ty::AssociatedItem> {
match self.looking_for {
LookingFor::MethodName(name) => {
self.fcx.associated_item(def_id, name)
self.fcx.associated_item(def_id, name).map_or(Vec::new(), |x| vec![x])
}
LookingFor::ReturnType(return_ty) => {
self.tcx
.associated_items(def_id)
.map(|did| self.tcx.associated_item(did.def_id))
.find(|m| self.matches_return_type(m, return_ty))
.filter(|m| self.matches_return_type(m, return_ty))
.collect()
}
}
}