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, AsPredicate}
;
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 ty
::type_is_error(rcvr_ty
) {
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
= ty
::lookup_struct_fields(cx
, 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
= ty
::lookup_field_type(cx
, 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
.as_predicate());
105 let mut selcx
= SelectionContext
::new(infcx
, fcx
);
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 ty
::item_path_str(fcx
.tcx(), 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 ty
::impl_trait_ref(fcx
.tcx(), impl_did
) {
193 Some(trait_ref
) => format
!(" of the trait `{}`",
194 ty
::item_path_str(fcx
.tcx(),
198 span_note
!(fcx
.sess(), item_span
,
199 "candidate #{} is defined in an impl{} for the type `{}`",
204 CandidateSource
::TraitSource(trait_did
) => {
205 let (_
, item
) = trait_item(fcx
.tcx(), trait_did
, item_name
).unwrap();
206 let item_span
= fcx
.tcx().map
.def_id_span(item
.def_id(), span
);
207 span_note
!(fcx
.sess(), item_span
,
208 "candidate #{} is defined in the trait `{}`",
210 ty
::item_path_str(fcx
.tcx(), trait_did
));
218 pub type AllTraitsVec
= Vec
<TraitInfo
>;
220 fn suggest_traits_to_import
<'a
, 'tcx
>(fcx
: &FnCtxt
<'a
, 'tcx
>,
223 item_name
: ast
::Name
,
224 rcvr_expr
: Option
<&ast
::Expr
>,
225 valid_out_of_scope_traits
: Vec
<ast
::DefId
>)
229 if !valid_out_of_scope_traits
.is_empty() {
230 let mut candidates
= valid_out_of_scope_traits
;
234 "items from traits can only be used if the trait is in scope; \
235 the following {traits_are} implemented but not in scope, \
236 perhaps add a `use` for {one_of_them}:",
237 traits_are
= if candidates
.len() == 1 {"trait is"}
else {"traits are"}
,
238 one_of_them
= if candidates
.len() == 1 {"it"}
else {"one of them"}
);
240 fcx
.sess().fileline_help(span
, &msg
[..]);
242 for (i
, trait_did
) in candidates
.iter().enumerate() {
243 fcx
.sess().fileline_help(span
,
244 &*format
!("candidate #{}: use `{}`",
246 ty
::item_path_str(fcx
.tcx(), *trait_did
)))
252 let type_is_local
= type_derefs_to_local(fcx
, span
, rcvr_ty
, rcvr_expr
);
254 // there's no implemented traits, so lets suggest some traits to
255 // implement, by finding ones that have the item name, and are
256 // legal to implement.
257 let mut candidates
= all_traits(fcx
.ccx
)
259 // we approximate the coherence rules to only suggest
260 // traits that are legal to implement by requiring that
261 // either the type or trait is local. Multidispatch means
262 // this isn't perfect (that is, there are cases when
263 // implementing a trait would be legal but is rejected
265 (type_is_local
|| ast_util
::is_local(info
.def_id
))
266 && trait_item(tcx
, info
.def_id
, item_name
).is_some()
268 .collect
::<Vec
<_
>>();
270 if !candidates
.is_empty() {
271 // sort from most relevant to least relevant
272 candidates
.sort_by(|a
, b
| a
.cmp(b
).reverse());
275 // FIXME #21673 this help message could be tuned to the case
276 // of a type parameter: suggest adding a trait bound rather
277 // than implementing.
279 "items from traits can only be used if the trait is implemented and in scope; \
280 the following {traits_define} an item `{name}`, \
281 perhaps you need to implement {one_of_them}:",
282 traits_define
= if candidates
.len() == 1 {"trait defines"}
else {"traits define"}
,
283 one_of_them
= if candidates
.len() == 1 {"it"}
else {"one of them"}
,
286 fcx
.sess().fileline_help(span
, &msg
[..]);
288 for (i
, trait_info
) in candidates
.iter().enumerate() {
289 fcx
.sess().fileline_help(span
,
290 &*format
!("candidate #{}: `{}`",
292 ty
::item_path_str(fcx
.tcx(), trait_info
.def_id
)))
297 /// Checks whether there is a local type somewhere in the chain of
298 /// autoderefs of `rcvr_ty`.
299 fn type_derefs_to_local
<'a
, 'tcx
>(fcx
: &FnCtxt
<'a
, 'tcx
>,
302 rcvr_expr
: Option
<&ast
::Expr
>) -> bool
{
303 fn is_local(ty
: Ty
) -> bool
{
305 ty
::TyEnum(did
, _
) | ty
::TyStruct(did
, _
) => ast_util
::is_local(did
),
307 ty
::TyTrait(ref tr
) => ast_util
::is_local(tr
.principal_def_id()),
309 ty
::TyParam(_
) => true,
311 // everything else (primitive types etc.) is effectively
312 // non-local (there are "edge" cases, e.g. (LocalType,), but
313 // the noise from these sort of types is usually just really
314 // annoying, rather than any sort of help).
319 // This occurs for UFCS desugaring of `T::method`, where there is no
320 // receiver expression for the method call, and thus no autoderef.
321 if rcvr_expr
.is_none() {
322 return is_local(fcx
.resolve_type_vars_if_possible(rcvr_ty
));
325 check
::autoderef(fcx
, span
, rcvr_ty
, None
,
326 check
::UnresolvedTypeAction
::Ignore
, check
::NoPreference
,
336 #[derive(Copy, Clone)]
337 pub struct TraitInfo
{
338 pub def_id
: ast
::DefId
,
342 fn new(def_id
: ast
::DefId
) -> TraitInfo
{
348 impl PartialEq
for TraitInfo
{
349 fn eq(&self, other
: &TraitInfo
) -> bool
{
350 self.cmp(other
) == Ordering
::Equal
353 impl Eq
for TraitInfo {}
354 impl PartialOrd
for TraitInfo
{
355 fn partial_cmp(&self, other
: &TraitInfo
) -> Option
<Ordering
> { Some(self.cmp(other)) }
357 impl Ord
for TraitInfo
{
358 fn cmp(&self, other
: &TraitInfo
) -> Ordering
{
359 // accessible traits are more important/relevant than
360 // inaccessible ones, local crates are more important than
361 // remote ones (local: cnum == 0), and NodeIds just for
364 let lhs
= (other
.def_id
.krate
, other
.def_id
.node
);
365 let rhs
= (self.def_id
.krate
, self.def_id
.node
);
370 /// Retrieve all traits in this crate and any dependent crates.
371 pub fn all_traits
<'a
>(ccx
: &'a CrateCtxt
) -> AllTraits
<'a
> {
372 if ccx
.all_traits
.borrow().is_none() {
375 let mut traits
= vec
![];
381 traits
: &'a
mut AllTraitsVec
,
383 impl<'v
, 'a
> visit
::Visitor
<'v
> for Visitor
<'a
> {
384 fn visit_item(&mut self, i
: &'v ast
::Item
) {
386 ast
::ItemTrait(..) => {
387 self.traits
.push(TraitInfo
::new(ast_util
::local_def(i
.id
)));
391 visit
::walk_item(self, i
)
394 visit
::walk_crate(&mut Visitor
{
396 }, ccx
.tcx
.map
.krate());
399 fn handle_external_def(traits
: &mut AllTraitsVec
,
401 cstore
: &cstore
::CStore
,
402 dl
: decoder
::DefLike
) {
404 decoder
::DlDef(def
::DefTrait(did
)) => {
405 traits
.push(TraitInfo
::new(did
));
407 decoder
::DlDef(def
::DefMod(did
)) => {
408 csearch
::each_child_of_item(cstore
, did
, |dl
, _
, _
| {
409 handle_external_def(traits
, ccx
, cstore
, dl
)
415 let cstore
= &ccx
.tcx
.sess
.cstore
;
416 cstore
.iter_crate_data(|cnum
, _
| {
417 csearch
::each_top_level_item_of_crate(cstore
, cnum
, |dl
, _
, _
| {
418 handle_external_def(&mut traits
, ccx
, cstore
, dl
)
422 *ccx
.all_traits
.borrow_mut() = Some(traits
);
425 let borrow
= ccx
.all_traits
.borrow();
426 assert
!(borrow
.is_some());
433 pub struct AllTraits
<'a
> {
434 borrow
: cell
::Ref
<'a
, Option
<AllTraitsVec
>>,
438 impl<'a
> Iterator
for AllTraits
<'a
> {
439 type Item
= TraitInfo
;
441 fn next(&mut self) -> Option
<TraitInfo
> {
442 let AllTraits { ref borrow, ref mut idx }
= *self;
444 borrow
.as_ref().unwrap().get(*idx
).map(|info
| {