Skip to content

Commit

Permalink
Rollup merge of rust-lang#89248 - hkmatsumoto:suggest-similarly-named…
Browse files Browse the repository at this point in the history
…-assoc-items, r=estebank

Suggest similarly named associated items in trait impls

Fix rust-lang#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.
  • Loading branch information
Manishearth committed Sep 30, 2021
2 parents bee3ae3 + cef736f commit 6d150cd
Show file tree
Hide file tree
Showing 8 changed files with 216 additions and 18 deletions.
30 changes: 27 additions & 3 deletions compiler/rustc_resolve/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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) => {
Expand Down
28 changes: 20 additions & 8 deletions compiler/rustc_resolve/src/late.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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(
Expand All @@ -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,
Expand All @@ -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(
Expand All @@ -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.
Expand All @@ -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));
}
}
}
Expand Down
38 changes: 36 additions & 2 deletions compiler/rustc_resolve/src/late/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_resolve/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
5 changes: 4 additions & 1 deletion src/test/ui/error-codes/E0407.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
5 changes: 4 additions & 1 deletion src/test/ui/hygiene/assoc_item_ctxt.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
48 changes: 48 additions & 0 deletions src/test/ui/suggestions/suggest-trait-items.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 74 additions & 0 deletions src/test/ui/suggestions/suggest-trait-items.stderr
Original file line number Diff line number Diff line change
@@ -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`.

0 comments on commit 6d150cd

Please sign in to comment.