Skip to content

Implement suggestions for traits to import. #21008

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jan 17, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/librustc/session/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,9 @@ impl Session {
pub fn fileline_note(&self, sp: Span, msg: &str) {
self.diagnostic().fileline_note(sp, msg)
}
pub fn fileline_help(&self, sp: Span, msg: &str) {
self.diagnostic().fileline_help(sp, msg)
}
pub fn note(&self, msg: &str) {
self.diagnostic().handler().note(msg)
}
Expand Down
112 changes: 8 additions & 104 deletions src/librustc_typeck/check/method/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,14 @@

use astconv::AstConv;
use check::{FnCtxt};
use check::{impl_self_ty};
use check::vtable;
use check::vtable::select_new_fcx_obligations;
use middle::subst;
use middle::traits;
use middle::ty::*;
use middle::ty;
use middle::infer;
use util::ppaux::{Repr, UserString};
use util::ppaux::Repr;

use std::rc::Rc;
use syntax::ast::{DefId};
Expand All @@ -30,14 +29,18 @@ use syntax::codemap::Span;
pub use self::MethodError::*;
pub use self::CandidateSource::*;

pub use self::suggest::{report_error, AllTraitsVec};

mod confirm;
mod doc;
mod probe;
mod suggest;

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

// Multiple methods might apply.
Ambiguity(Vec<CandidateSource>),
Expand All @@ -63,7 +66,7 @@ pub fn exists<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
{
match probe::probe(fcx, span, method_name, self_ty, call_expr_id) {
Ok(_) => true,
Err(NoMatch(_)) => false,
Err(NoMatch(_, _)) => false,
Err(Ambiguity(_)) => true,
}
}
Expand Down Expand Up @@ -294,105 +297,6 @@ pub fn lookup_in_trait_adjusted<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
Some(callee)
}

pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
span: Span,
rcvr_ty: Ty<'tcx>,
method_name: ast::Name,
error: MethodError)
{
match error {
NoMatch(static_sources) => {
let cx = fcx.tcx();
let method_ustring = method_name.user_string(cx);

// True if the type is a struct and contains a field with
// the same name as the not-found method
let is_field = match rcvr_ty.sty {
ty_struct(did, _) =>
ty::lookup_struct_fields(cx, did)
.iter()
.any(|f| f.name.user_string(cx) == method_ustring),
_ => false
};

fcx.type_error_message(
span,
|actual| {
format!("type `{}` does not implement any \
method in scope named `{}`",
actual,
method_ustring)
},
rcvr_ty,
None);

// If the method has the name of a field, give a help note
if is_field {
cx.sess.span_note(span,
&format!("use `(s.{0})(...)` if you meant to call the \
function stored in the `{0}` field", method_ustring)[]);
}

if static_sources.len() > 0 {
fcx.tcx().sess.fileline_note(
span,
"found defined static methods, maybe a `self` is missing?");

report_candidates(fcx, span, method_name, static_sources);
}
}

Ambiguity(sources) => {
span_err!(fcx.sess(), span, E0034,
"multiple applicable methods in scope");

report_candidates(fcx, span, method_name, sources);
}
}

fn report_candidates(fcx: &FnCtxt,
span: Span,
method_name: ast::Name,
mut sources: Vec<CandidateSource>) {
sources.sort();
sources.dedup();

for (idx, source) in sources.iter().enumerate() {
match *source {
ImplSource(impl_did) => {
// Provide the best span we can. Use the method, if local to crate, else
// the impl, if local to crate (method may be defaulted), else the call site.
let method = impl_method(fcx.tcx(), impl_did, method_name).unwrap();
let impl_span = fcx.tcx().map.def_id_span(impl_did, span);
let method_span = fcx.tcx().map.def_id_span(method.def_id, impl_span);

let impl_ty = impl_self_ty(fcx, span, impl_did).ty;

let insertion = match impl_trait_ref(fcx.tcx(), impl_did) {
None => format!(""),
Some(trait_ref) => format!(" of the trait `{}`",
ty::item_path_str(fcx.tcx(),
trait_ref.def_id)),
};

span_note!(fcx.sess(), method_span,
"candidate #{} is defined in an impl{} for the type `{}`",
idx + 1u,
insertion,
impl_ty.user_string(fcx.tcx()));
}
TraitSource(trait_did) => {
let (_, method) = trait_method(fcx.tcx(), trait_did, method_name).unwrap();
let method_span = fcx.tcx().map.def_id_span(method.def_id, span);
span_note!(fcx.sess(), method_span,
"candidate #{} is defined in the trait `{}`",
idx + 1u,
ty::item_path_str(fcx.tcx(), trait_did));
}
}
}
}
}

