1
Fork 0

Suggest missing trait bounds when a method exists but the bounds aren't satisfied

This commit is contained in:
Gulshan Singh 2015-06-18 22:20:45 -04:00
parent 5c5753e876
commit a006a82724
4 changed files with 120 additions and 32 deletions

View file

@ -16,7 +16,7 @@ use middle::def;
use middle::privacy::{AllPublic, DependsOn, LastPrivate, LastMod};
use middle::subst;
use middle::traits;
use middle::ty::{self, AsPredicate, ToPolyTraitRef};
use middle::ty::{self, AsPredicate, ToPolyTraitRef, TraitRef};
use middle::infer;
use syntax::ast::DefId;
@ -32,11 +32,9 @@ mod confirm;
mod probe;
mod suggest;
pub enum MethodError {
// Did not find an applicable method, but we did find various
// static methods that may apply, as well as a list of
// not-in-scope traits which may work.
NoMatch(Vec<CandidateSource>, Vec<ast::DefId>, probe::Mode),
pub enum MethodError<'tcx> {
// Did not find an applicable method, but we did find various near-misses that may work.
NoMatch(NoMatchData<'tcx>),
// Multiple methods might apply.
Ambiguity(Vec<CandidateSource>),
@ -45,9 +43,32 @@ pub enum MethodError {
ClosureAmbiguity(/* DefId of fn trait */ ast::DefId),
}
// Contains a list of static methods that may apply, a list of unsatisfied trait predicates which
// could lead to matches if satisfied, and a list of not-in-scope traits which may work.
pub struct NoMatchData<'tcx> {
pub static_candidates: Vec<CandidateSource>,
pub unsatisfied_predicates: Vec<TraitRef<'tcx>>,
pub out_of_scope_traits: Vec<ast::DefId>,
pub mode: probe::Mode
}
impl<'tcx> NoMatchData<'tcx> {
pub fn new(static_candidates: Vec<CandidateSource>,
unsatisfied_predicates: Vec<TraitRef<'tcx>>,
out_of_scope_traits: Vec<ast::DefId>,
mode: probe::Mode) -> Self {
NoMatchData {
static_candidates: static_candidates,
unsatisfied_predicates: unsatisfied_predicates,
out_of_scope_traits: out_of_scope_traits,
mode: mode
}
}
}
// A pared down enum describing just the places from which a method
// candidate can arise. Used for error reporting only.
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq)]
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub enum CandidateSource {
ImplSource(ast::DefId),
TraitSource(/* trait id */ ast::DefId),
@ -93,7 +114,7 @@ pub fn lookup<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
supplied_method_types: Vec<ty::Ty<'tcx>>,
call_expr: &'tcx ast::Expr,
self_expr: &'tcx ast::Expr)
-> Result<ty::MethodCallee<'tcx>, MethodError>
-> Result<ty::MethodCallee<'tcx>, MethodError<'tcx>>
{
debug!("lookup(method_name={}, self_ty={:?}, call_expr={:?}, self_expr={:?})",
method_name,
@ -305,7 +326,7 @@ pub fn resolve_ufcs<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
method_name: ast::Name,
self_ty: ty::Ty<'tcx>,
expr_id: ast::NodeId)
-> Result<(def::Def, LastPrivate), MethodError>
-> Result<(def::Def, LastPrivate), MethodError<'tcx>>
{
let mode = probe::Mode::Path;
let pick = try!(probe::probe(fcx, span, mode, method_name, self_ty, expr_id));

View file

@ -9,8 +9,9 @@
// except according to those terms.
use super::MethodError;
use super::NoMatchData;
use super::ItemIndex;
use super::{CandidateSource,ImplSource,TraitSource};
use super::{CandidateSource, ImplSource, TraitSource};
use super::suggest;
use check;
@ -19,7 +20,7 @@ use middle::fast_reject;
use middle::subst;
use middle::subst::Subst;
use middle::traits;
use middle::ty::{self, RegionEscape, Ty, ToPolyTraitRef};
use middle::ty::{self, RegionEscape, Ty, ToPolyTraitRef, TraitRef};
use middle::ty_fold::TypeFoldable;
use middle::infer;
use middle::infer::InferCtxt;
@ -42,7 +43,14 @@ struct ProbeContext<'a, 'tcx:'a> {
inherent_candidates: Vec<Candidate<'tcx>>,
extension_candidates: Vec<Candidate<'tcx>>,
impl_dups: HashSet<ast::DefId>,
/// Collects near misses when the candidate functions are missing a `self` keyword and is only
/// used for error reporting
static_candidates: Vec<CandidateSource>,
/// Collects near misses when trait bounds for type parameters are unsatisfied and is only used
/// for error reporting
unsatisfied_predicates: Vec<TraitRef<'tcx>>
}
#[derive(Debug)]
@ -104,7 +112,7 @@ pub enum PickKind<'tcx> {
WhereClausePick(/* Trait */ ty::PolyTraitRef<'tcx>, ItemIndex),
}
pub type PickResult<'tcx> = Result<Pick<'tcx>, MethodError>;
pub type PickResult<'tcx> = Result<Pick<'tcx>, MethodError<'tcx>>;
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub enum Mode {
@ -141,7 +149,8 @@ pub fn probe<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
let steps = if mode == Mode::MethodCall {
match create_steps(fcx, span, self_ty) {
Some(steps) => steps,
None => return Err(MethodError::NoMatch(Vec::new(), Vec::new(), mode)),
None =>return Err(MethodError::NoMatch(NoMatchData::new(Vec::new(), Vec::new(),
Vec::new(), mode))),
}
} else {
vec![CandidateStep {
@ -242,6 +251,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
steps: Rc::new(steps),
opt_simplified_steps: opt_simplified_steps,
static_candidates: Vec::new(),
unsatisfied_predicates: Vec::new(),
}
}
@ -563,7 +573,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
fn assemble_extension_candidates_for_traits_in_scope(&mut self,
expr_id: ast::NodeId)
-> Result<(),MethodError>
-> Result<(), MethodError<'tcx>>
{
let mut duplicates = HashSet::new();
let opt_applicable_traits = self.fcx.ccx.trait_map.get(&expr_id);
@ -577,7 +587,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
Ok(())
}
fn assemble_extension_candidates_for_all_traits(&mut self) -> Result<(),MethodError> {
fn assemble_extension_candidates_for_all_traits(&mut self) -> Result<(), MethodError<'tcx>> {
let mut duplicates = HashSet::new();
for trait_info in suggest::all_traits(self.fcx.ccx) {
if duplicates.insert(trait_info.def_id) {
@ -589,7 +599,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
fn assemble_extension_candidates_for_trait(&mut self,
trait_def_id: ast::DefId)
-> Result<(),MethodError>
-> Result<(), MethodError<'tcx>>
{
debug!("assemble_extension_candidates_for_trait(trait_def_id={:?})",
trait_def_id);
@ -709,7 +719,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
trait_def_id: ast::DefId,
item: ty::ImplOrTraitItem<'tcx>,
item_index: usize)
-> Result<(),MethodError>
-> Result<(), MethodError<'tcx>>
{
// Check if this is one of the Fn,FnMut,FnOnce traits.
let tcx = self.tcx();
@ -868,6 +878,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
}
let static_candidates = mem::replace(&mut self.static_candidates, vec![]);
let unsatisfied_predicates = mem::replace(&mut self.unsatisfied_predicates, vec![]);
// things failed, so lets look at all traits, for diagnostic purposes now:
self.reset();
@ -892,7 +903,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
}
}
}).collect(),
Some(Err(MethodError::NoMatch(_, others, _))) => {
Some(Err(MethodError::NoMatch(NoMatchData { out_of_scope_traits: others, .. }))) => {
assert!(others.is_empty());
vec![]
}
@ -903,7 +914,8 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
None => vec![],
};
Err(MethodError::NoMatch(static_candidates, out_of_scope_traits, self.mode))
Err(MethodError::NoMatch(NoMatchData::new(static_candidates, unsatisfied_predicates,
out_of_scope_traits, self.mode)))
}
fn pick_core(&mut self) -> Option<PickResult<'tcx>> {
@ -991,8 +1003,11 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
fn pick_method(&mut self, self_ty: Ty<'tcx>) -> Option<PickResult<'tcx>> {
debug!("pick_method(self_ty={})", self.infcx().ty_to_string(self_ty));
let mut possibly_unsatisfied_predicates = Vec::new();
debug!("searching inherent candidates");
match self.consider_candidates(self_ty, &self.inherent_candidates) {
match self.consider_candidates(self_ty, &self.inherent_candidates,
&mut possibly_unsatisfied_predicates) {
None => {}
Some(pick) => {
return Some(pick);
@ -1000,16 +1015,23 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
}
debug!("searching extension candidates");
self.consider_candidates(self_ty, &self.extension_candidates)
let res = self.consider_candidates(self_ty, &self.extension_candidates,
&mut possibly_unsatisfied_predicates);
if let None = res {
self.unsatisfied_predicates.extend(possibly_unsatisfied_predicates);
}
res
}
fn consider_candidates(&self,
self_ty: Ty<'tcx>,
probes: &[Candidate<'tcx>])
probes: &[Candidate<'tcx>],
possibly_unsatisfied_predicates: &mut Vec<TraitRef<'tcx>>)
-> Option<PickResult<'tcx>> {
let mut applicable_candidates: Vec<_> =
probes.iter()
.filter(|&probe| self.consider_probe(self_ty, probe))
.filter(|&probe| self.consider_probe(self_ty,
probe,possibly_unsatisfied_predicates))
.collect();
debug!("applicable_candidates: {:?}", applicable_candidates);
@ -1032,7 +1054,8 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
})
}
fn consider_probe(&self, self_ty: Ty<'tcx>, probe: &Candidate<'tcx>) -> bool {
fn consider_probe(&self, self_ty: Ty<'tcx>, probe: &Candidate<'tcx>,
possibly_unsatisfied_predicates: &mut Vec<TraitRef<'tcx>>) -> bool {
debug!("consider_probe: self_ty={:?} probe={:?}",
self_ty,
probe);
@ -1071,10 +1094,18 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
debug!("impl_obligations={:?}", obligations);
// Evaluate those obligations to see if they might possibly hold.
obligations.iter()
.chain(norm_obligations.iter()).chain(ref_obligations.iter())
.all(|o| selcx.evaluate_obligation(o))
let mut all_true = true;
for o in obligations.iter()
.chain(norm_obligations.iter())
.chain(ref_obligations.iter()) {
if !selcx.evaluate_obligation(o) {
all_true = false;
if let &ty::Predicate::Trait(ref pred) = &o.predicate {
possibly_unsatisfied_predicates.push(pred.0.trait_ref);
}
}
}
all_true
}
ProjectionCandidate(..) |

View file

@ -29,7 +29,7 @@ use syntax::print::pprust;
use std::cell;
use std::cmp::Ordering;
use super::{MethodError, CandidateSource, impl_item, trait_item};
use super::{MethodError, NoMatchData, CandidateSource, impl_item, trait_item};
use super::probe::Mode;
pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
@ -37,7 +37,7 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
rcvr_ty: Ty<'tcx>,
item_name: ast::Name,
rcvr_expr: Option<&ast::Expr>,
error: MethodError)
error: MethodError<'tcx>)
{
// avoid suggestions when we don't know what's going on.
if ty::type_is_error(rcvr_ty) {
@ -45,7 +45,10 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
}
match error {
MethodError::NoMatch(static_sources, out_of_scope_traits, mode) => {
MethodError::NoMatch(NoMatchData { static_candidates: static_sources,
unsatisfied_predicates,
out_of_scope_traits,
mode }) => {
let cx = fcx.tcx();
fcx.type_error_message(
@ -118,13 +121,28 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
}
if !static_sources.is_empty() {
fcx.tcx().sess.fileline_note(
cx.sess.fileline_note(
span,
"found defined static methods, maybe a `self` is missing?");
report_candidates(fcx, span, item_name, static_sources);
}
if !unsatisfied_predicates.is_empty() {
let bound_list = unsatisfied_predicates.iter()
.map(|p| format!("`{} : {}`",
p.self_ty(),
p))
.collect::<Vec<_>>()
.connect(", ");
cx.sess.fileline_note(
span,
&format!("the method `{}` exists but the \
following trait bounds were not satisfied: {}",
item_name,
bound_list));
}
suggest_traits_to_import(fcx, span, rcvr_ty, item_name,
rcvr_expr, out_of_scope_traits)
}

View file

@ -0,0 +1,18 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
struct Foo;
fn main() {
let a: Result<(), Foo> = Ok(());
a.unwrap();
//~^ ERROR no method named `unwrap` found for type `core::result::Result<(), Foo>`
//~| NOTE the following trait bounds were not satisfied: `Foo : core::fmt::Debug`
}