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, | ~~~ ```
This commit is contained in:
commit
14663e09b7
@ -284,6 +284,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
|
|||||||
self.one_bound_for_assoc_type(
|
self.one_bound_for_assoc_type(
|
||||||
|| traits::supertraits(tcx, trait_ref),
|
|| traits::supertraits(tcx, trait_ref),
|
||||||
trait_ref.skip_binder().print_only_trait_name(),
|
trait_ref.skip_binder().print_only_trait_name(),
|
||||||
|
None,
|
||||||
binding.item_name,
|
binding.item_name,
|
||||||
path_span,
|
path_span,
|
||||||
match binding.kind {
|
match binding.kind {
|
||||||
|
@ -6,10 +6,9 @@ use crate::errors::{
|
|||||||
use rustc_data_structures::fx::FxHashMap;
|
use rustc_data_structures::fx::FxHashMap;
|
||||||
use rustc_errors::{pluralize, struct_span_err, Applicability, Diagnostic, ErrorGuaranteed};
|
use rustc_errors::{pluralize, struct_span_err, Applicability, Diagnostic, ErrorGuaranteed};
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||||
use rustc_infer::traits::FulfillmentError;
|
use rustc_infer::traits::FulfillmentError;
|
||||||
use rustc_middle::ty::TyCtxt;
|
use rustc_middle::ty::{self, suggest_constraining_type_param, Ty, TyCtxt};
|
||||||
use rustc_middle::ty::{self, Ty};
|
|
||||||
use rustc_session::parse::feature_err;
|
use rustc_session::parse::feature_err;
|
||||||
use rustc_span::edit_distance::find_best_match_for_name;
|
use rustc_span::edit_distance::find_best_match_for_name;
|
||||||
use rustc_span::symbol::{sym, Ident};
|
use rustc_span::symbol::{sym, Ident};
|
||||||
@ -102,6 +101,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||||||
&self,
|
&self,
|
||||||
all_candidates: impl Fn() -> I,
|
all_candidates: impl Fn() -> I,
|
||||||
ty_param_name: &str,
|
ty_param_name: &str,
|
||||||
|
ty_param_def_id: Option<LocalDefId>,
|
||||||
assoc_name: Ident,
|
assoc_name: Ident,
|
||||||
span: Span,
|
span: Span,
|
||||||
) -> ErrorGuaranteed
|
) -> ErrorGuaranteed
|
||||||
@ -190,13 +190,61 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||||||
})
|
})
|
||||||
.collect::<Vec<_>>()[..]
|
.collect::<Vec<_>>()[..]
|
||||||
{
|
{
|
||||||
|
let trait_name = self.tcx().def_path_str(*best_trait);
|
||||||
|
let an = if suggested_name != assoc_name.name { "a similarly named" } else { "an" };
|
||||||
err.span_label(
|
err.span_label(
|
||||||
assoc_name.span,
|
assoc_name.span,
|
||||||
format!(
|
format!(
|
||||||
"there is a similarly named associated type `{suggested_name}` in the trait `{}`",
|
"there is {an} associated type `{suggested_name}` in the \
|
||||||
self.tcx().def_path_str(*best_trait)
|
trait `{trait_name}`",
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
let hir = self.tcx().hir();
|
||||||
|
if let Some(def_id) = ty_param_def_id
|
||||||
|
&& let parent = hir.get_parent_item(hir.local_def_id_to_hir_id(def_id))
|
||||||
|
&& let Some(generics) = hir.get_generics(parent.def_id)
|
||||||
|
{
|
||||||
|
if generics.bounds_for_param(def_id)
|
||||||
|
.flat_map(|pred| pred.bounds.iter())
|
||||||
|
.any(|b| match b {
|
||||||
|
hir::GenericBound::Trait(t, ..) => {
|
||||||
|
t.trait_ref.trait_def_id().as_ref() == Some(best_trait)
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
})
|
||||||
|
{
|
||||||
|
// The type param already has a bound for `trait_name`, we just need to
|
||||||
|
// change the associated type.
|
||||||
|
err.span_suggestion_verbose(
|
||||||
|
assoc_name.span,
|
||||||
|
format!(
|
||||||
|
"change the associated type name to use `{suggested_name}` from \
|
||||||
|
`{trait_name}`",
|
||||||
|
),
|
||||||
|
suggested_name.to_string(),
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
} else if suggest_constraining_type_param(
|
||||||
|
self.tcx(),
|
||||||
|
generics,
|
||||||
|
&mut err,
|
||||||
|
&ty_param_name,
|
||||||
|
&trait_name,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
&& suggested_name != assoc_name.name
|
||||||
|
{
|
||||||
|
// We suggested constraining a type parameter, but the associated type on it
|
||||||
|
// was also not an exact match, so we also suggest changing it.
|
||||||
|
err.span_suggestion_verbose(
|
||||||
|
assoc_name.span,
|
||||||
|
"and also change the associated type name",
|
||||||
|
suggested_name.to_string(),
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
return err.emit();
|
return err.emit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1062,6 +1062,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
param_name,
|
param_name,
|
||||||
|
Some(ty_param_def_id),
|
||||||
assoc_name,
|
assoc_name,
|
||||||
span,
|
span,
|
||||||
None,
|
None,
|
||||||
@ -1075,6 +1076,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||||||
&self,
|
&self,
|
||||||
all_candidates: impl Fn() -> I,
|
all_candidates: impl Fn() -> I,
|
||||||
ty_param_name: impl Display,
|
ty_param_name: impl Display,
|
||||||
|
ty_param_def_id: Option<LocalDefId>,
|
||||||
assoc_name: Ident,
|
assoc_name: Ident,
|
||||||
span: Span,
|
span: Span,
|
||||||
is_equality: Option<ty::Term<'tcx>>,
|
is_equality: Option<ty::Term<'tcx>>,
|
||||||
@ -1096,6 +1098,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||||||
let reported = self.complain_about_assoc_type_not_found(
|
let reported = self.complain_about_assoc_type_not_found(
|
||||||
all_candidates,
|
all_candidates,
|
||||||
&ty_param_name.to_string(),
|
&ty_param_name.to_string(),
|
||||||
|
ty_param_def_id,
|
||||||
assoc_name,
|
assoc_name,
|
||||||
span,
|
span,
|
||||||
);
|
);
|
||||||
@ -1143,30 +1146,26 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||||||
err.span_label(
|
err.span_label(
|
||||||
bound_span,
|
bound_span,
|
||||||
format!(
|
format!(
|
||||||
"ambiguous `{}` from `{}`",
|
"ambiguous `{assoc_name}` from `{}`",
|
||||||
assoc_name,
|
|
||||||
bound.print_only_trait_path(),
|
bound.print_only_trait_path(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
if let Some(constraint) = &is_equality {
|
if let Some(constraint) = &is_equality {
|
||||||
where_bounds.push(format!(
|
where_bounds.push(format!(
|
||||||
" T: {trait}::{assoc} = {constraint}",
|
" T: {trait}::{assoc_name} = {constraint}",
|
||||||
trait=bound.print_only_trait_path(),
|
trait=bound.print_only_trait_path(),
|
||||||
assoc=assoc_name,
|
|
||||||
constraint=constraint,
|
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
err.span_suggestion_verbose(
|
err.span_suggestion_verbose(
|
||||||
span.with_hi(assoc_name.span.lo()),
|
span.with_hi(assoc_name.span.lo()),
|
||||||
"use fully qualified syntax to disambiguate",
|
"use fully qualified syntax to disambiguate",
|
||||||
format!("<{} as {}>::", ty_param_name, bound.print_only_trait_path()),
|
format!("<{ty_param_name} as {}>::", bound.print_only_trait_path()),
|
||||||
Applicability::MaybeIncorrect,
|
Applicability::MaybeIncorrect,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err.note(format!(
|
err.note(format!(
|
||||||
"associated type `{}` could derive from `{}`",
|
"associated type `{ty_param_name}` could derive from `{}`",
|
||||||
ty_param_name,
|
|
||||||
bound.print_only_trait_path(),
|
bound.print_only_trait_path(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@ -1174,8 +1173,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||||||
if !where_bounds.is_empty() {
|
if !where_bounds.is_empty() {
|
||||||
err.help(format!(
|
err.help(format!(
|
||||||
"consider introducing a new type parameter `T` and adding `where` constraints:\
|
"consider introducing a new type parameter `T` and adding `where` constraints:\
|
||||||
\n where\n T: {},\n{}",
|
\n where\n T: {ty_param_name},\n{}",
|
||||||
ty_param_name,
|
|
||||||
where_bounds.join(",\n"),
|
where_bounds.join(",\n"),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@ -1397,6 +1395,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
kw::SelfUpper,
|
kw::SelfUpper,
|
||||||
|
None,
|
||||||
assoc_ident,
|
assoc_ident,
|
||||||
span,
|
span,
|
||||||
None,
|
None,
|
||||||
|
@ -364,7 +364,7 @@ fn predicate_constraint(generics: &hir::Generics<'_>, pred: ty::Predicate<'_>) -
|
|||||||
/// Type parameter needs more bounds. The trivial case is `T` `where T: Bound`, but
|
/// Type parameter needs more bounds. The trivial case is `T` `where T: Bound`, but
|
||||||
/// it can also be an `impl Trait` param that needs to be decomposed to a type
|
/// it can also be an `impl Trait` param that needs to be decomposed to a type
|
||||||
/// param for cleaner code.
|
/// param for cleaner code.
|
||||||
fn suggest_restriction<'tcx>(
|
pub fn suggest_restriction<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
item_id: LocalDefId,
|
item_id: LocalDefId,
|
||||||
hir_generics: &hir::Generics<'tcx>,
|
hir_generics: &hir::Generics<'tcx>,
|
||||||
|
@ -2,7 +2,12 @@ error[E0220]: associated type `Assoc` not found for `V`
|
|||||||
--> $DIR/issue-96287.rs:7:33
|
--> $DIR/issue-96287.rs:7:33
|
||||||
|
|
|
|
||||||
LL | pub type Foo<V> = impl Trait<V::Assoc>;
|
LL | pub type Foo<V> = impl Trait<V::Assoc>;
|
||||||
| ^^^^^ there is a similarly named associated type `Assoc` in the trait `TraitWithAssoc`
|
| ^^^^^ there is an associated type `Assoc` in the trait `TraitWithAssoc`
|
||||||
|
|
|
||||||
|
help: consider restricting type parameter `V`
|
||||||
|
|
|
||||||
|
LL | pub type Foo<V: TraitWithAssoc> = impl Trait<V::Assoc>;
|
||||||
|
| ++++++++++++++++
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
21
tests/ui/resolve/issue-55673.fixed
Normal file
21
tests/ui/resolve/issue-55673.fixed
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// run-rustfix
|
||||||
|
#![allow(dead_code)]
|
||||||
|
trait Foo {
|
||||||
|
type Bar;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn foo<T: Foo>()
|
||||||
|
where
|
||||||
|
T::Bar: std::fmt::Debug,
|
||||||
|
//~^ ERROR associated type `Baa` not found for `T`
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bar<T>()
|
||||||
|
where
|
||||||
|
T::Bar: std::fmt::Debug, T: Foo
|
||||||
|
//~^ ERROR associated type `Baa` not found for `T`
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
@ -1,3 +1,5 @@
|
|||||||
|
// run-rustfix
|
||||||
|
#![allow(dead_code)]
|
||||||
trait Foo {
|
trait Foo {
|
||||||
type Bar;
|
type Bar;
|
||||||
}
|
}
|
||||||
@ -9,4 +11,11 @@ where
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn bar<T>()
|
||||||
|
where
|
||||||
|
T::Baa: std::fmt::Debug,
|
||||||
|
//~^ ERROR associated type `Baa` not found for `T`
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
@ -1,9 +1,29 @@
|
|||||||
error[E0220]: associated type `Baa` not found for `T`
|
error[E0220]: associated type `Baa` not found for `T`
|
||||||
--> $DIR/issue-55673.rs:7:8
|
--> $DIR/issue-55673.rs:9:8
|
||||||
|
|
|
|
||||||
LL | T::Baa: std::fmt::Debug,
|
LL | T::Baa: std::fmt::Debug,
|
||||||
| ^^^ there is a similarly named associated type `Bar` in the trait `Foo`
|
| ^^^ 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,
|
||||||
|
| ~~~
|
||||||
|
|
||||||
error: aborting due to previous error
|
error[E0220]: associated type `Baa` not found for `T`
|
||||||
|
--> $DIR/issue-55673.rs:16:8
|
||||||
|
|
|
||||||
|
LL | T::Baa: std::fmt::Debug,
|
||||||
|
| ^^^ there is a similarly named associated type `Bar` in the trait `Foo`
|
||||||
|
|
|
||||||
|
help: consider further restricting type parameter `T`
|
||||||
|
|
|
||||||
|
LL | T::Baa: std::fmt::Debug, T: Foo
|
||||||
|
| ~~~~~~~~
|
||||||
|
help: and also change the associated type name
|
||||||
|
|
|
||||||
|
LL | T::Bar: std::fmt::Debug,
|
||||||
|
| ~~~
|
||||||
|
|
||||||
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0220`.
|
For more information about this error, try `rustc --explain E0220`.
|
||||||
|
@ -2,13 +2,13 @@ error[E0220]: associated type `Res` not found for `Self`
|
|||||||
--> $DIR/issue-59029-1.rs:5:52
|
--> $DIR/issue-59029-1.rs:5:52
|
||||||
|
|
|
|
||||||
LL | trait MkSvc<Target, Req> = Svc<Target> where Self::Res: Svc<Req>;
|
LL | trait MkSvc<Target, Req> = Svc<Target> where Self::Res: Svc<Req>;
|
||||||
| ^^^ there is a similarly named associated type `Res` in the trait `Svc`
|
| ^^^ there is an associated type `Res` in the trait `Svc`
|
||||||
|
|
||||||
error[E0220]: associated type `Res` not found for `Self`
|
error[E0220]: associated type `Res` not found for `Self`
|
||||||
--> $DIR/issue-59029-1.rs:5:52
|
--> $DIR/issue-59029-1.rs:5:52
|
||||||
|
|
|
|
||||||
LL | trait MkSvc<Target, Req> = Svc<Target> where Self::Res: Svc<Req>;
|
LL | trait MkSvc<Target, Req> = Svc<Target> where Self::Res: Svc<Req>;
|
||||||
| ^^^ there is a similarly named associated type `Res` in the trait `Svc`
|
| ^^^ there is an associated type `Res` in the trait `Svc`
|
||||||
|
|
|
|
||||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||||
|
|
||||||
|
19
tests/ui/type-alias-impl-trait/not_well_formed.fixed
Normal file
19
tests/ui/type-alias-impl-trait/not_well_formed.fixed
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// run-rustfix
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
fn main() {}
|
||||||
|
|
||||||
|
trait TraitWithAssoc {
|
||||||
|
type Assoc;
|
||||||
|
}
|
||||||
|
|
||||||
|
type Foo<V: TraitWithAssoc> = impl Trait<V::Assoc>; //~ associated type `Assoc` not found for `V`
|
||||||
|
|
||||||
|
trait Trait<U> {}
|
||||||
|
|
||||||
|
impl<W> Trait<W> for () {}
|
||||||
|
|
||||||
|
fn foo_desugared<T: TraitWithAssoc>(_: T) -> Foo<T> {
|
||||||
|
()
|
||||||
|
}
|
@ -1,4 +1,6 @@
|
|||||||
|
// run-rustfix
|
||||||
#![feature(type_alias_impl_trait)]
|
#![feature(type_alias_impl_trait)]
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
||||||
|
@ -1,8 +1,13 @@
|
|||||||
error[E0220]: associated type `Assoc` not found for `V`
|
error[E0220]: associated type `Assoc` not found for `V`
|
||||||
--> $DIR/not_well_formed.rs:9:29
|
--> $DIR/not_well_formed.rs:11:29
|
||||||
|
|
|
|
||||||
LL | type Foo<V> = impl Trait<V::Assoc>;
|
LL | type Foo<V> = impl Trait<V::Assoc>;
|
||||||
| ^^^^^ there is a similarly named associated type `Assoc` in the trait `TraitWithAssoc`
|
| ^^^^^ there is an associated type `Assoc` in the trait `TraitWithAssoc`
|
||||||
|
|
|
||||||
|
help: consider restricting type parameter `V`
|
||||||
|
|
|
||||||
|
LL | type Foo<V: TraitWithAssoc> = impl Trait<V::Assoc>;
|
||||||
|
| ++++++++++++++++
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user