| // Copyright 2014 The Rust Project Developers. See the COPYRIGHT |
| // file at the top-level directory of this distribution and at |
| // http://rust-lang.org/COPYRIGHT. |
| // |
| // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
| // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
| // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your |
| // option. This file may not be copied, modified, or distributed |
| // except according to those terms. |
| |
| //! Give useful errors and suggestions to users when an item can't be |
| //! found or is otherwise invalid. |
| |
| use CrateCtxt; |
| |
| use check::{FnCtxt}; |
| use rustc::hir::map as hir_map; |
| use rustc::ty::{self, Ty, ToPolyTraitRef, ToPredicate, TypeFoldable}; |
| use middle::cstore; |
| use hir::def::Def; |
| use hir::def_id::DefId; |
| use middle::lang_items::FnOnceTraitLangItem; |
| use rustc::ty::subst::Substs; |
| use rustc::traits::{Obligation, SelectionContext}; |
| use util::nodemap::{FnvHashSet}; |
| |
| use syntax::ast; |
| use errors::DiagnosticBuilder; |
| use syntax_pos::Span; |
| |
| use rustc::hir::print as pprust; |
| use rustc::hir; |
| use rustc::hir::Expr_; |
| |
| use std::cell; |
| use std::cmp::Ordering; |
| |
| use super::{MethodError, NoMatchData, CandidateSource}; |
| use super::probe::Mode; |
| |
| impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { |
| fn is_fn_ty(&self, ty: &Ty<'tcx>, span: Span) -> bool { |
| let tcx = self.tcx; |
| match ty.sty { |
| // Not all of these (e.g. unsafe fns) implement FnOnce |
| // so we look for these beforehand |
| ty::TyClosure(..) | ty::TyFnDef(..) | ty::TyFnPtr(_) => true, |
| // If it's not a simple function, look for things which implement FnOnce |
| _ => { |
| let fn_once = match tcx.lang_items.require(FnOnceTraitLangItem) { |
| Ok(fn_once) => fn_once, |
| Err(..) => return false |
| }; |
| |
| self.autoderef(span, ty).any(|(ty, _)| self.probe(|_| { |
| let fn_once_substs = |
| Substs::new_trait(vec![self.next_ty_var()], vec![], ty); |
| let trait_ref = |
| ty::TraitRef::new(fn_once, |
| tcx.mk_substs(fn_once_substs)); |
| let poly_trait_ref = trait_ref.to_poly_trait_ref(); |
| let obligation = Obligation::misc(span, |
| self.body_id, |
| poly_trait_ref |
| .to_predicate()); |
| SelectionContext::new(self).evaluate_obligation(&obligation) |
| })) |
| } |
| } |
| } |
| |
| pub fn report_method_error(&self, |
| span: Span, |
| rcvr_ty: Ty<'tcx>, |
| item_name: ast::Name, |
| rcvr_expr: Option<&hir::Expr>, |
| error: MethodError<'tcx>) |
| { |
| // avoid suggestions when we don't know what's going on. |
| if rcvr_ty.references_error() { |
| return |
| } |
| |
| let report_candidates = |err: &mut DiagnosticBuilder, |
| mut sources: Vec<CandidateSource>| { |
| |
| sources.sort(); |
| sources.dedup(); |
| // Dynamic limit to avoid hiding just one candidate, which is silly. |
| let limit = if sources.len() == 5 { 5 } else { 4 }; |
| |
| for (idx, source) in sources.iter().take(limit).enumerate() { |
| match *source { |
| CandidateSource::ImplSource(impl_did) => { |
| // Provide the best span we can. Use the item, if local to crate, else |
| // the impl, if local to crate (item may be defaulted), else nothing. |
| let item = self.impl_item(impl_did, item_name) |
| .or_else(|| { |
| self.trait_item( |
| self.tcx.impl_trait_ref(impl_did).unwrap().def_id, |
| |
| item_name |
| ) |
| }).unwrap(); |
| let note_span = self.tcx.map.span_if_local(item.def_id()).or_else(|| { |
| self.tcx.map.span_if_local(impl_did) |
| }); |
| |
| let impl_ty = self.impl_self_ty(span, impl_did).ty; |
| |
| let insertion = match self.tcx.impl_trait_ref(impl_did) { |
| None => format!(""), |
| Some(trait_ref) => { |
| format!(" of the trait `{}`", |
| self.tcx.item_path_str(trait_ref.def_id)) |
| } |
| }; |
| |
| let note_str = format!("candidate #{} is defined in an impl{} \ |
| for the type `{}`", |
| idx + 1, |
| insertion, |
| impl_ty); |
| if let Some(note_span) = note_span { |
| // We have a span pointing to the method. Show note with snippet. |
| err.span_note(note_span, ¬e_str); |
| } else { |
| err.note(¬e_str); |
| } |
| } |
| CandidateSource::TraitSource(trait_did) => { |
| let item = self.trait_item(trait_did, item_name).unwrap(); |
| let item_span = self.tcx.map.def_id_span(item.def_id(), span); |
| span_note!(err, item_span, |
| "candidate #{} is defined in the trait `{}`", |
| idx + 1, |
| self.tcx.item_path_str(trait_did)); |
| } |
| } |
| } |
| if sources.len() > limit { |
| err.note(&format!("and {} others", sources.len() - limit)); |
| } |
| }; |
| |
| match error { |
| MethodError::NoMatch(NoMatchData { static_candidates: static_sources, |
| unsatisfied_predicates, |
| out_of_scope_traits, |
| mode, .. }) => { |
| let tcx = self.tcx; |
| |
| let mut err = self.type_error_struct( |
| span, |
| |actual| { |
| format!("no {} named `{}` found for type `{}` \ |
| in the current scope", |
| if mode == Mode::MethodCall { "method" } |
| else { "associated item" }, |
| item_name, |
| actual) |
| }, |
| rcvr_ty, |
| None); |
| |
| // If the item has the name of a field, give a help note |
| if let (&ty::TyStruct(def, substs), Some(expr)) = (&rcvr_ty.sty, rcvr_expr) { |
| if let Some(field) = def.struct_variant().find_field_named(item_name) { |
| let expr_string = match tcx.sess.codemap().span_to_snippet(expr.span) { |
| Ok(expr_string) => expr_string, |
| _ => "s".into() // Default to a generic placeholder for the |
| // expression when we can't generate a string |
| // snippet |
| }; |
| |
| let field_ty = field.ty(tcx, substs); |
| |
| if self.is_fn_ty(&field_ty, span) { |
| err.span_note(span, |
| &format!("use `({0}.{1})(...)` if you meant to call \ |
| the function stored in the `{1}` field", |
| expr_string, item_name)); |
| } else { |
| err.span_note(span, &format!("did you mean to write `{0}.{1}`?", |
| expr_string, item_name)); |
| } |
| } |
| } |
| |
| if self.is_fn_ty(&rcvr_ty, span) { |
| macro_rules! report_function { |
| ($span:expr, $name:expr) => { |
| err.note(&format!("{} is a function, perhaps you wish to call it", |
| $name)); |
| } |
| } |
| |
| if let Some(expr) = rcvr_expr { |
| if let Ok (expr_string) = tcx.sess.codemap().span_to_snippet(expr.span) { |
| report_function!(expr.span, expr_string); |
| } |
| else if let Expr_::ExprPath(_, path) = expr.node.clone() { |
| if let Some(segment) = path.segments.last() { |
| report_function!(expr.span, segment.name); |
| } |
| } |
| } |
| } |
| |
| if !static_sources.is_empty() { |
| err.note( |
| "found the following associated functions; to be used as \ |
| methods, functions must have a `self` parameter"); |
| |
| report_candidates(&mut err, static_sources); |
| } |
| |
| if !unsatisfied_predicates.is_empty() { |
| let bound_list = unsatisfied_predicates.iter() |
| .map(|p| format!("`{} : {}`", |
| p.self_ty(), |
| p)) |
| .collect::<Vec<_>>() |
| .join(", "); |
| err.note( |
| &format!("the method `{}` exists but the \ |
| following trait bounds were not satisfied: {}", |
| item_name, |
| bound_list)); |
| } |
| |
| self.suggest_traits_to_import(&mut err, span, rcvr_ty, item_name, |
| rcvr_expr, out_of_scope_traits); |
| err.emit(); |
| } |
| |
| MethodError::Ambiguity(sources) => { |
| let mut err = struct_span_err!(self.sess(), span, E0034, |
| "multiple applicable items in scope"); |
| |
| report_candidates(&mut err, sources); |
| err.emit(); |
| } |
| |
| MethodError::ClosureAmbiguity(trait_def_id) => { |
| let msg = format!("the `{}` method from the `{}` trait cannot be explicitly \ |
| invoked on this closure as we have not yet inferred what \ |
| kind of closure it is", |
| item_name, |
| self.tcx.item_path_str(trait_def_id)); |
| let msg = if let Some(callee) = rcvr_expr { |
| format!("{}; use overloaded call notation instead (e.g., `{}()`)", |
| msg, pprust::expr_to_string(callee)) |
| } else { |
| msg |
| }; |
| self.sess().span_err(span, &msg); |
| } |
| |
| MethodError::PrivateMatch(def) => { |
| let msg = format!("{} `{}` is private", def.kind_name(), item_name); |
| self.tcx.sess.span_err(span, &msg); |
| } |
| } |
| } |
| |
| fn suggest_traits_to_import(&self, |
| err: &mut DiagnosticBuilder, |
| span: Span, |
| rcvr_ty: Ty<'tcx>, |
| item_name: ast::Name, |
| rcvr_expr: Option<&hir::Expr>, |
| valid_out_of_scope_traits: Vec<DefId>) |
| { |
| if !valid_out_of_scope_traits.is_empty() { |
| let mut candidates = valid_out_of_scope_traits; |
| candidates.sort(); |
| candidates.dedup(); |
| let msg = format!( |
| "items from traits can only be used if the trait is in scope; \ |
| the following {traits_are} implemented but not in scope, \ |
| perhaps add a `use` for {one_of_them}:", |
| traits_are = if candidates.len() == 1 {"trait is"} else {"traits are"}, |
| one_of_them = if candidates.len() == 1 {"it"} else {"one of them"}); |
| |
| err.help(&msg[..]); |
| |
| let limit = if candidates.len() == 5 { 5 } else { 4 }; |
| for (i, trait_did) in candidates.iter().take(limit).enumerate() { |
| err.help(&format!("candidate #{}: `use {}`", |
| i + 1, |
| self.tcx.item_path_str(*trait_did))); |
| } |
| if candidates.len() > limit { |
| err.note(&format!("and {} others", candidates.len() - limit)); |
| } |
| return |
| } |
| |
| let type_is_local = self.type_derefs_to_local(span, rcvr_ty, rcvr_expr); |
| |
| // there's no implemented traits, so lets suggest some traits to |
| // implement, by finding ones that have the item name, and are |
| // legal to implement. |
| let mut candidates = all_traits(self.ccx) |
| .filter(|info| { |
| // we approximate the coherence rules to only suggest |
| // traits that are legal to implement by requiring that |
| // either the type or trait is local. Multidispatch means |
| // this isn't perfect (that is, there are cases when |
| // implementing a trait would be legal but is rejected |
| // here). |
| (type_is_local || info.def_id.is_local()) |
| && self.trait_item(info.def_id, item_name).is_some() |
| }) |
| .collect::<Vec<_>>(); |
| |
| if !candidates.is_empty() { |
| // sort from most relevant to least relevant |
| candidates.sort_by(|a, b| a.cmp(b).reverse()); |
| candidates.dedup(); |
| |
| // FIXME #21673 this help message could be tuned to the case |
| // of a type parameter: suggest adding a trait bound rather |
| // than implementing. |
| let msg = format!( |
| "items from traits can only be used if the trait is implemented and in scope; \ |
| the following {traits_define} an item `{name}`, \ |
| perhaps you need to implement {one_of_them}:", |
| traits_define = if candidates.len() == 1 {"trait defines"} else {"traits define"}, |
| one_of_them = if candidates.len() == 1 {"it"} else {"one of them"}, |
| name = item_name); |
| |
| err.help(&msg[..]); |
| |
| for (i, trait_info) in candidates.iter().enumerate() { |
| err.help(&format!("candidate #{}: `{}`", |
| i + 1, |
| self.tcx.item_path_str(trait_info.def_id))); |
| } |
| } |
| } |
| |
| /// Checks whether there is a local type somewhere in the chain of |
| /// autoderefs of `rcvr_ty`. |
| fn type_derefs_to_local(&self, |
| span: Span, |
| rcvr_ty: Ty<'tcx>, |
| rcvr_expr: Option<&hir::Expr>) -> bool { |
| fn is_local(ty: Ty) -> bool { |
| match ty.sty { |
| ty::TyEnum(def, _) | ty::TyStruct(def, _) => def.did.is_local(), |
| |
| ty::TyTrait(ref tr) => tr.principal_def_id().is_local(), |
| |
| ty::TyParam(_) => true, |
| |
| // everything else (primitive types etc.) is effectively |
| // non-local (there are "edge" cases, e.g. (LocalType,), but |
| // the noise from these sort of types is usually just really |
| // annoying, rather than any sort of help). |
| _ => false |
| } |
| } |
| |
| // This occurs for UFCS desugaring of `T::method`, where there is no |
| // receiver expression for the method call, and thus no autoderef. |
| if rcvr_expr.is_none() { |
| return is_local(self.resolve_type_vars_with_obligations(rcvr_ty)); |
| } |
| |
| self.autoderef(span, rcvr_ty).any(|(ty, _)| is_local(ty)) |
| } |
| } |
| |
| pub type AllTraitsVec = Vec<TraitInfo>; |
| |
| #[derive(Copy, Clone)] |
| pub struct TraitInfo { |
| pub def_id: DefId, |
| } |
| |
| impl TraitInfo { |
| fn new(def_id: DefId) -> TraitInfo { |
| TraitInfo { |
| def_id: def_id, |
| } |
| } |
| } |
| impl PartialEq for TraitInfo { |
| fn eq(&self, other: &TraitInfo) -> bool { |
| self.cmp(other) == Ordering::Equal |
| } |
| } |
| impl Eq for TraitInfo {} |
| impl PartialOrd for TraitInfo { |
| fn partial_cmp(&self, other: &TraitInfo) -> Option<Ordering> { Some(self.cmp(other)) } |
| } |
| impl Ord for TraitInfo { |
| fn cmp(&self, other: &TraitInfo) -> Ordering { |
| // local crates are more important than remote ones (local: |
| // cnum == 0), and otherwise we throw in the defid for totality |
| |
| let lhs = (other.def_id.krate, other.def_id); |
| let rhs = (self.def_id.krate, self.def_id); |
| lhs.cmp(&rhs) |
| } |
| } |
| |
| /// Retrieve all traits in this crate and any dependent crates. |
| pub fn all_traits<'a>(ccx: &'a CrateCtxt) -> AllTraits<'a> { |
| if ccx.all_traits.borrow().is_none() { |
| use rustc::hir::intravisit; |
| |
| let mut traits = vec![]; |
| |
| // Crate-local: |
| // |
| // meh. |
| struct Visitor<'a, 'tcx:'a> { |
| map: &'a hir_map::Map<'tcx>, |
| traits: &'a mut AllTraitsVec, |
| } |
| impl<'v, 'a, 'tcx> intravisit::Visitor<'v> for Visitor<'a, 'tcx> { |
| fn visit_item(&mut self, i: &'v hir::Item) { |
| match i.node { |
| hir::ItemTrait(..) => { |
| let def_id = self.map.local_def_id(i.id); |
| self.traits.push(TraitInfo::new(def_id)); |
| } |
| _ => {} |
| } |
| } |
| } |
| ccx.tcx.map.krate().visit_all_items(&mut Visitor { |
| map: &ccx.tcx.map, |
| traits: &mut traits |
| }); |
| |
| // Cross-crate: |
| let mut external_mods = FnvHashSet(); |
| fn handle_external_def(traits: &mut AllTraitsVec, |
| external_mods: &mut FnvHashSet<DefId>, |
| ccx: &CrateCtxt, |
| cstore: &for<'a> cstore::CrateStore<'a>, |
| dl: cstore::DefLike) { |
| match dl { |
| cstore::DlDef(Def::Trait(did)) => { |
| traits.push(TraitInfo::new(did)); |
| } |
| cstore::DlDef(Def::Mod(did)) => { |
| if !external_mods.insert(did) { |
| return; |
| } |
| for child in cstore.item_children(did) { |
| handle_external_def(traits, external_mods, |
| ccx, cstore, child.def) |
| } |
| } |
| _ => {} |
| } |
| } |
| let cstore = &*ccx.tcx.sess.cstore; |
| |
| for cnum in ccx.tcx.sess.cstore.crates() { |
| for child in cstore.crate_top_level_items(cnum) { |
| handle_external_def(&mut traits, &mut external_mods, |
| ccx, cstore, child.def) |
| } |
| } |
| |
| *ccx.all_traits.borrow_mut() = Some(traits); |
| } |
| |
| let borrow = ccx.all_traits.borrow(); |
| assert!(borrow.is_some()); |
| AllTraits { |
| borrow: borrow, |
| idx: 0 |
| } |
| } |
| |
| pub struct AllTraits<'a> { |
| borrow: cell::Ref<'a, Option<AllTraitsVec>>, |
| idx: usize |
| } |
| |
| impl<'a> Iterator for AllTraits<'a> { |
| type Item = TraitInfo; |
| |
| fn next(&mut self) -> Option<TraitInfo> { |
| let AllTraits { ref borrow, ref mut idx } = *self; |
| // ugh. |
| borrow.as_ref().unwrap().get(*idx).map(|info| { |
| *idx += 1; |
| *info |
| }) |
| } |
| } |