-
Notifications
You must be signed in to change notification settings - Fork 13.6k
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
Changes from 2 commits
c64a2ed
6efa14b
5b802ed
120691c
5d8e6ea
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 String operations are brittle and can break as things change in the codebase. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is the type of I think that might have a string representation, and if not a field that is only the name with type There was a problem hiding this comment. Choose a reason for hiding this commentThe 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: |
||
} | ||
} | ||
} | ||
|
||
let mut err = struct_span_err!( | ||
tcx.sess, | ||
span, | ||
|
@@ -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 { | ||
|
@@ -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. | ||
ABouttefeux marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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, | ||
} | ||
ABouttefeux marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} 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; | ||
estebank marked this conversation as resolved.
Show resolved
Hide resolved
|
||
let mut note = format!("The {item_kind} was found for"); | ||
ABouttefeux marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if inherent_impls_candidate.len() > 1 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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")); | ||
} | ||
|
Uh oh!
There was an error while loading. Please reload this page.