Skip to content

Commit 14663e0

Browse files
authored
Rollup merge of #116257 - estebank:issue-101351, r=b-naber
Suggest trait bounds for used associated type on type param Fix #101351. When an associated type on a type parameter is used, and the type parameter isn't constrained by the correct trait, suggest the appropriate trait bound: ``` error[E0220]: associated type `Associated` not found for `T` --> file.rs:6:15 | 6 | field: T::Associated, | ^^^^^^^^^^ there is a similarly named associated type `Associated` in the trait `Foo` | help: consider restricting type parameter `T` | 5 | struct Generic<T: Foo> { | +++++ ``` When an associated type on a type parameter has a typo, suggest fixing it: ``` error[E0220]: associated type `Baa` not found for `T` --> $DIR/issue-55673.rs:9:8 | LL | T::Baa: std::fmt::Debug, | ^^^ there is a similarly named associated type `Bar` in the trait `Foo` | help: change the associated type name to use `Bar` from `Foo` | LL | T::Bar: std::fmt::Debug, | ~~~ ```
2 parents cf25110 + 20c622e commit 14663e0

File tree

12 files changed

+152
-23
lines changed

12 files changed

+152
-23
lines changed

compiler/rustc_hir_analysis/src/astconv/bounds.rs

+1
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
284284
self.one_bound_for_assoc_type(
285285
|| traits::supertraits(tcx, trait_ref),
286286
trait_ref.skip_binder().print_only_trait_name(),
287+
None,
287288
binding.item_name,
288289
path_span,
289290
match binding.kind {

compiler/rustc_hir_analysis/src/astconv/errors.rs

+53-5
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,9 @@ use crate::errors::{
66
use rustc_data_structures::fx::FxHashMap;
77
use rustc_errors::{pluralize, struct_span_err, Applicability, Diagnostic, ErrorGuaranteed};
88
use rustc_hir as hir;
9-
use rustc_hir::def_id::DefId;
9+
use rustc_hir::def_id::{DefId, LocalDefId};
1010
use rustc_infer::traits::FulfillmentError;
11-
use rustc_middle::ty::TyCtxt;
12-
use rustc_middle::ty::{self, Ty};
11+
use rustc_middle::ty::{self, suggest_constraining_type_param, Ty, TyCtxt};
1312
use rustc_session::parse::feature_err;
1413
use rustc_span::edit_distance::find_best_match_for_name;
1514
use rustc_span::symbol::{sym, Ident};
@@ -102,6 +101,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
102101
&self,
103102
all_candidates: impl Fn() -> I,
104103
ty_param_name: &str,
104+
ty_param_def_id: Option<LocalDefId>,
105105
assoc_name: Ident,
106106
span: Span,
107107
) -> ErrorGuaranteed
@@ -190,13 +190,61 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
190190
})
191191
.collect::<Vec<_>>()[..]
192192
{
193+
let trait_name = self.tcx().def_path_str(*best_trait);
194+
let an = if suggested_name != assoc_name.name { "a similarly named" } else { "an" };
193195
err.span_label(
194196
assoc_name.span,
195197
format!(
196-
"there is a similarly named associated type `{suggested_name}` in the trait `{}`",
197-
self.tcx().def_path_str(*best_trait)
198+
"there is {an} associated type `{suggested_name}` in the \
199+
trait `{trait_name}`",
198200
),
199201
);
202+
let hir = self.tcx().hir();
203+
if let Some(def_id) = ty_param_def_id
204+
&& let parent = hir.get_parent_item(hir.local_def_id_to_hir_id(def_id))
205+
&& let Some(generics) = hir.get_generics(parent.def_id)
206+
{
207+
if generics.bounds_for_param(def_id)
208+
.flat_map(|pred| pred.bounds.iter())
209+
.any(|b| match b {
210+
hir::GenericBound::Trait(t, ..) => {
211+
t.trait_ref.trait_def_id().as_ref() == Some(best_trait)
212+
}
213+
_ => false,
214+
})
215+
{
216+
// The type param already has a bound for `trait_name`, we just need to
217+
// change the associated type.
218+
err.span_suggestion_verbose(
219+
assoc_name.span,
220+
format!(
221+
"change the associated type name to use `{suggested_name}` from \
222+
`{trait_name}`",
223+
),
224+
suggested_name.to_string(),
225+
Applicability::MaybeIncorrect,
226+
);
227+
} else if suggest_constraining_type_param(
228+
self.tcx(),
229+
generics,
230+
&mut err,
231+
&ty_param_name,
232+
&trait_name,
233+
None,
234+
None,
235+
)
236+
&& suggested_name != assoc_name.name
237+
{
238+
// We suggested constraining a type parameter, but the associated type on it
239+
// was also not an exact match, so we also suggest changing it.
240+
err.span_suggestion_verbose(
241+
assoc_name.span,
242+
"and also change the associated type name",
243+
suggested_name.to_string(),
244+
Applicability::MaybeIncorrect,
245+
);
246+
}
247+
}
200248
return err.emit();
201249
}
202250
}

