]> git.lizzy.rs Git - rust.git/blob - src/librustc_typeck/check/method/suggest.rs
Change some instances of .connect() to .join()
[rust.git] / src / librustc_typeck / check / method / suggest.rs
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.
4 //
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.
10
11 //! Give useful errors and suggestions to users when an item can't be
12 //! found or is otherwise invalid.
13
14 use CrateCtxt;
15
16 use astconv::AstConv;
17 use check::{self, FnCtxt};
18 use middle::ty::{self, Ty, ToPolyTraitRef, ToPredicate, HasTypeFlags};
19 use middle::def;
20 use middle::lang_items::FnOnceTraitLangItem;
21 use middle::subst::Substs;
22 use middle::traits::{Obligation, SelectionContext};
23 use metadata::{csearch, cstore, decoder};
24
25 use syntax::{ast, ast_util};
26 use syntax::codemap::Span;
27 use syntax::print::pprust;
28
29 use std::cell;
30 use std::cmp::Ordering;
31
32 use super::{MethodError, NoMatchData, CandidateSource, impl_item, trait_item};
33 use super::probe::Mode;
34
35 pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
36                               span: Span,
37                               rcvr_ty: Ty<'tcx>,
38                               item_name: ast::Name,
39                               rcvr_expr: Option<&ast::Expr>,
40                               error: MethodError<'tcx>)
41 {
42     // avoid suggestions when we don't know what's going on.
43     if rcvr_ty.references_error() {
44         return
45     }
46
47     match error {
48         MethodError::NoMatch(NoMatchData { static_candidates: static_sources,
49                                            unsatisfied_predicates,
50                                            out_of_scope_traits,
51                                            mode }) => {
52             let cx = fcx.tcx();
53
54             fcx.type_error_message(
55                 span,
56                 |actual| {
57                     format!("no {} named `{}` found for type `{}` \
58                              in the current scope",
59                             if mode == Mode::MethodCall { "method" }
60                             else { "associated item" },
61                             item_name,
62                             actual)
63                 },
64                 rcvr_ty,
65                 None);
66
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 = cx.lookup_struct_fields(did);
70
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
76                                         // snippet
77                     };
78
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));
84                     };
85
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));
89                     };
90
91                     // Determine if the field can be used as a function in some way
92                     let field_ty = cx.lookup_field_type(did, field.id, substs);
93                     if let Ok(fn_once_trait_did) = cx.lang_items.require(FnOnceTraitLangItem) {
94                         let infcx = fcx.infcx();
95                         infcx.probe(|_| {
96                             let fn_once_substs = Substs::new_trait(vec![infcx.next_ty_var()],
97                                                                    Vec::new(),
98                                                                    field_ty);
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,
103                                                               fcx.body_id,
104                                                               poly_trait_ref.to_predicate());
105                             let mut selcx = SelectionContext::new(infcx);
106
107                             if selcx.evaluate_obligation(&obligation) {
108                                 span_stored_function();
109                             } else {
110                                 span_did_you_mean();
111                             }
112                         });
113                     } else {
114                         match field_ty.sty {
115                             // fallback to matching a closure or function pointer
116                             ty::TyClosure(..) | ty::TyBareFn(..) => span_stored_function(),
117                             _ => span_did_you_mean(),
118                         }
119                     }
120                 }
121             }
122
123             if !static_sources.is_empty() {
124                 cx.sess.fileline_note(
125                     span,
126                     "found defined static methods, maybe a `self` is missing?");
127
128                 report_candidates(fcx, span, item_name, static_sources);
129             }
130
131             if !unsatisfied_predicates.is_empty() {
132                 let bound_list = unsatisfied_predicates.iter()
133                     .map(|p| format!("`{} : {}`",
134                                      p.self_ty(),
135                                      p))
136                     .collect::<Vec<_>>()
137                     .join(", ");
138                 cx.sess.fileline_note(
139                     span,
140                     &format!("the method `{}` exists but the \
141                              following trait bounds were not satisfied: {}",
142                              item_name,
143                              bound_list));
144             }
145
146             suggest_traits_to_import(fcx, span, rcvr_ty, item_name,
147                                      rcvr_expr, out_of_scope_traits)
148         }
149
150         MethodError::Ambiguity(sources) => {
151             span_err!(fcx.sess(), span, E0034,
152                       "multiple applicable items in scope");
153
154             report_candidates(fcx, span, item_name, sources);
155         }
156
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",
161                                item_name,
162                                fcx.tcx().item_path_str(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))
166             } else {
167                 msg
168             };
169             fcx.sess().span_err(span, &msg);
170         }
171     }
172
173     fn report_candidates(fcx: &FnCtxt,
174                          span: Span,
175                          item_name: ast::Name,
176                          mut sources: Vec<CandidateSource>) {
177         sources.sort();
178         sources.dedup();
179
180         for (idx, source) in sources.iter().enumerate() {
181             match *source {
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);
188
189                     let impl_ty = check::impl_self_ty(fcx, span, impl_did).ty;
190
191                     let insertion = match fcx.tcx().impl_trait_ref(impl_did) {
192                         None => format!(""),
193                         Some(trait_ref) => {
194                             format!(" of the trait `{}`",
195                                     fcx.tcx().item_path_str(trait_ref.def_id))
196                         }
197                     };
198
199                     span_note!(fcx.sess(), item_span,
200                                "candidate #{} is defined in an impl{} for the type `{}`",
201                                idx + 1,
202                                insertion,
203                                impl_ty);
204                 }
205                 CandidateSource::TraitSource(trait_did) => {
206                     let item = trait_item(fcx.tcx(), trait_did, item_name).unwrap();
207                     let item_span = fcx.tcx().map.def_id_span(item.def_id(), span);
208                     span_note!(fcx.sess(), item_span,
209                                "candidate #{} is defined in the trait `{}`",
210                                idx + 1,
211                                fcx.tcx().item_path_str(trait_did));
212                 }
213             }
214         }
215     }
216 }
217
218
219 pub type AllTraitsVec = Vec<TraitInfo>;
220
221 fn suggest_traits_to_import<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
222                                       span: Span,
223                                       rcvr_ty: Ty<'tcx>,
224                                       item_name: ast::Name,
225                                       rcvr_expr: Option<&ast::Expr>,
226                                       valid_out_of_scope_traits: Vec<ast::DefId>)
227 {
228     let tcx = fcx.tcx();
229
230     if !valid_out_of_scope_traits.is_empty() {
231         let mut candidates = valid_out_of_scope_traits;
232         candidates.sort();
233         candidates.dedup();
234         let msg = format!(
235             "items from traits can only be used if the trait is in scope; \
236              the following {traits_are} implemented but not in scope, \
237              perhaps add a `use` for {one_of_them}:",
238             traits_are = if candidates.len() == 1 {"trait is"} else {"traits are"},
239             one_of_them = if candidates.len() == 1 {"it"} else {"one of them"});
240
241         fcx.sess().fileline_help(span, &msg[..]);
242
243         for (i, trait_did) in candidates.iter().enumerate() {
244             fcx.sess().fileline_help(span,
245                                      &*format!("candidate #{}: use `{}`",
246                                                i + 1,
247                                                fcx.tcx().item_path_str(*trait_did)))
248
249         }
250         return
251     }
252
253     let type_is_local = type_derefs_to_local(fcx, span, rcvr_ty, rcvr_expr);
254
255     // there's no implemented traits, so lets suggest some traits to
256     // implement, by finding ones that have the item name, and are
257     // legal to implement.
258     let mut candidates = all_traits(fcx.ccx)
259         .filter(|info| {
260             // we approximate the coherence rules to only suggest
261             // traits that are legal to implement by requiring that
262             // either the type or trait is local. Multidispatch means
263             // this isn't perfect (that is, there are cases when
264             // implementing a trait would be legal but is rejected
265             // here).
266             (type_is_local || ast_util::is_local(info.def_id))
267                 && trait_item(tcx, info.def_id, item_name).is_some()
268         })
269         .collect::<Vec<_>>();
270
271     if !candidates.is_empty() {
272         // sort from most relevant to least relevant
273         candidates.sort_by(|a, b| a.cmp(b).reverse());
274         candidates.dedup();
275
276         // FIXME #21673 this help message could be tuned to the case
277         // of a type parameter: suggest adding a trait bound rather
278         // than implementing.
279         let msg = format!(
280             "items from traits can only be used if the trait is implemented and in scope; \
281              the following {traits_define} an item `{name}`, \
282              perhaps you need to implement {one_of_them}:",
283             traits_define = if candidates.len() == 1 {"trait defines"} else {"traits define"},
284             one_of_them = if candidates.len() == 1 {"it"} else {"one of them"},
285             name = item_name);
286
287         fcx.sess().fileline_help(span, &msg[..]);
288
289         for (i, trait_info) in candidates.iter().enumerate() {
290             fcx.sess().fileline_help(span,
291                                      &*format!("candidate #{}: `{}`",
292                                                i + 1,
293                                                fcx.tcx().item_path_str(trait_info.def_id)))
294         }
295     }
296 }
297
298 /// Checks whether there is a local type somewhere in the chain of
299 /// autoderefs of `rcvr_ty`.
300 fn type_derefs_to_local<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
301                                   span: Span,
302                                   rcvr_ty: Ty<'tcx>,
303                                   rcvr_expr: Option<&ast::Expr>) -> bool {
304     fn is_local(ty: Ty) -> bool {
305         match ty.sty {
306             ty::TyEnum(did, _) | ty::TyStruct(did, _) => ast_util::is_local(did),
307
308             ty::TyTrait(ref tr) => ast_util::is_local(tr.principal_def_id()),
309
310             ty::TyParam(_) => true,
311
312             // everything else (primitive types etc.) is effectively
313             // non-local (there are "edge" cases, e.g. (LocalType,), but
314             // the noise from these sort of types is usually just really
315             // annoying, rather than any sort of help).
316             _ => false
317         }
318     }
319
320     // This occurs for UFCS desugaring of `T::method`, where there is no
321     // receiver expression for the method call, and thus no autoderef.
322     if rcvr_expr.is_none() {
323         return is_local(fcx.resolve_type_vars_if_possible(rcvr_ty));
324     }
325
326     check::autoderef(fcx, span, rcvr_ty, None,
327                      check::UnresolvedTypeAction::Ignore, check::NoPreference,
328                      |ty, _| {
329         if is_local(ty) {
330             Some(())
331         } else {
332             None
333         }
334     }).2.is_some()
335 }
336
337 #[derive(Copy, Clone)]
338 pub struct TraitInfo {
339     pub def_id: ast::DefId,
340 }
341
342 impl TraitInfo {
343     fn new(def_id: ast::DefId) -> TraitInfo {
344         TraitInfo {
345             def_id: def_id,
346         }
347     }
348 }
349 impl PartialEq for TraitInfo {
350     fn eq(&self, other: &TraitInfo) -> bool {
351         self.cmp(other) == Ordering::Equal
352     }
353 }
354 impl Eq for TraitInfo {}
355 impl PartialOrd for TraitInfo {
356     fn partial_cmp(&self, other: &TraitInfo) -> Option<Ordering> { Some(self.cmp(other)) }
357 }
358 impl Ord for TraitInfo {
359     fn cmp(&self, other: &TraitInfo) -> Ordering {
360         // accessible traits are more important/relevant than
361         // inaccessible ones, local crates are more important than
362         // remote ones (local: cnum == 0), and NodeIds just for
363         // totality.
364
365         let lhs = (other.def_id.krate, other.def_id.node);
366         let rhs = (self.def_id.krate, self.def_id.node);
367         lhs.cmp(&rhs)
368     }
369 }
370
371 /// Retrieve all traits in this crate and any dependent crates.
372 pub fn all_traits<'a>(ccx: &'a CrateCtxt) -> AllTraits<'a> {
373     if ccx.all_traits.borrow().is_none() {
374         use syntax::visit;
375
376         let mut traits = vec![];
377
378         // Crate-local:
379         //
380         // meh.
381         struct Visitor<'a> {
382             traits: &'a mut AllTraitsVec,
383         }
384         impl<'v, 'a> visit::Visitor<'v> for Visitor<'a> {
385             fn visit_item(&mut self, i: &'v ast::Item) {
386                 match i.node {
387                     ast::ItemTrait(..) => {
388                         self.traits.push(TraitInfo::new(ast_util::local_def(i.id)));
389                     }
390                     _ => {}
391                 }
392                 visit::walk_item(self, i)
393             }
394         }
395         visit::walk_crate(&mut Visitor {
396             traits: &mut traits
397         }, ccx.tcx.map.krate());
398
399         // Cross-crate:
400         fn handle_external_def(traits: &mut AllTraitsVec,
401                                ccx: &CrateCtxt,
402                                cstore: &cstore::CStore,
403                                dl: decoder::DefLike) {
404             match dl {
405                 decoder::DlDef(def::DefTrait(did)) => {
406                     traits.push(TraitInfo::new(did));
407                 }
408                 decoder::DlDef(def::DefMod(did)) => {
409                     csearch::each_child_of_item(cstore, did, |dl, _, _| {
410                         handle_external_def(traits, ccx, cstore, dl)
411                     })
412                 }
413                 _ => {}
414             }
415         }
416         let cstore = &ccx.tcx.sess.cstore;
417         cstore.iter_crate_data(|cnum, _| {
418             csearch::each_top_level_item_of_crate(cstore, cnum, |dl, _, _| {
419                 handle_external_def(&mut traits, ccx, cstore, dl)
420             })
421         });
422
423         *ccx.all_traits.borrow_mut() = Some(traits);
424     }
425
426     let borrow = ccx.all_traits.borrow();
427     assert!(borrow.is_some());
428     AllTraits {
429         borrow: borrow,
430         idx: 0
431     }
432 }
433
434 pub struct AllTraits<'a> {
435     borrow: cell::Ref<'a, Option<AllTraitsVec>>,
436     idx: usize
437 }
438
439 impl<'a> Iterator for AllTraits<'a> {
440     type Item = TraitInfo;
441
442     fn next(&mut self) -> Option<TraitInfo> {
443         let AllTraits { ref borrow, ref mut idx } = *self;
444         // ugh.
445         borrow.as_ref().unwrap().get(*idx).map(|info| {
446             *idx += 1;
447             *info
448         })
449     }
450 }