Rollup merge of #89248 - hkmatsumoto:suggest-similarly-named-assoc-items, r=estebank
Suggest similarly named associated items in trait impls Fix #85942 Previously, the compiler didn't suggest similarly named associated items unlike we do in many situations. This patch adds such diagnostics for associated functions, types, and constants.
This commit is contained in:
commit
837ac87709
8 changed files with 216 additions and 18 deletions
|
@ -198,7 +198,7 @@ impl<'a> Resolver<'a> {
|
|||
err.span_label(first_use_span, format!("first use of `{}`", name));
|
||||
err
|
||||
}
|
||||
ResolutionError::MethodNotMemberOfTrait(method, trait_) => {
|
||||
ResolutionError::MethodNotMemberOfTrait(method, trait_, candidate) => {
|
||||
let mut err = struct_span_err!(
|
||||
self.session,
|
||||
span,
|
||||
|
@ -208,9 +208,17 @@ impl<'a> Resolver<'a> {
|
|||
trait_
|
||||
);
|
||||
err.span_label(span, format!("not a member of trait `{}`", trait_));
|
||||
if let Some(candidate) = candidate {
|
||||
err.span_suggestion(
|
||||
method.span,
|
||||
"there is an associated function with a similar name",
|
||||
candidate.to_ident_string(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
err
|
||||
}
|
||||
ResolutionError::TypeNotMemberOfTrait(type_, trait_) => {
|
||||
ResolutionError::TypeNotMemberOfTrait(type_, trait_, candidate) => {
|
||||
let mut err = struct_span_err!(
|
||||
self.session,
|
||||
span,
|
||||
|
@ -220,9 +228,17 @@ impl<'a> Resolver<'a> {
|
|||
trait_
|
||||
);
|
||||
err.span_label(span, format!("not a member of trait `{}`", trait_));
|
||||
if let Some(candidate) = candidate {
|
||||
err.span_suggestion(
|
||||
type_.span,
|
||||
"there is an associated type with a similar name",
|
||||
candidate.to_ident_string(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
err
|
||||
}
|
||||
ResolutionError::ConstNotMemberOfTrait(const_, trait_) => {
|
||||
ResolutionError::ConstNotMemberOfTrait(const_, trait_, candidate) => {
|
||||
let mut err = struct_span_err!(
|
||||
self.session,
|
||||
span,
|
||||
|
@ -232,6 +248,14 @@ impl<'a> Resolver<'a> {
|
|||
trait_
|
||||
);
|
||||
err.span_label(span, format!("not a member of trait `{}`", trait_));
|
||||
if let Some(candidate) = candidate {
|
||||
err.span_suggestion(
|
||||
const_.span,
|
||||
"there is an associated constant with a similar name",
|
||||
candidate.to_ident_string(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
err
|
||||
}
|
||||
ResolutionError::VariableNotBoundInPattern(binding_error) => {
|
||||
|
|
|
@ -1309,14 +1309,15 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
|||
use crate::ResolutionError::*;
|
||||
match &item.kind {
|
||||
AssocItemKind::Const(_default, _ty, _expr) => {
|
||||
debug!("resolve_implementation AssocItemKind::Const",);
|
||||
debug!("resolve_implementation AssocItemKind::Const");
|
||||
// If this is a trait impl, ensure the const
|
||||
// exists in trait
|
||||
this.check_trait_item(
|
||||
item.ident,
|
||||
&item.kind,
|
||||
ValueNS,
|
||||
item.span,
|
||||
|n, s| ConstNotMemberOfTrait(n, s),
|
||||
|i, s, c| ConstNotMemberOfTrait(i, s, c),
|
||||
);
|
||||
|
||||
// We allow arbitrary const expressions inside of associated consts,
|
||||
|
@ -1338,6 +1339,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
|||
);
|
||||
}
|
||||
AssocItemKind::Fn(box FnKind(.., generics, _)) => {
|
||||
debug!("resolve_implementation AssocItemKind::Fn");
|
||||
// We also need a new scope for the impl item type parameters.
|
||||
this.with_generic_param_rib(
|
||||
generics,
|
||||
|
@ -1347,9 +1349,10 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
|||
// exists in trait
|
||||
this.check_trait_item(
|
||||
item.ident,
|
||||
&item.kind,
|
||||
ValueNS,
|
||||
item.span,
|
||||
|n, s| MethodNotMemberOfTrait(n, s),
|
||||
|i, s, c| MethodNotMemberOfTrait(i, s, c),
|
||||
);
|
||||
|
||||
visit::walk_assoc_item(
|
||||
|
@ -1366,6 +1369,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
|||
_,
|
||||
_,
|
||||
)) => {
|
||||
debug!("resolve_implementation AssocItemKind::TyAlias");
|
||||
// We also need a new scope for the impl item type parameters.
|
||||
this.with_generic_param_rib(
|
||||
generics,
|
||||
|
@ -1375,9 +1379,10 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
|||
// exists in trait
|
||||
this.check_trait_item(
|
||||
item.ident,
|
||||
&item.kind,
|
||||
TypeNS,
|
||||
item.span,
|
||||
|n, s| TypeNotMemberOfTrait(n, s),
|
||||
|i, s, c| TypeNotMemberOfTrait(i, s, c),
|
||||
);
|
||||
|
||||
visit::walk_assoc_item(
|
||||
|
@ -1401,9 +1406,15 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
|||
});
|
||||
}
|
||||
|
||||
fn check_trait_item<F>(&mut self, ident: Ident, ns: Namespace, span: Span, err: F)
|
||||
where
|
||||
F: FnOnce(Symbol, &str) -> ResolutionError<'_>,
|
||||
fn check_trait_item<F>(
|
||||
&mut self,
|
||||
ident: Ident,
|
||||
kind: &AssocItemKind,
|
||||
ns: Namespace,
|
||||
span: Span,
|
||||
err: F,
|
||||
) where
|
||||
F: FnOnce(Ident, &str, Option<Symbol>) -> ResolutionError<'_>,
|
||||
{
|
||||
// If there is a TraitRef in scope for an impl, then the method must be in the
|
||||
// trait.
|
||||
|
@ -1420,8 +1431,9 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
|||
)
|
||||
.is_err()
|
||||
{
|
||||
let candidate = self.find_similarly_named_assoc_item(ident.name, kind);
|
||||
let path = &self.current_trait_ref.as_ref().unwrap().1.path;
|
||||
self.report_error(span, err(ident.name, &path_names_to_string(path)));
|
||||
self.report_error(span, err(ident, &path_names_to_string(path), candidate));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,8 +7,8 @@ use crate::{PathResult, PathSource, Segment};
|
|||
|
||||
use rustc_ast::visit::FnKind;
|
||||
use rustc_ast::{
|
||||
self as ast, Expr, ExprKind, GenericParam, GenericParamKind, Item, ItemKind, NodeId, Path, Ty,
|
||||
TyKind,
|
||||
self as ast, AssocItemKind, Expr, ExprKind, GenericParam, GenericParamKind, Item, ItemKind,
|
||||
NodeId, Path, Ty, TyKind,
|
||||
};
|
||||
use rustc_ast_pretty::pprust::path_segment_to_string;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
|
@ -1150,6 +1150,40 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
|
|||
true
|
||||
}
|
||||
|
||||
/// Given the target `ident` and `kind`, search for the similarly named associated item
|
||||
/// in `self.current_trait_ref`.
|
||||
crate fn find_similarly_named_assoc_item(
|
||||
&mut self,
|
||||
ident: Symbol,
|
||||
kind: &AssocItemKind,
|
||||
) -> Option<Symbol> {
|
||||
let module = if let Some((module, _)) = self.current_trait_ref {
|
||||
module
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
if ident == kw::Underscore {
|
||||
// We do nothing for `_`.
|
||||
return None;
|
||||
}
|
||||
|
||||
let resolutions = self.r.resolutions(module);
|
||||
let targets = resolutions
|
||||
.borrow()
|
||||
.iter()
|
||||
.filter_map(|(key, res)| res.borrow().binding.map(|binding| (key, binding.res())))
|
||||
.filter(|(_, res)| match (kind, res) {
|
||||
(AssocItemKind::Const(..), Res::Def(DefKind::AssocConst, _)) => true,
|
||||
(AssocItemKind::Fn(_), Res::Def(DefKind::AssocFn, _)) => true,
|
||||
(AssocItemKind::TyAlias(..), Res::Def(DefKind::AssocTy, _)) => true,
|
||||
_ => false,
|
||||
})
|
||||
.map(|(key, _)| key.ident.name)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
find_best_match_for_name(&targets, ident, None)
|
||||
}
|
||||
|
||||
fn lookup_assoc_candidate<FilterFn>(
|
||||
&mut self,
|
||||
ident: Ident,
|
||||
|
|
|
@ -206,11 +206,11 @@ enum ResolutionError<'a> {
|
|||
/// parameter list.
|
||||
NameAlreadyUsedInParameterList(Symbol, Span),
|
||||
/// Error E0407: method is not a member of trait.
|
||||
MethodNotMemberOfTrait(Symbol, &'a str),
|
||||
MethodNotMemberOfTrait(Ident, &'a str, Option<Symbol>),
|
||||
/// Error E0437: type is not a member of trait.
|
||||
TypeNotMemberOfTrait(Symbol, &'a str),
|
||||
TypeNotMemberOfTrait(Ident, &'a str, Option<Symbol>),
|
||||
/// Error E0438: const is not a member of trait.
|
||||
ConstNotMemberOfTrait(Symbol, &'a str),
|
||||
ConstNotMemberOfTrait(Ident, &'a str, Option<Symbol>),
|
||||
/// Error E0408: variable `{}` is not bound in all patterns.
|
||||
VariableNotBoundInPattern(&'a BindingError),
|
||||
/// Error E0409: variable `{}` is bound in inconsistent ways within the same match arm.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue