Skip to content

Suggest struct or union to add generic that impls trait #138042

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

Merged
merged 2 commits into from
Mar 7, 2025
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
63 changes: 61 additions & 2 deletions compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
rustc_errors::struct_span_code_err!(self.dcx(), self_ty.span, E0782, "{}", msg);
if self_ty.span.can_be_used_for_suggestions()
&& !self.maybe_suggest_impl_trait(self_ty, &mut diag)
&& !self.maybe_suggest_dyn_trait(self_ty, label, sugg, &mut diag)
{
// FIXME: Only emit this suggestion if the trait is dyn-compatible.
diag.multipart_suggestion_verbose(label, sugg, Applicability::MachineApplicable);
self.maybe_suggest_add_generic_impl_trait(self_ty, &mut diag);
}
// 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);
Expand Down Expand Up @@ -123,6 +123,33 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}
}

fn maybe_suggest_add_generic_impl_trait(
&self,
self_ty: &hir::Ty<'_>,
diag: &mut Diag<'_>,
) -> bool {
let tcx = self.tcx();
let msg = "you might be missing a type parameter";
let mut sugg = vec![];

let parent_id = tcx.hir_get_parent_item(self_ty.hir_id).def_id;
let parent_item = tcx.hir_node_by_def_id(parent_id).expect_item();
match parent_item.kind {
hir::ItemKind::Struct(_, generics) | hir::ItemKind::Enum(_, generics) => {
sugg.push((
generics.where_clause_span,
format!(
"<T: {}>",
self.tcx().sess.source_map().span_to_snippet(self_ty.span).unwrap()
),
));
sugg.push((self_ty.span, "T".to_string()));
}
_ => {}
}
diag.multipart_suggestion_verbose(msg, sugg, Applicability::MachineApplicable);
true
}
/// Make sure that we are in the condition to suggest the blanket implementation.
fn maybe_suggest_blanket_trait_impl<G: EmissionGuarantee>(
&self,
Expand Down Expand Up @@ -171,6 +198,38 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}
}

fn maybe_suggest_dyn_trait(
&self,
self_ty: &hir::Ty<'_>,
label: &str,
sugg: Vec<(Span, String)>,
diag: &mut Diag<'_>,
) -> bool {
let tcx = self.tcx();
let parent_id = tcx.hir_get_parent_item(self_ty.hir_id).def_id;
let parent_item = tcx.hir_node_by_def_id(parent_id).expect_item();

// If the parent item is an enum, don't suggest the dyn trait.
if let hir::ItemKind::Enum(..) = parent_item.kind {
return false;
}

// If the parent item is a struct, check if self_ty is the last field.
if let hir::ItemKind::Struct(variant_data, _) = parent_item.kind {
if variant_data.fields().last().unwrap().ty.span != self_ty.span {
return false;
}
}

// FIXME: Only emit this suggestion if the trait is dyn-compatible.
diag.multipart_suggestion_verbose(
label.to_string(),
sugg,
Applicability::MachineApplicable,
);
true
}

fn add_generic_param_suggestion(
&self,
generics: &hir::Generics<'_>,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//@ edition:2021
trait Trait {}

struct Foo1 {
a: Trait,
//~^ ERROR expected a type, found a trait
b: u32,
}

struct Foo2 {
a: i32,
b: Trait,
//~^ ERROR expected a type, found a trait
}


enum Enum1 {
A(Trait),
//~^ ERROR expected a type, found a trait
B(u32),
}

enum Enum2 {
A(u32),
B(Trait),
//~^ ERROR expected a type, found a trait
}


fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
error[E0782]: expected a type, found a trait
--> $DIR/suggest-struct-or-union-add-generic-impl-trait.rs:5:8
|
LL | a: Trait,
| ^^^^^
|
help: you might be missing a type parameter
|
LL ~ struct Foo1<T: Trait> {
LL ~ a: T,
|

error[E0782]: expected a type, found a trait
--> $DIR/suggest-struct-or-union-add-generic-impl-trait.rs:12:8
|
LL | b: Trait,
| ^^^^^
|
help: you can add the `dyn` keyword if you want a trait object
|
LL | b: dyn Trait,
| +++

error[E0782]: expected a type, found a trait
--> $DIR/suggest-struct-or-union-add-generic-impl-trait.rs:18:7
|
LL | A(Trait),
| ^^^^^
|
help: you might be missing a type parameter
|
LL ~ enum Enum1<T: Trait> {
LL ~ A(T),
|

error[E0782]: expected a type, found a trait
--> $DIR/suggest-struct-or-union-add-generic-impl-trait.rs:25:7
|
LL | B(Trait),
| ^^^^^
|
help: you might be missing a type parameter
|
LL ~ enum Enum2<T: Trait> {
LL | A(u32),
LL ~ B(T),
|

error: aborting due to 4 previous errors

For more information about this error, try `rustc --explain E0782`.
Loading