compiler/rustc_hir_analysis/src/astconv/mod.rs

+9-10
Original file line numberDiff line numberDiff line change
@@ -1062,6 +1062,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
10621062
)
10631063
},
10641064
param_name,
1065+
Some(ty_param_def_id),
10651066
assoc_name,
10661067
span,
10671068
None,
@@ -1075,6 +1076,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
10751076
&self,
10761077
all_candidates: impl Fn() -> I,
10771078
ty_param_name: impl Display,
1079+
ty_param_def_id: Option<LocalDefId>,
10781080
assoc_name: Ident,
10791081
span: Span,
10801082
is_equality: Option<ty::Term<'tcx>>,
@@ -1096,6 +1098,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
10961098
let reported = self.complain_about_assoc_type_not_found(
10971099
all_candidates,
10981100
&ty_param_name.to_string(),
1101+
ty_param_def_id,
10991102
assoc_name,
11001103
span,
11011104
);
@@ -1143,39 +1146,34 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
11431146
err.span_label(
11441147
bound_span,
11451148
format!(
1146-
"ambiguous `{}` from `{}`",
1147-
assoc_name,
1149+
"ambiguous `{assoc_name}` from `{}`",
11481150
bound.print_only_trait_path(),
11491151
),
11501152
);
11511153
if let Some(constraint) = &is_equality {
11521154
where_bounds.push(format!(
1153-
" T: {trait}::{assoc} = {constraint}",
1155+
" T: {trait}::{assoc_name} = {constraint}",
11541156
trait=bound.print_only_trait_path(),
1155-
assoc=assoc_name,
1156-
constraint=constraint,
11571157
));
11581158
} else {
11591159
err.span_suggestion_verbose(
11601160
span.with_hi(assoc_name.span.lo()),
11611161
"use fully qualified syntax to disambiguate",
1162-
format!("<{} as {}>::", ty_param_name, bound.print_only_trait_path()),
1162+
format!("<{ty_param_name} as {}>::", bound.print_only_trait_path()),
11631163
Applicability::MaybeIncorrect,
11641164
);
11651165
}
11661166
} else {
11671167
err.note(format!(
1168-
"associated type `{}` could derive from `{}`",
1169-
ty_param_name,
1168+
"associated type `{ty_param_name}` could derive from `{}`",
11701169
bound.print_only_trait_path(),
11711170
));
11721171
}
11731172
}
11741173
if !where_bounds.is_empty() {
11751174
err.help(format!(
11761175
"consider introducing a new type parameter `T` and adding `where` constraints:\
1177-
\n where\n T: {},\n{}",
1178-
ty_param_name,
1176+
\n where\n T: {ty_param_name},\n{}",
11791177
where_bounds.join(",\n"),
11801178
));
11811179
}
@@ -1397,6 +1395,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
13971395
)
13981396
},
13991397
kw::SelfUpper,
1398+
None,
14001399
assoc_ident,
14011400
span,
14021401
None,

compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,7 @@ fn predicate_constraint(generics: &hir::Generics<'_>, pred: ty::Predicate<'_>) -
364364
/// Type parameter needs more bounds. The trivial case is `T` `where T: Bound`, but
365365
/// it can also be an `impl Trait` param that needs to be decomposed to a type
366366
/// param for cleaner code.
367-
fn suggest_restriction<'tcx>(
367+
pub fn suggest_restriction<'tcx>(
368368
tcx: TyCtxt<'tcx>,
369369
item_id: LocalDefId,
370370
hir_generics: &hir::Generics<'tcx>,

tests/rustdoc-ui/issues/issue-96287.stderr

+6-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@ error[E0220]: associated type `Assoc` not found for `V`
22
--> $DIR/issue-96287.rs:7:33
33
|
44
LL | pub type Foo<V> = impl Trait<V::Assoc>;
5-
| ^^^^^ there is a similarly named associated type `Assoc` in the trait `TraitWithAssoc`
5+
| ^^^^^ there is an associated type `Assoc` in the trait `TraitWithAssoc`
6+
|
7+
help: consider restricting type parameter `V`
8+
|
9+
LL | pub type Foo<V: TraitWithAssoc> = impl Trait<V::Assoc>;
10+
| ++++++++++++++++
611

712
error: aborting due to previous error
813

tests/ui/resolve/issue-55673.fixed

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// run-rustfix
2+
#![allow(dead_code)]
3+
trait Foo {
4+
type Bar;
5+
}
6+
7+
fn foo<T: Foo>()
8+
where
9+
T::Bar: std::fmt::Debug,
10+
//~^ ERROR associated type `Baa` not found for `T`
11+
{
12+
}
13+
14+
fn bar<T>()
15+
where
16+
T::Bar: std::fmt::Debug, T: Foo
17+
//~^ ERROR associated type `Baa` not found for `T`
18+
{
19+
}
20+
21+
fn main() {}

tests/ui/resolve/issue-55673.rs

+9
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// run-rustfix
2+
#![allow(dead_code)]
13
trait Foo {
24
type Bar;
35
}
@@ -9,4 +11,11 @@ where
911
{
1012
}
1113

14+
fn bar<T>()
15+
where
16+
T::Baa: std::fmt::Debug,
17+
//~^ ERROR associated type `Baa` not found for `T`
18+
{
19+
}
20+
1221
fn main() {}

tests/ui/resolve/issue-55673.stderr

+22-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,29 @@
11
error[E0220]: associated type `Baa` not found for `T`
2-
--> $DIR/issue-55673.rs:7:8
2+
--> $DIR/issue-55673.rs:9:8
33
|
44
LL | T::Baa: std::fmt::Debug,
55
| ^^^ there is a similarly named associated type `Bar` in the trait `Foo`
6+
|
7+
help: change the associated type name to use `Bar` from `Foo`
8+
|
9+
LL | T::Bar: std::fmt::Debug,
10+
| ~~~
11+
12+
error[E0220]: associated type `Baa` not found for `T`
13+
--> $DIR/issue-55673.rs:16:8
14+
|
15+
LL | T::Baa: std::fmt::Debug,
16+
| ^^^ there is a similarly named associated type `Bar` in the trait `Foo`
17+
|
18+
help: consider further restricting type parameter `T`
19+
|
20+
LL | T::Baa: std::fmt::Debug, T: Foo
21+
| ~~~~~~~~
22+
help: and also change the associated type name
23+
|
24+
LL | T::Bar: std::fmt::Debug,
25+
| ~~~
626

7-
error: aborting due to previous error
27+
error: aborting due to 2 previous errors
828

929
For more information about this error, try `rustc --explain E0220`.

tests/ui/traits/issue-59029-1.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ error[E0220]: associated type `Res` not found for `Self`
22
--> $DIR/issue-59029-1.rs:5:52
33
|
44
LL | trait MkSvc<Target, Req> = Svc<Target> where Self::Res: Svc<Req>;
5-
| ^^^ there is a similarly named associated type `Res` in the trait `Svc`
5+
| ^^^ there is an associated type `Res` in the trait `Svc`
66

77
error[E0220]: associated type `Res` not found for `Self`
88
--> $DIR/issue-59029-1.rs:5:52
99
|
1010
LL | trait MkSvc<Target, Req> = Svc<Target> where Self::Res: Svc<Req>;
11-
| ^^^ there is a similarly named associated type `Res` in the trait `Svc`
11+
| ^^^ there is an associated type `Res` in the trait `Svc`
1212
|
1313
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
1414

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// run-rustfix
2+
#![feature(type_alias_impl_trait)]
3+
#![allow(dead_code)]
4+
5+
fn main() {}
6+
7+
trait TraitWithAssoc {
8+
type Assoc;
9+
}
10+
11+
type Foo<V: TraitWithAssoc> = impl Trait<V::Assoc>; //~ associated type `Assoc` not found for `V`
12+
13+
trait Trait<U> {}
14+
15+
impl<W> Trait<W> for () {}
16+
17+
fn foo_desugared<T: TraitWithAssoc>(_: T) -> Foo<T> {
18+
()
19+
}

tests/ui/type-alias-impl-trait/not_well_formed.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
// run-rustfix
12
#![feature(type_alias_impl_trait)]
3+
#![allow(dead_code)]
24

35
fn main() {}
46

tests/ui/type-alias-impl-trait/not_well_formed.stderr

+7-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
error[E0220]: associated type `Assoc` not found for `V`
2-
--> $DIR/not_well_formed.rs:9:29
2+
--> $DIR/not_well_formed.rs:11:29
33
|
44
LL | type Foo<V> = impl Trait<V::Assoc>;
5-
| ^^^^^ there is a similarly named associated type `Assoc` in the trait `TraitWithAssoc`
5+
| ^^^^^ there is an associated type `Assoc` in the trait `TraitWithAssoc`
6+
|
7+
help: consider restricting type parameter `V`
8+
|
9+
LL | type Foo<V: TraitWithAssoc> = impl Trait<V::Assoc>;
10+
| ++++++++++++++++
611

712
error: aborting due to previous error
813

0 commit comments

Comments
 (0)