Skip to content

E0599 suggestions and elision of generic argument if no canditate is found #84221

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 5 commits into from
May 27, 2021
Merged
Show file tree
Hide file tree
Changes from 2 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
106 changes: 105 additions & 1 deletion compiler/rustc_typeck/src/check/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return None;
} else {
span = item_name.span;

// issue #81576, elision of generic argument when no methode can be found in any implementation
let mut ty_str_reported = ty_str.clone();
if let ty::Adt(_, ref generics) = actual.kind() {
if generics.len() > 0 {
let candidate_numbers: usize = self
.autoderef(span, actual)
.map(|(ty, _)| {
if let ty::Adt(ref adt_deref, _) = ty.kind() {
self.tcx
.inherent_impls(adt_deref.did)
.iter()
.filter_map(|def_id| {
self.associated_item(
*def_id,
item_name,
Namespace::ValueNS,
)
})
.count()
} else {
0
}
})
.sum();
if candidate_numbers == 0 && unsatisfied_predicates.is_empty() {
if let Some((path_string, _)) = ty_str.split_once('<') {
ty_str_reported = path_string.to_string();
}
Comment on lines +415 to +417
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally we'd just take the name ident of the type and use that. I think item_name.to_string() would work here.

String operations are brittle and can break as things change in the codebase.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

item_name is the method we are looking to call. I did not find a way to print the type without the generic parameter otherwise. Do you know a way to do it ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the type of item_name from which you get span from?

https://github.com/rust-lang/rust/pull/84221/files/6efa14b3add08188c5322db8694a8cbbea7851e5#diff-e8458f9222609acec4270bb72dcf235e7aa575268adff217d113052c3751c53cL385

I think that might have a string representation, and if not a field that is only the name with type Ident, which has a string representation of only the identifier.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it is an Ident, but this does not represent the type. This is the indent of the method

For instance it is used here:
https://github.com/ABouttefeux/rust/blob/120691c590c4309fda31994931b9a561b4249c33/compiler/rustc_typeck/src/check/method/suggest.rs#L422-L431

}
}
}

let mut err = struct_span_err!(
tcx.sess,
span,
Expand All @@ -391,7 +424,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
item_kind,
item_name,
actual.prefix_string(self.tcx),
ty_str,
ty_str_reported,
);
if let Mode::MethodCall = mode {
if let SelfSource::MethodCall(call) = source {
Expand Down Expand Up @@ -449,6 +482,77 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let mut label_span_not_found = || {
if unsatisfied_predicates.is_empty() {
err.span_label(span, format!("{item_kind} not found in `{ty_str}`"));
if let ty::Adt(ref adt, _) = rcvr_ty.kind() {
let mut inherent_impls_candidate = self
.tcx
.inherent_impls(adt.did)
.iter()
.copied()
.filter(|def_id| {
if let Some(assoc) =
self.associated_item(*def_id, item_name, Namespace::ValueNS)
{
// Check for both mode is the same so we avoid suggesting
// incorect associated item.
match (mode, assoc.fn_has_self_parameter) {
(Mode::MethodCall, true) => {
if let SelfSource::MethodCall(_) = source {
// We check that the suggest type is actually
// different from the received one
// So we avoid suggestion method with Box<Self>
// for instance
self.tcx.at(span).type_of(*def_id) != actual
&& self.tcx.at(span).type_of(*def_id)
!= rcvr_ty
} else {
false
}
}
(Mode::Path, false) => true,
_ => false,
}
} else {
false
}
})
.collect::<Vec<_>>();
if inherent_impls_candidate.len() > 0 {
inherent_impls_candidate.sort();
inherent_impls_candidate.dedup();
// number of type to shows at most.
const LIMIT: usize = 3;
let mut note = format!("The {item_kind} was found for");
if inherent_impls_candidate.len() > 1 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it might be better to make a list of items instead of enumerating them in text, because that is easier to scan when reading the error.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might work better as well in the case of really long type names with lots of type parameters (that we see in the wild).

for impl_item in inherent_impls_candidate.iter().take(LIMIT - 2)
{
let impl_ty = self.tcx.at(span).type_of(*impl_item);
note = format!("{} {},", note, impl_ty);
}
let impl_ty = self.tcx.at(span).type_of(
inherent_impls_candidate
[inherent_impls_candidate.len() - 1],
);
if inherent_impls_candidate.len() > LIMIT {
note = format!("{} {},", note, impl_ty);
} else {
note = format!("{} {} and", note, impl_ty);
}
}
let impl_ty = self
.tcx
.at(span)
.type_of(*inherent_impls_candidate.last().unwrap());
note = format!("{} {}", note, impl_ty);
if inherent_impls_candidate.len() > LIMIT {
note = format!(
"{} and {} more",
note,
inherent_impls_candidate.len() - LIMIT
);
}
err.note(&format!("{}.", note));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't capitalize or have a final period on sentences. We only break that convention if we have long text with multiple sentences, but it is heavily frown upon.

}
}
} else {
err.span_label(span, format!("{item_kind} cannot be called on `{ty_str}` due to unsatisfied trait bounds"));
}
Expand Down
4 changes: 2 additions & 2 deletions src/test/ui/auto-ref-slice-plus-ref.stderr
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
error[E0599]: no method named `test_mut` found for struct `Vec<{integer}>` in the current scope
error[E0599]: no method named `test_mut` found for struct `Vec` in the current scope
--> $DIR/auto-ref-slice-plus-ref.rs:7:7
|
LL | a.test_mut();
Expand All @@ -11,7 +11,7 @@ note: `MyIter` defines an item `test_mut`, perhaps you need to implement it
LL | trait MyIter {
| ^^^^^^^^^^^^

error[E0599]: no method named `test` found for struct `Vec<{integer}>` in the current scope
error[E0599]: no method named `test` found for struct `Vec` in the current scope
--> $DIR/auto-ref-slice-plus-ref.rs:8:7
|
LL | a.test();
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/class-cast-to-trait.stderr
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
error[E0599]: no method named `eat` found for struct `Box<dyn Noisy>` in the current scope
error[E0599]: no method named `eat` found for struct `Box` in the current scope
--> $DIR/class-cast-to-trait.rs:53:8
|
LL | nyan.eat();
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/confuse-field-and-method/issue-18343.stderr
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
error[E0599]: no method named `closure` found for struct `Obj<[closure@$DIR/issue-18343.rs:6:28: 6:33]>` in the current scope
error[E0599]: no method named `closure` found for struct `Obj` in the current scope
--> $DIR/issue-18343.rs:7:7
|
LL | struct Obj<F> where F: FnMut() -> u32 {
Expand Down
12 changes: 6 additions & 6 deletions src/test/ui/confuse-field-and-method/issue-2392.stderr
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
error[E0599]: no method named `closure` found for struct `Obj<[closure@$DIR/issue-2392.rs:35:36: 35:41]>` in the current scope
error[E0599]: no method named `closure` found for struct `Obj` in the current scope
--> $DIR/issue-2392.rs:36:15
|
LL | struct Obj<F> where F: FnOnce() -> u32 {
Expand All @@ -12,7 +12,7 @@ help: to call the function stored in `closure`, surround the field access with p
LL | (o_closure.closure)();
| ^ ^

error[E0599]: no method named `not_closure` found for struct `Obj<[closure@$DIR/issue-2392.rs:35:36: 35:41]>` in the current scope
error[E0599]: no method named `not_closure` found for struct `Obj` in the current scope
--> $DIR/issue-2392.rs:38:15
|
LL | struct Obj<F> where F: FnOnce() -> u32 {
Expand All @@ -23,7 +23,7 @@ LL | o_closure.not_closure();
| |
| field, not a method

error[E0599]: no method named `closure` found for struct `Obj<fn() -> u32 {func}>` in the current scope
error[E0599]: no method named `closure` found for struct `Obj` in the current scope
--> $DIR/issue-2392.rs:42:12
|
LL | struct Obj<F> where F: FnOnce() -> u32 {
Expand Down Expand Up @@ -65,7 +65,7 @@ help: to call the function stored in `boxed_closure`, surround the field access
LL | (boxed_closure.boxed_closure)();
| ^ ^

error[E0599]: no method named `closure` found for struct `Obj<fn() -> u32 {func}>` in the current scope
error[E0599]: no method named `closure` found for struct `Obj` in the current scope
--> $DIR/issue-2392.rs:53:12
|
LL | struct Obj<F> where F: FnOnce() -> u32 {
Expand All @@ -79,7 +79,7 @@ help: to call the function stored in `closure`, surround the field access with p
LL | (w.wrap.closure)();
| ^ ^

error[E0599]: no method named `not_closure` found for struct `Obj<fn() -> u32 {func}>` in the current scope
error[E0599]: no method named `not_closure` found for struct `Obj` in the current scope
--> $DIR/issue-2392.rs:55:12
|
LL | struct Obj<F> where F: FnOnce() -> u32 {
Expand All @@ -90,7 +90,7 @@ LL | w.wrap.not_closure();
| |
| field, not a method

error[E0599]: no method named `closure` found for struct `Obj<Box<(dyn FnOnce() -> u32 + 'static)>>` in the current scope
error[E0599]: no method named `closure` found for struct `Obj` in the current scope
--> $DIR/issue-2392.rs:58:24
|
LL | struct Obj<F> where F: FnOnce() -> u32 {
Expand Down
24 changes: 12 additions & 12 deletions src/test/ui/impl-trait/no-method-suggested-traits.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ LL | use no_method_suggested_traits::qux::PrivPub;
LL | use no_method_suggested_traits::Reexported;
|

error[E0599]: no method named `method` found for struct `Rc<&mut Box<&u32>>` in the current scope
error[E0599]: no method named `method` found for struct `Rc` in the current scope
--> $DIR/no-method-suggested-traits.rs:26:44
|
LL | std::rc::Rc::new(&mut Box::new(&1u32)).method();
Expand Down Expand Up @@ -46,7 +46,7 @@ help: the following trait is implemented but not in scope; perhaps add a `use` f
LL | use foo::Bar;
|

error[E0599]: no method named `method` found for struct `Rc<&mut Box<&char>>` in the current scope
error[E0599]: no method named `method` found for struct `Rc` in the current scope
--> $DIR/no-method-suggested-traits.rs:32:43
|
LL | std::rc::Rc::new(&mut Box::new(&'a')).method();
Expand All @@ -70,7 +70,7 @@ help: the following trait is implemented but not in scope; perhaps add a `use` f
LL | use no_method_suggested_traits::foo::PubPub;
|

error[E0599]: no method named `method` found for struct `Rc<&mut Box<&i32>>` in the current scope
error[E0599]: no method named `method` found for struct `Rc` in the current scope
--> $DIR/no-method-suggested-traits.rs:37:44
|
LL | std::rc::Rc::new(&mut Box::new(&1i32)).method();
Expand Down Expand Up @@ -98,7 +98,7 @@ LL | Foo.method();
candidate #3: `no_method_suggested_traits::qux::PrivPub`
candidate #4: `Reexported`

error[E0599]: no method named `method` found for struct `Rc<&mut Box<&Foo>>` in the current scope
error[E0599]: no method named `method` found for struct `Rc` in the current scope
--> $DIR/no-method-suggested-traits.rs:42:43
|
LL | std::rc::Rc::new(&mut Box::new(&Foo)).method();
Expand All @@ -124,7 +124,7 @@ note: `foo::Bar` defines an item `method2`, perhaps you need to implement it
LL | pub trait Bar {
| ^^^^^^^^^^^^^

error[E0599]: no method named `method2` found for struct `Rc<&mut Box<&u64>>` in the current scope
error[E0599]: no method named `method2` found for struct `Rc` in the current scope
--> $DIR/no-method-suggested-traits.rs:47:44
|
LL | std::rc::Rc::new(&mut Box::new(&1u64)).method2();
Expand All @@ -150,7 +150,7 @@ note: `foo::Bar` defines an item `method2`, perhaps you need to implement it
LL | pub trait Bar {
| ^^^^^^^^^^^^^

error[E0599]: no method named `method2` found for struct `Rc<&mut Box<&no_method_suggested_traits::Foo>>` in the current scope
error[E0599]: no method named `method2` found for struct `Rc` in the current scope
--> $DIR/no-method-suggested-traits.rs:52:71
|
LL | std::rc::Rc::new(&mut Box::new(&no_method_suggested_traits::Foo)).method2();
Expand All @@ -176,7 +176,7 @@ note: `foo::Bar` defines an item `method2`, perhaps you need to implement it
LL | pub trait Bar {
| ^^^^^^^^^^^^^

error[E0599]: no method named `method2` found for struct `Rc<&mut Box<&no_method_suggested_traits::Bar>>` in the current scope
error[E0599]: no method named `method2` found for struct `Rc` in the current scope
--> $DIR/no-method-suggested-traits.rs:56:74
|
LL | std::rc::Rc::new(&mut Box::new(&no_method_suggested_traits::Bar::X)).method2();
Expand All @@ -202,7 +202,7 @@ LL | Foo.method3();
= note: the following trait defines an item `method3`, perhaps you need to implement it:
candidate #1: `PubPub`

error[E0599]: no method named `method3` found for struct `Rc<&mut Box<&Foo>>` in the current scope
error[E0599]: no method named `method3` found for struct `Rc` in the current scope
--> $DIR/no-method-suggested-traits.rs:61:43
|
LL | std::rc::Rc::new(&mut Box::new(&Foo)).method3();
Expand All @@ -225,7 +225,7 @@ LL | Bar::X.method3();
= note: the following trait defines an item `method3`, perhaps you need to implement it:
candidate #1: `PubPub`

error[E0599]: no method named `method3` found for struct `Rc<&mut Box<&Bar>>` in the current scope
error[E0599]: no method named `method3` found for struct `Rc` in the current scope
--> $DIR/no-method-suggested-traits.rs:65:46
|
LL | std::rc::Rc::new(&mut Box::new(&Bar::X)).method3();
Expand All @@ -241,7 +241,7 @@ error[E0599]: no method named `method3` found for type `usize` in the current sc
LL | 1_usize.method3();
| ^^^^^^^ method not found in `usize`

error[E0599]: no method named `method3` found for struct `Rc<&mut Box<&usize>>` in the current scope
error[E0599]: no method named `method3` found for struct `Rc` in the current scope
--> $DIR/no-method-suggested-traits.rs:70:47
|
LL | std::rc::Rc::new(&mut Box::new(&1_usize)).method3();
Expand All @@ -253,7 +253,7 @@ error[E0599]: no method named `method3` found for struct `no_method_suggested_tr
LL | no_method_suggested_traits::Foo.method3();
| ^^^^^^^ method not found in `no_method_suggested_traits::Foo`

error[E0599]: no method named `method3` found for struct `Rc<&mut Box<&no_method_suggested_traits::Foo>>` in the current scope
error[E0599]: no method named `method3` found for struct `Rc` in the current scope
--> $DIR/no-method-suggested-traits.rs:72:71
|
LL | std::rc::Rc::new(&mut Box::new(&no_method_suggested_traits::Foo)).method3();
Expand All @@ -265,7 +265,7 @@ error[E0599]: no method named `method3` found for enum `no_method_suggested_trai
LL | no_method_suggested_traits::Bar::X.method3();
| ^^^^^^^ method not found in `no_method_suggested_traits::Bar`

error[E0599]: no method named `method3` found for struct `Rc<&mut Box<&no_method_suggested_traits::Bar>>` in the current scope
error[E0599]: no method named `method3` found for struct `Rc` in the current scope
--> $DIR/no-method-suggested-traits.rs:75:74
|
LL | std::rc::Rc::new(&mut Box::new(&no_method_suggested_traits::Bar::X)).method3();
Expand Down
2 changes: 2 additions & 0 deletions src/test/ui/issues/issue-30123.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ error[E0599]: no function or associated item named `new_undirected` found for st
|
LL | let ug = Graph::<i32, i32>::new_undirected();
| ^^^^^^^^^^^^^^ function or associated item not found in `issue_30123_aux::Graph<i32, i32>`
|
= note: The function or associated item was found for issue_30123_aux::Graph<N, E, Undirected>.

error: aborting due to previous error

Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/issues/issue-41880.stderr
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
error[E0599]: no method named `iter` found for struct `Iterate<{integer}, [closure@$DIR/issue-41880.rs:26:24: 26:31]>` in the current scope
error[E0599]: no method named `iter` found for struct `Iterate` in the current scope
--> $DIR/issue-41880.rs:27:24
|
LL | pub struct Iterate<T, F> {
Expand Down
Loading