Skip to content

Commit 3a89b6e

Browse files
committed
Try to only suggest traits that can be implemented for method calls.
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 #21420.
1 parent 849a38a commit 3a89b6e

File tree

3 files changed

+67
-6
lines changed

3 files changed

+67
-6
lines changed

src/librustc_typeck/check/method/suggest.rs

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ pub fn report_error<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
3434
method_name: ast::Name,
3535
error: MethodError)
3636
{
37+
// avoid suggestions when we don't know what's going on.
38+
if ty::type_is_error(rcvr_ty) {
39+
return
40+
}
41+
3742
match error {
3843
MethodError::NoMatch(static_sources, out_of_scope_traits) => {
3944
let cx = fcx.tcx();
@@ -135,7 +140,7 @@ pub type AllTraitsVec = Vec<TraitInfo>;
135140

136141
fn suggest_traits_to_import<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
137142
span: Span,
138-
_rcvr_ty: Ty<'tcx>,
143+
rcvr_ty: Ty<'tcx>,
139144
method_name: ast::Name,
140145
valid_out_of_scope_traits: Vec<ast::DefId>)
141146
{
@@ -165,9 +170,40 @@ fn suggest_traits_to_import<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
165170
return
166171
}
167172

168-
// there's no implemented traits, so lets suggest some traits to implement
173+
let type_is_local = match rcvr_ty.sty {
174+
ty::ty_enum(did, _) | ty::ty_struct(did, _) => ast_util::is_local(did),
175+
176+
ty::ty_trait(ref tr) => ast_util::is_local(tr.principal_def_id()),
177+
178+
// these presumably just need to be bounded appropriately
179+
// FIXME #21673 the help message could be tuned to this case.
180+
ty::ty_param(_) => true,
181+
182+
// the user cannot implement traits for unboxed closures, so
183+
// there's no point suggesting anything at all, local or not.
184+
ty::ty_unboxed_closure(..) => return,
185+
186+
// everything else (primitive types etc.) is effectively
187+
// non-local (there are "edge" cases, e.g. (LocalType,), but
188+
// the noise from these sort of types is usually just really
189+
// annoying, rather than any sort of help).
190+
_ => false
191+
};
192+
193+
// there's no implemented traits, so lets suggest some traits to
194+
// implement, by finding ones that have the method name, and are
195+
// legal to implement.
169196
let mut candidates = all_traits(fcx.ccx)
170-
.filter(|info| trait_method(tcx, info.def_id, method_name).is_some())
197+
.filter(|info| {
198+
// we approximate the coherence rules to only suggest
199+
// traits that are legal to implement by requiring that
200+
// either the type or trait is local. Multidispatch means
201+
// this isn't perfect (that is, there are cases when
202+
// implementing a trait would be legal but is rejected
203+
// here).
204+
(type_is_local || ast_util::is_local(info.def_id))
205+
&& trait_method(tcx, info.def_id, method_name).is_some()
206+
})
171207
.collect::<Vec<_>>();
172208

173209
if candidates.len() > 0 {

src/test/auxiliary/no_method_suggested_traits.rs

Lines changed: 3 additions & 0 deletions
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/no-method-suggested-traits.rs

Lines changed: 25 additions & 3 deletions
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) {}
@@ -41,7 +44,7 @@ fn main() {
4144
//~^^ HELP the following trait is implemented but not in scope, perhaps add a `use` for it:
4245
//~^^^ HELP `no_method_suggested_traits::foo::PubPub`
4346

44-
1u64.method();
47+
Foo.method();
4548
//~^ ERROR does not implement
4649
//~^^ HELP following traits define a method `method`, perhaps you need to implement one of them
4750
//~^^^ HELP `foo::Bar`
@@ -55,8 +58,27 @@ fn main() {
5558
//~^ ERROR does not implement
5659
//~^^ HELP the following trait defines a method `method2`, perhaps you need to implement it
5760
//~^^^ HELP `foo::Bar`
58-
1u64.method3();
61+
62+
no_method_suggested_traits::Foo.method2();
63+
//~^ ERROR does not implement
64+
//~^^ HELP following trait defines a method `method2`, perhaps you need to implement it
65+
//~^^^ HELP `foo::Bar`
66+
no_method_suggested_traits::Bar::X.method2();
67+
//~^ ERROR does not implement
68+
//~^^ HELP following trait defines a method `method2`, perhaps you need to implement it
69+
//~^^^ HELP `foo::Bar`
70+
71+
Foo.method3();
5972
//~^ ERROR does not implement
60-
//~^^ HELP the following trait defines a method `method3`, perhaps you need to implement it
73+
//~^^ HELP following trait defines a method `method3`, perhaps you need to implement it
6174
//~^^^ HELP `no_method_suggested_traits::foo::PubPub`
75+
Bar::X.method3();
76+
//~^ ERROR does not implement
77+
//~^^ HELP following trait defines a method `method3`, perhaps you need to implement it
78+
//~^^^ HELP `no_method_suggested_traits::foo::PubPub`
79+
80+
// should have no help:
81+
1us.method3(); //~ ERROR does not implement
82+
no_method_suggested_traits::Foo.method3(); //~ ERROR does not implement
83+
no_method_suggested_traits::Bar::X.method3(); //~ ERROR does not implement
6284
}

0 commit comments

Comments
 (0)