Suggest missing trait bounds when a method exists but the bounds aren't satisfied
This commit is contained in:
parent
5c5753e876
commit
a006a82724
4 changed files with 120 additions and 32 deletions
|
@ -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));
|
||||
|
|
|
@ -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(..) |
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
18
src/test/compile-fail/method-help-unsatisfied-bound.rs
Normal file
18
src/test/compile-fail/method-help-unsatisfied-bound.rs
Normal 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`
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue