1 // Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 //! Give useful errors and suggestions to users when an item can't be
12 //! found or is otherwise invalid.
17 use check::{self, FnCtxt};
18 use front::map as hir_map;
19 use middle::ty::{self, Ty, ToPolyTraitRef, ToPredicate, HasTypeFlags};
20 use middle::cstore::{self, CrateStore, DefLike};
22 use middle::def_id::DefId;
23 use middle::lang_items::FnOnceTraitLangItem;
24 use middle::subst::Substs;
25 use middle::traits::{Obligation, SelectionContext};
26 use util::nodemap::{FnvHashSet};
29 use syntax::codemap::Span;
30 use rustc_front::print::pprust;
34 use std::cmp::Ordering;
36 use super::{MethodError, NoMatchData, CandidateSource, impl_item, trait_item};
37 use super::probe::Mode;
39 pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
43 rcvr_expr: Option<&hir::Expr>,
44 error: MethodError<'tcx>)
46 // avoid suggestions when we don't know what's going on.
47 if rcvr_ty.references_error() {
52 MethodError::NoMatch(NoMatchData { static_candidates: static_sources,
53 unsatisfied_predicates,
58 fcx.type_error_message(
61 format!("no {} named `{}` found for type `{}` \
62 in the current scope",
63 if mode == Mode::MethodCall { "method" }
64 else { "associated item" },
71 // If the item has the name of a field, give a help note
72 if let (&ty::TyStruct(def, substs), Some(expr)) = (&rcvr_ty.sty, rcvr_expr) {
73 if let Some(field) = def.struct_variant().find_field_named(item_name) {
74 let expr_string = match cx.sess.codemap().span_to_snippet(expr.span) {
75 Ok(expr_string) => expr_string,
76 _ => "s".into() // Default to a generic placeholder for the
77 // expression when we can't generate a string
81 let span_stored_function = || {
82 cx.sess.span_note(span,
83 &format!("use `({0}.{1})(...)` if you meant to call \
84 the function stored in the `{1}` field",
85 expr_string, item_name));
88 let span_did_you_mean = || {
89 cx.sess.span_note(span, &format!("did you mean to write `{0}.{1}`?",
90 expr_string, item_name));
93 // Determine if the field can be used as a function in some way
94 let field_ty = field.ty(cx, substs);
97 // Not all of these (e.g. unsafe fns) implement FnOnce
98 // so we look for these beforehand
99 ty::TyClosure(..) | ty::TyBareFn(..) => span_stored_function(),
100 // If it's not a simple function, look for things which implement FnOnce
102 if let Ok(fn_once_trait_did) =
103 cx.lang_items.require(FnOnceTraitLangItem) {
104 let infcx = fcx.infcx();
106 let fn_once_substs = Substs::new_trait(vec![
107 infcx.next_ty_var()],
110 let trait_ref = ty::TraitRef::new(fn_once_trait_did,
111 cx.mk_substs(fn_once_substs));
112 let poly_trait_ref = trait_ref.to_poly_trait_ref();
113 let obligation = Obligation::misc(span,
117 let mut selcx = SelectionContext::new(infcx);
119 if selcx.evaluate_obligation(&obligation) {
120 span_stored_function();
133 if !static_sources.is_empty() {
134 cx.sess.fileline_note(
136 "found defined static methods, maybe a `self` is missing?");
138 report_candidates(fcx, span, item_name, static_sources);
141 if !unsatisfied_predicates.is_empty() {
142 let bound_list = unsatisfied_predicates.iter()
143 .map(|p| format!("`{} : {}`",
148 cx.sess.fileline_note(
150 &format!("the method `{}` exists but the \
151 following trait bounds were not satisfied: {}",
156 suggest_traits_to_import(fcx, span, rcvr_ty, item_name,
157 rcvr_expr, out_of_scope_traits)
160 MethodError::Ambiguity(sources) => {
161 span_err!(fcx.sess(), span, E0034,
162 "multiple applicable items in scope");
164 report_candidates(fcx, span, item_name, sources);
167 MethodError::ClosureAmbiguity(trait_def_id) => {
168 let msg = format!("the `{}` method from the `{}` trait cannot be explicitly \
169 invoked on this closure as we have not yet inferred what \
170 kind of closure it is",
172 fcx.tcx().item_path_str(trait_def_id));
173 let msg = if let Some(callee) = rcvr_expr {
174 format!("{}; use overloaded call notation instead (e.g., `{}()`)",
175 msg, pprust::expr_to_string(callee))
179 fcx.sess().span_err(span, &msg);
183 fn report_candidates(fcx: &FnCtxt,
185 item_name: ast::Name,
186 mut sources: Vec<CandidateSource>) {
190 for (idx, source) in sources.iter().enumerate() {
192 CandidateSource::ImplSource(impl_did) => {
193 // Provide the best span we can. Use the item, if local to crate, else
194 // the impl, if local to crate (item may be defaulted), else the call site.
195 let item = impl_item(fcx.tcx(), impl_did, item_name)
199 fcx.tcx().impl_trait_ref(impl_did).unwrap().def_id,
203 let impl_span = fcx.tcx().map.def_id_span(impl_did, span);
204 let item_span = fcx.tcx().map.def_id_span(item.def_id(), impl_span);
206 let impl_ty = check::impl_self_ty(fcx, span, impl_did).ty;
208 let insertion = match fcx.tcx().impl_trait_ref(impl_did) {
211 format!(" of the trait `{}`",
212 fcx.tcx().item_path_str(trait_ref.def_id))
216 span_note!(fcx.sess(), item_span,
217 "candidate #{} is defined in an impl{} for the type `{}`",
222 CandidateSource::TraitSource(trait_did) => {
223 let item = trait_item(fcx.tcx(), trait_did, item_name).unwrap();
224 let item_span = fcx.tcx().map.def_id_span(item.def_id(), span);
225 span_note!(fcx.sess(), item_span,
226 "candidate #{} is defined in the trait `{}`",
228 fcx.tcx().item_path_str(trait_did));
236 pub type AllTraitsVec = Vec<TraitInfo>;
238 fn suggest_traits_to_import<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
241 item_name: ast::Name,
242 rcvr_expr: Option<&hir::Expr>,
243 valid_out_of_scope_traits: Vec<DefId>)
247 if !valid_out_of_scope_traits.is_empty() {
248 let mut candidates = valid_out_of_scope_traits;
252 "items from traits can only be used if the trait is in scope; \
253 the following {traits_are} implemented but not in scope, \
254 perhaps add a `use` for {one_of_them}:",
255 traits_are = if candidates.len() == 1 {"trait is"} else {"traits are"},
256 one_of_them = if candidates.len() == 1 {"it"} else {"one of them"});
258 fcx.sess().fileline_help(span, &msg[..]);
260 for (i, trait_did) in candidates.iter().enumerate() {
261 fcx.sess().fileline_help(span,
262 &*format!("candidate #{}: use `{}`",
264 fcx.tcx().item_path_str(*trait_did)))
270 let type_is_local = type_derefs_to_local(fcx, span, rcvr_ty, rcvr_expr);
272 // there's no implemented traits, so lets suggest some traits to
273 // implement, by finding ones that have the item name, and are
274 // legal to implement.
275 let mut candidates = all_traits(fcx.ccx)
277 // we approximate the coherence rules to only suggest
278 // traits that are legal to implement by requiring that
279 // either the type or trait is local. Multidispatch means
280 // this isn't perfect (that is, there are cases when
281 // implementing a trait would be legal but is rejected
283 (type_is_local || info.def_id.is_local())
284 && trait_item(tcx, info.def_id, item_name).is_some()
286 .collect::<Vec<_>>();
288 if !candidates.is_empty() {
289 // sort from most relevant to least relevant
290 candidates.sort_by(|a, b| a.cmp(b).reverse());
293 // FIXME #21673 this help message could be tuned to the case
294 // of a type parameter: suggest adding a trait bound rather
295 // than implementing.
297 "items from traits can only be used if the trait is implemented and in scope; \
298 the following {traits_define} an item `{name}`, \
299 perhaps you need to implement {one_of_them}:",
300 traits_define = if candidates.len() == 1 {"trait defines"} else {"traits define"},
301 one_of_them = if candidates.len() == 1 {"it"} else {"one of them"},
304 fcx.sess().fileline_help(span, &msg[..]);
306 for (i, trait_info) in candidates.iter().enumerate() {
307 fcx.sess().fileline_help(span,
308 &*format!("candidate #{}: `{}`",
310 fcx.tcx().item_path_str(trait_info.def_id)))
315 /// Checks whether there is a local type somewhere in the chain of
316 /// autoderefs of `rcvr_ty`.
317 fn type_derefs_to_local<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
320 rcvr_expr: Option<&hir::Expr>) -> bool {
321 fn is_local(ty: Ty) -> bool {
323 ty::TyEnum(def, _) | ty::TyStruct(def, _) => def.did.is_local(),
325 ty::TyTrait(ref tr) => tr.principal_def_id().is_local(),
327 ty::TyParam(_) => true,
329 // everything else (primitive types etc.) is effectively
330 // non-local (there are "edge" cases, e.g. (LocalType,), but
331 // the noise from these sort of types is usually just really
332 // annoying, rather than any sort of help).
337 // This occurs for UFCS desugaring of `T::method`, where there is no
338 // receiver expression for the method call, and thus no autoderef.
339 if rcvr_expr.is_none() {
340 return is_local(fcx.resolve_type_vars_if_possible(rcvr_ty));
343 check::autoderef(fcx, span, rcvr_ty, None,
344 check::UnresolvedTypeAction::Ignore, ty::NoPreference,
354 #[derive(Copy, Clone)]
355 pub struct TraitInfo {
360 fn new(def_id: DefId) -> TraitInfo {
366 impl PartialEq for TraitInfo {
367 fn eq(&self, other: &TraitInfo) -> bool {
368 self.cmp(other) == Ordering::Equal
371 impl Eq for TraitInfo {}
372 impl PartialOrd for TraitInfo {
373 fn partial_cmp(&self, other: &TraitInfo) -> Option<Ordering> { Some(self.cmp(other)) }
375 impl Ord for TraitInfo {
376 fn cmp(&self, other: &TraitInfo) -> Ordering {
377 // local crates are more important than remote ones (local:
378 // cnum == 0), and otherwise we throw in the defid for totality
380 let lhs = (other.def_id.krate, other.def_id);
381 let rhs = (self.def_id.krate, self.def_id);
386 /// Retrieve all traits in this crate and any dependent crates.
387 pub fn all_traits<'a>(ccx: &'a CrateCtxt) -> AllTraits<'a> {
388 if ccx.all_traits.borrow().is_none() {
389 use rustc_front::intravisit;
391 let mut traits = vec![];
396 struct Visitor<'a, 'tcx:'a> {
397 map: &'a hir_map::Map<'tcx>,
398 traits: &'a mut AllTraitsVec,
400 impl<'v, 'a, 'tcx> intravisit::Visitor<'v> for Visitor<'a, 'tcx> {
401 fn visit_item(&mut self, i: &'v hir::Item) {
403 hir::ItemTrait(..) => {
404 let def_id = self.map.local_def_id(i.id);
405 self.traits.push(TraitInfo::new(def_id));
411 ccx.tcx.map.krate().visit_all_items(&mut Visitor {
417 let mut external_mods = FnvHashSet();
418 fn handle_external_def(traits: &mut AllTraitsVec,
419 external_mods: &mut FnvHashSet<DefId>,
421 cstore: &for<'a> cstore::CrateStore<'a>,
422 dl: cstore::DefLike) {
424 cstore::DlDef(def::DefTrait(did)) => {
425 traits.push(TraitInfo::new(did));
427 cstore::DlDef(def::DefMod(did)) => {
428 if !external_mods.insert(did) {
431 for child in cstore.item_children(did) {
432 handle_external_def(traits, external_mods,
433 ccx, cstore, child.def)
439 let cstore = &*ccx.tcx.sess.cstore;
441 for cnum in ccx.tcx.sess.cstore.crates() {
442 for child in cstore.crate_top_level_items(cnum) {
443 handle_external_def(&mut traits, &mut external_mods,
444 ccx, cstore, child.def)
448 *ccx.all_traits.borrow_mut() = Some(traits);
451 let borrow = ccx.all_traits.borrow();
452 assert!(borrow.is_some());
459 pub struct AllTraits<'a> {
460 borrow: cell::Ref<'a, Option<AllTraitsVec>>,
464 impl<'a> Iterator for AllTraits<'a> {
465 type Item = TraitInfo;
467 fn next(&mut self) -> Option<TraitInfo> {
468 let AllTraits { ref borrow, ref mut idx } = *self;
470 borrow.as_ref().unwrap().get(*idx).map(|info| {