]> git.lizzy.rs Git - rust.git/blob - src/librustc_typeck/check/method/suggest.rs
debuginfo: Make debuginfo source location assignment more stable (Pt. 1)
[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 a method 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};
19 use middle::def;
20 use metadata::{csearch, cstore, decoder};
21 use util::ppaux::UserString;
22
23 use syntax::{ast, ast_util};
24 use syntax::codemap::Span;
25
26 use std::cell;
27 use std::cmp::Ordering;
28
29 use super::{MethodError, CandidateSource, impl_method, trait_method};
30
31 pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
32                               span: Span,
33                               rcvr_ty: Ty<'tcx>,
34                               method_name: ast::Name,
35                               error: MethodError)
36 {
37     match error {
38         MethodError::NoMatch(static_sources, out_of_scope_traits) => {
39             let cx = fcx.tcx();
40             let method_ustring = method_name.user_string(cx);
41
42             // True if the type is a struct and contains a field with
43             // the same name as the not-found method
44             let is_field = match rcvr_ty.sty {
45                 ty::ty_struct(did, _) =>
46                     ty::lookup_struct_fields(cx, did)
47                         .iter()
48                         .any(|f| f.name.user_string(cx) == method_ustring),
49                 _ => false
50             };
51
52             fcx.type_error_message(
53                 span,
54                 |actual| {
55                     format!("type `{}` does not implement any \
56                              method in scope named `{}`",
57                             actual,
58                             method_ustring)
59                 },
60                 rcvr_ty,
61                 None);
62
63             // If the method has the name of a field, give a help note
64             if is_field {
65                 cx.sess.span_note(span,
66                     &format!("use `(s.{0})(...)` if you meant to call the \
67                             function stored in the `{0}` field", method_ustring)[]);
68             }
69
70             if static_sources.len() > 0 {
71                 fcx.tcx().sess.fileline_note(
72                     span,
73                     "found defined static methods, maybe a `self` is missing?");
74
75                 report_candidates(fcx, span, method_name, static_sources);
76             }
77
78             suggest_traits_to_import(fcx, span, rcvr_ty, method_name, out_of_scope_traits)
79         }
80
81         MethodError::Ambiguity(sources) => {
82             span_err!(fcx.sess(), span, E0034,
83                       "multiple applicable methods in scope");
84
85             report_candidates(fcx, span, method_name, sources);
86         }
87     }
88
89     fn report_candidates(fcx: &FnCtxt,
90                          span: Span,
91                          method_name: ast::Name,
92                          mut sources: Vec<CandidateSource>) {
93         sources.sort();
94         sources.dedup();
95
96         for (idx, source) in sources.iter().enumerate() {
97             match *source {
98                 CandidateSource::ImplSource(impl_did) => {
99                     // Provide the best span we can. Use the method, if local to crate, else
100                     // the impl, if local to crate (method may be defaulted), else the call site.
101                     let method = impl_method(fcx.tcx(), impl_did, method_name).unwrap();
102                     let impl_span = fcx.tcx().map.def_id_span(impl_did, span);
103                     let method_span = fcx.tcx().map.def_id_span(method.def_id, impl_span);
104
105                     let impl_ty = check::impl_self_ty(fcx, span, impl_did).ty;
106
107                     let insertion = match ty::impl_trait_ref(fcx.tcx(), impl_did) {
108                         None => format!(""),
109                         Some(trait_ref) => format!(" of the trait `{}`",
110                                                    ty::item_path_str(fcx.tcx(),
111                                                                      trait_ref.def_id)),
112                     };
113
114                     span_note!(fcx.sess(), method_span,
115                                "candidate #{} is defined in an impl{} for the type `{}`",
116                                idx + 1u,
117                                insertion,
118                                impl_ty.user_string(fcx.tcx()));
119                 }
120                 CandidateSource::TraitSource(trait_did) => {
121                     let (_, method) = trait_method(fcx.tcx(), trait_did, method_name).unwrap();
122                     let method_span = fcx.tcx().map.def_id_span(method.def_id, span);
123                     span_note!(fcx.sess(), method_span,
124                                "candidate #{} is defined in the trait `{}`",
125                                idx + 1u,
126                                ty::item_path_str(fcx.tcx(), trait_did));
127                 }
128             }
129         }
130     }
131 }
132
133
134 pub type AllTraitsVec = Vec<TraitInfo>;
135
136 fn suggest_traits_to_import<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
137                                       span: Span,
138                                       _rcvr_ty: Ty<'tcx>,
139                                       method_name: ast::Name,
140                                       valid_out_of_scope_traits: Vec<ast::DefId>)
141 {
142     let tcx = fcx.tcx();
143     let method_ustring = method_name.user_string(tcx);
144
145     if !valid_out_of_scope_traits.is_empty() {
146         let mut candidates = valid_out_of_scope_traits;
147         candidates.sort();
148         let msg = format!(
149             "methods from traits can only be called if the trait is in scope; \
150              the following {traits_are} implemented but not in scope, \
151              perhaps add a `use` for {one_of_them}:",
152             traits_are = if candidates.len() == 1 {"trait is"} else {"traits are"},
153             one_of_them = if candidates.len() == 1 {"it"} else {"one of them"});
154
155         fcx.sess().fileline_help(span, &msg[]);
156
157         for (i, trait_did) in candidates.iter().enumerate() {
158             fcx.sess().fileline_help(span,
159                                      &*format!("candidate #{}: use `{}`",
160                                                i + 1,
161                                                ty::item_path_str(fcx.tcx(), *trait_did)))
162
163         }
164         return
165     }
166
167     // there's no implemented traits, so lets suggest some traits to implement
168     let mut candidates = all_traits(fcx.ccx)
169         .filter(|info| trait_method(tcx, info.def_id, method_name).is_some())
170         .collect::<Vec<_>>();
171
172     if candidates.len() > 0 {
173         // sort from most relevant to least relevant
174         candidates.sort_by(|a, b| a.cmp(b).reverse());
175
176         let msg = format!(
177             "methods from traits can only be called if the trait is implemented and in scope; \
178              the following {traits_define} a method `{name}`, \
179              perhaps you need to implement {one_of_them}:",
180             traits_define = if candidates.len() == 1 {"trait defines"} else {"traits define"},
181             one_of_them = if candidates.len() == 1 {"it"} else {"one of them"},
182             name = method_ustring);
183
184         fcx.sess().fileline_help(span, &msg[]);
185
186         for (i, trait_info) in candidates.iter().enumerate() {
187             fcx.sess().fileline_help(span,
188                                      &*format!("candidate #{}: `{}`",
189                                                i + 1,
190                                                ty::item_path_str(fcx.tcx(), trait_info.def_id)))
191         }
192     }
193 }
194
195 #[derive(Copy)]
196 pub struct TraitInfo {
197     pub def_id: ast::DefId,
198 }
199
200 impl TraitInfo {
201     fn new(def_id: ast::DefId) -> TraitInfo {
202         TraitInfo {
203             def_id: def_id,
204         }
205     }
206 }
207 impl PartialEq for TraitInfo {
208     fn eq(&self, other: &TraitInfo) -> bool {
209         self.cmp(other) == Ordering::Equal
210     }
211 }
212 impl Eq for TraitInfo {}
213 impl PartialOrd for TraitInfo {
214     fn partial_cmp(&self, other: &TraitInfo) -> Option<Ordering> { Some(self.cmp(other)) }
215 }
216 impl Ord for TraitInfo {
217     fn cmp(&self, other: &TraitInfo) -> Ordering {
218         // accessible traits are more important/relevant than
219         // inaccessible ones, local crates are more important than
220         // remote ones (local: cnum == 0), and NodeIds just for
221         // totality.
222
223         let lhs = (other.def_id.krate, other.def_id.node);
224         let rhs = (self.def_id.krate, self.def_id.node);
225         lhs.cmp(&rhs)
226     }
227 }
228
229 /// Retrieve all traits in this crate and any dependent crates.
230 pub fn all_traits<'a>(ccx: &'a CrateCtxt) -> AllTraits<'a> {
231     if ccx.all_traits.borrow().is_none() {
232         use syntax::visit;
233
234         let mut traits = vec![];
235
236         // Crate-local:
237         //
238         // meh.
239         struct Visitor<'a, 'b: 'a, 'tcx: 'a + 'b> {
240             traits: &'a mut AllTraitsVec,
241         }
242         impl<'v,'a, 'b, 'tcx> visit::Visitor<'v> for Visitor<'a, 'b, 'tcx> {
243             fn visit_item(&mut self, i: &'v ast::Item) {
244                 match i.node {
245                     ast::ItemTrait(..) => {
246                         self.traits.push(TraitInfo::new(ast_util::local_def(i.id)));
247                     }
248                     _ => {}
249                 }
250                 visit::walk_item(self, i)
251             }
252         }
253         visit::walk_crate(&mut Visitor {
254             traits: &mut traits
255         }, ccx.tcx.map.krate());
256
257         // Cross-crate:
258         fn handle_external_def(traits: &mut AllTraitsVec,
259                                ccx: &CrateCtxt,
260                                cstore: &cstore::CStore,
261                                dl: decoder::DefLike) {
262             match dl {
263                 decoder::DlDef(def::DefTrait(did)) => {
264                     traits.push(TraitInfo::new(did));
265                 }
266                 decoder::DlDef(def::DefMod(did)) => {
267                     csearch::each_child_of_item(cstore, did, |dl, _, _| {
268                         handle_external_def(traits, ccx, cstore, dl)
269                     })
270                 }
271                 _ => {}
272             }
273         }
274         let cstore = &ccx.tcx.sess.cstore;
275         cstore.iter_crate_data(|&mut: cnum, _| {
276             csearch::each_top_level_item_of_crate(cstore, cnum, |dl, _, _| {
277                 handle_external_def(&mut traits, ccx, cstore, dl)
278             })
279         });
280
281         *ccx.all_traits.borrow_mut() = Some(traits);
282     }
283
284     let borrow = ccx.all_traits.borrow();
285     assert!(borrow.is_some());
286     AllTraits {
287         borrow: borrow,
288         idx: 0
289     }
290 }
291
292 pub struct AllTraits<'a> {
293     borrow: cell::Ref<'a Option<AllTraitsVec>>,
294     idx: usize
295 }
296
297 impl<'a> Iterator for AllTraits<'a> {
298     type Item = TraitInfo;
299
300     fn next(&mut self) -> Option<TraitInfo> {
301         let AllTraits { ref borrow, ref mut idx } = *self;
302         // ugh.
303         borrow.as_ref().unwrap().get(*idx).map(|info| {
304             *idx += 1;
305             *info
306         })
307     }
308 }