Skip to content

Commit a006a82

Browse files
committed
Suggest missing trait bounds when a method exists but the bounds aren't satisfied
1 parent 5c5753e commit a006a82

File tree

4 files changed

+120
-32
lines changed

4 files changed

+120
-32
lines changed

src/librustc_typeck/check/method/mod.rs

+30-9
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use middle::def;
1616
use middle::privacy::{AllPublic, DependsOn, LastPrivate, LastMod};
1717
use middle::subst;
1818
use middle::traits;
19-
use middle::ty::{self, AsPredicate, ToPolyTraitRef};
19+
use middle::ty::{self, AsPredicate, ToPolyTraitRef, TraitRef};
2020
use middle::infer;
2121

2222
use syntax::ast::DefId;
@@ -32,11 +32,9 @@ mod confirm;
3232
mod probe;
3333
mod suggest;
3434

35-
pub enum MethodError {
36-
// Did not find an applicable method, but we did find various
37-
// static methods that may apply, as well as a list of
38-
// not-in-scope traits which may work.
39-
NoMatch(Vec<CandidateSource>, Vec<ast::DefId>, probe::Mode),
35+
pub enum MethodError<'tcx> {
36+
// Did not find an applicable method, but we did find various near-misses that may work.
37+
NoMatch(NoMatchData<'tcx>),
4038

4139
// Multiple methods might apply.
4240
Ambiguity(Vec<CandidateSource>),
@@ -45,9 +43,32 @@ pub enum MethodError {
4543
ClosureAmbiguity(/* DefId of fn trait */ ast::DefId),
4644
}
4745

46+
// Contains a list of static methods that may apply, a list of unsatisfied trait predicates which
47+
// could lead to matches if satisfied, and a list of not-in-scope traits which may work.
48+
pub struct NoMatchData<'tcx> {
49+
pub static_candidates: Vec<CandidateSource>,
50+
pub unsatisfied_predicates: Vec<TraitRef<'tcx>>,
51+
pub out_of_scope_traits: Vec<ast::DefId>,
52+
pub mode: probe::Mode
53+
}
54+
55+
impl<'tcx> NoMatchData<'tcx> {
56+
pub fn new(static_candidates: Vec<CandidateSource>,
57+
unsatisfied_predicates: Vec<TraitRef<'tcx>>,
58+
out_of_scope_traits: Vec<ast::DefId>,
59+
mode: probe::Mode) -> Self {
60+
NoMatchData {
61+
static_candidates: static_candidates,
62+
unsatisfied_predicates: unsatisfied_predicates,
63+
out_of_scope_traits: out_of_scope_traits,
64+
mode: mode
65+
}
66+
}
67+
}
68+
4869
// A pared down enum describing just the places from which a method
4970
// candidate can arise. Used for error reporting only.
50-
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq)]
71+
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
5172
pub enum CandidateSource {
5273
ImplSource(ast::DefId),
5374
TraitSource(/* trait id */ ast::DefId),
@@ -93,7 +114,7 @@ pub fn lookup<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
93114
supplied_method_types: Vec<ty::Ty<'tcx>>,
94115
call_expr: &'tcx ast::Expr,
95116
self_expr: &'tcx ast::Expr)
96-
-> Result<ty::MethodCallee<'tcx>, MethodError>
117+
-> Result<ty::MethodCallee<'tcx>, MethodError<'tcx>>
97118
{
98119
debug!("lookup(method_name={}, self_ty={:?}, call_expr={:?}, self_expr={:?})",
99120
method_name,
@@ -305,7 +326,7 @@ pub fn resolve_ufcs<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
305326
method_name: ast::Name,
306327
self_ty: ty::Ty<'tcx>,
307328
expr_id: ast::NodeId)
308-
-> Result<(def::Def, LastPrivate), MethodError>
329+
-> Result<(def::Def, LastPrivate), MethodError<'tcx>>
309330
{
310331
let mode = probe::Mode::Path;
311332
let pick = try!(probe::probe(fcx, span, mode, method_name, self_ty, expr_id));

src/librustc_typeck/check/method/probe.rs

+50-19
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@
99
// except according to those terms.
1010

1111
use super::MethodError;
12+
use super::NoMatchData;
1213
use super::ItemIndex;
13-
use super::{CandidateSource,ImplSource,TraitSource};
14+
use super::{CandidateSource, ImplSource, TraitSource};
1415
use super::suggest;
1516

1617
use check;
@@ -19,7 +20,7 @@ use middle::fast_reject;
1920
use middle::subst;
2021
use middle::subst::Subst;
2122
use middle::traits;
22-
use middle::ty::{self, RegionEscape, Ty, ToPolyTraitRef};
23+
use middle::ty::{self, RegionEscape, Ty, ToPolyTraitRef, TraitRef};
2324
use middle::ty_fold::TypeFoldable;
2425
use middle::infer;
2526
use middle::infer::InferCtxt;
@@ -42,7 +43,14 @@ struct ProbeContext<'a, 'tcx:'a> {
4243
inherent_candidates: Vec<Candidate<'tcx>>,
4344
extension_candidates: Vec<Candidate<'tcx>>,
4445
impl_dups: HashSet<ast::DefId>,
46+
47+
/// Collects near misses when the candidate functions are missing a `self` keyword and is only
48+
/// used for error reporting
4549
static_candidates: Vec<CandidateSource>,
50+
51+
/// Collects near misses when trait bounds for type parameters are unsatisfied and is only used
52+
/// for error reporting
53+
unsatisfied_predicates: Vec<TraitRef<'tcx>>
4654
}
4755

4856
#[derive(Debug)]
@@ -104,7 +112,7 @@ pub enum PickKind<'tcx> {
104112
WhereClausePick(/* Trait */ ty::PolyTraitRef<'tcx>, ItemIndex),
105113
}
106114

107-
pub type PickResult<'tcx> = Result<Pick<'tcx>, MethodError>;
115+
pub type PickResult<'tcx> = Result<Pick<'tcx>, MethodError<'tcx>>;
108116

109117
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
110118
pub enum Mode {
@@ -141,7 +149,8 @@ pub fn probe<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
141149
let steps = if mode == Mode::MethodCall {
142150
match create_steps(fcx, span, self_ty) {
143151
Some(steps) => steps,
144-
None => return Err(MethodError::NoMatch(Vec::new(), Vec::new(), mode)),
152+
None =>return Err(MethodError::NoMatch(NoMatchData::new(Vec::new(), Vec::new(),
153+
Vec::new(), mode))),
145154
}
146155
} else {
147156
vec![CandidateStep {
@@ -242,6 +251,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
242251
steps: Rc::new(steps),
243252
opt_simplified_steps: opt_simplified_steps,
244253
static_candidates: Vec::new(),
254+
unsatisfied_predicates: Vec::new(),
245255
}
246256
}
247257

@@ -563,7 +573,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
563573

564574
fn assemble_extension_candidates_for_traits_in_scope(&mut self,
565575
expr_id: ast::NodeId)
566-
-> Result<(),MethodError>
576+
-> Result<(), MethodError<'tcx>>
567577
{
568578
let mut duplicates = HashSet::new();
569579
let opt_applicable_traits = self.fcx.ccx.trait_map.get(&expr_id);
@@ -577,7 +587,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
577587
Ok(())
578588
}
579589

580-
fn assemble_extension_candidates_for_all_traits(&mut self) -> Result<(),MethodError> {
590+
fn assemble_extension_candidates_for_all_traits(&mut self) -> Result<(), MethodError<'tcx>> {
581591
let mut duplicates = HashSet::new();
582592
for trait_info in suggest::all_traits(self.fcx.ccx) {
583593
if duplicates.insert(trait_info.def_id) {
@@ -589,7 +599,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
589599

590600
fn assemble_extension_candidates_for_trait(&mut self,
591601
trait_def_id: ast::DefId)
592-
-> Result<(),MethodError>
602+
-> Result<(), MethodError<'tcx>>
593603
{
594604
debug!("assemble_extension_candidates_for_trait(trait_def_id={:?})",
595605
trait_def_id);
@@ -709,7 +719,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
709719
trait_def_id: ast::DefId,
710720
item: ty::ImplOrTraitItem<'tcx>,
711721
item_index: usize)
712-
-> Result<(),MethodError>
722+
-> Result<(), MethodError<'tcx>>
713723
{
714724
// Check if this is one of the Fn,FnMut,FnOnce traits.
715725
let tcx = self.tcx();
@@ -868,6 +878,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
868878
}
869879

870880
let static_candidates = mem::replace(&mut self.static_candidates, vec![]);
881+
let unsatisfied_predicates = mem::replace(&mut self.unsatisfied_predicates, vec![]);
871882

872883
// things failed, so lets look at all traits, for diagnostic purposes now:
873884
self.reset();
@@ -892,7 +903,7 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
892903
}
893904
}
894905
}).collect(),
895-
Some(Err(MethodError::NoMatch(_, others, _))) => {
906+
Some(Err(MethodError::NoMatch(NoMatchData { out_of_scope_traits: others, .. }))) => {
896907
assert!(others.is_empty());
897908
vec![]
898909
}
@@ -903,7 +914,8 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
903914
None => vec![],
904915
};
905916

906-
Err(MethodError::NoMatch(static_candidates, out_of_scope_traits, self.mode))
917+
Err(MethodError::NoMatch(NoMatchData::new(static_candidates, unsatisfied_predicates,
918+
out_of_scope_traits, self.mode)))
907919
}
908920

909921
fn pick_core(&mut self) -> Option<PickResult<'tcx>> {
@@ -991,25 +1003,35 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
9911003
fn pick_method(&mut self, self_ty: Ty<'tcx>) -> Option<PickResult<'tcx>> {
9921004
debug!("pick_method(self_ty={})", self.infcx().ty_to_string(self_ty));
9931005

1006+
let mut possibly_unsatisfied_predicates = Vec::new();
1007+
9941008
debug!("searching inherent candidates");
995-
match self.consider_candidates(self_ty, &self.inherent_candidates) {
1009+
match self.consider_candidates(self_ty, &self.inherent_candidates,
1010+
&mut possibly_unsatisfied_predicates) {
9961011
None => {}
9971012
Some(pick) => {
9981013
return Some(pick);
9991014
}
10001015
}
10011016

10021017
debug!("searching extension candidates");
1003-
self.consider_candidates(self_ty, &self.extension_candidates)
1018+
let res = self.consider_candidates(self_ty, &self.extension_candidates,
1019+
&mut possibly_unsatisfied_predicates);
1020+
if let None = res {
1021+
self.unsatisfied_predicates.extend(possibly_unsatisfied_predicates);
1022+
}
1023+
res
10041024
}
10051025

10061026
fn consider_candidates(&self,
10071027
self_ty: Ty<'tcx>,
1008-
probes: &[Candidate<'tcx>])
1028+
probes: &[Candidate<'tcx>],
1029+
possibly_unsatisfied_predicates: &mut Vec<TraitRef<'tcx>>)
10091030
-> Option<PickResult<'tcx>> {
10101031
let mut applicable_candidates: Vec<_> =
10111032
probes.iter()
1012-
.filter(|&probe| self.consider_probe(self_ty, probe))
1033+
.filter(|&probe| self.consider_probe(self_ty,
1034+
probe,possibly_unsatisfied_predicates))
10131035
.collect();
10141036

10151037
debug!("applicable_candidates: {:?}", applicable_candidates);
@@ -1032,7 +1054,8 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
10321054
})
10331055
}
10341056

1035-
fn consider_probe(&self, self_ty: Ty<'tcx>, probe: &Candidate<'tcx>) -> bool {
1057+
fn consider_probe(&self, self_ty: Ty<'tcx>, probe: &Candidate<'tcx>,
1058+
possibly_unsatisfied_predicates: &mut Vec<TraitRef<'tcx>>) -> bool {
10361059
debug!("consider_probe: self_ty={:?} probe={:?}",
10371060
self_ty,
10381061
probe);
@@ -1071,10 +1094,18 @@ impl<'a,'tcx> ProbeContext<'a,'tcx> {
10711094
debug!("impl_obligations={:?}", obligations);
10721095

10731096
// Evaluate those obligations to see if they might possibly hold.
1074-
obligations.iter()
1075-
.chain(norm_obligations.iter()).chain(ref_obligations.iter())
1076-
.all(|o| selcx.evaluate_obligation(o))
1077-
1097+
let mut all_true = true;
1098+
for o in obligations.iter()
1099+
.chain(norm_obligations.iter())
1100+
.chain(ref_obligations.iter()) {
1101+
if !selcx.evaluate_obligation(o) {
1102+
all_true = false;
1103+
if let &ty::Predicate::Trait(ref pred) = &o.predicate {
1104+
possibly_unsatisfied_predicates.push(pred.0.trait_ref);
1105+
}
1106+
}
1107+
}
1108+
all_true
10781109
}
10791110

10801111
ProjectionCandidate(..) |

src/librustc_typeck/check/method/suggest.rs

+22-4
Original file line numberDiff line numberDiff line change
@@ -29,23 +29,26 @@ use syntax::print::pprust;
2929
use std::cell;
3030
use std::cmp::Ordering;
3131

32-
use super::{MethodError, CandidateSource, impl_item, trait_item};
32+
use super::{MethodError, NoMatchData, CandidateSource, impl_item, trait_item};
3333
use super::probe::Mode;
3434

3535
pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
3636
span: Span,
3737
rcvr_ty: Ty<'tcx>,
3838
item_name: ast::Name,
3939
rcvr_expr: Option<&ast::Expr>,
40-
error: MethodError)
40+
error: MethodError<'tcx>)
4141
{
4242
// avoid suggestions when we don't know what's going on.
4343
if ty::type_is_error(rcvr_ty) {
4444
return
4545
}
4646

4747
match error {
48-
MethodError::NoMatch(static_sources, out_of_scope_traits, mode) => {
48+
MethodError::NoMatch(NoMatchData { static_candidates: static_sources,
49+
unsatisfied_predicates,
50+
out_of_scope_traits,
51+
mode }) => {
4952
let cx = fcx.tcx();
5053

5154
fcx.type_error_message(
@@ -118,13 +121,28 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
118121
}
119122

120123
if !static_sources.is_empty() {
121-
fcx.tcx().sess.fileline_note(
124+
cx.sess.fileline_note(
122125
span,
123126
"found defined static methods, maybe a `self` is missing?");
124127

125128
report_candidates(fcx, span, item_name, static_sources);
126129
}
127130

131+
if !unsatisfied_predicates.is_empty() {
132+
let bound_list = unsatisfied_predicates.iter()
133+
.map(|p| format!("`{} : {}`",
134+
p.self_ty(),
135+
p))
136+
.collect::<Vec<_>>()
137+
.connect(", ");
138+
cx.sess.fileline_note(
139+
span,
140+
&format!("the method `{}` exists but the \
141+
following trait bounds were not satisfied: {}",
142+
item_name,
143+
bound_list));
144+
}
145+
128146
suggest_traits_to_import(fcx, span, rcvr_ty, item_name,
129147
rcvr_expr, out_of_scope_traits)
130148
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
struct Foo;
12+
13+
fn main() {
14+
let a: Result<(), Foo> = Ok(());
15+
a.unwrap();
16+
//~^ ERROR no method named `unwrap` found for type `core::result::Result<(), Foo>`
17+
//~| NOTE the following trait bounds were not satisfied: `Foo : core::fmt::Debug`
18+
}

0 commit comments

Comments
 (0)