Skip to content

Commit ed530d7

Browse files
committed
auto merge of #21008 : huonw/rust/trait-suggestions, r=nikomatsakis
For a call like `foo.bar()` where the method `bar` can't be resolved, the compiler will search for traits that have methods with name `bar` to give a more informative error, providing a list of possibilities. Closes #7643.
2 parents 653e688 + 0a55aac commit ed530d7

File tree

8 files changed

+495
-106
lines changed

8 files changed

+495
-106
lines changed

src/librustc/session/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,9 @@ impl Session {
174174
pub fn fileline_note(&self, sp: Span, msg: &str) {
175175
self.diagnostic().fileline_note(sp, msg)
176176
}
177+
pub fn fileline_help(&self, sp: Span, msg: &str) {
178+
self.diagnostic().fileline_help(sp, msg)
179+
}
177180
pub fn note(&self, msg: &str) {
178181
self.diagnostic().handler().note(msg)
179182
}

src/librustc_typeck/check/method/mod.rs

+8-104
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,14 @@
1212
1313
use astconv::AstConv;
1414
use check::{FnCtxt};
15-
use check::{impl_self_ty};
1615
use check::vtable;
1716
use check::vtable::select_new_fcx_obligations;
1817
use middle::subst;
1918
use middle::traits;
2019
use middle::ty::*;
2120
use middle::ty;
2221
use middle::infer;
23-
use util::ppaux::{Repr, UserString};
22+
use util::ppaux::Repr;
2423

2524
use std::rc::Rc;
2625
use syntax::ast::{DefId};
@@ -30,14 +29,18 @@ use syntax::codemap::Span;
3029
pub use self::MethodError::*;
3130
pub use self::CandidateSource::*;
3231

32+
pub use self::suggest::{report_error, AllTraitsVec};
33+
3334
mod confirm;
3435
mod doc;
3536
mod probe;
37+
mod suggest;
3638

3739
pub enum MethodError {
3840
// Did not find an applicable method, but we did find various
39-
// static methods that may apply.
40-
NoMatch(Vec<CandidateSource>),
41+
// static methods that may apply, as well as a list of
42+
// not-in-scope traits which may work.
43+
NoMatch(Vec<CandidateSource>, Vec<ast::DefId>),
4144

4245
// Multiple methods might apply.
4346
Ambiguity(Vec<CandidateSource>),
@@ -63,7 +66,7 @@ pub fn exists<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
6366
{
6467
match probe::probe(fcx, span, method_name, self_ty, call_expr_id) {
6568
Ok(_) => true,
66-
Err(NoMatch(_)) => false,
69+
Err(NoMatch(_, _)) => false,
6770
Err(Ambiguity(_)) => true,
6871
}
6972
}
@@ -294,105 +297,6 @@ pub fn lookup_in_trait_adjusted<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
294297
Some(callee)
295298
}
296299

297-
pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
298-
span: Span,
299-
rcvr_ty: Ty<'tcx>,
300-
method_name: ast::Name,
301-
error: MethodError)
302-
{
303-
match error {
304-
NoMatch(static_sources) => {
305-
let cx = fcx.tcx();
306-
let method_ustring = method_name.user_string(cx);
307-
308-
// True if the type is a struct and contains a field with
309-
// the same name as the not-found method
310-
let is_field = match rcvr_ty.sty {
311-
ty_struct(did, _) =>
312-
ty::lookup_struct_fields(cx, did)
313-
.iter()
314-
.any(|f| f.name.user_string(cx) == method_ustring),
315-
_ => false
316-
};
317-
318-
fcx.type_error_message(
319-
span,
320-
|actual| {
321-
format!("type `{}` does not implement any \
322-
method in scope named `{}`",
323-
actual,
324-
method_ustring)
325-
},
326-
rcvr_ty,
327-
None);
328-
329-
// If the method has the name of a field, give a help note
330-
if is_field {
331-
cx.sess.span_note(span,
332-
&format!("use `(s.{0})(...)` if you meant to call the \
333-
function stored in the `{0}` field", method_ustring)[]);
334-
}
335-
336-
if static_sources.len() > 0 {
337-
fcx.tcx().sess.fileline_note(
338-
span,
339-
"found defined static methods, maybe a `self` is missing?");
340-
341-
report_candidates(fcx, span, method_name, static_sources);
342-
}
343-
}
344-
345-
Ambiguity(sources) => {
346-
span_err!(fcx.sess(), span, E0034,
347-
"multiple applicable methods in scope");
348-
349-
report_candidates(fcx, span, method_name, sources);
350-
}
351-
}
352-
353-
fn report_candidates(fcx: &FnCtxt,
354-
span: Span,
355-
method_name: ast::Name,
356-
mut sources: Vec<CandidateSource>) {
357-
sources.sort();
358-
sources.dedup();
359-
360-
for (idx, source) in sources.iter().enumerate() {
361-
match *source {
362-
ImplSource(impl_did) => {
363-
// Provide the best span we can. Use the method, if local to crate, else
364-
// the impl, if local to crate (method may be defaulted), else the call site.
365-
let method = impl_method(fcx.tcx(), impl_did, method_name).unwrap();
366-
let impl_span = fcx.tcx().map.def_id_span(impl_did, span);
367-
let method_span = fcx.tcx().map.def_id_span(method.def_id, impl_span);
368-
369-
let impl_ty = impl_self_ty(fcx, span, impl_did).ty;
370-
371-
let insertion = match impl_trait_ref(fcx.tcx(), impl_did) {
372-
None => format!(""),
373-
Some(trait_ref) => format!(" of the trait `{}`",
374-
ty::item_path_str(fcx.tcx(),
375-
trait_ref.def_id)),
376-
};
377-
378-
span_note!(fcx.sess(), method_span,
379-
"candidate #{} is defined in an impl{} for the type `{}`",
380-
idx + 1u,
381-
insertion,
382-
impl_ty.user_string(fcx.tcx()));
383-
}
384-
TraitSource(trait_did) => {
385-
let (_, method) = trait_method(fcx.tcx(), trait_did, method_name).unwrap();
386-
let method_span = fcx.tcx().map.def_id_span(method.def_id, span);
387-
span_note!(fcx.sess(), method_span,
388-
"candidate #{} is defined in the trait `{}`",
389-
idx + 1u,
390-
ty::item_path_str(fcx.tcx(), trait_did));
391-
}
392-
}
393-
}
394-
}
395-
}
396300

397301
/// Find method with name `method_name` defined in `trait_def_id` and return it, along with its
398302
/// index (or `None`, if no such method).

src/librustc_typeck/check/method/probe.rs

+62-2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use super::{MethodError,Ambiguity,NoMatch};
1212
use super::MethodIndex;
1313
use super::{CandidateSource,ImplSource,TraitSource};
14+
use super::suggest;
1415

1516
use check;
1617
use check::{FnCtxt, NoPreference};
@@ -25,6 +26,7 @@ use middle::infer::InferCtxt;
2526
use syntax::ast;
2627
use syntax::codemap::{Span, DUMMY_SP};
2728
use std::collections::HashSet;
29+
use std::mem;
2830
use std::rc::Rc;
2931
use util::ppaux::Repr;
3032

@@ -42,6 +44,7 @@ struct ProbeContext<'a, 'tcx:'a> {
4244
extension_candidates: Vec<Candidate<'tcx>>,
4345
impl_dups: HashSet<ast::DefId>,
4446
static_candidates: Vec<CandidateSource>,
47+
all_traits_search: bool,
4548
}
4649

4750
struct CandidateStep<'tcx> {
@@ -127,7 +130,7 @@ pub fn probe<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
127130
// take place in the `fcx.infcx().probe` below.
128131
let steps = match create_steps(fcx, span, self_ty) {
129132
Some(steps) => steps,
130-
None => return Err(NoMatch(Vec::new())),
133+
None => return Err(NoMatch(Vec::new(), Vec::new())),
131134
};
132135

133136
// Create a list of simplified self types, if we can.
@@ -208,9 +211,17 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
208211
steps: Rc::new(steps),
209212
opt_simplified_steps: opt_simplified_steps,
210213
static_candidates: Vec::new(),
214+
all_traits_search: false,
211215
}
212216
}
213217

