Skip to content

Commit 3d072a1

Browse files
committed
Auto merge of rust-lang#21675 - huonw:less-false-positives, r=nikomatsakis
That is, when offering suggestions for unresolved method calls, avoid suggesting traits for which implementing the trait for the receiver type either makes little sense (e.g. type errors, or sugared unboxed closures), or violates coherence. The latter is approximated by ensuring that at least one of `{receiver type, trait}` is local. This isn't precisely correct due to multidispatch, but the error messages one encounters in such situation are useless more often than not; it is better to be conservative and miss some cases, than have overly many false positives (e.g. writing `some_slice.map(|x| ...)` uselessly suggested that one should implement `IteratorExt` for `&[T]`, while the correct fix is to call `.iter()`). Closes rust-lang#21420.
2 parents cfc9109 + e81ae40 commit 3d072a1

File tree

4 files changed

+138
-7
lines changed

4 files changed

+138
-7
lines changed

src/librustc_typeck/check/method/suggest.rs

+57-3
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
3636
callee_expr: &ast::Expr,
3737
error: MethodError)
3838
{
39+
// avoid suggestions when we don't know what's going on.
40+
if ty::type_is_error(rcvr_ty) {
41+
return
42+
}
43+
3944
match error {
4045
MethodError::NoMatch(static_sources, out_of_scope_traits) => {
4146
let cx = fcx.tcx();
@@ -149,7 +154,7 @@ pub type AllTraitsVec = Vec<TraitInfo>;
149154

150155
fn suggest_traits_to_import<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
151156
span: Span,
152-
_rcvr_ty: Ty<'tcx>,
157+
rcvr_ty: Ty<'tcx>,
153158
method_name: ast::Name,
154159
valid_out_of_scope_traits: Vec<ast::DefId>)
155160
{
@@ -179,16 +184,32 @@ fn suggest_traits_to_import<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
179184
return
180185
}
181186

182-
// there's no implemented traits, so lets suggest some traits to implement
187+
let type_is_local = type_derefs_to_local(fcx, span, rcvr_ty);
188+
189+
// there's no implemented traits, so lets suggest some traits to
190+
// implement, by finding ones that have the method name, and are
191+
// legal to implement.
183192
let mut candidates = all_traits(fcx.ccx)
184-
.filter(|info| trait_method(tcx, info.def_id, method_name).is_some())
193+
.filter(|info| {
194+
// we approximate the coherence rules to only suggest
195+
// traits that are legal to implement by requiring that
196+
// either the type or trait is local. Multidispatch means
197+
// this isn't perfect (that is, there are cases when
198+
// implementing a trait would be legal but is rejected
199+
// here).
200+
(type_is_local || ast_util::is_local(info.def_id))
201+
&& trait_method(tcx, info.def_id, method_name).is_some()
202+
})
185203
.collect::<Vec<_>>();
186204

187205
if candidates.len() > 0 {
188206
// sort from most relevant to least relevant
189207
candidates.sort_by(|a, b| a.cmp(b).reverse());
190208
candidates.dedup();
191209

210+
// FIXME #21673 this help message could be tuned to the case
211+
// of a type parameter: suggest adding a trait bound rather
212+
// than implementing.
192213
let msg = format!(
193214
"methods from traits can only be called if the trait is implemented and in scope; \
194215
the following {traits_define} a method `{name}`, \
@@ -208,6 +229,39 @@ fn suggest_traits_to_import<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
208229
}
209230
}
210231

232+
/// Checks whether there is a local type somewhere in the chain of
233+
/// autoderefs of `rcvr_ty`.
234+
fn type_derefs_to_local<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
235+
span: Span,
236+
rcvr_ty: Ty<'tcx>) -> bool {
237+
check::autoderef(fcx, span, rcvr_ty, None,
238+
check::UnresolvedTypeAction::Ignore, check::NoPreference,
239+
|&: ty, _| {
240+
let is_local = match ty.sty {
241+
ty::ty_enum(did, _) | ty::ty_struct(did, _) => ast_util::is_local(did),
242+
243+
ty::ty_trait(ref tr) => ast_util::is_local(tr.principal_def_id()),
244+
245+
ty::ty_param(_) => true,
246+
247+
// the user cannot implement traits for unboxed closures, so
248+
// there's no point suggesting anything at all, local or not.
249+
ty::ty_closure(..) => return Some(false),
250+
251+
// everything else (primitive types etc.) is effectively
252+
// non-local (there are "edge" cases, e.g. (LocalType,), but
253+
// the noise from these sort of types is usually just really
254+
// annoying, rather than any sort of help).
255+
_ => false
256+
};
257+
if is_local {
258+
Some(true)
259+
} else {
260+
None
261+
}
262+
}).2.unwrap_or(false)
263+
}
264+
211265
#[derive(Copy)]
212266
pub struct TraitInfo {
213267
pub def_id: ast::DefId,

src/test/auxiliary/no_method_suggested_traits.rs

+3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010

1111
pub use reexport::Reexported;
1212

13+
pub struct Foo;
14+
pub enum Bar { X }
15+
1316
pub mod foo {
1417
pub trait PubPub {
1518
fn method(&self) {}

src/test/compile-fail/method-suggestion-no-duplication.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010

1111
// issue #21405
1212

13-
fn foo<F>(f: F) where F: FnMut(usize) {}
13+
struct Foo;
14+
15+
fn foo<F>(f: F) where F: FnMut(Foo) {}
1416

1517
fn main() {
1618
foo(|s| s.is_empty());

src/test/compile-fail/no-method-suggested-traits.rs

+75-3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212

1313
extern crate no_method_suggested_traits;
1414

15+
struct Foo;
16+
enum Bar { X }
17+
1518
mod foo {
1619
trait Bar {
1720
fn method(&self) {}
@@ -25,23 +28,48 @@ mod foo {
2528
}
2629

2730
fn main() {
31+
// test the values themselves, and autoderef.
32+
33+
2834
1u32.method();
2935
//~^ HELP following traits are implemented but not in scope, perhaps add a `use` for one of them
3036
//~^^ ERROR does not implement
3137
//~^^^ HELP `foo::Bar`
3238
//~^^^^ HELP `no_method_suggested_traits::foo::PubPub`
39+
std::rc::Rc::new(&mut Box::new(&1u32)).method();
40+
//~^ HELP following traits are implemented but not in scope, perhaps add a `use` for one of them
41+
//~^^ ERROR does not implement
42+
//~^^^ HELP `foo::Bar`
43+
//~^^^^ HELP `no_method_suggested_traits::foo::PubPub`
3344

3445
'a'.method();
3546
//~^ ERROR does not implement
3647
//~^^ HELP the following trait is implemented but not in scope, perhaps add a `use` for it:
3748
//~^^^ HELP `foo::Bar`
49+
std::rc::Rc::new(&mut Box::new(&'a')).method();
50+
//~^ ERROR does not implement
51+
//~^^ HELP the following trait is implemented but not in scope, perhaps add a `use` for it:
52+
//~^^^ HELP `foo::Bar`
3853

3954
1i32.method();
4055
//~^ ERROR does not implement
4156
//~^^ HELP the following trait is implemented but not in scope, perhaps add a `use` for it:
4257
//~^^^ HELP `no_method_suggested_traits::foo::PubPub`
58+
std::rc::Rc::new(&mut Box::new(&1i32)).method();
59+
//~^ ERROR does not implement
60+
//~^^ HELP the following trait is implemented but not in scope, perhaps add a `use` for it:
61+
//~^^^ HELP `no_method_suggested_traits::foo::PubPub`
4362

44-
1u64.method();
63+
Foo.method();
64+
//~^ ERROR does not implement
65+
//~^^ HELP following traits define a method `method`, perhaps you need to implement one of them
66+
//~^^^ HELP `foo::Bar`
67+
//~^^^^ HELP `no_method_suggested_traits::foo::PubPub`
68+
//~^^^^^ HELP `no_method_suggested_traits::reexport::Reexported`
69+
//~^^^^^^ HELP `no_method_suggested_traits::bar::PubPriv`
70+
//~^^^^^^^ HELP `no_method_suggested_traits::qux::PrivPub`
71+
//~^^^^^^^^ HELP `no_method_suggested_traits::quz::PrivPriv`
72+
std::rc::Rc::new(&mut Box::new(&Foo)).method();
4573
//~^ ERROR does not implement
4674
//~^^ HELP following traits define a method `method`, perhaps you need to implement one of them
4775
//~^^^ HELP `foo::Bar`
@@ -55,8 +83,52 @@ fn main() {
5583
//~^ ERROR does not implement
5684
//~^^ HELP the following trait defines a method `method2`, perhaps you need to implement it
5785
//~^^^ HELP `foo::Bar`
58-
1u64.method3();
86+
std::rc::Rc::new(&mut Box::new(&1u64)).method2();
5987
//~^ ERROR does not implement
60-
//~^^ HELP the following trait defines a method `method3`, perhaps you need to implement it
88+
//~^^ HELP the following trait defines a method `method2`, perhaps you need to implement it
89+
//~^^^ HELP `foo::Bar`
90+
91+
no_method_suggested_traits::Foo.method2();
92+
//~^ ERROR does not implement
93+
//~^^ HELP following trait defines a method `method2`, perhaps you need to implement it
94+
//~^^^ HELP `foo::Bar`
95+
std::rc::Rc::new(&mut Box::new(&no_method_suggested_traits::Foo)).method2();
96+
//~^ ERROR does not implement
97+
//~^^ HELP following trait defines a method `method2`, perhaps you need to implement it
98+
//~^^^ HELP `foo::Bar`
99+
no_method_suggested_traits::Bar::X.method2();
100+
//~^ ERROR does not implement
101+
//~^^ HELP following trait defines a method `method2`, perhaps you need to implement it
102+
//~^^^ HELP `foo::Bar`
103+
std::rc::Rc::new(&mut Box::new(&no_method_suggested_traits::Bar::X)).method2();
104+
//~^ ERROR does not implement
105+
//~^^ HELP following trait defines a method `method2`, perhaps you need to implement it
106+
//~^^^ HELP `foo::Bar`
107+
108+
Foo.method3();
109+
//~^ ERROR does not implement
110+
//~^^ HELP following trait defines a method `method3`, perhaps you need to implement it
111+
//~^^^ HELP `no_method_suggested_traits::foo::PubPub`
112+
std::rc::Rc::new(&mut Box::new(&Foo)).method3();
113+
//~^ ERROR does not implement
114+
//~^^ HELP following trait defines a method `method3`, perhaps you need to implement it
115+
//~^^^ HELP `no_method_suggested_traits::foo::PubPub`
116+
Bar::X.method3();
117+
//~^ ERROR does not implement
118+
//~^^ HELP following trait defines a method `method3`, perhaps you need to implement it
61119
//~^^^ HELP `no_method_suggested_traits::foo::PubPub`
120+
std::rc::Rc::new(&mut Box::new(&Bar::X)).method3();
121+
//~^ ERROR does not implement
122+
//~^^ HELP following trait defines a method `method3`, perhaps you need to implement it
123+
//~^^^ HELP `no_method_suggested_traits::foo::PubPub`
124+
125+
// should have no help:
126+
1us.method3(); //~ ERROR does not implement
127+
std::rc::Rc::new(&mut Box::new(&1us)).method3(); //~ ERROR does not implement
128+
no_method_suggested_traits::Foo.method3(); //~ ERROR does not implement
129+
std::rc::Rc::new(&mut Box::new(&no_method_suggested_traits::Foo)).method3();
130+
//~^ ERROR does not implement
131+
no_method_suggested_traits::Bar::X.method3(); //~ ERROR does not implement
132+
std::rc::Rc::new(&mut Box::new(&no_method_suggested_traits::Bar::X)).method3();
133+
//~^ ERROR does not implement
62134
}

0 commit comments

Comments
 (0)