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.
15 use rustc
::hir
::map
as hir_map
;
16 use rustc
::ty
::{self, Ty, TyCtxt, ToPolyTraitRef, ToPredicate, TypeFoldable}
;
18 use hir
::def_id
::{CRATE_DEF_INDEX, DefId}
;
19 use middle
::lang_items
::FnOnceTraitLangItem
;
20 use rustc
::traits
::{Obligation, SelectionContext}
;
21 use util
::nodemap
::FxHashSet
;
24 use errors
::DiagnosticBuilder
;
28 use rustc
::hir
::print
;
29 use rustc
::infer
::type_variable
::TypeVariableOrigin
;
32 use std
::cmp
::Ordering
;
34 use super::{MethodError, NoMatchData, CandidateSource}
;
35 use super::probe
::Mode
;
37 impl<'a
, 'gcx
, 'tcx
> FnCtxt
<'a
, 'gcx
, 'tcx
> {
38 fn is_fn_ty(&self, ty
: &Ty
<'tcx
>, span
: Span
) -> bool
{
41 // Not all of these (e.g. unsafe fns) implement FnOnce
42 // so we look for these beforehand
45 ty
::TyFnPtr(_
) => true,
46 // If it's not a simple function, look for things which implement FnOnce
48 let fn_once
= match tcx
.lang_items
.require(FnOnceTraitLangItem
) {
49 Ok(fn_once
) => fn_once
,
50 Err(..) => return false,
53 self.autoderef(span
, ty
).any(|(ty
, _
)| {
55 let fn_once_substs
= tcx
.mk_substs_trait(ty
,
56 &[self.next_ty_var(TypeVariableOrigin
::MiscVariable(span
))]);
57 let trait_ref
= ty
::TraitRef
::new(fn_once
, fn_once_substs
);
58 let poly_trait_ref
= trait_ref
.to_poly_trait_ref();
60 Obligation
::misc(span
,
63 poly_trait_ref
.to_predicate());
64 SelectionContext
::new(self).evaluate_obligation(&obligation
)
71 pub fn report_method_error(&self,
75 rcvr_expr
: Option
<&hir
::Expr
>,
76 error
: MethodError
<'tcx
>,
77 args
: Option
<&'gcx
[hir
::Expr
]>) {
78 // avoid suggestions when we don't know what's going on.
79 if rcvr_ty
.references_error() {
83 let report_candidates
= |err
: &mut DiagnosticBuilder
, mut sources
: Vec
<CandidateSource
>| {
87 // Dynamic limit to avoid hiding just one candidate, which is silly.
88 let limit
= if sources
.len() == 5 { 5 }
else { 4 }
;
90 for (idx
, source
) in sources
.iter().take(limit
).enumerate() {
92 CandidateSource
::ImplSource(impl_did
) => {
93 // Provide the best span we can. Use the item, if local to crate, else
94 // the impl, if local to crate (item may be defaulted), else nothing.
95 let item
= self.associated_item(impl_did
, item_name
)
98 self.tcx
.impl_trait_ref(impl_did
).unwrap().def_id
,
103 let note_span
= self.tcx
.hir
.span_if_local(item
.def_id
).or_else(|| {
104 self.tcx
.hir
.span_if_local(impl_did
)
107 let impl_ty
= self.impl_self_ty(span
, impl_did
).ty
;
109 let insertion
= match self.tcx
.impl_trait_ref(impl_did
) {
112 format
!(" of the trait `{}`",
113 self.tcx
.item_path_str(trait_ref
.def_id
))
117 let note_str
= format
!("candidate #{} is defined in an impl{} \
122 if let Some(note_span
) = note_span
{
123 // We have a span pointing to the method. Show note with snippet.
124 err
.span_note(note_span
, ¬e_str
);
129 CandidateSource
::TraitSource(trait_did
) => {
130 let item
= self.associated_item(trait_did
, item_name
).unwrap();
131 let item_span
= self.tcx
.def_span(item
.def_id
);
134 "candidate #{} is defined in the trait `{}`",
136 self.tcx
.item_path_str(trait_did
));
137 err
.help(&format
!("to disambiguate the method call, write `{}::{}({}{})` \
139 self.tcx
.item_path_str(trait_did
),
141 if rcvr_ty
.is_region_ptr() && args
.is_some() {
142 if rcvr_ty
.is_mutable_pointer() {
150 args
.map(|arg
| arg
.iter()
151 .map(|arg
| print
::to_string(print
::NO_ANN
,
152 |s
| s
.print_expr(arg
)))
154 .join(", ")).unwrap_or("...".to_owned())));
158 if sources
.len() > limit
{
159 err
.note(&format
!("and {} others", sources
.len() - limit
));
164 MethodError
::NoMatch(NoMatchData
{ static_candidates
: static_sources
,
165 unsatisfied_predicates
,
171 let actual
= self.resolve_type_vars_if_possible(&rcvr_ty
);
172 let mut err
= if !actual
.references_error() {
173 struct_span_err
!(tcx
.sess
, span
, E0599
,
174 "no {} named `{}` found for type `{}` in the \
176 if mode
== Mode
::MethodCall
{
179 match item_name
.as_str().chars().next() {
181 if name
.is_lowercase() {
182 "function or associated item"
193 self.ty_to_string(actual
))
195 self.tcx
.sess
.diagnostic().struct_dummy()
198 // If the method name is the name of a field with a function or closure type,
199 // give a helping note that it has to be called as (x.f)(...).
200 if let Some(expr
) = rcvr_expr
{
201 for (ty
, _
) in self.autoderef(span
, rcvr_ty
) {
203 ty
::TyAdt(def
, substs
) if !def
.is_enum() => {
204 if let Some(field
) = def
.struct_variant()
205 .find_field_named(item_name
) {
206 let snippet
= tcx
.sess
.codemap().span_to_snippet(expr
.span
);
207 let expr_string
= match snippet
{
208 Ok(expr_string
) => expr_string
,
209 _
=> "s".into(), // Default to a generic placeholder for the
210 // expression when we can't generate a
214 let field_ty
= field
.ty(tcx
, substs
);
215 let scope
= self.tcx
.hir
.get_module_parent(self.body_id
);
216 if field
.vis
.is_accessible_from(scope
, self.tcx
) {
217 if self.is_fn_ty(&field_ty
, span
) {
218 err
.help(&format
!("use `({0}.{1})(...)` if you \
219 meant to call the function \
220 stored in the `{1}` field",
224 err
.help(&format
!("did you mean to write `{0}.{1}` \
225 instead of `{0}.{1}(...)`?",
229 err
.span_label(span
, "field, not a method");
231 err
.span_label(span
, "private field, not a method");
241 if self.is_fn_ty(&rcvr_ty
, span
) {
242 macro_rules
! report_function
{
243 ($span
:expr
, $name
:expr
) => {
244 err
.note(&format
!("{} is a function, perhaps you wish to call it",
249 if let Some(expr
) = rcvr_expr
{
250 if let Ok(expr_string
) = tcx
.sess
.codemap().span_to_snippet(expr
.span
) {
251 report_function
!(expr
.span
, expr_string
);
252 } else if let hir
::ExprPath(hir
::QPath
::Resolved(_
, ref path
)) = expr
.node
{
253 if let Some(segment
) = path
.segments
.last() {
254 report_function
!(expr
.span
, segment
.name
);
260 if !static_sources
.is_empty() {
261 err
.note("found the following associated functions; to be used as methods, \
262 functions must have a `self` parameter");
263 err
.help(&format
!("try with `{}::{}`", self.ty_to_string(actual
), item_name
));
265 report_candidates(&mut err
, static_sources
);
268 if !unsatisfied_predicates
.is_empty() {
269 let bound_list
= unsatisfied_predicates
.iter()
270 .map(|p
| format
!("`{} : {}`", p
.self_ty(), p
))
273 err
.note(&format
!("the method `{}` exists but the following trait bounds \
274 were not satisfied:\n{}",
279 self.suggest_traits_to_import(&mut err
,
284 out_of_scope_traits
);
288 MethodError
::Ambiguity(sources
) => {
289 let mut err
= struct_span_err
!(self.sess(),
292 "multiple applicable items in scope");
293 err
.span_label(span
, format
!("multiple `{}` found", item_name
));
295 report_candidates(&mut err
, sources
);
299 MethodError
::ClosureAmbiguity(trait_def_id
) => {
300 let msg
= format
!("the `{}` method from the `{}` trait cannot be explicitly \
301 invoked on this closure as we have not yet inferred what \
302 kind of closure it is",
304 self.tcx
.item_path_str(trait_def_id
));
305 let msg
= if let Some(callee
) = rcvr_expr
{
306 format
!("{}; use overloaded call notation instead (e.g., `{}()`)",
308 self.tcx
.hir
.node_to_pretty_string(callee
.id
))
312 self.sess().span_err(span
, &msg
);
315 MethodError
::PrivateMatch(def
, out_of_scope_traits
) => {
316 let mut err
= struct_span_err
!(self.tcx
.sess
, span
, E0624
,
317 "{} `{}` is private", def
.kind_name(), item_name
);
318 self.suggest_valid_traits(&mut err
, out_of_scope_traits
);
322 MethodError
::IllegalSizedBound(candidates
) => {
323 let msg
= format
!("the `{}` method cannot be invoked on a trait object", item_name
);
324 let mut err
= self.sess().struct_span_err(span
, &msg
);
325 if !candidates
.is_empty() {
326 let help
= format
!("{an}other candidate{s} {were} found in the following \
327 trait{s}, perhaps add a `use` for {one_of_them}:",
328 an
= if candidates
.len() == 1 {"an" }
else { "" }
,
329 s
= if candidates
.len() == 1 { "" }
else { "s" }
,
330 were
= if candidates
.len() == 1 { "was" }
else { "were" }
,
331 one_of_them
= if candidates
.len() == 1 {
336 self.suggest_use_candidates(&mut err
, help
, candidates
);
343 fn suggest_use_candidates(&self,
344 err
: &mut DiagnosticBuilder
,
346 candidates
: Vec
<DefId
>) {
347 let limit
= if candidates
.len() == 5 { 5 }
else { 4 }
;
348 for (i
, trait_did
) in candidates
.iter().take(limit
).enumerate() {
349 msg
.push_str(&format
!("\ncandidate #{}: `use {};`",
351 self.tcx
.item_path_str(*trait_did
)));
353 if candidates
.len() > limit
{
354 msg
.push_str(&format
!("\nand {} others", candidates
.len() - limit
));
359 fn suggest_valid_traits(&self,
360 err
: &mut DiagnosticBuilder
,
361 valid_out_of_scope_traits
: Vec
<DefId
>) -> bool
{
362 if !valid_out_of_scope_traits
.is_empty() {
363 let mut candidates
= valid_out_of_scope_traits
;
366 err
.help("items from traits can only be used if the trait is in scope");
367 let msg
= format
!("the following {traits_are} implemented but not in scope, \
368 perhaps add a `use` for {one_of_them}:",
369 traits_are
= if candidates
.len() == 1 {
374 one_of_them
= if candidates
.len() == 1 {
380 self.suggest_use_candidates(err
, msg
, candidates
);
387 fn suggest_traits_to_import(&self,
388 err
: &mut DiagnosticBuilder
,
391 item_name
: ast
::Name
,
392 rcvr_expr
: Option
<&hir
::Expr
>,
393 valid_out_of_scope_traits
: Vec
<DefId
>) {
394 if self.suggest_valid_traits(err
, valid_out_of_scope_traits
) {
398 let type_is_local
= self.type_derefs_to_local(span
, rcvr_ty
, rcvr_expr
);
400 // there's no implemented traits, so lets suggest some traits to
401 // implement, by finding ones that have the item name, and are
402 // legal to implement.
403 let mut candidates
= all_traits(self.tcx
)
405 // we approximate the coherence rules to only suggest
406 // traits that are legal to implement by requiring that
407 // either the type or trait is local. Multidispatch means
408 // this isn't perfect (that is, there are cases when
409 // implementing a trait would be legal but is rejected
411 (type_is_local
|| info
.def_id
.is_local())
412 && self.associated_item(info
.def_id
, item_name
).is_some()
414 .collect
::<Vec
<_
>>();
416 if !candidates
.is_empty() {
417 // sort from most relevant to least relevant
418 candidates
.sort_by(|a
, b
| a
.cmp(b
).reverse());
421 // FIXME #21673 this help message could be tuned to the case
422 // of a type parameter: suggest adding a trait bound rather
423 // than implementing.
424 err
.help("items from traits can only be used if the trait is implemented and in scope");
425 let mut msg
= format
!("the following {traits_define} an item `{name}`, \
426 perhaps you need to implement {one_of_them}:",
427 traits_define
= if candidates
.len() == 1 {
432 one_of_them
= if candidates
.len() == 1 {
439 for (i
, trait_info
) in candidates
.iter().enumerate() {
440 msg
.push_str(&format
!("\ncandidate #{}: `{}`",
442 self.tcx
.item_path_str(trait_info
.def_id
)));
448 /// Checks whether there is a local type somewhere in the chain of
449 /// autoderefs of `rcvr_ty`.
450 fn type_derefs_to_local(&self,
453 rcvr_expr
: Option
<&hir
::Expr
>)
455 fn is_local(ty
: Ty
) -> bool
{
457 ty
::TyAdt(def
, _
) => def
.did
.is_local(),
459 ty
::TyDynamic(ref tr
, ..) => tr
.principal()
460 .map_or(false, |p
| p
.def_id().is_local()),
462 ty
::TyParam(_
) => true,
464 // everything else (primitive types etc.) is effectively
465 // non-local (there are "edge" cases, e.g. (LocalType,), but
466 // the noise from these sort of types is usually just really
467 // annoying, rather than any sort of help).
472 // This occurs for UFCS desugaring of `T::method`, where there is no
473 // receiver expression for the method call, and thus no autoderef.
474 if rcvr_expr
.is_none() {
475 return is_local(self.resolve_type_vars_with_obligations(rcvr_ty
));
478 self.autoderef(span
, rcvr_ty
).any(|(ty
, _
)| is_local(ty
))
482 pub type AllTraitsVec
= Vec
<DefId
>;
484 #[derive(Copy, Clone)]
485 pub struct TraitInfo
{
490 fn new(def_id
: DefId
) -> TraitInfo
{
491 TraitInfo { def_id: def_id }
494 impl PartialEq
for TraitInfo
{
495 fn eq(&self, other
: &TraitInfo
) -> bool
{
496 self.cmp(other
) == Ordering
::Equal
499 impl Eq
for TraitInfo {}
500 impl PartialOrd
for TraitInfo
{
501 fn partial_cmp(&self, other
: &TraitInfo
) -> Option
<Ordering
> {
502 Some(self.cmp(other
))
505 impl Ord
for TraitInfo
{
506 fn cmp(&self, other
: &TraitInfo
) -> Ordering
{
507 // local crates are more important than remote ones (local:
508 // cnum == 0), and otherwise we throw in the defid for totality
510 let lhs
= (other
.def_id
.krate
, other
.def_id
);
511 let rhs
= (self.def_id
.krate
, self.def_id
);
516 /// Retrieve all traits in this crate and any dependent crates.
517 pub fn all_traits
<'a
, 'gcx
, 'tcx
>(tcx
: TyCtxt
<'a
, 'gcx
, 'tcx
>) -> AllTraits
<'a
> {
518 if tcx
.all_traits
.borrow().is_none() {
519 use rustc
::hir
::itemlikevisit
;
521 let mut traits
= vec
![];
526 struct Visitor
<'a
, 'tcx
: 'a
> {
527 map
: &'a hir_map
::Map
<'tcx
>,
528 traits
: &'a
mut AllTraitsVec
,
530 impl<'v
, 'a
, 'tcx
> itemlikevisit
::ItemLikeVisitor
<'v
> for Visitor
<'a
, 'tcx
> {
531 fn visit_item(&mut self, i
: &'v hir
::Item
) {
533 hir
::ItemTrait(..) => {
534 let def_id
= self.map
.local_def_id(i
.id
);
535 self.traits
.push(def_id
);
541 fn visit_trait_item(&mut self, _trait_item
: &hir
::TraitItem
) {
544 fn visit_impl_item(&mut self, _impl_item
: &hir
::ImplItem
) {
547 tcx
.hir
.krate().visit_all_item_likes(&mut Visitor
{
553 let mut external_mods
= FxHashSet();
554 fn handle_external_def(tcx
: TyCtxt
,
555 traits
: &mut AllTraitsVec
,
556 external_mods
: &mut FxHashSet
<DefId
>,
558 let def_id
= def
.def_id();
564 if !external_mods
.insert(def_id
) {
567 for child
in tcx
.sess
.cstore
.item_children(def_id
, tcx
.sess
) {
568 handle_external_def(tcx
, traits
, external_mods
, child
.def
)
574 for cnum
in tcx
.sess
.cstore
.crates() {
577 index
: CRATE_DEF_INDEX
,
579 handle_external_def(tcx
, &mut traits
, &mut external_mods
, Def
::Mod(def_id
));
582 *tcx
.all_traits
.borrow_mut() = Some(traits
);
585 let borrow
= tcx
.all_traits
.borrow();
586 assert
!(borrow
.is_some());
593 pub struct AllTraits
<'a
> {
594 borrow
: cell
::Ref
<'a
, Option
<AllTraitsVec
>>,
598 impl<'a
> Iterator
for AllTraits
<'a
> {
599 type Item
= TraitInfo
;
601 fn next(&mut self) -> Option
<TraitInfo
> {
602 let AllTraits { ref borrow, ref mut idx }
= *self;
604 borrow
.as_ref().unwrap().get(*idx
).map(|info
| {
606 TraitInfo
::new(*info
)