From a006a82724d1efec712f0f0d337aeeaf3f14258c Mon Sep 17 00:00:00 2001 From: Gulshan Singh Date: Thu, 18 Jun 2015 22:20:45 -0400 Subject: [PATCH] Suggest missing trait bounds when a method exists but the bounds aren't satisfied --- src/librustc_typeck/check/method/mod.rs | 39 ++++++++--- src/librustc_typeck/check/method/probe.rs | 69 ++++++++++++++----- src/librustc_typeck/check/method/suggest.rs | 26 +++++-- .../method-help-unsatisfied-bound.rs | 18 +++++ 4 files changed, 120 insertions(+), 32 deletions(-) create mode 100644 src/test/compile-fail/method-help-unsatisfied-bound.rs diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs index 85a4b02cf804e..e5a57fa9c138e 100644 --- a/src/librustc_typeck/check/method/mod.rs +++ b/src/librustc_typeck/check/method/mod.rs @@ -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, Vec, 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), @@ -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, + pub unsatisfied_predicates: Vec>, + pub out_of_scope_traits: Vec, + pub mode: probe::Mode +} + +impl<'tcx> NoMatchData<'tcx> { + pub fn new(static_candidates: Vec, + unsatisfied_predicates: Vec>, + out_of_scope_traits: Vec, + 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>, call_expr: &'tcx ast::Expr, self_expr: &'tcx ast::Expr) - -> Result, MethodError> + -> Result, 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)); diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index ecf6cad32be76..46bffa8ccabda 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -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>, extension_candidates: Vec>, impl_dups: HashSet, + + /// Collects near misses when the candidate functions are missing a `self` keyword and is only + /// used for error reporting static_candidates: Vec, + + /// Collects near misses when trait bounds for type parameters are unsatisfied and is only used + /// for error reporting + unsatisfied_predicates: Vec> } #[derive(Debug)] @@ -104,7 +112,7 @@ pub enum PickKind<'tcx> { WhereClausePick(/* Trait */ ty::PolyTraitRef<'tcx>, ItemIndex), } -pub type PickResult<'tcx> = Result, MethodError>; +pub type PickResult<'tcx> = Result, 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> { @@ -991,8 +1003,11 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> { fn pick_method(&mut self, self_ty: Ty<'tcx>) -> Option> { 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>) -> Option> { 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>) -> 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(..) | diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs index 4fbe42455ae86..2325b9852c73d 100644 --- a/src/librustc_typeck/check/method/suggest.rs +++ b/src/librustc_typeck/check/method/suggest.rs @@ -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::>() + .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) } diff --git a/src/test/compile-fail/method-help-unsatisfied-bound.rs b/src/test/compile-fail/method-help-unsatisfied-bound.rs new file mode 100644 index 0000000000000..375d5076ea2f8 --- /dev/null +++ b/src/test/compile-fail/method-help-unsatisfied-bound.rs @@ -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 or the MIT license +// , 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` +}