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};
21 use middle::def_id::DefId;
22 use middle::lang_items::FnOnceTraitLangItem;
23 use middle::subst::Substs;
24 use middle::traits::{Obligation, SelectionContext};
25 use metadata::{csearch, cstore, decoder};
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);
95 if let Ok(fn_once_trait_did) = cx.lang_items.require(FnOnceTraitLangItem) {
96 let infcx = fcx.infcx();
98 let fn_once_substs = Substs::new_trait(vec![infcx.next_ty_var()],
101 let trait_ref = ty::TraitRef::new(fn_once_trait_did,
102 cx.mk_substs(fn_once_substs));
103 let poly_trait_ref = trait_ref.to_poly_trait_ref();
104 let obligation = Obligation::misc(span,
106 poly_trait_ref.to_predicate());
107 let mut selcx = SelectionContext::new(infcx);
109 if selcx.evaluate_obligation(&obligation) {
110 span_stored_function();
117 // fallback to matching a closure or function pointer
118 ty::TyClosure(..) | ty::TyBareFn(..) => span_stored_function(),
119 _ => span_did_you_mean(),
125 if !static_sources.is_empty() {
126 cx.sess.fileline_note(
128 "found defined static methods, maybe a `self` is missing?");
130 report_candidates(fcx, span, item_name, static_sources);
133 if !unsatisfied_predicates.is_empty() {
134 let bound_list = unsatisfied_predicates.iter()
135 .map(|p| format!("`{} : {}`",
140 cx.sess.fileline_note(
142 &format!("the method `{}` exists but the \
143 following trait bounds were not satisfied: {}",
148 suggest_traits_to_import(fcx, span, rcvr_ty, item_name,
149 rcvr_expr, out_of_scope_traits)
152 MethodError::Ambiguity(sources) => {
153 span_err!(fcx.sess(), span, E0034,
154 "multiple applicable items in scope");
156 report_candidates(fcx, span, item_name, sources);
159 MethodError::ClosureAmbiguity(trait_def_id) => {
160 let msg = format!("the `{}` method from the `{}` trait cannot be explicitly \
161 invoked on this closure as we have not yet inferred what \
162 kind of closure it is",
164 fcx.tcx().item_path_str(trait_def_id));
165 let msg = if let Some(callee) = rcvr_expr {
166 format!("{}; use overloaded call notation instead (e.g., `{}()`)",
167 msg, pprust::expr_to_string(callee))
171 fcx.sess().span_err(span, &msg);
175 fn report_candidates(fcx: &FnCtxt,
177 item_name: ast::Name,
178 mut sources: Vec<CandidateSource>) {
182 for (idx, source) in sources.iter().enumerate() {
184 CandidateSource::ImplSource(impl_did) => {
185 // Provide the best span we can. Use the item, if local to crate, else
186 // the impl, if local to crate (item may be defaulted), else the call site.
187 let item = impl_item(fcx.tcx(), impl_did, item_name)
191 fcx.tcx().impl_trait_ref(impl_did).unwrap().def_id,
195 let impl_span = fcx.tcx().map.def_id_span(impl_did, span);
196 let item_span = fcx.tcx().map.def_id_span(item.def_id(), impl_span);
198 let impl_ty = check::impl_self_ty(fcx, span, impl_did).ty;
200 let insertion = match fcx.tcx().impl_trait_ref(impl_did) {
203 format!(" of the trait `{}`",
204 fcx.tcx().item_path_str(trait_ref.def_id))
208 span_note!(fcx.sess(), item_span,
209 "candidate #{} is defined in an impl{} for the type `{}`",
214 CandidateSource::TraitSource(trait_did) => {
215 let item = trait_item(fcx.tcx(), trait_did, item_name).unwrap();
216 let item_span = fcx.tcx().map.def_id_span(item.def_id(), span);
217 span_note!(fcx.sess(), item_span,
218 "candidate #{} is defined in the trait `{}`",
220 fcx.tcx().item_path_str(trait_did));
228 pub type AllTraitsVec = Vec<TraitInfo>;
230 fn suggest_traits_to_import<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
233 item_name: ast::Name,
234 rcvr_expr: Option<&hir::Expr>,
235 valid_out_of_scope_traits: Vec<DefId>)
239 if !valid_out_of_scope_traits.is_empty() {
240 let mut candidates = valid_out_of_scope_traits;
244 "items from traits can only be used if the trait is in scope; \
245 the following {traits_are} implemented but not in scope, \
246 perhaps add a `use` for {one_of_them}:",
247 traits_are = if candidates.len() == 1 {"trait is"} else {"traits are"},
248 one_of_them = if candidates.len() == 1 {"it"} else {"one of them"});
250 fcx.sess().fileline_help(span, &msg[..]);
252 for (i, trait_did) in candidates.iter().enumerate() {
253 fcx.sess().fileline_help(span,
254 &*format!("candidate #{}: use `{}`",
256 fcx.tcx().item_path_str(*trait_did)))
262 let type_is_local = type_derefs_to_local(fcx, span, rcvr_ty, rcvr_expr);
264 // there's no implemented traits, so lets suggest some traits to
265 // implement, by finding ones that have the item name, and are
266 // legal to implement.
267 let mut candidates = all_traits(fcx.ccx)
269 // we approximate the coherence rules to only suggest
270 // traits that are legal to implement by requiring that
271 // either the type or trait is local. Multidispatch means
272 // this isn't perfect (that is, there are cases when
273 // implementing a trait would be legal but is rejected
275 (type_is_local || info.def_id.is_local())
276 && trait_item(tcx, info.def_id, item_name).is_some()
278 .collect::<Vec<_>>();
280 if !candidates.is_empty() {
281 // sort from most relevant to least relevant
282 candidates.sort_by(|a, b| a.cmp(b).reverse());
285 // FIXME #21673 this help message could be tuned to the case
286 // of a type parameter: suggest adding a trait bound rather
287 // than implementing.
289 "items from traits can only be used if the trait is implemented and in scope; \
290 the following {traits_define} an item `{name}`, \
291 perhaps you need to implement {one_of_them}:",
292 traits_define = if candidates.len() == 1 {"trait defines"} else {"traits define"},
293 one_of_them = if candidates.len() == 1 {"it"} else {"one of them"},
296 fcx.sess().fileline_help(span, &msg[..]);
298 for (i, trait_info) in candidates.iter().enumerate() {
299 fcx.sess().fileline_help(span,
300 &*format!("candidate #{}: `{}`",
302 fcx.tcx().item_path_str(trait_info.def_id)))
307 /// Checks whether there is a local type somewhere in the chain of
308 /// autoderefs of `rcvr_ty`.
309 fn type_derefs_to_local<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
312 rcvr_expr: Option<&hir::Expr>) -> bool {
313 fn is_local(ty: Ty) -> bool {
315 ty::TyEnum(def, _) | ty::TyStruct(def, _) => def.did.is_local(),
317 ty::TyTrait(ref tr) => tr.principal_def_id().is_local(),
319 ty::TyParam(_) => true,
321 // everything else (primitive types etc.) is effectively
322 // non-local (there are "edge" cases, e.g. (LocalType,), but
323 // the noise from these sort of types is usually just really
324 // annoying, rather than any sort of help).
329 // This occurs for UFCS desugaring of `T::method`, where there is no
330 // receiver expression for the method call, and thus no autoderef.
331 if rcvr_expr.is_none() {
332 return is_local(fcx.resolve_type_vars_if_possible(rcvr_ty));
335 check::autoderef(fcx, span, rcvr_ty, None,
336 check::UnresolvedTypeAction::Ignore, ty::NoPreference,
346 #[derive(Copy, Clone)]
347 pub struct TraitInfo {
352 fn new(def_id: DefId) -> TraitInfo {
358 impl PartialEq for TraitInfo {
359 fn eq(&self, other: &TraitInfo) -> bool {
360 self.cmp(other) == Ordering::Equal
363 impl Eq for TraitInfo {}
364 impl PartialOrd for TraitInfo {
365 fn partial_cmp(&self, other: &TraitInfo) -> Option<Ordering> { Some(self.cmp(other)) }
367 impl Ord for TraitInfo {
368 fn cmp(&self, other: &TraitInfo) -> Ordering {
369 // local crates are more important than remote ones (local:
370 // cnum == 0), and otherwise we throw in the defid for totality
372 let lhs = (other.def_id.krate, other.def_id);
373 let rhs = (self.def_id.krate, self.def_id);
378 /// Retrieve all traits in this crate and any dependent crates.
379 pub fn all_traits<'a>(ccx: &'a CrateCtxt) -> AllTraits<'a> {
380 if ccx.all_traits.borrow().is_none() {
381 use rustc_front::visit;
383 let mut traits = vec![];
388 struct Visitor<'a, 'tcx:'a> {
389 map: &'a hir_map::Map<'tcx>,
390 traits: &'a mut AllTraitsVec,
392 impl<'v, 'a, 'tcx> visit::Visitor<'v> for Visitor<'a, 'tcx> {
393 fn visit_item(&mut self, i: &'v hir::Item) {
395 hir::ItemTrait(..) => {
396 let def_id = self.map.local_def_id(i.id);
397 self.traits.push(TraitInfo::new(def_id));
401 visit::walk_item(self, i)
404 visit::walk_crate(&mut Visitor {
407 }, ccx.tcx.map.krate());
410 let mut external_mods = FnvHashSet();
411 fn handle_external_def(traits: &mut AllTraitsVec,
412 external_mods: &mut FnvHashSet<DefId>,
414 cstore: &cstore::CStore,
415 dl: decoder::DefLike) {
417 decoder::DlDef(def::DefTrait(did)) => {
418 traits.push(TraitInfo::new(did));
420 decoder::DlDef(def::DefMod(did)) => {
421 if !external_mods.insert(did) {
424 csearch::each_child_of_item(cstore, did, |dl, _, _| {
425 handle_external_def(traits, external_mods,
432 let cstore = &ccx.tcx.sess.cstore;
433 cstore.iter_crate_data(|cnum, _| {
434 csearch::each_top_level_item_of_crate(cstore, cnum, |dl, _, _| {
435 handle_external_def(&mut traits,
441 *ccx.all_traits.borrow_mut() = Some(traits);
444 let borrow = ccx.all_traits.borrow();
445 assert!(borrow.is_some());
452 pub struct AllTraits<'a> {
453 borrow: cell::Ref<'a, Option<AllTraitsVec>>,
457 impl<'a> Iterator for AllTraits<'a> {
458 type Item = TraitInfo;
460 fn next(&mut self) -> Option<TraitInfo> {
461 let AllTraits { ref borrow, ref mut idx } = *self;
463 borrow.as_ref().unwrap().get(*idx).map(|info| {