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