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, UnresolvedTypeAction, autoderef};
18 use front::map as hir_map;
19 use rustc::ty::{self, Ty, ToPolyTraitRef, ToPredicate, TypeFoldable};
20 use middle::cstore::{self, CrateStore};
22 use middle::def_id::DefId;
23 use middle::lang_items::FnOnceTraitLangItem;
24 use rustc::ty::subst::Substs;
25 use rustc::ty::LvaluePreference;
26 use rustc::traits::{Obligation, SelectionContext};
27 use util::nodemap::{FnvHashSet};
31 use syntax::codemap::Span;
32 use syntax::errors::DiagnosticBuilder;
33 use rustc_front::print::pprust;
35 use rustc_front::hir::Expr_;
38 use std::cmp::Ordering;
40 use super::{MethodError, NoMatchData, CandidateSource, impl_item, trait_item};
41 use super::probe::Mode;
43 fn is_fn_ty<'a, 'tcx>(ty: &Ty<'tcx>, fcx: &FnCtxt<'a, 'tcx>, span: Span) -> bool {
46 // Not all of these (e.g. unsafe fns) implement FnOnce
47 // so we look for these beforehand
48 ty::TyClosure(..) | ty::TyFnDef(..) | ty::TyFnPtr(_) => true,
49 // If it's not a simple function, look for things which implement FnOnce
51 if let Ok(fn_once_trait_did) =
52 cx.lang_items.require(FnOnceTraitLangItem) {
53 let infcx = fcx.infcx();
54 let (_, _, opt_is_fn) = autoderef(fcx,
58 UnresolvedTypeAction::Ignore,
59 LvaluePreference::NoPreference,
63 Substs::new_trait(vec![infcx.next_ty_var()],
67 ty::TraitRef::new(fn_once_trait_did,
68 cx.mk_substs(fn_once_substs));
69 let poly_trait_ref = trait_ref.to_poly_trait_ref();
70 let obligation = Obligation::misc(span,
74 let mut selcx = SelectionContext::new(infcx);
76 if selcx.evaluate_obligation(&obligation) {
85 Some(result) => result,
95 pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
99 rcvr_expr: Option<&hir::Expr>,
100 error: MethodError<'tcx>)
102 // avoid suggestions when we don't know what's going on.
103 if rcvr_ty.references_error() {
108 MethodError::NoMatch(NoMatchData { static_candidates: static_sources,
109 unsatisfied_predicates,
114 let mut err = fcx.type_error_struct(
117 format!("no {} named `{}` found for type `{}` \
118 in the current scope",
119 if mode == Mode::MethodCall { "method" }
120 else { "associated item" },
127 // If the item has the name of a field, give a help note
128 if let (&ty::TyStruct(def, substs), Some(expr)) = (&rcvr_ty.sty, rcvr_expr) {
129 if let Some(field) = def.struct_variant().find_field_named(item_name) {
130 let expr_string = match cx.sess.codemap().span_to_snippet(expr.span) {
131 Ok(expr_string) => expr_string,
132 _ => "s".into() // Default to a generic placeholder for the
133 // expression when we can't generate a string
137 let field_ty = field.ty(cx, substs);
139 if is_fn_ty(&field_ty, &fcx, span) {
141 &format!("use `({0}.{1})(...)` if you meant to call \
142 the function stored in the `{1}` field",
143 expr_string, item_name));
145 err.span_note(span, &format!("did you mean to write `{0}.{1}`?",
146 expr_string, item_name));
151 if is_fn_ty(&rcvr_ty, &fcx, span) {
152 macro_rules! report_function {
153 ($span:expr, $name:expr) => {
156 &format!("{} is a function, perhaps you wish to call it",
161 if let Some(expr) = rcvr_expr {
162 if let Ok (expr_string) = cx.sess.codemap().span_to_snippet(expr.span) {
163 report_function!(expr.span, expr_string);
164 err.span_suggestion(expr.span,
165 "try calling the base function:",
169 else if let Expr_::ExprPath(_, path) = expr.node.clone() {
170 if let Some(segment) = path.segments.last() {
171 report_function!(expr.span, segment.identifier.name);
177 if !static_sources.is_empty() {
180 "found the following associated functions; to be used as \
181 methods, functions must have a `self` parameter");
183 report_candidates(fcx, &mut err, span, item_name, static_sources);
186 if !unsatisfied_predicates.is_empty() {
187 let bound_list = unsatisfied_predicates.iter()
188 .map(|p| format!("`{} : {}`",
195 &format!("the method `{}` exists but the \
196 following trait bounds were not satisfied: {}",
201 suggest_traits_to_import(fcx, &mut err, span, rcvr_ty, item_name,
202 rcvr_expr, out_of_scope_traits);
206 MethodError::Ambiguity(sources) => {
207 let mut err = struct_span_err!(fcx.sess(), span, E0034,
208 "multiple applicable items in scope");
210 report_candidates(fcx, &mut err, span, item_name, sources);
214 MethodError::ClosureAmbiguity(trait_def_id) => {
215 let msg = format!("the `{}` method from the `{}` trait cannot be explicitly \
216 invoked on this closure as we have not yet inferred what \
217 kind of closure it is",
219 fcx.tcx().item_path_str(trait_def_id));
220 let msg = if let Some(callee) = rcvr_expr {
221 format!("{}; use overloaded call notation instead (e.g., `{}()`)",
222 msg, pprust::expr_to_string(callee))
226 fcx.sess().span_err(span, &msg);
229 MethodError::PrivateMatch(def) => {
230 let msg = format!("{} `{}` is private", def.kind_name(), item_name);
231 fcx.tcx().sess.span_err(span, &msg);
235 fn report_candidates(fcx: &FnCtxt,
236 err: &mut DiagnosticBuilder,
238 item_name: ast::Name,
239 mut sources: Vec<CandidateSource>) {
243 for (idx, source) in sources.iter().enumerate() {
245 CandidateSource::ImplSource(impl_did) => {
246 // Provide the best span we can. Use the item, if local to crate, else
247 // the impl, if local to crate (item may be defaulted), else the call site.
248 let item = impl_item(fcx.tcx(), impl_did, item_name)
252 fcx.tcx().impl_trait_ref(impl_did).unwrap().def_id,
256 let impl_span = fcx.tcx().map.def_id_span(impl_did, span);
257 let item_span = fcx.tcx().map.def_id_span(item.def_id(), impl_span);
259 let impl_ty = check::impl_self_ty(fcx, span, impl_did).ty;
261 let insertion = match fcx.tcx().impl_trait_ref(impl_did) {
264 format!(" of the trait `{}`",
265 fcx.tcx().item_path_str(trait_ref.def_id))
269 span_note!(err, item_span,
270 "candidate #{} is defined in an impl{} for the type `{}`",
275 CandidateSource::TraitSource(trait_did) => {
276 let item = trait_item(fcx.tcx(), trait_did, item_name).unwrap();
277 let item_span = fcx.tcx().map.def_id_span(item.def_id(), span);
278 span_note!(err, item_span,
279 "candidate #{} is defined in the trait `{}`",
281 fcx.tcx().item_path_str(trait_did));
289 pub type AllTraitsVec = Vec<TraitInfo>;
291 fn suggest_traits_to_import<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
292 err: &mut DiagnosticBuilder,
295 item_name: ast::Name,
296 rcvr_expr: Option<&hir::Expr>,
297 valid_out_of_scope_traits: Vec<DefId>)
301 if !valid_out_of_scope_traits.is_empty() {
302 let mut candidates = valid_out_of_scope_traits;
306 "items from traits can only be used if the trait is in scope; \
307 the following {traits_are} implemented but not in scope, \
308 perhaps add a `use` for {one_of_them}:",
309 traits_are = if candidates.len() == 1 {"trait is"} else {"traits are"},
310 one_of_them = if candidates.len() == 1 {"it"} else {"one of them"});
312 err.fileline_help(span, &msg[..]);
314 for (i, trait_did) in candidates.iter().enumerate() {
315 err.fileline_help(span,
316 &format!("candidate #{}: `use {}`",
318 fcx.tcx().item_path_str(*trait_did)));
323 let type_is_local = type_derefs_to_local(fcx, span, rcvr_ty, rcvr_expr);
325 // there's no implemented traits, so lets suggest some traits to
326 // implement, by finding ones that have the item name, and are
327 // legal to implement.
328 let mut candidates = all_traits(fcx.ccx)
330 // we approximate the coherence rules to only suggest
331 // traits that are legal to implement by requiring that
332 // either the type or trait is local. Multidispatch means
333 // this isn't perfect (that is, there are cases when
334 // implementing a trait would be legal but is rejected
336 (type_is_local || info.def_id.is_local())
337 && trait_item(tcx, info.def_id, item_name).is_some()
339 .collect::<Vec<_>>();
341 if !candidates.is_empty() {
342 // sort from most relevant to least relevant
343 candidates.sort_by(|a, b| a.cmp(b).reverse());
346 // FIXME #21673 this help message could be tuned to the case
347 // of a type parameter: suggest adding a trait bound rather
348 // than implementing.
350 "items from traits can only be used if the trait is implemented and in scope; \
351 the following {traits_define} an item `{name}`, \
352 perhaps you need to implement {one_of_them}:",
353 traits_define = if candidates.len() == 1 {"trait defines"} else {"traits define"},
354 one_of_them = if candidates.len() == 1 {"it"} else {"one of them"},
357 err.fileline_help(span, &msg[..]);
359 for (i, trait_info) in candidates.iter().enumerate() {
360 err.fileline_help(span,
361 &format!("candidate #{}: `{}`",
363 fcx.tcx().item_path_str(trait_info.def_id)));
368 /// Checks whether there is a local type somewhere in the chain of
369 /// autoderefs of `rcvr_ty`.
370 fn type_derefs_to_local<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
373 rcvr_expr: Option<&hir::Expr>) -> bool {
374 fn is_local(ty: Ty) -> bool {
376 ty::TyEnum(def, _) | ty::TyStruct(def, _) => def.did.is_local(),
378 ty::TyTrait(ref tr) => tr.principal_def_id().is_local(),
380 ty::TyParam(_) => true,
382 // everything else (primitive types etc.) is effectively
383 // non-local (there are "edge" cases, e.g. (LocalType,), but
384 // the noise from these sort of types is usually just really
385 // annoying, rather than any sort of help).
390 // This occurs for UFCS desugaring of `T::method`, where there is no
391 // receiver expression for the method call, and thus no autoderef.
392 if rcvr_expr.is_none() {
393 return is_local(fcx.resolve_type_vars_if_possible(rcvr_ty));
396 check::autoderef(fcx, span, rcvr_ty, || None,
397 check::UnresolvedTypeAction::Ignore, ty::NoPreference,
407 #[derive(Copy, Clone)]
408 pub struct TraitInfo {
413 fn new(def_id: DefId) -> TraitInfo {
419 impl PartialEq for TraitInfo {
420 fn eq(&self, other: &TraitInfo) -> bool {
421 self.cmp(other) == Ordering::Equal
424 impl Eq for TraitInfo {}
425 impl PartialOrd for TraitInfo {
426 fn partial_cmp(&self, other: &TraitInfo) -> Option<Ordering> { Some(self.cmp(other)) }
428 impl Ord for TraitInfo {
429 fn cmp(&self, other: &TraitInfo) -> Ordering {
430 // local crates are more important than remote ones (local:
431 // cnum == 0), and otherwise we throw in the defid for totality
433 let lhs = (other.def_id.krate, other.def_id);
434 let rhs = (self.def_id.krate, self.def_id);
439 /// Retrieve all traits in this crate and any dependent crates.
440 pub fn all_traits<'a>(ccx: &'a CrateCtxt) -> AllTraits<'a> {
441 if ccx.all_traits.borrow().is_none() {
442 use rustc_front::intravisit;
444 let mut traits = vec![];
449 struct Visitor<'a, 'tcx:'a> {
450 map: &'a hir_map::Map<'tcx>,
451 traits: &'a mut AllTraitsVec,
453 impl<'v, 'a, 'tcx> intravisit::Visitor<'v> for Visitor<'a, 'tcx> {
454 fn visit_item(&mut self, i: &'v hir::Item) {
456 hir::ItemTrait(..) => {
457 let def_id = self.map.local_def_id(i.id);
458 self.traits.push(TraitInfo::new(def_id));
464 ccx.tcx.map.krate().visit_all_items(&mut Visitor {
470 let mut external_mods = FnvHashSet();
471 fn handle_external_def(traits: &mut AllTraitsVec,
472 external_mods: &mut FnvHashSet<DefId>,
474 cstore: &for<'a> cstore::CrateStore<'a>,
475 dl: cstore::DefLike) {
477 cstore::DlDef(Def::Trait(did)) => {
478 traits.push(TraitInfo::new(did));
480 cstore::DlDef(Def::Mod(did)) => {
481 if !external_mods.insert(did) {
484 for child in cstore.item_children(did) {
485 handle_external_def(traits, external_mods,
486 ccx, cstore, child.def)
492 let cstore = &*ccx.tcx.sess.cstore;
494 for cnum in ccx.tcx.sess.cstore.crates() {
495 for child in cstore.crate_top_level_items(cnum) {
496 handle_external_def(&mut traits, &mut external_mods,
497 ccx, cstore, child.def)
501 *ccx.all_traits.borrow_mut() = Some(traits);
504 let borrow = ccx.all_traits.borrow();
505 assert!(borrow.is_some());
512 pub struct AllTraits<'a> {
513 borrow: cell::Ref<'a, Option<AllTraitsVec>>,
517 impl<'a> Iterator for AllTraits<'a> {
518 type Item = TraitInfo;
520 fn next(&mut self) -> Option<TraitInfo> {
521 let AllTraits { ref borrow, ref mut idx } = *self;
523 borrow.as_ref().unwrap().get(*idx).map(|info| {