Suggest similarly named assoc items in trait impls
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:
parent
60fe8b3a65
commit
cef736f8a0
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;
|
||||
|
@ -1144,6 +1144,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,
|
||||
|
|
|
@ -202,11 +202,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