/// Find method with name `method_name` defined in `trait_def_id` and return it, along with its
/// index (or `None`, if no such method).
Expand Down
64 changes: 62 additions & 2 deletions src/librustc_typeck/check/method/probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use super::{MethodError,Ambiguity,NoMatch};
use super::MethodIndex;
use super::{CandidateSource,ImplSource,TraitSource};
use super::suggest;

use check;
use check::{FnCtxt, NoPreference};
Expand All @@ -25,6 +26,7 @@ use middle::infer::InferCtxt;
use syntax::ast;
use syntax::codemap::{Span, DUMMY_SP};
use std::collections::HashSet;
use std::mem;
use std::rc::Rc;
use util::ppaux::Repr;

Expand All @@ -42,6 +44,7 @@ struct ProbeContext<'a, 'tcx:'a> {
extension_candidates: Vec<Candidate<'tcx>>,
impl_dups: HashSet<ast::DefId>,
static_candidates: Vec<CandidateSource>,
all_traits_search: bool,
}

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

// Create a list of simplified self types, if we can.
Expand Down Expand Up @@ -208,9 +211,17 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
steps: Rc::new(steps),
opt_simplified_steps: opt_simplified_steps,
static_candidates: Vec::new(),
all_traits_search: false,
}
}

fn reset(&mut self) {
self.inherent_candidates.clear();
self.extension_candidates.clear();
self.impl_dups.clear();
self.static_candidates.clear();
}

fn tcx(&self) -> &'a ty::ctxt<'tcx> {
self.fcx.tcx()
}
Expand Down Expand Up @@ -446,6 +457,15 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
}
}

fn assemble_extension_candidates_for_all_traits(&mut self) {
let mut duplicates = HashSet::new();
for trait_info in suggest::all_traits(self.fcx.ccx) {
if duplicates.insert(trait_info.def_id) {
self.assemble_extension_candidates_for_trait(trait_info.def_id)
}
}
}

fn assemble_extension_candidates_for_trait(&mut self,
trait_def_id: ast::DefId) {
debug!("assemble_extension_candidates_for_trait(trait_def_id={})",
Expand Down Expand Up @@ -715,7 +735,47 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
}
}

Err(NoMatch(self.static_candidates))
let static_candidates = mem::replace(&mut self.static_candidates, vec![]);

let out_of_scope_traits = if !self.all_traits_search {
// things failed, and we haven't yet looked through all
// traits, so lets do that now:
self.reset();
self.all_traits_search = true;

let span = self.span;
let tcx = self.tcx();

self.assemble_extension_candidates_for_all_traits();

match self.pick() {
Ok(p) => vec![p.method_ty.container.id()],
Err(Ambiguity(v)) => v.into_iter().map(|source| {
match source {
TraitSource(id) => id,
ImplSource(impl_id) => {
match ty::trait_id_of_impl(tcx, impl_id) {
Some(id) => id,
None => tcx.sess.span_bug(span,
"found inherent method when looking \
at traits")
}
}
}
}).collect(),
// it'd be really weird for this assertion to trigger,
// given the `vec![]` in the else branch below
Err(NoMatch(_, others)) => {
assert!(others.is_empty());
vec![]
}
}
} else {
// we've just looked through all traits and didn't find
// anything at all.
vec![]
};
Err(NoMatch(static_candidates, out_of_scope_traits))
}

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