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};
28 use syntax::codemap::Span;
29 use rustc_front::print::pprust;
33 use std::cmp::Ordering;
35 use super::{MethodError, NoMatchData, CandidateSource, impl_item, trait_item};
36 use super::probe::Mode;
38 pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
42 rcvr_expr: Option<&hir::Expr>,
43 error: MethodError<'tcx>)
45 // avoid suggestions when we don't know what's going on.
46 if rcvr_ty.references_error() {
51 MethodError::NoMatch(NoMatchData { static_candidates: static_sources,
52 unsatisfied_predicates,
57 fcx.type_error_message(
60 format!("no {} named `{}` found for type `{}` \
61 in the current scope",
62 if mode == Mode::MethodCall { "method" }
63 else { "associated item" },
70 // If the item has the name of a field, give a help note
71 if let (&ty::TyStruct(def, substs), Some(expr)) = (&rcvr_ty.sty, rcvr_expr) {
72 if let Some(field) = def.struct_variant().find_field_named(item_name) {
73 let expr_string = match cx.sess.codemap().span_to_snippet(expr.span) {
74 Ok(expr_string) => expr_string,
75 _ => "s".into() // Default to a generic placeholder for the
76 // expression when we can't generate a string
80 let span_stored_function = || {
81 cx.sess.span_note(span,
82 &format!("use `({0}.{1})(...)` if you meant to call \
83 the function stored in the `{1}` field",
84 expr_string, item_name));
87 let span_did_you_mean = || {
88 cx.sess.span_note(span, &format!("did you mean to write `{0}.{1}`?",
89 expr_string, item_name));
92 // Determine if the field can be used as a function in some way
93 let field_ty = field.ty(cx, substs);
94 if let Ok(fn_once_trait_did) = cx.lang_items.require(FnOnceTraitLangItem) {
95 let infcx = fcx.infcx();
97 let fn_once_substs = Substs::new_trait(vec![infcx.next_ty_var()],
100 let trait_ref = ty::TraitRef::new(fn_once_trait_did,
101 cx.mk_substs(fn_once_substs));
102 let poly_trait_ref = trait_ref.to_poly_trait_ref();
103 let obligation = Obligation::misc(span,
105 poly_trait_ref.to_predicate());
106 let mut selcx = SelectionContext::new(infcx);
108 if selcx.evaluate_obligation(&obligation) {
109 span_stored_function();
116 // fallback to matching a closure or function pointer
117 ty::TyClosure(..) | ty::TyBareFn(..) => span_stored_function(),
118 _ => span_did_you_mean(),
124 if !static_sources.is_empty() {
125 cx.sess.fileline_note(
127 "found defined static methods, maybe a `self` is missing?");
129 report_candidates(fcx, span, item_name, static_sources);
132 if !unsatisfied_predicates.is_empty() {
133 let bound_list = unsatisfied_predicates.iter()
134 .map(|p| format!("`{} : {}`",
139 cx.sess.fileline_note(
141 &format!("the method `{}` exists but the \
142 following trait bounds were not satisfied: {}",
147 suggest_traits_to_import(fcx, span, rcvr_ty, item_name,
148 rcvr_expr, out_of_scope_traits)
151 MethodError::Ambiguity(sources) => {
152 span_err!(fcx.sess(), span, E0034,
153 "multiple applicable items in scope");
155 report_candidates(fcx, span, item_name, sources);
158 MethodError::ClosureAmbiguity(trait_def_id) => {
159 let msg = format!("the `{}` method from the `{}` trait cannot be explicitly \
160 invoked on this closure as we have not yet inferred what \
161 kind of closure it is",
163 fcx.tcx().item_path_str(trait_def_id));
164 let msg = if let Some(callee) = rcvr_expr {
165 format!("{}; use overloaded call notation instead (e.g., `{}()`)",
166 msg, pprust::expr_to_string(callee))
170 fcx.sess().span_err(span, &msg);
174 fn report_candidates(fcx: &FnCtxt,
176 item_name: ast::Name,
177 mut sources: Vec<CandidateSource>) {
181 for (idx, source) in sources.iter().enumerate() {
183 CandidateSource::ImplSource(impl_did) => {
184 // Provide the best span we can. Use the item, if local to crate, else
185 // the impl, if local to crate (item may be defaulted), else the call site.
186 let item = impl_item(fcx.tcx(), impl_did, item_name)
190 fcx.tcx().impl_trait_ref(impl_did).unwrap().def_id,
194 let impl_span = fcx.tcx().map.def_id_span(impl_did, span);
195 let item_span = fcx.tcx().map.def_id_span(item.def_id(), impl_span);
197 let impl_ty = check::impl_self_ty(fcx, span, impl_did).ty;
199 let insertion = match fcx.tcx().impl_trait_ref(impl_did) {
202 format!(" of the trait `{}`",
203 fcx.tcx().item_path_str(trait_ref.def_id))
207 span_note!(fcx.sess(), item_span,
208 "candidate #{} is defined in an impl{} for the type `{}`",
213 CandidateSource::TraitSource(trait_did) => {
214 let item = trait_item(fcx.tcx(), trait_did, item_name).unwrap();
215 let item_span = fcx.tcx().map.def_id_span(item.def_id(), span);
216 span_note!(fcx.sess(), item_span,
217 "candidate #{} is defined in the trait `{}`",
219 fcx.tcx().item_path_str(trait_did));
227 pub type AllTraitsVec = Vec<TraitInfo>;
229 fn suggest_traits_to_import<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
232 item_name: ast::Name,
233 rcvr_expr: Option<&hir::Expr>,
234 valid_out_of_scope_traits: Vec<DefId>)
238 if !valid_out_of_scope_traits.is_empty() {
239 let mut candidates = valid_out_of_scope_traits;
243 "items from traits can only be used if the trait is in scope; \
244 the following {traits_are} implemented but not in scope, \
245 perhaps add a `use` for {one_of_them}:",
246 traits_are = if candidates.len() == 1 {"trait is"} else {"traits are"},
247 one_of_them = if candidates.len() == 1 {"it"} else {"one of them"});
249 fcx.sess().fileline_help(span, &msg[..]);
251 for (i, trait_did) in candidates.iter().enumerate() {
252 fcx.sess().fileline_help(span,
253 &*format!("candidate #{}: use `{}`",
255 fcx.tcx().item_path_str(*trait_did)))
261 let type_is_local = type_derefs_to_local(fcx, span, rcvr_ty, rcvr_expr);
263 // there's no implemented traits, so lets suggest some traits to
264 // implement, by finding ones that have the item name, and are
265 // legal to implement.
266 let mut candidates = all_traits(fcx.ccx)
268 // we approximate the coherence rules to only suggest
269 // traits that are legal to implement by requiring that
270 // either the type or trait is local. Multidispatch means
271 // this isn't perfect (that is, there are cases when
272 // implementing a trait would be legal but is rejected
274 (type_is_local || info.def_id.is_local())
275 && trait_item(tcx, info.def_id, item_name).is_some()
277 .collect::<Vec<_>>();
279 if !candidates.is_empty() {
280 // sort from most relevant to least relevant
281 candidates.sort_by(|a, b| a.cmp(b).reverse());
284 // FIXME #21673 this help message could be tuned to the case
285 // of a type parameter: suggest adding a trait bound rather
286 // than implementing.
288 "items from traits can only be used if the trait is implemented and in scope; \
289 the following {traits_define} an item `{name}`, \
290 perhaps you need to implement {one_of_them}:",
291 traits_define = if candidates.len() == 1 {"trait defines"} else {"traits define"},
292 one_of_them = if candidates.len() == 1 {"it"} else {"one of them"},
295 fcx.sess().fileline_help(span, &msg[..]);
297 for (i, trait_info) in candidates.iter().enumerate() {
298 fcx.sess().fileline_help(span,
299 &*format!("candidate #{}: `{}`",
301 fcx.tcx().item_path_str(trait_info.def_id)))
306 /// Checks whether there is a local type somewhere in the chain of
307 /// autoderefs of `rcvr_ty`.
308 fn type_derefs_to_local<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
311 rcvr_expr: Option<&hir::Expr>) -> bool {
312 fn is_local(ty: Ty) -> bool {
314 ty::TyEnum(def, _) | ty::TyStruct(def, _) => def.did.is_local(),
316 ty::TyTrait(ref tr) => tr.principal_def_id().is_local(),
318 ty::TyParam(_) => true,
320 // everything else (primitive types etc.) is effectively
321 // non-local (there are "edge" cases, e.g. (LocalType,), but
322 // the noise from these sort of types is usually just really
323 // annoying, rather than any sort of help).
328 // This occurs for UFCS desugaring of `T::method`, where there is no
329 // receiver expression for the method call, and thus no autoderef.
330 if rcvr_expr.is_none() {
331 return is_local(fcx.resolve_type_vars_if_possible(rcvr_ty));
334 check::autoderef(fcx, span, rcvr_ty, None,
335 check::UnresolvedTypeAction::Ignore, ty::NoPreference,
345 #[derive(Copy, Clone)]
346 pub struct TraitInfo {
351 fn new(def_id: DefId) -> TraitInfo {
357 impl PartialEq for TraitInfo {
358 fn eq(&self, other: &TraitInfo) -> bool {
359 self.cmp(other) == Ordering::Equal
362 impl Eq for TraitInfo {}
363 impl PartialOrd for TraitInfo {
364 fn partial_cmp(&self, other: &TraitInfo) -> Option<Ordering> { Some(self.cmp(other)) }
366 impl Ord for TraitInfo {
367 fn cmp(&self, other: &TraitInfo) -> Ordering {
368 // local crates are more important than remote ones (local:
369 // cnum == 0), and otherwise we throw in the defid for totality
371 let lhs = (other.def_id.krate, other.def_id);
372 let rhs = (self.def_id.krate, self.def_id);
377 /// Retrieve all traits in this crate and any dependent crates.
378 pub fn all_traits<'a>(ccx: &'a CrateCtxt) -> AllTraits<'a> {
379 if ccx.all_traits.borrow().is_none() {
380 use rustc_front::visit;
382 let mut traits = vec![];
387 struct Visitor<'a, 'tcx:'a> {
388 map: &'a hir_map::Map<'tcx>,
389 traits: &'a mut AllTraitsVec,
391 impl<'v, 'a, 'tcx> visit::Visitor<'v> for Visitor<'a, 'tcx> {
392 fn visit_item(&mut self, i: &'v hir::Item) {
394 hir::ItemTrait(..) => {
395 let def_id = self.map.local_def_id(i.id);
396 self.traits.push(TraitInfo::new(def_id));
400 visit::walk_item(self, i)
403 visit::walk_crate(&mut Visitor {
406 }, ccx.tcx.map.krate());
409 fn handle_external_def(traits: &mut AllTraitsVec,
411 cstore: &cstore::CStore,
412 dl: decoder::DefLike) {
414 decoder::DlDef(def::DefTrait(did)) => {
415 traits.push(TraitInfo::new(did));
417 decoder::DlDef(def::DefMod(did)) => {
418 csearch::each_child_of_item(cstore, did, |dl, _, _| {
419 handle_external_def(traits, ccx, cstore, dl)
425 let cstore = &ccx.tcx.sess.cstore;
426 cstore.iter_crate_data(|cnum, _| {
427 csearch::each_top_level_item_of_crate(cstore, cnum, |dl, _, _| {
428 handle_external_def(&mut traits, ccx, cstore, dl)
432 *ccx.all_traits.borrow_mut() = Some(traits);
435 let borrow = ccx.all_traits.borrow();
436 assert!(borrow.is_some());
443 pub struct AllTraits<'a> {
444 borrow: cell::Ref<'a, Option<AllTraitsVec>>,
448 impl<'a> Iterator for AllTraits<'a> {
449 type Item = TraitInfo;
451 fn next(&mut self) -> Option<TraitInfo> {
452 let AllTraits { ref borrow, ref mut idx } = *self;
454 borrow.as_ref().unwrap().get(*idx).map(|info| {