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, TypeFoldable}
;
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 syntax
::errors
::DiagnosticBuilder
;
31 use rustc_front
::print
::pprust
;
35 use std
::cmp
::Ordering
;
37 use super::{MethodError, NoMatchData, CandidateSource, impl_item, trait_item}
;
38 use super::probe
::Mode
;
40 pub fn report_error
<'a
, 'tcx
>(fcx
: &FnCtxt
<'a
, 'tcx
>,
44 rcvr_expr
: Option
<&hir
::Expr
>,
45 error
: MethodError
<'tcx
>)
47 // avoid suggestions when we don't know what's going on.
48 if rcvr_ty
.references_error() {
53 MethodError
::NoMatch(NoMatchData
{ static_candidates
: static_sources
,
54 unsatisfied_predicates
,
59 let mut err
= fcx
.type_error_struct(
62 format
!("no {} named `{}` found for type `{}` \
63 in the current scope",
64 if mode
== Mode
::MethodCall { "method" }
65 else { "associated item" }
,
72 // If the item has the name of a field, give a help note
73 if let (&ty
::TyStruct(def
, substs
), Some(expr
)) = (&rcvr_ty
.sty
, rcvr_expr
) {
74 if let Some(field
) = def
.struct_variant().find_field_named(item_name
) {
75 let expr_string
= match cx
.sess
.codemap().span_to_snippet(expr
.span
) {
76 Ok(expr_string
) => expr_string
,
77 _
=> "s".into() // Default to a generic placeholder for the
78 // expression when we can't generate a string
82 macro_rules
! span_stored_function
{
85 &format
!("use `({0}.{1})(...)` if you meant to call \
86 the function stored in the `{1}` field",
87 expr_string
, item_name
));
91 macro_rules
! span_did_you_mean
{
93 err
.span_note(span
, &format
!("did you mean to write `{0}.{1}`?",
94 expr_string
, item_name
));
98 // Determine if the field can be used as a function in some way
99 let field_ty
= field
.ty(cx
, substs
);
102 // Not all of these (e.g. unsafe fns) implement FnOnce
103 // so we look for these beforehand
104 ty
::TyClosure(..) | ty
::TyBareFn(..) => {
105 span_stored_function
!();
107 // If it's not a simple function, look for things which implement FnOnce
109 if let Ok(fn_once_trait_did
) =
110 cx
.lang_items
.require(FnOnceTraitLangItem
) {
111 let infcx
= fcx
.infcx();
114 Substs
::new_trait(vec
![infcx
.next_ty_var()],
118 ty
::TraitRef
::new(fn_once_trait_did
,
119 cx
.mk_substs(fn_once_substs
));
120 let poly_trait_ref
= trait_ref
.to_poly_trait_ref();
121 let obligation
= Obligation
::misc(span
,
125 let mut selcx
= SelectionContext
::new(infcx
);
127 if selcx
.evaluate_obligation(&obligation
) {
128 span_stored_function
!();
130 span_did_you_mean
!();
134 span_did_you_mean
!();
141 if !static_sources
.is_empty() {
144 "found defined static methods, maybe a `self` is missing?");
146 report_candidates(fcx
, &mut err
, span
, item_name
, static_sources
);
149 if !unsatisfied_predicates
.is_empty() {
150 let bound_list
= unsatisfied_predicates
.iter()
151 .map(|p
| format
!("`{} : {}`",
158 &format
!("the method `{}` exists but the \
159 following trait bounds were not satisfied: {}",
164 suggest_traits_to_import(fcx
, &mut err
, span
, rcvr_ty
, item_name
,
165 rcvr_expr
, out_of_scope_traits
);
169 MethodError
::Ambiguity(sources
) => {
170 let mut err
= struct_span_err
!(fcx
.sess(), span
, E0034
,
171 "multiple applicable items in scope");
173 report_candidates(fcx
, &mut err
, span
, item_name
, sources
);
177 MethodError
::ClosureAmbiguity(trait_def_id
) => {
178 let msg
= format
!("the `{}` method from the `{}` trait cannot be explicitly \
179 invoked on this closure as we have not yet inferred what \
180 kind of closure it is",
182 fcx
.tcx().item_path_str(trait_def_id
));
183 let msg
= if let Some(callee
) = rcvr_expr
{
184 format
!("{}; use overloaded call notation instead (e.g., `{}()`)",
185 msg
, pprust
::expr_to_string(callee
))
189 fcx
.sess().span_err(span
, &msg
);
193 fn report_candidates(fcx
: &FnCtxt
,
194 err
: &mut DiagnosticBuilder
,
196 item_name
: ast
::Name
,
197 mut sources
: Vec
<CandidateSource
>) {
201 for (idx
, source
) in sources
.iter().enumerate() {
203 CandidateSource
::ImplSource(impl_did
) => {
204 // Provide the best span we can. Use the item, if local to crate, else
205 // the impl, if local to crate (item may be defaulted), else the call site.
206 let item
= impl_item(fcx
.tcx(), impl_did
, item_name
)
210 fcx
.tcx().impl_trait_ref(impl_did
).unwrap().def_id
,
214 let impl_span
= fcx
.tcx().map
.def_id_span(impl_did
, span
);
215 let item_span
= fcx
.tcx().map
.def_id_span(item
.def_id(), impl_span
);
217 let impl_ty
= check
::impl_self_ty(fcx
, span
, impl_did
).ty
;
219 let insertion
= match fcx
.tcx().impl_trait_ref(impl_did
) {
222 format
!(" of the trait `{}`",
223 fcx
.tcx().item_path_str(trait_ref
.def_id
))
227 span_note
!(err
, item_span
,
228 "candidate #{} is defined in an impl{} for the type `{}`",
233 CandidateSource
::TraitSource(trait_did
) => {
234 let item
= trait_item(fcx
.tcx(), trait_did
, item_name
).unwrap();
235 let item_span
= fcx
.tcx().map
.def_id_span(item
.def_id(), span
);
236 span_note
!(err
, item_span
,
237 "candidate #{} is defined in the trait `{}`",
239 fcx
.tcx().item_path_str(trait_did
));
247 pub type AllTraitsVec
= Vec
<TraitInfo
>;
249 fn suggest_traits_to_import
<'a
, 'tcx
>(fcx
: &FnCtxt
<'a
, 'tcx
>,
250 err
: &mut DiagnosticBuilder
,
253 item_name
: ast
::Name
,
254 rcvr_expr
: Option
<&hir
::Expr
>,
255 valid_out_of_scope_traits
: Vec
<DefId
>)
259 if !valid_out_of_scope_traits
.is_empty() {
260 let mut candidates
= valid_out_of_scope_traits
;
264 "items from traits can only be used if the trait is in scope; \
265 the following {traits_are} implemented but not in scope, \
266 perhaps add a `use` for {one_of_them}:",
267 traits_are
= if candidates
.len() == 1 {"trait is"}
else {"traits are"}
,
268 one_of_them
= if candidates
.len() == 1 {"it"}
else {"one of them"}
);
270 err
.fileline_help(span
, &msg
[..]);
272 for (i
, trait_did
) in candidates
.iter().enumerate() {
273 err
.fileline_help(span
,
274 &*format
!("candidate #{}: use `{}`",
276 fcx
.tcx().item_path_str(*trait_did
)));
281 let type_is_local
= type_derefs_to_local(fcx
, span
, rcvr_ty
, rcvr_expr
);
283 // there's no implemented traits, so lets suggest some traits to
284 // implement, by finding ones that have the item name, and are
285 // legal to implement.
286 let mut candidates
= all_traits(fcx
.ccx
)
288 // we approximate the coherence rules to only suggest
289 // traits that are legal to implement by requiring that
290 // either the type or trait is local. Multidispatch means
291 // this isn't perfect (that is, there are cases when
292 // implementing a trait would be legal but is rejected
294 (type_is_local
|| info
.def_id
.is_local())
295 && trait_item(tcx
, info
.def_id
, item_name
).is_some()
297 .collect
::<Vec
<_
>>();
299 if !candidates
.is_empty() {
300 // sort from most relevant to least relevant
301 candidates
.sort_by(|a
, b
| a
.cmp(b
).reverse());
304 // FIXME #21673 this help message could be tuned to the case
305 // of a type parameter: suggest adding a trait bound rather
306 // than implementing.
308 "items from traits can only be used if the trait is implemented and in scope; \
309 the following {traits_define} an item `{name}`, \
310 perhaps you need to implement {one_of_them}:",
311 traits_define
= if candidates
.len() == 1 {"trait defines"}
else {"traits define"}
,
312 one_of_them
= if candidates
.len() == 1 {"it"}
else {"one of them"}
,
315 err
.fileline_help(span
, &msg
[..]);
317 for (i
, trait_info
) in candidates
.iter().enumerate() {
318 err
.fileline_help(span
,
319 &*format
!("candidate #{}: `{}`",
321 fcx
.tcx().item_path_str(trait_info
.def_id
)));
326 /// Checks whether there is a local type somewhere in the chain of
327 /// autoderefs of `rcvr_ty`.
328 fn type_derefs_to_local
<'a
, 'tcx
>(fcx
: &FnCtxt
<'a
, 'tcx
>,
331 rcvr_expr
: Option
<&hir
::Expr
>) -> bool
{
332 fn is_local(ty
: Ty
) -> bool
{
334 ty
::TyEnum(def
, _
) | ty
::TyStruct(def
, _
) => def
.did
.is_local(),
336 ty
::TyTrait(ref tr
) => tr
.principal_def_id().is_local(),
338 ty
::TyParam(_
) => true,
340 // everything else (primitive types etc.) is effectively
341 // non-local (there are "edge" cases, e.g. (LocalType,), but
342 // the noise from these sort of types is usually just really
343 // annoying, rather than any sort of help).
348 // This occurs for UFCS desugaring of `T::method`, where there is no
349 // receiver expression for the method call, and thus no autoderef.
350 if rcvr_expr
.is_none() {
351 return is_local(fcx
.resolve_type_vars_if_possible(rcvr_ty
));
354 check
::autoderef(fcx
, span
, rcvr_ty
, None
,
355 check
::UnresolvedTypeAction
::Ignore
, ty
::NoPreference
,
365 #[derive(Copy, Clone)]
366 pub struct TraitInfo
{
371 fn new(def_id
: DefId
) -> TraitInfo
{
377 impl PartialEq
for TraitInfo
{
378 fn eq(&self, other
: &TraitInfo
) -> bool
{
379 self.cmp(other
) == Ordering
::Equal
382 impl Eq
for TraitInfo {}
383 impl PartialOrd
for TraitInfo
{
384 fn partial_cmp(&self, other
: &TraitInfo
) -> Option
<Ordering
> { Some(self.cmp(other)) }
386 impl Ord
for TraitInfo
{
387 fn cmp(&self, other
: &TraitInfo
) -> Ordering
{
388 // local crates are more important than remote ones (local:
389 // cnum == 0), and otherwise we throw in the defid for totality
391 let lhs
= (other
.def_id
.krate
, other
.def_id
);
392 let rhs
= (self.def_id
.krate
, self.def_id
);
397 /// Retrieve all traits in this crate and any dependent crates.
398 pub fn all_traits
<'a
>(ccx
: &'a CrateCtxt
) -> AllTraits
<'a
> {
399 if ccx
.all_traits
.borrow().is_none() {
400 use rustc_front
::intravisit
;
402 let mut traits
= vec
![];
407 struct Visitor
<'a
, 'tcx
:'a
> {
408 map
: &'a hir_map
::Map
<'tcx
>,
409 traits
: &'a
mut AllTraitsVec
,
411 impl<'v
, 'a
, 'tcx
> intravisit
::Visitor
<'v
> for Visitor
<'a
, 'tcx
> {
412 fn visit_item(&mut self, i
: &'v hir
::Item
) {
414 hir
::ItemTrait(..) => {
415 let def_id
= self.map
.local_def_id(i
.id
);
416 self.traits
.push(TraitInfo
::new(def_id
));
422 ccx
.tcx
.map
.krate().visit_all_items(&mut Visitor
{
428 let mut external_mods
= FnvHashSet();
429 fn handle_external_def(traits
: &mut AllTraitsVec
,
430 external_mods
: &mut FnvHashSet
<DefId
>,
432 cstore
: &for<'a
> cstore
::CrateStore
<'a
>,
433 dl
: cstore
::DefLike
) {
435 cstore
::DlDef(def
::DefTrait(did
)) => {
436 traits
.push(TraitInfo
::new(did
));
438 cstore
::DlDef(def
::DefMod(did
)) => {
439 if !external_mods
.insert(did
) {
442 for child
in cstore
.item_children(did
) {
443 handle_external_def(traits
, external_mods
,
444 ccx
, cstore
, child
.def
)
450 let cstore
= &*ccx
.tcx
.sess
.cstore
;
452 for cnum
in ccx
.tcx
.sess
.cstore
.crates() {
453 for child
in cstore
.crate_top_level_items(cnum
) {
454 handle_external_def(&mut traits
, &mut external_mods
,
455 ccx
, cstore
, child
.def
)
459 *ccx
.all_traits
.borrow_mut() = Some(traits
);
462 let borrow
= ccx
.all_traits
.borrow();
463 assert
!(borrow
.is_some());
470 pub struct AllTraits
<'a
> {
471 borrow
: cell
::Ref
<'a
, Option
<AllTraitsVec
>>,
475 impl<'a
> Iterator
for AllTraits
<'a
> {
476 type Item
= TraitInfo
;
478 fn next(&mut self) -> Option
<TraitInfo
> {
479 let AllTraits { ref borrow, ref mut idx }
= *self;
481 borrow
.as_ref().unwrap().get(*idx
).map(|info
| {