diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 435c79d2daf45..b1d02f90be113 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -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) => { diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 3c48a76224fd9..9563325796538 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -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(&mut self, ident: Ident, ns: Namespace, span: Span, err: F) - where - F: FnOnce(Symbol, &str) -> ResolutionError<'_>, + fn check_trait_item( + &mut self, + ident: Ident, + kind: &AssocItemKind, + ns: Namespace, + span: Span, + err: F, + ) where + F: FnOnce(Ident, &str, Option) -> 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)); } } } diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 19136c6ceeb1f..0e6ca1856bb30 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -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 { + 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::>(); + + find_best_match_for_name(&targets, ident, None) + } + fn lookup_assoc_candidate( &mut self, ident: Ident, diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 68db06370ffea..22d77d4432159 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -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), /// Error E0437: type is not a member of trait. - TypeNotMemberOfTrait(Symbol, &'a str), + TypeNotMemberOfTrait(Ident, &'a str, Option), /// Error E0438: const is not a member of trait. - ConstNotMemberOfTrait(Symbol, &'a str), + ConstNotMemberOfTrait(Ident, &'a str, Option), /// 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. diff --git a/src/test/ui/error-codes/E0407.stderr b/src/test/ui/error-codes/E0407.stderr index 567fc879040d3..6f6d1ff6a8f41 100644 --- a/src/test/ui/error-codes/E0407.stderr +++ b/src/test/ui/error-codes/E0407.stderr @@ -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 diff --git a/src/test/ui/hygiene/assoc_item_ctxt.stderr b/src/test/ui/hygiene/assoc_item_ctxt.stderr index 6e4fecf0ce4b2..517b1ff598888 100644 --- a/src/test/ui/hygiene/assoc_item_ctxt.stderr +++ b/src/test/ui/hygiene/assoc_item_ctxt.stderr @@ -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 diff --git a/src/test/ui/suggestions/suggest-trait-items.rs b/src/test/ui/suggestions/suggest-trait-items.rs new file mode 100644 index 0000000000000..9d42a73426096 --- /dev/null +++ b/src/test/ui/suggestions/suggest-trait-items.rs @@ -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() {} diff --git a/src/test/ui/suggestions/suggest-trait-items.stderr b/src/test/ui/suggestions/suggest-trait-items.stderr new file mode 100644 index 0000000000000..151bae7d1b9cb --- /dev/null +++ b/src/test/ui/suggestions/suggest-trait-items.stderr @@ -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`.