Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suggest trait bounds for used associated type on type param #116257

Merged
merged 2 commits into from
Oct 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions compiler/rustc_hir_analysis/src/astconv/bounds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
self.one_bound_for_assoc_type(
|| traits::supertraits(tcx, trait_ref),
trait_ref.skip_binder().print_only_trait_name(),
None,
binding.item_name,
path_span,
match binding.kind {
Expand Down
58 changes: 53 additions & 5 deletions compiler/rustc_hir_analysis/src/astconv/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@ use crate::errors::{
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{pluralize, struct_span_err, Applicability, Diagnostic, ErrorGuaranteed};
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_middle::ty::TyCtxt;
use rustc_middle::ty::{self, Ty};
use rustc_middle::ty::{self, suggest_constraining_type_param, Ty, TyCtxt};
use rustc_session::parse::feature_err;
use rustc_span::edit_distance::find_best_match_for_name;
use rustc_span::symbol::{sym, Ident};
Expand Down Expand Up @@ -102,6 +101,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
&self,
all_candidates: impl Fn() -> I,
ty_param_name: &str,
ty_param_def_id: Option<LocalDefId>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can you maybe combine ty_param_name and ty_param_def_id into a new type instead of passing them separately?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

assoc_name: Ident,
span: Span,
) -> ErrorGuaranteed
Expand Down Expand Up @@ -190,13 +190,61 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
})
.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(
assoc_name.span,
format!(
"there is a similarly named associated type `{suggested_name}` in the trait `{}`",
self.tcx().def_path_str(*best_trait)
"there is {an} associated type `{suggested_name}` in the \
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();
}
}
Expand Down
19 changes: 9 additions & 10 deletions compiler/rustc_hir_analysis/src/astconv/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1061,6 +1061,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
)
},
param_name,
Some(ty_param_def_id),
assoc_name,
span,
None,
Expand All @@ -1074,6 +1075,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
&self,
all_candidates: impl Fn() -> I,
ty_param_name: impl Display,
ty_param_def_id: Option<LocalDefId>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and use that new type here too?

assoc_name: Ident,
span: Span,
is_equality: Option<ty::Term<'tcx>>,
Expand All @@ -1095,6 +1097,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
let reported = self.complain_about_assoc_type_not_found(
all_candidates,
&ty_param_name.to_string(),
ty_param_def_id,
assoc_name,
span,
);
Expand Down Expand Up @@ -1142,39 +1145,34 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
err.span_label(
bound_span,
format!(
"ambiguous `{}` from `{}`",
assoc_name,
"ambiguous `{assoc_name}` from `{}`",
bound.print_only_trait_path(),
),
);
if let Some(constraint) = &is_equality {
where_bounds.push(format!(
" T: {trait}::{assoc} = {constraint}",
" T: {trait}::{assoc_name} = {constraint}",
trait=bound.print_only_trait_path(),
assoc=assoc_name,
constraint=constraint,
));
} else {
err.span_suggestion_verbose(
span.with_hi(assoc_name.span.lo()),
"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,
);
}
} else {
err.note(format!(
"associated type `{}` could derive from `{}`",
ty_param_name,
"associated type `{ty_param_name}` could derive from `{}`",
bound.print_only_trait_path(),
));
}
}
if !where_bounds.is_empty() {
err.help(format!(
"consider introducing a new type parameter `T` and adding `where` constraints:\
\n where\n T: {},\n{}",
ty_param_name,
\n where\n T: {ty_param_name},\n{}",
where_bounds.join(",\n"),
));
}
Expand Down Expand Up @@ -1396,6 +1394,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
)
},
kw::SelfUpper,
None,
assoc_ident,
span,
None,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
/// it can also be an `impl Trait` param that needs to be decomposed to a type
/// param for cleaner code.
fn suggest_restriction<'tcx>(
pub fn suggest_restriction<'tcx>(
tcx: TyCtxt<'tcx>,
item_id: LocalDefId,
hir_generics: &hir::Generics<'tcx>,
Expand Down
7 changes: 6 additions & 1 deletion tests/rustdoc-ui/issues/issue-96287.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ error[E0220]: associated type `Assoc` not found for `V`
--> $DIR/issue-96287.rs:7:33
|
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

Expand Down
21 changes: 21 additions & 0 deletions tests/ui/resolve/issue-55673.fixed
Original file line number Diff line number Diff line change
@@ -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() {}
9 changes: 9 additions & 0 deletions tests/ui/resolve/issue-55673.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// run-rustfix
#![allow(dead_code)]
trait Foo {
type Bar;
}
Expand All @@ -9,4 +11,11 @@ where
{
}

fn bar<T>()
where
T::Baa: std::fmt::Debug,
//~^ ERROR associated type `Baa` not found for `T`
{
}

fn main() {}
24 changes: 22 additions & 2 deletions tests/ui/resolve/issue-55673.stderr
Original file line number Diff line number Diff line change
@@ -1,9 +1,29 @@
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,
| ^^^ 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[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 previous error
error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0220`.
4 changes: 2 additions & 2 deletions tests/ui/traits/issue-59029-1.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ error[E0220]: associated type `Res` not found for `Self`
--> $DIR/issue-59029-1.rs:5:52
|
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`
--> $DIR/issue-59029-1.rs:5:52
|
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`

Expand Down
19 changes: 19 additions & 0 deletions tests/ui/type-alias-impl-trait/not_well_formed.fixed
Original file line number Diff line number Diff line change
@@ -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> {
()
}
2 changes: 2 additions & 0 deletions tests/ui/type-alias-impl-trait/not_well_formed.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
// run-rustfix
#![feature(type_alias_impl_trait)]
#![allow(dead_code)]

fn main() {}

Expand Down
9 changes: 7 additions & 2 deletions tests/ui/type-alias-impl-trait/not_well_formed.stderr
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
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>;
| ^^^^^ 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

Expand Down
Loading