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