Skip to content

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

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 1 commit into from
Jun 23, 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
39 changes: 30 additions & 9 deletions src/librustc_typeck/check/method/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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>),
Expand All @@ -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),
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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));
Expand Down
69 changes: 50 additions & 19 deletions src/librustc_typeck/check/method/probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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)]
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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(),
}
}

Expand Down Expand Up @@ -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);
Expand All @@ -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) {
Expand All @@ -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);
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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();
Expand All @@ -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![]
}
Expand All @@ -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>> {
Expand Down Expand Up @@ -991,25 +1003,35 @@ 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);
}
}

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);
Expand All @@ -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);
Expand Down Expand Up @@ -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(..) |
Expand Down
26 changes: 22 additions & 4 deletions src/librustc_typeck/check/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,23 +29,26 @@ 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>,
span: Span,
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) {
return
}

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(
Expand Down Expand Up @@ -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)
}
Expand Down
18 changes: 18 additions & 0 deletions src/test/compile-fail/method-help-unsatisfied-bound.rs
Original file line number Diff line number Diff line change
@@ -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`
}