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 rustc
::hir
::map
as hir_map
;
19 use rustc
::ty
::{self, Ty, ToPolyTraitRef, ToPredicate, TypeFoldable}
;
20 use middle
::cstore
::{self, CrateStore}
;
22 use hir
::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
::hir
::print
as pprust
;
35 use rustc
::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
) {
92 pub fn report_error
<'a
, 'tcx
>(fcx
: &FnCtxt
<'a
, 'tcx
>,
96 rcvr_expr
: Option
<&hir
::Expr
>,
97 error
: MethodError
<'tcx
>)
99 // avoid suggestions when we don't know what's going on.
100 if rcvr_ty
.references_error() {
105 MethodError
::NoMatch(NoMatchData
{ static_candidates
: static_sources
,
106 unsatisfied_predicates
,
111 let mut err
= fcx
.type_error_struct(
114 format
!("no {} named `{}` found for type `{}` \
115 in the current scope",
116 if mode
== Mode
::MethodCall { "method" }
117 else { "associated item" }
,
124 // If the item has the name of a field, give a help note
125 if let (&ty
::TyStruct(def
, substs
), Some(expr
)) = (&rcvr_ty
.sty
, rcvr_expr
) {
126 if let Some(field
) = def
.struct_variant().find_field_named(item_name
) {
127 let expr_string
= match cx
.sess
.codemap().span_to_snippet(expr
.span
) {
128 Ok(expr_string
) => expr_string
,
129 _
=> "s".into() // Default to a generic placeholder for the
130 // expression when we can't generate a string
134 let field_ty
= field
.ty(cx
, substs
);
136 if is_fn_ty(&field_ty
, &fcx
, span
) {
138 &format
!("use `({0}.{1})(...)` if you meant to call \
139 the function stored in the `{1}` field",
140 expr_string
, item_name
));
142 err
.span_note(span
, &format
!("did you mean to write `{0}.{1}`?",
143 expr_string
, item_name
));
148 if is_fn_ty(&rcvr_ty
, &fcx
, span
) {
149 macro_rules
! report_function
{
150 ($span
:expr
, $name
:expr
) => {
153 &format
!("{} is a function, perhaps you wish to call it",
158 if let Some(expr
) = rcvr_expr
{
159 if let Ok (expr_string
) = cx
.sess
.codemap().span_to_snippet(expr
.span
) {
160 report_function
!(expr
.span
, expr_string
);
161 err
.span_suggestion(expr
.span
,
162 "try calling the base function:",
166 else if let Expr_
::ExprPath(_
, path
) = expr
.node
.clone() {
167 if let Some(segment
) = path
.segments
.last() {
168 report_function
!(expr
.span
, segment
.identifier
.name
);
174 if !static_sources
.is_empty() {
177 "found the following associated functions; to be used as \
178 methods, functions must have a `self` parameter");
180 report_candidates(fcx
, &mut err
, span
, item_name
, static_sources
);
183 if !unsatisfied_predicates
.is_empty() {
184 let bound_list
= unsatisfied_predicates
.iter()
185 .map(|p
| format
!("`{} : {}`",
192 &format
!("the method `{}` exists but the \
193 following trait bounds were not satisfied: {}",
198 suggest_traits_to_import(fcx
, &mut err
, span
, rcvr_ty
, item_name
,
199 rcvr_expr
, out_of_scope_traits
);
203 MethodError
::Ambiguity(sources
) => {
204 let mut err
= struct_span_err
!(fcx
.sess(), span
, E0034
,
205 "multiple applicable items in scope");
207 report_candidates(fcx
, &mut err
, span
, item_name
, sources
);
211 MethodError
::ClosureAmbiguity(trait_def_id
) => {
212 let msg
= format
!("the `{}` method from the `{}` trait cannot be explicitly \
213 invoked on this closure as we have not yet inferred what \
214 kind of closure it is",
216 fcx
.tcx().item_path_str(trait_def_id
));
217 let msg
= if let Some(callee
) = rcvr_expr
{
218 format
!("{}; use overloaded call notation instead (e.g., `{}()`)",
219 msg
, pprust
::expr_to_string(callee
))
223 fcx
.sess().span_err(span
, &msg
);
226 MethodError
::PrivateMatch(def
) => {
227 let msg
= format
!("{} `{}` is private", def
.kind_name(), item_name
);
228 fcx
.tcx().sess
.span_err(span
, &msg
);
232 fn report_candidates(fcx
: &FnCtxt
,
233 err
: &mut DiagnosticBuilder
,
235 item_name
: ast
::Name
,
236 mut sources
: Vec
<CandidateSource
>) {
240 for (idx
, source
) in sources
.iter().enumerate() {
242 CandidateSource
::ImplSource(impl_did
) => {
243 // Provide the best span we can. Use the item, if local to crate, else
244 // the impl, if local to crate (item may be defaulted), else the call site.
245 let item
= impl_item(fcx
.tcx(), impl_did
, item_name
)
249 fcx
.tcx().impl_trait_ref(impl_did
).unwrap().def_id
,
253 let impl_span
= fcx
.tcx().map
.def_id_span(impl_did
, span
);
254 let item_span
= fcx
.tcx().map
.def_id_span(item
.def_id(), impl_span
);
256 let impl_ty
= check
::impl_self_ty(fcx
, span
, impl_did
).ty
;
258 let insertion
= match fcx
.tcx().impl_trait_ref(impl_did
) {
261 format
!(" of the trait `{}`",
262 fcx
.tcx().item_path_str(trait_ref
.def_id
))
266 span_note
!(err
, item_span
,
267 "candidate #{} is defined in an impl{} for the type `{}`",
272 CandidateSource
::TraitSource(trait_did
) => {
273 let item
= trait_item(fcx
.tcx(), trait_did
, item_name
).unwrap();
274 let item_span
= fcx
.tcx().map
.def_id_span(item
.def_id(), span
);
275 span_note
!(err
, item_span
,
276 "candidate #{} is defined in the trait `{}`",
278 fcx
.tcx().item_path_str(trait_did
));
286 pub type AllTraitsVec
= Vec
<TraitInfo
>;
288 fn suggest_traits_to_import
<'a
, 'tcx
>(fcx
: &FnCtxt
<'a
, 'tcx
>,
289 err
: &mut DiagnosticBuilder
,
292 item_name
: ast
::Name
,
293 rcvr_expr
: Option
<&hir
::Expr
>,
294 valid_out_of_scope_traits
: Vec
<DefId
>)
298 if !valid_out_of_scope_traits
.is_empty() {
299 let mut candidates
= valid_out_of_scope_traits
;
303 "items from traits can only be used if the trait is in scope; \
304 the following {traits_are} implemented but not in scope, \
305 perhaps add a `use` for {one_of_them}:",
306 traits_are
= if candidates
.len() == 1 {"trait is"}
else {"traits are"}
,
307 one_of_them
= if candidates
.len() == 1 {"it"}
else {"one of them"}
);
309 err
.fileline_help(span
, &msg
[..]);
311 for (i
, trait_did
) in candidates
.iter().enumerate() {
312 err
.fileline_help(span
,
313 &format
!("candidate #{}: `use {}`",
315 fcx
.tcx().item_path_str(*trait_did
)));
320 let type_is_local
= type_derefs_to_local(fcx
, span
, rcvr_ty
, rcvr_expr
);
322 // there's no implemented traits, so lets suggest some traits to
323 // implement, by finding ones that have the item name, and are
324 // legal to implement.
325 let mut candidates
= all_traits(fcx
.ccx
)
327 // we approximate the coherence rules to only suggest
328 // traits that are legal to implement by requiring that
329 // either the type or trait is local. Multidispatch means
330 // this isn't perfect (that is, there are cases when
331 // implementing a trait would be legal but is rejected
333 (type_is_local
|| info
.def_id
.is_local())
334 && trait_item(tcx
, info
.def_id
, item_name
).is_some()
336 .collect
::<Vec
<_
>>();
338 if !candidates
.is_empty() {
339 // sort from most relevant to least relevant
340 candidates
.sort_by(|a
, b
| a
.cmp(b
).reverse());
343 // FIXME #21673 this help message could be tuned to the case
344 // of a type parameter: suggest adding a trait bound rather
345 // than implementing.
347 "items from traits can only be used if the trait is implemented and in scope; \
348 the following {traits_define} an item `{name}`, \
349 perhaps you need to implement {one_of_them}:",
350 traits_define
= if candidates
.len() == 1 {"trait defines"}
else {"traits define"}
,
351 one_of_them
= if candidates
.len() == 1 {"it"}
else {"one of them"}
,
354 err
.fileline_help(span
, &msg
[..]);
356 for (i
, trait_info
) in candidates
.iter().enumerate() {
357 err
.fileline_help(span
,
358 &format
!("candidate #{}: `{}`",
360 fcx
.tcx().item_path_str(trait_info
.def_id
)));
365 /// Checks whether there is a local type somewhere in the chain of
366 /// autoderefs of `rcvr_ty`.
367 fn type_derefs_to_local
<'a
, 'tcx
>(fcx
: &FnCtxt
<'a
, 'tcx
>,
370 rcvr_expr
: Option
<&hir
::Expr
>) -> bool
{
371 fn is_local(ty
: Ty
) -> bool
{
373 ty
::TyEnum(def
, _
) | ty
::TyStruct(def
, _
) => def
.did
.is_local(),
375 ty
::TyTrait(ref tr
) => tr
.principal_def_id().is_local(),
377 ty
::TyParam(_
) => true,
379 // everything else (primitive types etc.) is effectively
380 // non-local (there are "edge" cases, e.g. (LocalType,), but
381 // the noise from these sort of types is usually just really
382 // annoying, rather than any sort of help).
387 // This occurs for UFCS desugaring of `T::method`, where there is no
388 // receiver expression for the method call, and thus no autoderef.
389 if rcvr_expr
.is_none() {
390 return is_local(fcx
.resolve_type_vars_if_possible(rcvr_ty
));
393 check
::autoderef(fcx
, span
, rcvr_ty
, || None
,
394 check
::UnresolvedTypeAction
::Ignore
, ty
::NoPreference
,
404 #[derive(Copy, Clone)]
405 pub struct TraitInfo
{
410 fn new(def_id
: DefId
) -> TraitInfo
{
416 impl PartialEq
for TraitInfo
{
417 fn eq(&self, other
: &TraitInfo
) -> bool
{
418 self.cmp(other
) == Ordering
::Equal
421 impl Eq
for TraitInfo {}
422 impl PartialOrd
for TraitInfo
{
423 fn partial_cmp(&self, other
: &TraitInfo
) -> Option
<Ordering
> { Some(self.cmp(other)) }
425 impl Ord
for TraitInfo
{
426 fn cmp(&self, other
: &TraitInfo
) -> Ordering
{
427 // local crates are more important than remote ones (local:
428 // cnum == 0), and otherwise we throw in the defid for totality
430 let lhs
= (other
.def_id
.krate
, other
.def_id
);
431 let rhs
= (self.def_id
.krate
, self.def_id
);
436 /// Retrieve all traits in this crate and any dependent crates.
437 pub fn all_traits
<'a
>(ccx
: &'a CrateCtxt
) -> AllTraits
<'a
> {
438 if ccx
.all_traits
.borrow().is_none() {
439 use rustc
::hir
::intravisit
;
441 let mut traits
= vec
![];
446 struct Visitor
<'a
, 'tcx
:'a
> {
447 map
: &'a hir_map
::Map
<'tcx
>,
448 traits
: &'a
mut AllTraitsVec
,
450 impl<'v
, 'a
, 'tcx
> intravisit
::Visitor
<'v
> for Visitor
<'a
, 'tcx
> {
451 fn visit_item(&mut self, i
: &'v hir
::Item
) {
453 hir
::ItemTrait(..) => {
454 let def_id
= self.map
.local_def_id(i
.id
);
455 self.traits
.push(TraitInfo
::new(def_id
));
461 ccx
.tcx
.map
.krate().visit_all_items(&mut Visitor
{
467 let mut external_mods
= FnvHashSet();
468 fn handle_external_def(traits
: &mut AllTraitsVec
,
469 external_mods
: &mut FnvHashSet
<DefId
>,
471 cstore
: &for<'a
> cstore
::CrateStore
<'a
>,
472 dl
: cstore
::DefLike
) {
474 cstore
::DlDef(Def
::Trait(did
)) => {
475 traits
.push(TraitInfo
::new(did
));
477 cstore
::DlDef(Def
::Mod(did
)) => {
478 if !external_mods
.insert(did
) {
481 for child
in cstore
.item_children(did
) {
482 handle_external_def(traits
, external_mods
,
483 ccx
, cstore
, child
.def
)
489 let cstore
= &*ccx
.tcx
.sess
.cstore
;
491 for cnum
in ccx
.tcx
.sess
.cstore
.crates() {
492 for child
in cstore
.crate_top_level_items(cnum
) {
493 handle_external_def(&mut traits
, &mut external_mods
,
494 ccx
, cstore
, child
.def
)
498 *ccx
.all_traits
.borrow_mut() = Some(traits
);
501 let borrow
= ccx
.all_traits
.borrow();
502 assert
!(borrow
.is_some());
509 pub struct AllTraits
<'a
> {
510 borrow
: cell
::Ref
<'a
, Option
<AllTraitsVec
>>,
514 impl<'a
> Iterator
for AllTraits
<'a
> {
515 type Item
= TraitInfo
;
517 fn next(&mut self) -> Option
<TraitInfo
> {
518 let AllTraits { ref borrow, ref mut idx }
= *self;
520 borrow
.as_ref().unwrap().get(*idx
).map(|info
| {