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
@ -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.
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
48
src/test/ui/suggestions/suggest-trait-items.rs
Normal file
48
src/test/ui/suggestions/suggest-trait-items.rs
Normal 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() {}
|
74
src/test/ui/suggestions/suggest-trait-items.stderr
Normal file
74
src/test/ui/suggestions/suggest-trait-items.stderr
Normal 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`.
|
Loading…
x
Reference in New Issue
Block a user