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