Suggest calling associated fn
inside trait
s
When calling a function that doesn't exist inside of a trait's associated `fn`, and another associated `fn` in that trait has that name, suggest calling it with the appropriate fully-qualified path. Expand the label to be more descriptive. Prompted by the following user experience: https://users.rust-lang.org/t/cannot-find-function/50663
This commit is contained in:
parent
9f6c670c4b
commit
9e16213610
9 changed files with 124 additions and 49 deletions
|
@ -353,8 +353,8 @@ impl<'a> PathSource<'a> {
|
|||
|
||||
#[derive(Default)]
|
||||
struct DiagnosticMetadata<'ast> {
|
||||
/// The current trait's associated types' ident, used for diagnostic suggestions.
|
||||
current_trait_assoc_types: Vec<Ident>,
|
||||
/// The current trait's associated items' ident, used for diagnostic suggestions.
|
||||
current_trait_assoc_items: Option<&'ast [P<AssocItem>]>,
|
||||
|
||||
/// The current self type if inside an impl (used for better errors).
|
||||
current_self_type: Option<Ty>,
|
||||
|
@ -1148,26 +1148,18 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
|||
result
|
||||
}
|
||||
|
||||
/// When evaluating a `trait` use its associated types' idents for suggestionsa in E0412.
|
||||
/// When evaluating a `trait` use its associated types' idents for suggestions in E0412.
|
||||
fn with_trait_items<T>(
|
||||
&mut self,
|
||||
trait_items: &Vec<P<AssocItem>>,
|
||||
trait_items: &'ast Vec<P<AssocItem>>,
|
||||
f: impl FnOnce(&mut Self) -> T,
|
||||
) -> T {
|
||||
let trait_assoc_types = replace(
|
||||
&mut self.diagnostic_metadata.current_trait_assoc_types,
|
||||
trait_items
|
||||
.iter()
|
||||
.filter_map(|item| match &item.kind {
|
||||
AssocItemKind::TyAlias(_, _, bounds, _) if bounds.is_empty() => {
|
||||
Some(item.ident)
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.collect(),
|
||||
let trait_assoc_items = replace(
|
||||
&mut self.diagnostic_metadata.current_trait_assoc_items,
|
||||
Some(&trait_items[..]),
|
||||
);
|
||||
let result = f(self);
|
||||
self.diagnostic_metadata.current_trait_assoc_types = trait_assoc_types;
|
||||
self.diagnostic_metadata.current_trait_assoc_items = trait_assoc_items;
|
||||
result
|
||||
}
|
||||
|
||||
|
|
|
@ -30,7 +30,21 @@ type Res = def::Res<ast::NodeId>;
|
|||
enum AssocSuggestion {
|
||||
Field,
|
||||
MethodWithSelf,
|
||||
AssocItem,
|
||||
AssocFn,
|
||||
AssocType,
|
||||
AssocConst,
|
||||
}
|
||||
|
||||
impl AssocSuggestion {
|
||||
fn action(&self) -> &'static str {
|
||||
match self {
|
||||
AssocSuggestion::Field => "use the available field",
|
||||
AssocSuggestion::MethodWithSelf => "call the method with the fully-qualified path",
|
||||
AssocSuggestion::AssocFn => "call the associated function",
|
||||
AssocSuggestion::AssocConst => "use the associated `const`",
|
||||
AssocSuggestion::AssocType => "use the associated type",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
crate enum MissingLifetimeSpot<'tcx> {
|
||||
|
@ -386,15 +400,18 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
|
|||
AssocSuggestion::MethodWithSelf if self_is_available => {
|
||||
err.span_suggestion(
|
||||
span,
|
||||
"try",
|
||||
"you might have meant to call the method",
|
||||
format!("self.{}", path_str),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
AssocSuggestion::MethodWithSelf | AssocSuggestion::AssocItem => {
|
||||
AssocSuggestion::MethodWithSelf
|
||||
| AssocSuggestion::AssocFn
|
||||
| AssocSuggestion::AssocConst
|
||||
| AssocSuggestion::AssocType => {
|
||||
err.span_suggestion(
|
||||
span,
|
||||
"try",
|
||||
&format!("you might have meant to {}", candidate.action()),
|
||||
format!("Self::{}", path_str),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
|
@ -1048,9 +1065,19 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
|
|||
}
|
||||
}
|
||||
|
||||
for assoc_type_ident in &self.diagnostic_metadata.current_trait_assoc_types {
|
||||
if *assoc_type_ident == ident {
|
||||
return Some(AssocSuggestion::AssocItem);
|
||||
if let Some(items) = self.diagnostic_metadata.current_trait_assoc_items {
|
||||
for assoc_item in &items[..] {
|
||||
if assoc_item.ident == ident {
|
||||
return Some(match &assoc_item.kind {
|
||||
ast::AssocItemKind::Const(..) => AssocSuggestion::AssocConst,
|
||||
ast::AssocItemKind::Fn(_, sig, ..) if sig.decl.has_self() => {
|
||||
AssocSuggestion::MethodWithSelf
|
||||
}
|
||||
ast::AssocItemKind::Fn(..) => AssocSuggestion::AssocFn,
|
||||
ast::AssocItemKind::TyAlias(..) => AssocSuggestion::AssocType,
|
||||
ast::AssocItemKind::MacCall(_) => continue,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1066,11 +1093,20 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
|
|||
) {
|
||||
let res = binding.res();
|
||||
if filter_fn(res) {
|
||||
return Some(if self.r.has_self.contains(&res.def_id()) {
|
||||
AssocSuggestion::MethodWithSelf
|
||||
if self.r.has_self.contains(&res.def_id()) {
|
||||
return Some(AssocSuggestion::MethodWithSelf);
|
||||
} else {
|
||||
AssocSuggestion::AssocItem
|
||||
});
|
||||
match res {
|
||||
Res::Def(DefKind::AssocFn, _) => return Some(AssocSuggestion::AssocFn),
|
||||
Res::Def(DefKind::AssocConst, _) => {
|
||||
return Some(AssocSuggestion::AssocConst);
|
||||
}
|
||||
Res::Def(DefKind::AssocTy, _) => {
|
||||
return Some(AssocSuggestion::AssocType);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue