Skip to content

Commit 36bea20

Browse files
authored
Rollup merge of rust-lang#89248 - hkmatsumoto:suggest-similarly-named-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.
2 parents ff7b1c2 + cef736f commit 36bea20

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;
@@ -1150,6 +1150,40 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
11501150
true
11511151
}
11521152

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

compiler/rustc_resolve/src/lib.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -206,11 +206,11 @@ enum ResolutionError<'a> {
206206
/// parameter list.
207207
NameAlreadyUsedInParameterList(Symbol, Span),
208208
/// Error E0407: method is not a member of trait.
209-
MethodNotMemberOfTrait(Symbol, &'a str),
209+
MethodNotMemberOfTrait(Ident, &'a str, Option<Symbol>),
210210
/// Error E0437: type is not a member of trait.
211-
TypeNotMemberOfTrait(Symbol, &'a str),
211+
TypeNotMemberOfTrait(Ident, &'a str, Option<Symbol>),
212212
/// Error E0438: const is not a member of trait.
213-
ConstNotMemberOfTrait(Symbol, &'a str),
213+
ConstNotMemberOfTrait(Ident, &'a str, Option<Symbol>),
214214
/// Error E0408: variable `{}` is not bound in all patterns.
215215
VariableNotBoundInPattern(&'a BindingError),
216216
/// 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)