218+
fn reset(&mut self) {
219+
self.inherent_candidates.clear();
220+
self.extension_candidates.clear();
221+
self.impl_dups.clear();
222+
self.static_candidates.clear();
223+
}
224+
214225
fn tcx(&self) -> &'a ty::ctxt<'tcx> {
215226
self.fcx.tcx()
216227
}
@@ -446,6 +457,15 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
446457
}
447458
}
448459

460+
fn assemble_extension_candidates_for_all_traits(&mut self) {
461+
let mut duplicates = HashSet::new();
462+
for trait_info in suggest::all_traits(self.fcx.ccx) {
463+
if duplicates.insert(trait_info.def_id) {
464+
self.assemble_extension_candidates_for_trait(trait_info.def_id)
465+
}
466+
}
467+
}
468+
449469
fn assemble_extension_candidates_for_trait(&mut self,
450470
trait_def_id: ast::DefId) {
451471
debug!("assemble_extension_candidates_for_trait(trait_def_id={})",
@@ -715,7 +735,47 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
715735
}
716736
}
717737

718-
Err(NoMatch(self.static_candidates))
738+
let static_candidates = mem::replace(&mut self.static_candidates, vec![]);
739+
740+
let out_of_scope_traits = if !self.all_traits_search {
741+
// things failed, and we haven't yet looked through all
742+
// traits, so lets do that now:
743+
self.reset();
744+
self.all_traits_search = true;
745+
746+
let span = self.span;
747+
let tcx = self.tcx();
748+
749+
self.assemble_extension_candidates_for_all_traits();
750+
751+
match self.pick() {
752+
Ok(p) => vec![p.method_ty.container.id()],
753+
Err(Ambiguity(v)) => v.into_iter().map(|source| {
754+
match source {
755+
TraitSource(id) => id,
756+
ImplSource(impl_id) => {
757+
match ty::trait_id_of_impl(tcx, impl_id) {
758+
Some(id) => id,
759+
None => tcx.sess.span_bug(span,
760+
"found inherent method when looking \
761+
at traits")
762+
}
763+
}
764+
}
765+
}).collect(),
766+
// it'd be really weird for this assertion to trigger,
767+
// given the `vec![]` in the else branch below
768+
Err(NoMatch(_, others)) => {
769+
assert!(others.is_empty());
770+
vec![]
771+
}
772+
}
773+
} else {
774+
// we've just looked through all traits and didn't find
775+
// anything at all.
776+
vec![]
777+
};
778+
Err(NoMatch(static_candidates, out_of_scope_traits))
719779
}
720780

721781
fn pick_step(&mut self, step: &CandidateStep<'tcx>) -> Option<PickResult<'tcx>> {

0 commit comments

Comments
 (0)