]>
Commit | Line | Data |
---|---|---|
85aaf69f SL |
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 | ||
d9579d0f | 11 | //! Give useful errors and suggestions to users when an item can't be |
85aaf69f SL |
12 | //! found or is otherwise invalid. |
13 | ||
14 | use CrateCtxt; | |
15 | ||
16 | use astconv::AstConv; | |
17 | use check::{self, FnCtxt}; | |
c1a9b12d | 18 | use middle::ty::{self, Ty, ToPolyTraitRef, ToPredicate, HasTypeFlags}; |
85aaf69f | 19 | use middle::def; |
62682a34 SL |
20 | use middle::lang_items::FnOnceTraitLangItem; |
21 | use middle::subst::Substs; | |
22 | use middle::traits::{Obligation, SelectionContext}; | |
85aaf69f | 23 | use metadata::{csearch, cstore, decoder}; |
85aaf69f SL |
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 | ||
62682a34 | 32 | use super::{MethodError, NoMatchData, CandidateSource, impl_item, trait_item}; |
d9579d0f | 33 | use super::probe::Mode; |
85aaf69f SL |
34 | |
35 | pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, | |
36 | span: Span, | |
37 | rcvr_ty: Ty<'tcx>, | |
d9579d0f | 38 | item_name: ast::Name, |
c34b1796 | 39 | rcvr_expr: Option<&ast::Expr>, |
62682a34 | 40 | error: MethodError<'tcx>) |
85aaf69f SL |
41 | { |
42 | // avoid suggestions when we don't know what's going on. | |
c1a9b12d | 43 | if rcvr_ty.references_error() { |
85aaf69f SL |
44 | return |
45 | } | |
46 | ||
47 | match error { | |
62682a34 SL |
48 | MethodError::NoMatch(NoMatchData { static_candidates: static_sources, |
49 | unsatisfied_predicates, | |
50 | out_of_scope_traits, | |
51 | mode }) => { | |
85aaf69f | 52 | let cx = fcx.tcx(); |
85aaf69f | 53 | |
85aaf69f SL |
54 | fcx.type_error_message( |
55 | span, | |
56 | |actual| { | |
d9579d0f AL |
57 | format!("no {} named `{}` found for type `{}` \ |
58 | in the current scope", | |
59 | if mode == Mode::MethodCall { "method" } | |
60 | else { "associated item" }, | |
62682a34 | 61 | item_name, |
d9579d0f | 62 | actual) |
85aaf69f SL |
63 | }, |
64 | rcvr_ty, | |
65 | None); | |
66 | ||
d9579d0f | 67 | // If the item has the name of a field, give a help note |
62682a34 | 68 | if let (&ty::TyStruct(did, substs), Some(expr)) = (&rcvr_ty.sty, rcvr_expr) { |
c1a9b12d | 69 | let fields = cx.lookup_struct_fields(did); |
62682a34 SL |
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 | |
c1a9b12d | 92 | let field_ty = cx.lookup_field_type(did, field.id, substs); |
62682a34 SL |
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, | |
c1a9b12d SL |
104 | poly_trait_ref.to_predicate()); |
105 | let mut selcx = SelectionContext::new(infcx); | |
62682a34 SL |
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 | } | |
c34b1796 | 120 | } |
85aaf69f SL |
121 | } |
122 | ||
9346a6ac | 123 | if !static_sources.is_empty() { |
62682a34 | 124 | cx.sess.fileline_note( |
85aaf69f SL |
125 | span, |
126 | "found defined static methods, maybe a `self` is missing?"); | |
127 | ||
d9579d0f | 128 | report_candidates(fcx, span, item_name, static_sources); |
85aaf69f SL |
129 | } |
130 | ||
62682a34 SL |
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<_>>() | |
c1a9b12d | 137 | .join(", "); |
62682a34 SL |
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 | ||
d9579d0f | 146 | suggest_traits_to_import(fcx, span, rcvr_ty, item_name, |
c34b1796 | 147 | rcvr_expr, out_of_scope_traits) |
85aaf69f SL |
148 | } |
149 | ||
150 | MethodError::Ambiguity(sources) => { | |
151 | span_err!(fcx.sess(), span, E0034, | |
d9579d0f | 152 | "multiple applicable items in scope"); |
85aaf69f | 153 | |
d9579d0f | 154 | report_candidates(fcx, span, item_name, sources); |
85aaf69f SL |
155 | } |
156 | ||
157 | MethodError::ClosureAmbiguity(trait_def_id) => { | |
c34b1796 AL |
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", | |
62682a34 | 161 | item_name, |
c1a9b12d | 162 | fcx.tcx().item_path_str(trait_def_id)); |
c34b1796 AL |
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); | |
85aaf69f SL |
170 | } |
171 | } | |
172 | ||
173 | fn report_candidates(fcx: &FnCtxt, | |
174 | span: Span, | |
d9579d0f | 175 | item_name: ast::Name, |
85aaf69f SL |
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) => { | |
d9579d0f AL |
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(); | |
85aaf69f | 186 | let impl_span = fcx.tcx().map.def_id_span(impl_did, span); |
d9579d0f | 187 | let item_span = fcx.tcx().map.def_id_span(item.def_id(), impl_span); |
85aaf69f SL |
188 | |
189 | let impl_ty = check::impl_self_ty(fcx, span, impl_did).ty; | |
190 | ||
c1a9b12d | 191 | let insertion = match fcx.tcx().impl_trait_ref(impl_did) { |
85aaf69f | 192 | None => format!(""), |
c1a9b12d SL |
193 | Some(trait_ref) => { |
194 | format!(" of the trait `{}`", | |
195 | fcx.tcx().item_path_str(trait_ref.def_id)) | |
196 | } | |
85aaf69f SL |
197 | }; |
198 | ||
d9579d0f | 199 | span_note!(fcx.sess(), item_span, |
85aaf69f SL |
200 | "candidate #{} is defined in an impl{} for the type `{}`", |
201 | idx + 1, | |
202 | insertion, | |
62682a34 | 203 | impl_ty); |
85aaf69f SL |
204 | } |
205 | CandidateSource::TraitSource(trait_did) => { | |
c1a9b12d | 206 | let item = trait_item(fcx.tcx(), trait_did, item_name).unwrap(); |
d9579d0f AL |
207 | let item_span = fcx.tcx().map.def_id_span(item.def_id(), span); |
208 | span_note!(fcx.sess(), item_span, | |
85aaf69f SL |
209 | "candidate #{} is defined in the trait `{}`", |
210 | idx + 1, | |
c1a9b12d | 211 | fcx.tcx().item_path_str(trait_did)); |
85aaf69f SL |
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>, | |
d9579d0f | 224 | item_name: ast::Name, |
c34b1796 | 225 | rcvr_expr: Option<&ast::Expr>, |
85aaf69f SL |
226 | valid_out_of_scope_traits: Vec<ast::DefId>) |
227 | { | |
228 | let tcx = fcx.tcx(); | |
85aaf69f SL |
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!( | |
d9579d0f | 235 | "items from traits can only be used if the trait is in scope; \ |
85aaf69f SL |
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, | |
c1a9b12d | 247 | fcx.tcx().item_path_str(*trait_did))) |
85aaf69f SL |
248 | |
249 | } | |
250 | return | |
251 | } | |
252 | ||
c34b1796 | 253 | let type_is_local = type_derefs_to_local(fcx, span, rcvr_ty, rcvr_expr); |
85aaf69f SL |
254 | |
255 | // there's no implemented traits, so lets suggest some traits to | |
d9579d0f | 256 | // implement, by finding ones that have the item name, and are |
85aaf69f SL |
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)) | |
d9579d0f | 267 | && trait_item(tcx, info.def_id, item_name).is_some() |
85aaf69f SL |
268 | }) |
269 | .collect::<Vec<_>>(); | |
270 | ||
9346a6ac | 271 | if !candidates.is_empty() { |
85aaf69f SL |
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!( | |
d9579d0f AL |
280 | "items from traits can only be used if the trait is implemented and in scope; \ |
281 | the following {traits_define} an item `{name}`, \ | |
85aaf69f SL |
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"}, | |
62682a34 | 285 | name = item_name); |
85aaf69f SL |
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, | |
c1a9b12d | 293 | fcx.tcx().item_path_str(trait_info.def_id))) |
85aaf69f SL |
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, | |
c34b1796 AL |
302 | rcvr_ty: Ty<'tcx>, |
303 | rcvr_expr: Option<&ast::Expr>) -> bool { | |
304 | fn is_local(ty: Ty) -> bool { | |
305 | match ty.sty { | |
62682a34 | 306 | ty::TyEnum(did, _) | ty::TyStruct(did, _) => ast_util::is_local(did), |
85aaf69f | 307 | |
62682a34 | 308 | ty::TyTrait(ref tr) => ast_util::is_local(tr.principal_def_id()), |
85aaf69f | 309 | |
62682a34 | 310 | ty::TyParam(_) => true, |
85aaf69f | 311 | |
85aaf69f SL |
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 | |
c34b1796 AL |
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(()) | |
85aaf69f SL |
331 | } else { |
332 | None | |
333 | } | |
c34b1796 | 334 | }).2.is_some() |
85aaf69f SL |
335 | } |
336 | ||
c34b1796 | 337 | #[derive(Copy, Clone)] |
85aaf69f SL |
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> { | |
bd371182 | 435 | borrow: cell::Ref<'a, Option<AllTraitsVec>>, |
85aaf69f SL |
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 | } |