]> git.proxmox.com Git - rustc.git/blame - src/librustc_typeck/check/method/suggest.rs
Imported Upstream version 1.3.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;
17use check::{self, FnCtxt};
c1a9b12d 18use middle::ty::{self, Ty, ToPolyTraitRef, ToPredicate, HasTypeFlags};
85aaf69f 19use middle::def;
62682a34
SL
20use middle::lang_items::FnOnceTraitLangItem;
21use middle::subst::Substs;
22use middle::traits::{Obligation, SelectionContext};
85aaf69f 23use metadata::{csearch, cstore, decoder};
85aaf69f
SL
24
25use syntax::{ast, ast_util};
26use syntax::codemap::Span;
27use syntax::print::pprust;
28
29use std::cell;
30use std::cmp::Ordering;
31
62682a34 32use super::{MethodError, NoMatchData, CandidateSource, impl_item, trait_item};
d9579d0f 33use super::probe::Mode;
85aaf69f
SL
34
35pub 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
219pub type AllTraitsVec = Vec<TraitInfo>;
220
221fn 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`.
300fn 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
338pub struct TraitInfo {
339 pub def_id: ast::DefId,
340}
341
342impl TraitInfo {
343 fn new(def_id: ast::DefId) -> TraitInfo {
344 TraitInfo {
345 def_id: def_id,
346 }
347 }
348}
349impl PartialEq for TraitInfo {
350 fn eq(&self, other: &TraitInfo) -> bool {
351 self.cmp(other) == Ordering::Equal
352 }
353}
354impl Eq for TraitInfo {}
355impl PartialOrd for TraitInfo {
356 fn partial_cmp(&self, other: &TraitInfo) -> Option<Ordering> { Some(self.cmp(other)) }
357}
358impl 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.
372pub 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
434pub struct AllTraits<'a> {
bd371182 435 borrow: cell::Ref<'a, Option<AllTraitsVec>>,
85aaf69f
SL
436 idx: usize
437}
438
439impl<'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}