Skip to content

Commit cef736f

Browse files
committed
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.
1 parent 60fe8b3 commit cef736f

File tree

8 files changed

+216
-18
lines changed

8 files changed

+216
-18
lines changed

compiler/rustc_resolve/src/diagnostics.rs

+27-3
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ impl<'a> Resolver<'a> {
198198
err.span_label(first_use_span, format!("first use of `{}`", name));
199199
err
200200
}
201-
ResolutionError::MethodNotMemberOfTrait(method, trait_) => {
201+
ResolutionError::MethodNotMemberOfTrait(method, trait_, candidate) => {
202202
let mut err = struct_span_err!(
203203
self.session,
204204
span,
@@ -208,9 +208,17 @@ impl<'a> Resolver<'a> {
208208
trait_
209209
);
210210
err.span_label(span, format!("not a member of trait `{}`", trait_));
211+
if let Some(candidate) = candidate {
212+
err.span_suggestion(
213+
method.span,
214+
"there is an associated function with a similar name",
215+
candidate.to_ident_string(),
216+
Applicability::MaybeIncorrect,
217+
);
218+
}
211219
err
212220
}
213-
ResolutionError::TypeNotMemberOfTrait(type_, trait_) => {
221+
ResolutionError::TypeNotMemberOfTrait(type_, trait_, candidate) => {
214222
let mut err = struct_span_err!(
215223
self.session,
216224
span,
@@ -220,9 +228,17 @@ impl<'a> Resolver<'a> {
220228
trait_
221229
);
222230
err.span_label(span, format!("not a member of trait `{}`", trait_));
231+
if let Some(candidate) = candidate {
232+
err.span_suggestion(
233+
type_.span,
234+
"there is an associated type with a similar name",
235+
candidate.to_ident_string(),
236+
Applicability::MaybeIncorrect,
237+
);
238+
}
223239
err
224240
}
225-
ResolutionError::ConstNotMemberOfTrait(const_, trait_) => {
241+
ResolutionError::ConstNotMemberOfTrait(const_, trait_, candidate) => {
226242
let mut err = struct_span_err!(
227243
self.session,
228244
span,
@@ -232,6 +248,14 @@ impl<'a> Resolver<'a> {
232248
trait_
233249
);
234250
err.span_label(span, format!("not a member of trait `{}`", trait_));
251+
if let Some(candidate) = candidate {
252+
err.span_suggestion(
253+
const_.span,
254+
"there is an associated constant with a similar name",
255+
candidate.to_ident_string(),
256+
Applicability::MaybeIncorrect,
257+
);
258+
}
235259
err
236260
}
237261
ResolutionError::VariableNotBoundInPattern(binding_error) => {

compiler/rustc_resolve/src/late.rs

+20-8
Original file line numberDiff line numberDiff line change
@@ -1309,14 +1309,15 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
13091309
use crate::ResolutionError::*;
13101310
match &item.kind {
13111311
AssocItemKind::Const(_default, _ty, _expr) => {
1312-
debug!("resolve_implementation AssocItemKind::Const",);
1312+
debug!("resolve_implementation AssocItemKind::Const");
13131313
// If this is a trait impl, ensure the const
13141314
// exists in trait
13151315
this.check_trait_item(
13161316
item.ident,
1317+
&item.kind,
13171318
ValueNS,
13181319
item.span,
1319-
|n, s| ConstNotMemberOfTrait(n, s),
1320+
|i, s, c| ConstNotMemberOfTrait(i, s, c),
13201321
);
13211322

13221323
// We allow arbitrary const expressions inside of associated consts,
@@ -1338,6 +1339,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
13381339
);
13391340
}
13401341
AssocItemKind::Fn(box FnKind(.., generics, _)) => {
1342+
debug!("resolve_implementation AssocItemKind::Fn");
13411343
// We also need a new scope for the impl item type parameters.
13421344
this.with_generic_param_rib(
13431345
generics,
@@ -1347,9 +1349,10 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
13471349
// exists in trait
13481350
this.check_trait_item(
13491351
item.ident,
1352+
&item.kind,
13501353
ValueNS,
13511354
item.span,
1352-
|n, s| MethodNotMemberOfTrait(n, s),
1355+
|i, s, c| MethodNotMemberOfTrait(i, s, c),
13531356
);
13541357

13551358
visit::walk_assoc_item(
@@ -1366,6 +1369,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
13661369
_,
13671370
_,
13681371
)) => {
1372+
debug!("resolve_implementation AssocItemKind::TyAlias");
13691373
// We also need a new scope for the impl item type parameters.
13701374
this.with_generic_param_rib(
13711375
generics,
@@ -1375,9 +1379,10 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
13751379
// exists in trait
13761380
this.check_trait_item(
13771381
item.ident,
1382+
&item.kind,
13781383
TypeNS,
13791384
item.span,
1380-
|n, s| TypeNotMemberOfTrait(n, s),
1385+
|i, s, c| TypeNotMemberOfTrait(i, s, c),
13811386
);
13821387

13831388
visit::walk_assoc_item(
@@ -1401,9 +1406,15 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
14011406
});
14021407
}
14031408

1404-
fn check_trait_item<F>(&mut self, ident: Ident, ns: Namespace, span: Span, err: F)
1405-
where
1406-
F: FnOnce(Symbol, &str) -> ResolutionError<'_>,
1409+
fn check_trait_item<F>(
1410+
&mut self,
1411+
ident: Ident,
1412+
kind: &AssocItemKind,
1413+
ns: Namespace,
1414+
span: Span,
1415+
err: F,
1416+
) where
1417+
F: FnOnce(Ident, &str, Option<Symbol>) -> ResolutionError<'_>,
14071418
{
14081419
// If there is a TraitRef in scope for an impl, then the method must be in the
14091420
// trait.
@@ -1420,8 +1431,9 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
14201431
)
14211432
.is_err()
14221433
{
1434+
let candidate = self.find_similarly_named_assoc_item(ident.name, kind);
14231435
let path = &self.current_trait_ref.as_ref().unwrap().1.path;
1424-
self.report_error(span, err(ident.name, &path_names_to_string(path)));
1436+
self.report_error(span, err(ident, &path_names_to_string(path), candidate));
14251437
}
14261438
}
14271439
}

compiler/rustc_resolve/src/late/diagnostics.rs

+36-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ use crate::{PathResult, PathSource, Segment};
77

88
use rustc_ast::visit::FnKind;
99
use rustc_ast::{
10-
self as ast, Expr, ExprKind, GenericParam, GenericParamKind, Item, ItemKind, NodeId, Path, Ty,
11-
TyKind,
10+
self as ast, AssocItemKind, Expr, ExprKind, GenericParam, GenericParamKind, Item, ItemKind,
11+
NodeId, Path, Ty, TyKind,
1212
};
1313
use rustc_ast_pretty::pprust::path_segment_to_string;
1414
use rustc_data_structures::fx::FxHashSet;
@@ -1144,6 +1144,40 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
11441144
true
11451145
}
11461146

1147+
/// Given the target `ident` and `kind`, search for the similarly named associated item
1148+
/// in `self.current_trait_ref`.
1149+
crate fn find_similarly_named_assoc_item(
1150+
&mut self,
1151+
ident: Symbol,
1152+
kind: &AssocItemKind,
1153+
) -> Option<Symbol> {
1154+
let module = if let Some((module, _)) = self.current_trait_ref {
1155+
module
1156+
} else {
1157+
return None;
1158+
};
1159+
if ident == kw::Underscore {
1160+
// We do nothing for `_`.
1161+
return None;
1162+
}
1163+
1164+
let resolutions = self.r.resolutions(module);
1165+
let targets = resolutions
1166+
.borrow()
1167+
.iter()
1168+
.filter_map(|(key, res)| res.borrow().binding.map(|binding| (key, binding.res())))
1169+
.filter(|(_, res)| match (kind, res) {
1170+
(AssocItemKind::Const(..), Res::Def(DefKind::AssocConst, _)) => true,
1171+
(AssocItemKind::Fn(_), Res::Def(DefKind::AssocFn, _)) => true,
1172+
(AssocItemKind::TyAlias(..), Res::Def(DefKind::AssocTy, _)) => true,
1173+
_ => false,
1174+
})
1175+
.map(|(key, _)| key.ident.name)
1176+
.collect::<Vec<_>>();
1177+
1178+
find_best_match_for_name(&targets, ident, None)
1179+
}
1180+
11471181
fn lookup_assoc_candidate<FilterFn>(
11481182
&mut self,
11491183
ident: Ident,

compiler/rustc_resolve/src/lib.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -202,11 +202,11 @@ enum ResolutionError<'a> {
202202
/// parameter list.
203203
NameAlreadyUsedInParameterList(Symbol, Span),
204204
/// Error E0407: method is not a member of trait.
205-
MethodNotMemberOfTrait(Symbol, &'a str),
205+
MethodNotMemberOfTrait(Ident, &'a str, Option<Symbol>),
206206
/// Error E0437: type is not a member of trait.
207-
TypeNotMemberOfTrait(Symbol, &'a str),
207+
TypeNotMemberOfTrait(Ident, &'a str, Option<Symbol>),
208208
/// Error E0438: const is not a member of trait.
209-
ConstNotMemberOfTrait(Symbol, &'a str),
209+
ConstNotMemberOfTrait(Ident, &'a str, Option<Symbol>),
210210
/// Error E0408: variable `{}` is not bound in all patterns.
211211
VariableNotBoundInPattern(&'a BindingError),
212212
/// Error E0409: variable `{}` is bound in inconsistent ways within the same match arm.

src/test/ui/error-codes/E0407.stderr

+4-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ error[E0407]: method `b` is not a member of trait `Foo`
22
--> $DIR/E0407.rs:9:5
33
|
44
LL | fn b() {}
5-
| ^^^^^^^^^ not a member of trait `Foo`
5+
| ^^^-^^^^^
6+
| | |
7+
| | help: there is an associated function with a similar name: `a`
8+
| not a member of trait `Foo`
69

710
error: aborting due to previous error
811

src/test/ui/hygiene/assoc_item_ctxt.stderr

+4-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ error[E0407]: method `method` is not a member of trait `Tr`
22
--> $DIR/assoc_item_ctxt.rs:35:13
33
|
44
LL | fn method() {}
5-
| ^^^^^^^^^^^^^^ not a member of trait `Tr`
5+
| ^^^------^^^^^
6+
| | |
7+
| | help: there is an associated function with a similar name: `method`
8+
| not a member of trait `Tr`
69
...
710
LL | mac_trait_impl!();
811
| ------------------ in this macro invocation
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
trait Foo {
2+
type Type;
3+
4+
fn foo();
5+
fn bar();
6+
fn qux();
7+
}
8+
9+
struct A;
10+
11+
impl Foo for A {
12+
//~^ ERROR not all trait items implemented
13+
type Typ = ();
14+
//~^ ERROR type `Typ` is not a member of trait
15+
//~| HELP there is an associated type with a similar name
16+
17+
fn fooo() {}
18+
//~^ ERROR method `fooo` is not a member of trait
19+
//~| HELP there is an associated function with a similar name
20+
21+
fn barr() {}
22+
//~^ ERROR method `barr` is not a member of trait
23+
//~| HELP there is an associated function with a similar name
24+
25+
fn quux() {}
26+
//~^ ERROR method `quux` is not a member of trait
27+
//~| HELP there is an associated function with a similar name
28+
}
29+
//~^ HELP implement the missing item
30+
//~| HELP implement the missing item
31+
//~| HELP implement the missing item
32+
//~| HELP implement the missing item
33+
34+
trait Bar {
35+
const Const: i32;
36+
}
37+
38+
struct B;
39+
40+
impl Bar for B {
41+
//~^ ERROR not all trait items implemented
42+
const Cnst: i32 = 0;
43+
//~^ ERROR const `Cnst` is not a member of trait
44+
//~| HELP there is an associated constant with a similar name
45+
}
46+
//~^ HELP implement the missing item
47+
48+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
error[E0437]: type `Typ` is not a member of trait `Foo`
2+
--> $DIR/suggest-trait-items.rs:13:5
3+
|
4+
LL | type Typ = ();
5+
| ^^^^^---^^^^^^
6+
| | |
7+
| | help: there is an associated type with a similar name: `Type`
8+
| not a member of trait `Foo`
9+
10+
error[E0407]: method `fooo` is not a member of trait `Foo`
11+
--> $DIR/suggest-trait-items.rs:17:5
12+
|
13+
LL | fn fooo() {}
14+
| ^^^----^^^^^
15+
| | |
16+
| | help: there is an associated function with a similar name: `foo`
17+
| not a member of trait `Foo`
18+
19+
error[E0407]: method `barr` is not a member of trait `Foo`
20+
--> $DIR/suggest-trait-items.rs:21:5
21+
|
22+
LL | fn barr() {}
23+
| ^^^----^^^^^
24+
| | |
25+
| | help: there is an associated function with a similar name: `bar`
26+
| not a member of trait `Foo`
27+
28+
error[E0407]: method `quux` is not a member of trait `Foo`
29+
--> $DIR/suggest-trait-items.rs:25:5
30+
|
31+
LL | fn quux() {}
32+
| ^^^----^^^^^
33+
| | |
34+
| | help: there is an associated function with a similar name: `qux`
35+
| not a member of trait `Foo`
36+
37+
error[E0438]: const `Cnst` is not a member of trait `Bar`
38+
--> $DIR/suggest-trait-items.rs:42:5
39+
|
40+
LL | const Cnst: i32 = 0;
41+
| ^^^^^^----^^^^^^^^^^
42+
| | |
43+
| | help: there is an associated constant with a similar name: `Const`
44+
| not a member of trait `Bar`
45+
46+
error[E0046]: not all trait items implemented, missing: `Type`, `foo`, `bar`, `qux`
47+
--> $DIR/suggest-trait-items.rs:11:1
48+
|
49+
LL | type Type;
50+
| ---------- `Type` from trait
51+
LL |
52+
LL | fn foo();
53+
| --------- `foo` from trait
54+
LL | fn bar();
55+
| --------- `bar` from trait
56+
LL | fn qux();
57+
| --------- `qux` from trait
58+
...
59+
LL | impl Foo for A {
60+
| ^^^^^^^^^^^^^^ missing `Type`, `foo`, `bar`, `qux` in implementation
61+
62+
error[E0046]: not all trait items implemented, missing: `Const`
63+
--> $DIR/suggest-trait-items.rs:40:1
64+
|
65+
LL | const Const: i32;
66+
| ----------------- `Const` from trait
67+
...
68+
LL | impl Bar for B {
69+
| ^^^^^^^^^^^^^^ missing `Const` in implementation
70+
71+
error: aborting due to 7 previous errors
72+
73+
Some errors have detailed explanations: E0046, E0407, E0437, E0438.
74+
For more information about an error, try `rustc --explain E0046`.

0 commit comments

Comments
 (0)