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