]> git.proxmox.com Git - rustc.git/blame - src/librustc_typeck/check/method/suggest.rs
Imported Upstream version 1.9.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
16use astconv::AstConv;
54a0048b
SL
17use check::{self, FnCtxt, UnresolvedTypeAction, autoderef};
18use rustc::hir::map as hir_map;
19use rustc::ty::{self, Ty, ToPolyTraitRef, ToPredicate, TypeFoldable};
7453a54e 20use middle::cstore::{self, CrateStore};
54a0048b
SL
21use hir::def::Def;
22use hir::def_id::DefId;
62682a34 23use middle::lang_items::FnOnceTraitLangItem;
54a0048b
SL
24use rustc::ty::subst::Substs;
25use rustc::ty::LvaluePreference;
26use rustc::traits::{Obligation, SelectionContext};
b039eaaf 27use util::nodemap::{FnvHashSet};
85aaf69f 28
54a0048b 29
e9174d1e 30use syntax::ast;
85aaf69f 31use syntax::codemap::Span;
9cc50fc6 32use syntax::errors::DiagnosticBuilder;
54a0048b
SL
33use rustc::hir::print as pprust;
34use rustc::hir;
35use rustc::hir::Expr_;
85aaf69f
SL
36
37use std::cell;
38use std::cmp::Ordering;
39
62682a34 40use super::{MethodError, NoMatchData, CandidateSource, impl_item, trait_item};
d9579d0f 41use super::probe::Mode;
85aaf69f 42
54a0048b
SL
43fn 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
85aaf69f
SL
92pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
93 span: Span,
94 rcvr_ty: Ty<'tcx>,
d9579d0f 95 item_name: ast::Name,
e9174d1e 96 rcvr_expr: Option<&hir::Expr>,
62682a34 97 error: MethodError<'tcx>)
85aaf69f
SL
98{
99 // avoid suggestions when we don't know what's going on.
c1a9b12d 100 if rcvr_ty.references_error() {
85aaf69f
SL
101 return
102 }
103
104 match error {
62682a34
SL
105 MethodError::NoMatch(NoMatchData { static_candidates: static_sources,
106 unsatisfied_predicates,
107 out_of_scope_traits,
54a0048b 108 mode, .. }) => {
85aaf69f 109 let cx = fcx.tcx();
85aaf69f 110
9cc50fc6 111 let mut err = fcx.type_error_struct(
85aaf69f
SL
112 span,
113 |actual| {
d9579d0f
AL
114 format!("no {} named `{}` found for type `{}` \
115 in the current scope",
116 if mode == Mode::MethodCall { "method" }
117 else { "associated item" },
62682a34 118 item_name,
d9579d0f 119 actual)
85aaf69f
SL
120 },
121 rcvr_ty,
122 None);
123
d9579d0f 124 // If the item has the name of a field, give a help note
e9174d1e
SL
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) {
62682a34
SL
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
54a0048b 134 let field_ty = field.ty(cx, substs);
62682a34 135
54a0048b
SL
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));
9cc50fc6 144 }
54a0048b
SL
145 }
146 }
62682a34 147
54a0048b
SL
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 }
92a42be0 157
54a0048b
SL
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);
62682a34
SL
169 }
170 }
c34b1796 171 }
85aaf69f
SL
172 }
173
9346a6ac 174 if !static_sources.is_empty() {
9cc50fc6 175 err.fileline_note(
85aaf69f 176 span,
54a0048b
SL
177 "found the following associated functions; to be used as \
178 methods, functions must have a `self` parameter");
85aaf69f 179
9cc50fc6 180 report_candidates(fcx, &mut err, span, item_name, static_sources);
85aaf69f
SL
181 }
182
62682a34
SL
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<_>>()
c1a9b12d 189 .join(", ");
9cc50fc6 190 err.fileline_note(
62682a34
SL
191 span,
192 &format!("the method `{}` exists but the \
193 following trait bounds were not satisfied: {}",
194 item_name,
195 bound_list));
196 }
197
9cc50fc6
SL
198 suggest_traits_to_import(fcx, &mut err, span, rcvr_ty, item_name,
199 rcvr_expr, out_of_scope_traits);
200 err.emit();
85aaf69f
SL
201 }
202
203 MethodError::Ambiguity(sources) => {
9cc50fc6
SL
204 let mut err = struct_span_err!(fcx.sess(), span, E0034,
205 "multiple applicable items in scope");
85aaf69f 206
9cc50fc6
SL
207 report_candidates(fcx, &mut err, span, item_name, sources);
208 err.emit();
85aaf69f
SL
209 }
210
211 MethodError::ClosureAmbiguity(trait_def_id) => {
c34b1796
AL
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",
62682a34 215 item_name,
c1a9b12d 216 fcx.tcx().item_path_str(trait_def_id));
c34b1796
AL
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);
85aaf69f 224 }
54a0048b
SL
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 }
85aaf69f
SL
230 }
231
232 fn report_candidates(fcx: &FnCtxt,
9cc50fc6 233 err: &mut DiagnosticBuilder,
85aaf69f 234 span: Span,
d9579d0f 235 item_name: ast::Name,
85aaf69f
SL
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) => {
d9579d0f
AL
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.
b039eaaf
SL
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();
85aaf69f 253 let impl_span = fcx.tcx().map.def_id_span(impl_did, span);
d9579d0f 254 let item_span = fcx.tcx().map.def_id_span(item.def_id(), impl_span);
85aaf69f
SL
255
256 let impl_ty = check::impl_self_ty(fcx, span, impl_did).ty;
257
c1a9b12d 258 let insertion = match fcx.tcx().impl_trait_ref(impl_did) {
85aaf69f 259 None => format!(""),
c1a9b12d
SL
260 Some(trait_ref) => {
261 format!(" of the trait `{}`",
262 fcx.tcx().item_path_str(trait_ref.def_id))
263 }
85aaf69f
SL
264 };
265
9cc50fc6 266 span_note!(err, item_span,
85aaf69f
SL
267 "candidate #{} is defined in an impl{} for the type `{}`",
268 idx + 1,
269 insertion,
62682a34 270 impl_ty);
85aaf69f
SL
271 }
272 CandidateSource::TraitSource(trait_did) => {
c1a9b12d 273 let item = trait_item(fcx.tcx(), trait_did, item_name).unwrap();
d9579d0f 274 let item_span = fcx.tcx().map.def_id_span(item.def_id(), span);
9cc50fc6 275 span_note!(err, item_span,
85aaf69f
SL
276 "candidate #{} is defined in the trait `{}`",
277 idx + 1,
c1a9b12d 278 fcx.tcx().item_path_str(trait_did));
85aaf69f
SL
279 }
280 }
281 }
282 }
283}
284
285
286pub type AllTraitsVec = Vec<TraitInfo>;
287
288fn suggest_traits_to_import<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
9cc50fc6 289 err: &mut DiagnosticBuilder,
85aaf69f
SL
290 span: Span,
291 rcvr_ty: Ty<'tcx>,
d9579d0f 292 item_name: ast::Name,
e9174d1e
SL
293 rcvr_expr: Option<&hir::Expr>,
294 valid_out_of_scope_traits: Vec<DefId>)
85aaf69f
SL
295{
296 let tcx = fcx.tcx();
85aaf69f
SL
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!(
d9579d0f 303 "items from traits can only be used if the trait is in scope; \
85aaf69f
SL
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
9cc50fc6 309 err.fileline_help(span, &msg[..]);
85aaf69f
SL
310
311 for (i, trait_did) in candidates.iter().enumerate() {
9cc50fc6 312 err.fileline_help(span,
7453a54e 313 &format!("candidate #{}: `use {}`",
9cc50fc6
SL
314 i + 1,
315 fcx.tcx().item_path_str(*trait_did)));
85aaf69f
SL
316 }
317 return
318 }
319
c34b1796 320 let type_is_local = type_derefs_to_local(fcx, span, rcvr_ty, rcvr_expr);
85aaf69f
SL
321
322 // there's no implemented traits, so lets suggest some traits to
d9579d0f 323 // implement, by finding ones that have the item name, and are
85aaf69f
SL
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).
e9174d1e 333 (type_is_local || info.def_id.is_local())
d9579d0f 334 && trait_item(tcx, info.def_id, item_name).is_some()
85aaf69f
SL
335 })
336 .collect::<Vec<_>>();
337
9346a6ac 338 if !candidates.is_empty() {
85aaf69f
SL
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!(
d9579d0f
AL
347 "items from traits can only be used if the trait is implemented and in scope; \
348 the following {traits_define} an item `{name}`, \
85aaf69f
SL
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"},
62682a34 352 name = item_name);
85aaf69f 353
9cc50fc6 354 err.fileline_help(span, &msg[..]);
85aaf69f
SL
355
356 for (i, trait_info) in candidates.iter().enumerate() {
9cc50fc6 357 err.fileline_help(span,
7453a54e 358 &format!("candidate #{}: `{}`",
9cc50fc6
SL
359 i + 1,
360 fcx.tcx().item_path_str(trait_info.def_id)));
85aaf69f
SL
361 }
362 }
363}
364
365/// Checks whether there is a local type somewhere in the chain of
366/// autoderefs of `rcvr_ty`.
367fn type_derefs_to_local<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
368 span: Span,
c34b1796 369 rcvr_ty: Ty<'tcx>,
e9174d1e 370 rcvr_expr: Option<&hir::Expr>) -> bool {
c34b1796
AL
371 fn is_local(ty: Ty) -> bool {
372 match ty.sty {
e9174d1e 373 ty::TyEnum(def, _) | ty::TyStruct(def, _) => def.did.is_local(),
85aaf69f 374
e9174d1e 375 ty::TyTrait(ref tr) => tr.principal_def_id().is_local(),
85aaf69f 376
62682a34 377 ty::TyParam(_) => true,
85aaf69f 378
85aaf69f
SL
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
c34b1796
AL
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
54a0048b 393 check::autoderef(fcx, span, rcvr_ty, || None,
e9174d1e 394 check::UnresolvedTypeAction::Ignore, ty::NoPreference,
c34b1796
AL
395 |ty, _| {
396 if is_local(ty) {
397 Some(())
85aaf69f
SL
398 } else {
399 None
400 }
c34b1796 401 }).2.is_some()
85aaf69f
SL
402}
403
c34b1796 404#[derive(Copy, Clone)]
85aaf69f 405pub struct TraitInfo {
e9174d1e 406 pub def_id: DefId,
85aaf69f
SL
407}
408
409impl TraitInfo {
e9174d1e 410 fn new(def_id: DefId) -> TraitInfo {
85aaf69f
SL
411 TraitInfo {
412 def_id: def_id,
413 }
414 }
415}
416impl PartialEq for TraitInfo {
417 fn eq(&self, other: &TraitInfo) -> bool {
418 self.cmp(other) == Ordering::Equal
419 }
420}
421impl Eq for TraitInfo {}
422impl PartialOrd for TraitInfo {
423 fn partial_cmp(&self, other: &TraitInfo) -> Option<Ordering> { Some(self.cmp(other)) }
424}
425impl Ord for TraitInfo {
426 fn cmp(&self, other: &TraitInfo) -> Ordering {
b039eaaf
SL
427 // local crates are more important than remote ones (local:
428 // cnum == 0), and otherwise we throw in the defid for totality
85aaf69f 429
b039eaaf
SL
430 let lhs = (other.def_id.krate, other.def_id);
431 let rhs = (self.def_id.krate, self.def_id);
85aaf69f
SL
432 lhs.cmp(&rhs)
433 }
434}
435
436/// Retrieve all traits in this crate and any dependent crates.
437pub fn all_traits<'a>(ccx: &'a CrateCtxt) -> AllTraits<'a> {
438 if ccx.all_traits.borrow().is_none() {
54a0048b 439 use rustc::hir::intravisit;
85aaf69f
SL
440
441 let mut traits = vec![];
442
443 // Crate-local:
444 //
445 // meh.
b039eaaf
SL
446 struct Visitor<'a, 'tcx:'a> {
447 map: &'a hir_map::Map<'tcx>,
85aaf69f
SL
448 traits: &'a mut AllTraitsVec,
449 }
92a42be0 450 impl<'v, 'a, 'tcx> intravisit::Visitor<'v> for Visitor<'a, 'tcx> {
e9174d1e 451 fn visit_item(&mut self, i: &'v hir::Item) {
85aaf69f 452 match i.node {
e9174d1e 453 hir::ItemTrait(..) => {
b039eaaf
SL
454 let def_id = self.map.local_def_id(i.id);
455 self.traits.push(TraitInfo::new(def_id));
85aaf69f
SL
456 }
457 _ => {}
458 }
85aaf69f
SL
459 }
460 }
92a42be0 461 ccx.tcx.map.krate().visit_all_items(&mut Visitor {
b039eaaf 462 map: &ccx.tcx.map,
85aaf69f 463 traits: &mut traits
92a42be0 464 });
85aaf69f
SL
465
466 // Cross-crate:
b039eaaf 467 let mut external_mods = FnvHashSet();
85aaf69f 468 fn handle_external_def(traits: &mut AllTraitsVec,
b039eaaf 469 external_mods: &mut FnvHashSet<DefId>,
85aaf69f 470 ccx: &CrateCtxt,
92a42be0
SL
471 cstore: &for<'a> cstore::CrateStore<'a>,
472 dl: cstore::DefLike) {
85aaf69f 473 match dl {
7453a54e 474 cstore::DlDef(Def::Trait(did)) => {
85aaf69f
SL
475 traits.push(TraitInfo::new(did));
476 }
7453a54e 477 cstore::DlDef(Def::Mod(did)) => {
b039eaaf
SL
478 if !external_mods.insert(did) {
479 return;
480 }
92a42be0 481 for child in cstore.item_children(did) {
b039eaaf 482 handle_external_def(traits, external_mods,
92a42be0
SL
483 ccx, cstore, child.def)
484 }
85aaf69f
SL
485 }
486 _ => {}
487 }
488 }
92a42be0
SL
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 }
85aaf69f
SL
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
509pub struct AllTraits<'a> {
bd371182 510 borrow: cell::Ref<'a, Option<AllTraitsVec>>,
85aaf69f
SL
511 idx: usize
512}
513
514impl<'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}