diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index 08cf6d3a59ec5..2d9b25deb052a 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::ops::Deref; use check::FnCtxt; use rustc::infer::InferOk; @@ -18,7 +19,7 @@ use syntax_pos::{self, Span}; use rustc::hir; use rustc::hir::print; use rustc::hir::def::Def; -use rustc::ty::{self, Ty, AssociatedItem}; +use rustc::ty::{self, Ty, AssociatedItem, AssociatedItemContainer}; use errors::{DiagnosticBuilder, CodeMapper}; use super::method::probe; @@ -139,49 +140,44 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { expected) { err.help(&suggestion); } else { - let mode = probe::Mode::MethodCall; - let suggestions = self.probe_for_return_type(syntax_pos::DUMMY_SP, - mode, - expected, - checked_ty, - ast::DUMMY_NODE_ID); - if suggestions.len() > 0 { - err.help(&format!("here are some functions which \ - might fulfill your needs:\n{}", - self.get_best_match(&suggestions).join("\n"))); - } + let suggestions = self.get_conversion_suggestions(expected, checked_ty); + err.span_suggestions(expr.span.next_point(), + "try using a conversion method", + suggestions); } (expected, Some(err)) } - fn format_method_suggestion(&self, method: &AssociatedItem) -> String { - format!("- .{}({})", - method.name, - if self.has_no_input_arg(method) { - "" - } else { - "..." - }) - } + fn get_conversion_suggestions(&self, expected: Ty<'tcx>, checked_ty: Ty<'tcx>) -> Vec { + let mut methods = self.probe_for_return_type(syntax_pos::DUMMY_SP, + probe::Mode::MethodCall, + expected, + checked_ty, + ast::DUMMY_NODE_ID); - fn display_suggested_methods(&self, methods: &[AssociatedItem]) -> Vec { - methods.iter() - .take(5) - .map(|method| self.format_method_suggestion(&*method)) - .collect::>() - } + // We want methods with no arguments that are plausibly conversions: + // though a whitelist of trait DefIDs might be a better way of doing + // this (FIXME!), we presently resort to the expediency of detecting + // conversions by both name, and whether the method comes from a trait + // (thereby picking methods from traits like `Into<_>` and `ToString`, + // rather than inherent methods that merely have a suggestive name) + let conversion_method_name = |n: &str| { + n.starts_with("to_") || n.starts_with("into") || + n.starts_with("as_") || n.starts_with("borrow") + }; + methods.retain(|m| { + if !self.has_no_input_arg(m) { + return false; + } + if let AssociatedItemContainer::TraitContainer(_def_id) = m.container { + let name = m.name.as_str(); + conversion_method_name(name.deref()) + } else { + false + } + }); - fn get_best_match(&self, methods: &[AssociatedItem]) -> Vec { - let no_argument_methods: Vec<_> = - methods.iter() - .filter(|ref x| self.has_no_input_arg(&*x)) - .map(|x| x.clone()) - .collect(); - if no_argument_methods.len() > 0 { - self.display_suggested_methods(&no_argument_methods) - } else { - self.display_suggested_methods(&methods) - } + methods.into_iter().map(|m| format!(".{}()", m.name)).collect() } // This function checks if the method isn't static and takes other arguments than `self`. diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index 81e5b2fe00a6a..15099bfb043c4 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -183,7 +183,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { scope_expr_id); let method_names = self.probe_op(span, mode, None, Some(return_type), IsSuggestion(true), - self_ty, scope_expr_id, ProbeScope::TraitsInScope, + self_ty, scope_expr_id, ProbeScope::AllTraits, |probe_cx| Ok(probe_cx.candidate_method_names())) .unwrap_or(vec![]); method_names @@ -192,7 +192,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { self.probe_op( span, mode, Some(method_name), Some(return_type), IsSuggestion(true), self_ty, scope_expr_id, - ProbeScope::TraitsInScope, |probe_cx| probe_cx.pick() + ProbeScope::AllTraits, |probe_cx| probe_cx.pick() ).ok().map(|pick| pick.item) }) .collect()