1
Fork 0

Auto merge of #26435 - gsingh93:master, r=nikomatsakis

When a method exists in an impl but can not be used due to missing trait bounds for the type parameters, we should inform the user which trait bounds are missing.

For example, this code
```
// Note this is missing a Debug impl
struct Foo;

fn main() {
    let a: Result<(), Foo> = Ok(());
    a.unwrap()
}
```
Now gives the following error:
```
/home/gulshan/tmp/tmp.rs:6:7: 6:15 error: no method named `unwrap` found for type `core::result::Result<(), Foo>` in the current scope
/home/gulshan/tmp/tmp.rs:6     a.unwrap()
                                 ^~~~~~~~
/home/gulshan/tmp/tmp.rs:6:7: 6:15 note: The method `unwrap` exists but the following trait bounds were not satisfied: `Foo : core::fmt::Debug`
error: aborting due to previous error
```

Fixes https://github.com/rust-lang/rust/issues/20941.
This commit is contained in:
bors 2015-06-23 01:00:48 +00:00
commit cffaf0e7a8
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`
}