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