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 middle::ty::{self, Ty, ToPolyTraitRef, ToPredicate, HasTypeFlags};
20 use middle::lang_items::FnOnceTraitLangItem;
21 use middle::subst::Substs;
22 use middle::traits::{Obligation, SelectionContext};
23 use metadata::{csearch, cstore, decoder};
25 use syntax::{ast, ast_util};
26 use syntax::codemap::Span;
27 use syntax::print::pprust;
30 use std::cmp::Ordering;
32 use super::{MethodError, NoMatchData, CandidateSource, impl_item, trait_item};
33 use super::probe::Mode;
35 pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
39 rcvr_expr: Option<&ast::Expr>,
40 error: MethodError<'tcx>)
42 // avoid suggestions when we don't know what's going on.
43 if rcvr_ty.references_error() {
48 MethodError::NoMatch(NoMatchData { static_candidates: static_sources,
49 unsatisfied_predicates,
54 fcx.type_error_message(
57 format!("no {} named `{}` found for type `{}` \
58 in the current scope",
59 if mode == Mode::MethodCall { "method" }
60 else { "associated item" },
67 // If the item has the name of a field, give a help note
68 if let (&ty::TyStruct(did, substs), Some(expr)) = (&rcvr_ty.sty, rcvr_expr) {
69 let fields = cx.lookup_struct_fields(did);
71 if let Some(field) = fields.iter().find(|f| f.name == item_name) {
72 let expr_string = match cx.sess.codemap().span_to_snippet(expr.span) {
73 Ok(expr_string) => expr_string,
74 _ => "s".into() // Default to a generic placeholder for the
75 // expression when we can't generate a string
79 let span_stored_function = || {
80 cx.sess.span_note(span,
81 &format!("use `({0}.{1})(...)` if you meant to call \
82 the function stored in the `{1}` field",
83 expr_string, item_name));
86 let span_did_you_mean = || {
87 cx.sess.span_note(span, &format!("did you mean to write `{0}.{1}`?",
88 expr_string, item_name));
91 // Determine if the field can be used as a function in some way
92 let field_ty = cx.lookup_field_type(did, field.id, substs);
93 if let Ok(fn_once_trait_did) = cx.lang_items.require(FnOnceTraitLangItem) {
94 let infcx = fcx.infcx();
96 let fn_once_substs = Substs::new_trait(vec![infcx.next_ty_var()],
99 let trait_ref = ty::TraitRef::new(fn_once_trait_did,
100 cx.mk_substs(fn_once_substs));
101 let poly_trait_ref = trait_ref.to_poly_trait_ref();
102 let obligation = Obligation::misc(span,
104 poly_trait_ref.to_predicate());
105 let mut selcx = SelectionContext::new(infcx);
107 if selcx.evaluate_obligation(&obligation) {
108 span_stored_function();
115 // fallback to matching a closure or function pointer
116 ty::TyClosure(..) | ty::TyBareFn(..) => span_stored_function(),
117 _ => span_did_you_mean(),
123 if !static_sources.is_empty() {
124 cx.sess.fileline_note(
126 "found defined static methods, maybe a `self` is missing?");
128 report_candidates(fcx, span, item_name, static_sources);
131 if !unsatisfied_predicates.is_empty() {
132 let bound_list = unsatisfied_predicates.iter()
133 .map(|p| format!("`{} : {}`",
138 cx.sess.fileline_note(
140 &format!("the method `{}` exists but the \
141 following trait bounds were not satisfied: {}",
146 suggest_traits_to_import(fcx, span, rcvr_ty, item_name,
147 rcvr_expr, out_of_scope_traits)
150 MethodError::Ambiguity(sources) => {
151 span_err!(fcx.sess(), span, E0034,
152 "multiple applicable items in scope");
154 report_candidates(fcx, span, item_name, sources);
157 MethodError::ClosureAmbiguity(trait_def_id) => {
158 let msg = format!("the `{}` method from the `{}` trait cannot be explicitly \
159 invoked on this closure as we have not yet inferred what \
160 kind of closure it is",
162 fcx.tcx().item_path_str(trait_def_id));
163 let msg = if let Some(callee) = rcvr_expr {
164 format!("{}; use overloaded call notation instead (e.g., `{}()`)",
165 msg, pprust::expr_to_string(callee))
169 fcx.sess().span_err(span, &msg);
173 fn report_candidates(fcx: &FnCtxt,
175 item_name: ast::Name,
176 mut sources: Vec<CandidateSource>) {
180 for (idx, source) in sources.iter().enumerate() {
182 CandidateSource::ImplSource(impl_did) => {
183 // Provide the best span we can. Use the item, if local to crate, else
184 // the impl, if local to crate (item may be defaulted), else the call site.
185 let item = impl_item(fcx.tcx(), impl_did, item_name).unwrap();
186 let impl_span = fcx.tcx().map.def_id_span(impl_did, span);
187 let item_span = fcx.tcx().map.def_id_span(item.def_id(), impl_span);
189 let impl_ty = check::impl_self_ty(fcx, span, impl_did).ty;
191 let insertion = match fcx.tcx().impl_trait_ref(impl_did) {
194 format!(" of the trait `{}`",
195 fcx.tcx().item_path_str(trait_ref.def_id))
199 span_note!(fcx.sess(), item_span,
200 "candidate #{} is defined in an impl{} for the type `{}`",
205 CandidateSource::TraitSource(trait_did) => {
206 let item = trait_item(fcx.tcx(), trait_did, item_name).unwrap();
207 let item_span = fcx.tcx().map.def_id_span(item.def_id(), span);
208 span_note!(fcx.sess(), item_span,
209 "candidate #{} is defined in the trait `{}`",
211 fcx.tcx().item_path_str(trait_did));
219 pub type AllTraitsVec = Vec<TraitInfo>;
221 fn suggest_traits_to_import<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
224 item_name: ast::Name,
225 rcvr_expr: Option<&ast::Expr>,
226 valid_out_of_scope_traits: Vec<ast::DefId>)
230 if !valid_out_of_scope_traits.is_empty() {
231 let mut candidates = valid_out_of_scope_traits;
235 "items from traits can only be used if the trait is in scope; \
236 the following {traits_are} implemented but not in scope, \
237 perhaps add a `use` for {one_of_them}:",
238 traits_are = if candidates.len() == 1 {"trait is"} else {"traits are"},
239 one_of_them = if candidates.len() == 1 {"it"} else {"one of them"});
241 fcx.sess().fileline_help(span, &msg[..]);
243 for (i, trait_did) in candidates.iter().enumerate() {
244 fcx.sess().fileline_help(span,
245 &*format!("candidate #{}: use `{}`",
247 fcx.tcx().item_path_str(*trait_did)))
253 let type_is_local = type_derefs_to_local(fcx, span, rcvr_ty, rcvr_expr);
255 // there's no implemented traits, so lets suggest some traits to
256 // implement, by finding ones that have the item name, and are
257 // legal to implement.
258 let mut candidates = all_traits(fcx.ccx)
260 // we approximate the coherence rules to only suggest
261 // traits that are legal to implement by requiring that
262 // either the type or trait is local. Multidispatch means
263 // this isn't perfect (that is, there are cases when
264 // implementing a trait would be legal but is rejected
266 (type_is_local || ast_util::is_local(info.def_id))
267 && trait_item(tcx, info.def_id, item_name).is_some()
269 .collect::<Vec<_>>();
271 if !candidates.is_empty() {
272 // sort from most relevant to least relevant
273 candidates.sort_by(|a, b| a.cmp(b).reverse());
276 // FIXME #21673 this help message could be tuned to the case
277 // of a type parameter: suggest adding a trait bound rather
278 // than implementing.
280 "items from traits can only be used if the trait is implemented and in scope; \
281 the following {traits_define} an item `{name}`, \
282 perhaps you need to implement {one_of_them}:",
283 traits_define = if candidates.len() == 1 {"trait defines"} else {"traits define"},
284 one_of_them = if candidates.len() == 1 {"it"} else {"one of them"},
287 fcx.sess().fileline_help(span, &msg[..]);
289 for (i, trait_info) in candidates.iter().enumerate() {
290 fcx.sess().fileline_help(span,
291 &*format!("candidate #{}: `{}`",
293 fcx.tcx().item_path_str(trait_info.def_id)))
298 /// Checks whether there is a local type somewhere in the chain of
299 /// autoderefs of `rcvr_ty`.
300 fn type_derefs_to_local<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
303 rcvr_expr: Option<&ast::Expr>) -> bool {
304 fn is_local(ty: Ty) -> bool {
306 ty::TyEnum(did, _) | ty::TyStruct(did, _) => ast_util::is_local(did),
308 ty::TyTrait(ref tr) => ast_util::is_local(tr.principal_def_id()),
310 ty::TyParam(_) => true,
312 // everything else (primitive types etc.) is effectively
313 // non-local (there are "edge" cases, e.g. (LocalType,), but
314 // the noise from these sort of types is usually just really
315 // annoying, rather than any sort of help).
320 // This occurs for UFCS desugaring of `T::method`, where there is no
321 // receiver expression for the method call, and thus no autoderef.
322 if rcvr_expr.is_none() {
323 return is_local(fcx.resolve_type_vars_if_possible(rcvr_ty));
326 check::autoderef(fcx, span, rcvr_ty, None,
327 check::UnresolvedTypeAction::Ignore, check::NoPreference,
337 #[derive(Copy, Clone)]
338 pub struct TraitInfo {
339 pub def_id: ast::DefId,
343 fn new(def_id: ast::DefId) -> TraitInfo {
349 impl PartialEq for TraitInfo {
350 fn eq(&self, other: &TraitInfo) -> bool {
351 self.cmp(other) == Ordering::Equal
354 impl Eq for TraitInfo {}
355 impl PartialOrd for TraitInfo {
356 fn partial_cmp(&self, other: &TraitInfo) -> Option<Ordering> { Some(self.cmp(other)) }
358 impl Ord for TraitInfo {
359 fn cmp(&self, other: &TraitInfo) -> Ordering {
360 // accessible traits are more important/relevant than
361 // inaccessible ones, local crates are more important than
362 // remote ones (local: cnum == 0), and NodeIds just for
365 let lhs = (other.def_id.krate, other.def_id.node);
366 let rhs = (self.def_id.krate, self.def_id.node);
371 /// Retrieve all traits in this crate and any dependent crates.
372 pub fn all_traits<'a>(ccx: &'a CrateCtxt) -> AllTraits<'a> {
373 if ccx.all_traits.borrow().is_none() {
376 let mut traits = vec![];
382 traits: &'a mut AllTraitsVec,
384 impl<'v, 'a> visit::Visitor<'v> for Visitor<'a> {
385 fn visit_item(&mut self, i: &'v ast::Item) {
387 ast::ItemTrait(..) => {
388 self.traits.push(TraitInfo::new(ast_util::local_def(i.id)));
392 visit::walk_item(self, i)
395 visit::walk_crate(&mut Visitor {
397 }, ccx.tcx.map.krate());
400 fn handle_external_def(traits: &mut AllTraitsVec,
402 cstore: &cstore::CStore,
403 dl: decoder::DefLike) {
405 decoder::DlDef(def::DefTrait(did)) => {
406 traits.push(TraitInfo::new(did));
408 decoder::DlDef(def::DefMod(did)) => {
409 csearch::each_child_of_item(cstore, did, |dl, _, _| {
410 handle_external_def(traits, ccx, cstore, dl)
416 let cstore = &ccx.tcx.sess.cstore;
417 cstore.iter_crate_data(|cnum, _| {
418 csearch::each_top_level_item_of_crate(cstore, cnum, |dl, _, _| {
419 handle_external_def(&mut traits, ccx, cstore, dl)
423 *ccx.all_traits.borrow_mut() = Some(traits);
426 let borrow = ccx.all_traits.borrow();
427 assert!(borrow.is_some());
434 pub struct AllTraits<'a> {
435 borrow: cell::Ref<'a, Option<AllTraitsVec>>,
439 impl<'a> Iterator for AllTraits<'a> {
440 type Item = TraitInfo;
442 fn next(&mut self) -> Option<TraitInfo> {
443 let AllTraits { ref borrow, ref mut idx } = *self;
445 borrow.as_ref().unwrap().get(*idx).map(|info| {