Skip to content

Commit

Permalink
Suggest assoc ty bound on bare dyn trait in eq constraint
Browse files Browse the repository at this point in the history
  • Loading branch information
fmease committed Mar 22, 2024
1 parent d3d77a8 commit 22b9e96
Show file tree
Hide file tree
Showing 3 changed files with 159 additions and 0 deletions.
38 changes: 38 additions & 0 deletions compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
if self_ty.span.can_be_used_for_suggestions()
&& !self.maybe_suggest_impl_trait(self_ty, &mut diag)
{
// FIXME: Only emit this suggestion if the trait is object safe.
diag.multipart_suggestion_verbose(label, sugg, Applicability::MachineApplicable);
}
// Check if the impl trait that we are considering is an impl of a local trait.
self.maybe_suggest_blanket_trait_impl(self_ty, &mut diag);
self.maybe_suggest_assoc_ty_bound(self_ty, &mut diag);
diag.stash(self_ty.span, StashKey::TraitMissingMethod);
} else {
let msg = "trait objects without an explicit `dyn` are deprecated";
Expand Down Expand Up @@ -153,6 +155,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
fn maybe_suggest_impl_trait(&self, self_ty: &hir::Ty<'_>, diag: &mut Diag<'_>) -> bool {
let tcx = self.tcx();
let parent_id = tcx.hir().get_parent_item(self_ty.hir_id).def_id;
// FIXME: If `type_alias_impl_trait` is enabled, also look for `Trait0<Ty = Trait1>`
// and suggest `Trait0<Ty = impl Trait1>`.
let (sig, generics, owner) = match tcx.hir_node_by_def_id(parent_id) {
hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, generics, _), .. }) => {
(sig, generics, None)
Expand Down Expand Up @@ -260,4 +264,38 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}
false
}

fn maybe_suggest_assoc_ty_bound(&self, self_ty: &hir::Ty<'_>, diag: &mut Diag<'_>) {
let mut parents = self.tcx().hir().parent_iter(self_ty.hir_id);

if let Some((_, hir::Node::TypeBinding(binding))) = parents.next()
&& let hir::TypeBindingKind::Equality { term: hir::Term::Ty(obj_ty) } = binding.kind
{
if let Some((_, hir::Node::TraitRef(..))) = parents.next()
&& let Some((_, hir::Node::Ty(ty))) = parents.next()
&& let hir::TyKind::TraitObject(..) = ty.kind
{
// Assoc ty bounds aren't permitted inside trait object types.
return;
}

let lo = if binding.gen_args.span_ext.is_dummy() {
binding.ident.span
} else {
binding.gen_args.span_ext
};
let hi = obj_ty.span;

if !lo.eq_ctxt(hi) {
return;
}

diag.span_suggestion_verbose(
lo.between(hi),
"you might have meant to write a bound here",
": ",
Applicability::MaybeIncorrect,
);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Regression test for issue #105056.
//@ edition: 2021

fn f(_: impl Trait<T = Copy>) {}
//~^ ERROR trait objects must include the `dyn` keyword
//~| HELP add `dyn` keyword before this trait
//~| HELP you might have meant to write a bound here
//~| ERROR the trait `Copy` cannot be made into an object

fn g(_: impl Trait<T = std::fmt::Debug + Eq>) {}
//~^ ERROR trait objects must include the `dyn` keyword
//~| HELP add `dyn` keyword before this trait
//~| HELP you might have meant to write a bound here
//~| ERROR only auto traits can be used as additional traits in a trait object
//~| HELP consider creating a new trait
//~| ERROR the trait `Eq` cannot be made into an object

fn h(_: impl Trait<T<> = 'static + for<'a> Fn(&'a ())>) {}
//~^ ERROR trait objects must include the `dyn` keyword
//~| HELP add `dyn` keyword before this trait
//~| HELP you might have meant to write a bound here

// Don't suggest assoc ty bound in trait object types, that's not valid:
type Obj = dyn Trait<T = Clone>;
//~^ ERROR trait objects must include the `dyn` keyword
//~| HELP add `dyn` keyword before this trait

trait Trait { type T; }

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
error[E0038]: the trait `Copy` cannot be made into an object
--> $DIR/suggest-assoc-ty-bound-on-eq-bound.rs:4:20
|
LL | fn f(_: impl Trait<T = Copy>) {}
| ^^^^^^^^ `Copy` cannot be made into an object
|
= note: the trait cannot be made into an object because it requires `Self: Sized`
= note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>

error[E0225]: only auto traits can be used as additional traits in a trait object
--> $DIR/suggest-assoc-ty-bound-on-eq-bound.rs:10:42
|
LL | fn g(_: impl Trait<T = std::fmt::Debug + Eq>) {}
| --------------- ^^ additional non-auto trait
| |
| first non-auto trait
|
= help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: Debug + Eq {}`
= note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit <https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits>

error[E0038]: the trait `Eq` cannot be made into an object
--> $DIR/suggest-assoc-ty-bound-on-eq-bound.rs:10:24
|
LL | fn g(_: impl Trait<T = std::fmt::Debug + Eq>) {}
| ^^^^^^^^^^^^^^^^^^^^ `Eq` cannot be made into an object
|
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
--> $SRC_DIR/core/src/cmp.rs:LL:COL
|
= note: the trait cannot be made into an object because it uses `Self` as a type parameter

error[E0782]: trait objects must include the `dyn` keyword
--> $DIR/suggest-assoc-ty-bound-on-eq-bound.rs:4:24
|
LL | fn f(_: impl Trait<T = Copy>) {}
| ^^^^
|
help: add `dyn` keyword before this trait
|
LL | fn f(_: impl Trait<T = dyn Copy>) {}
| +++
help: you might have meant to write a bound here
|
LL | fn f(_: impl Trait<T: Copy>) {}
| ~

error[E0782]: trait objects must include the `dyn` keyword
--> $DIR/suggest-assoc-ty-bound-on-eq-bound.rs:10:24
|
LL | fn g(_: impl Trait<T = std::fmt::Debug + Eq>) {}
| ^^^^^^^^^^^^^^^^^^^^
|
help: add `dyn` keyword before this trait
|
LL | fn g(_: impl Trait<T = dyn std::fmt::Debug + Eq>) {}
| +++
help: you might have meant to write a bound here
|
LL | fn g(_: impl Trait<T: std::fmt::Debug + Eq>) {}
| ~

error[E0782]: trait objects must include the `dyn` keyword
--> $DIR/suggest-assoc-ty-bound-on-eq-bound.rs:18:26
|
LL | fn h(_: impl Trait<T<> = 'static + for<'a> Fn(&'a ())>) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: add `dyn` keyword before this trait
|
LL | fn h(_: impl Trait<T<> = dyn 'static + for<'a> Fn(&'a ())>) {}
| +++
help: you might have meant to write a bound here
|
LL | fn h(_: impl Trait<T<>: 'static + for<'a> Fn(&'a ())>) {}
| ~

error[E0782]: trait objects must include the `dyn` keyword
--> $DIR/suggest-assoc-ty-bound-on-eq-bound.rs:24:26
|
LL | type Obj = dyn Trait<T = Clone>;
| ^^^^^
|
help: add `dyn` keyword before this trait
|
LL | type Obj = dyn Trait<T = dyn Clone>;
| +++

error: aborting due to 7 previous errors

Some errors have detailed explanations: E0038, E0225, E0782.
For more information about an error, try `rustc --explain E0038`.

0 comments on commit 22b9e96

Please sign in to comment.