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:
Hirochika Matsumoto 2021-09-26 00:53:37 +09:00
parent 60fe8b3a65
commit cef736f8a0
8 changed files with 216 additions and 18 deletions

View file

@ -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) => {

View file

@ -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));
}
}
}

View file

@ -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,

View file

@ -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.

View file

@ -2,7 +2,10 @@ error[E0407]: method `b` is not a member of trait `Foo`
--> $DIR/E0407.rs:9:5
|
LL | fn b() {}
| ^^^^^^^^^ not a member of trait `Foo`
| ^^^-^^^^^
| | |
| | help: there is an associated function with a similar name: `a`
| not a member of trait `Foo`
error: aborting due to previous error

View file

@ -2,7 +2,10 @@ error[E0407]: method `method` is not a member of trait `Tr`
--> $DIR/assoc_item_ctxt.rs:35:13
|
LL | fn method() {}
| ^^^^^^^^^^^^^^ not a member of trait `Tr`
| ^^^------^^^^^
| | |
| | help: there is an associated function with a similar name: `method`
| not a member of trait `Tr`
...
LL | mac_trait_impl!();
| ------------------ in this macro invocation

View file

@ -0,0 +1,48 @@
trait Foo {
type Type;
fn foo();
fn bar();
fn qux();
}
struct A;
impl Foo for A {
//~^ ERROR not all trait items implemented
type Typ = ();
//~^ ERROR type `Typ` is not a member of trait
//~| HELP there is an associated type with a similar name
fn fooo() {}
//~^ ERROR method `fooo` is not a member of trait
//~| HELP there is an associated function with a similar name
fn barr() {}
//~^ ERROR method `barr` is not a member of trait
//~| HELP there is an associated function with a similar name
fn quux() {}
//~^ ERROR method `quux` is not a member of trait
//~| HELP there is an associated function with a similar name
}
//~^ HELP implement the missing item
//~| HELP implement the missing item
//~| HELP implement the missing item
//~| HELP implement the missing item
trait Bar {
const Const: i32;
}
struct B;
impl Bar for B {
//~^ ERROR not all trait items implemented
const Cnst: i32 = 0;
//~^ ERROR const `Cnst` is not a member of trait
//~| HELP there is an associated constant with a similar name
}
//~^ HELP implement the missing item
fn main() {}

View file

@ -0,0 +1,74 @@
error[E0437]: type `Typ` is not a member of trait `Foo`
--> $DIR/suggest-trait-items.rs:13:5
|
LL | type Typ = ();
| ^^^^^---^^^^^^
| | |
| | help: there is an associated type with a similar name: `Type`
| not a member of trait `Foo`
error[E0407]: method `fooo` is not a member of trait `Foo`
--> $DIR/suggest-trait-items.rs:17:5
|
LL | fn fooo() {}
| ^^^----^^^^^
| | |
| | help: there is an associated function with a similar name: `foo`
| not a member of trait `Foo`
error[E0407]: method `barr` is not a member of trait `Foo`
--> $DIR/suggest-trait-items.rs:21:5
|
LL | fn barr() {}
| ^^^----^^^^^
| | |
| | help: there is an associated function with a similar name: `bar`
| not a member of trait `Foo`
error[E0407]: method `quux` is not a member of trait `Foo`
--> $DIR/suggest-trait-items.rs:25:5
|
LL | fn quux() {}
| ^^^----^^^^^
| | |
| | help: there is an associated function with a similar name: `qux`
| not a member of trait `Foo`
error[E0438]: const `Cnst` is not a member of trait `Bar`
--> $DIR/suggest-trait-items.rs:42:5
|
LL | const Cnst: i32 = 0;
| ^^^^^^----^^^^^^^^^^
| | |
| | help: there is an associated constant with a similar name: `Const`
| not a member of trait `Bar`
error[E0046]: not all trait items implemented, missing: `Type`, `foo`, `bar`, `qux`
--> $DIR/suggest-trait-items.rs:11:1
|
LL | type Type;
| ---------- `Type` from trait
LL |
LL | fn foo();
| --------- `foo` from trait
LL | fn bar();
| --------- `bar` from trait
LL | fn qux();
| --------- `qux` from trait
...
LL | impl Foo for A {
| ^^^^^^^^^^^^^^ missing `Type`, `foo`, `bar`, `qux` in implementation
error[E0046]: not all trait items implemented, missing: `Const`
--> $DIR/suggest-trait-items.rs:40:1
|
LL | const Const: i32;
| ----------------- `Const` from trait
...
LL | impl Bar for B {
| ^^^^^^^^^^^^^^ missing `Const` in implementation
error: aborting due to 7 previous errors
Some errors have detailed explanations: E0046, E0407, E0437, E0438.
For more information about an error, try `rustc --explain E0046`.