1
Fork 0

Rework maybe_suggest_add_generic_impl_trait

This commit is contained in:
Michael Goulet 2025-03-08 19:44:43 +00:00
parent 07292ccccd
commit bca0ab8d7a
5 changed files with 108 additions and 29 deletions

View file

@ -123,6 +123,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
} }
} }
/// For a struct or enum with an invalid bare trait object field, suggest turning
/// it into a generic type bound.
fn maybe_suggest_add_generic_impl_trait( fn maybe_suggest_add_generic_impl_trait(
&self, &self,
self_ty: &hir::Ty<'_>, self_ty: &hir::Ty<'_>,
@ -132,21 +134,38 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let msg = "you might be missing a type parameter"; let msg = "you might be missing a type parameter";
let mut sugg = vec![]; let mut sugg = vec![];
let parent_id = tcx.hir_get_parent_item(self_ty.hir_id).def_id; let parent_hir_id = tcx.parent_hir_id(self_ty.hir_id);
let parent_item = tcx.hir_node_by_def_id(parent_id).expect_item(); let parent_item = tcx.hir_get_parent_item(self_ty.hir_id).def_id;
match parent_item.kind {
hir::ItemKind::Struct(_, generics) | hir::ItemKind::Enum(_, generics) => { let generics = match tcx.hir_node_by_def_id(parent_item) {
sugg.push(( hir::Node::Item(hir::Item {
generics.where_clause_span, kind: hir::ItemKind::Struct(variant, generics), ..
format!( }) => {
"<T: {}>", if !variant.fields().iter().any(|field| field.hir_id == parent_hir_id) {
self.tcx().sess.source_map().span_to_snippet(self_ty.span).unwrap() return false;
), }
)); generics
sugg.push((self_ty.span, "T".to_string()));
} }
_ => {} hir::Node::Item(hir::Item { kind: hir::ItemKind::Enum(def, generics), .. }) => {
} if !def
.variants
.iter()
.flat_map(|variant| variant.data.fields().iter())
.any(|field| field.hir_id == parent_hir_id)
{
return false;
}
generics
}
_ => return false,
};
// FIXME: `T` may already be taken.
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); diag.multipart_suggestion_verbose(msg, sugg, Applicability::MachineApplicable);
true true
} }
@ -198,6 +217,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
} }
} }
/// Try our best to approximate when adding `dyn` would be helpful for a bare
/// trait object.
///
/// Right now, this is if the type is either directly nested in another ty,
/// or if it's in the tail field within a struct. This approximates what the
/// user would've gotten on edition 2015, except for the case where we have
/// an *obvious* knock-on `Sized` error.
fn maybe_suggest_dyn_trait( fn maybe_suggest_dyn_trait(
&self, &self,
self_ty: &hir::Ty<'_>, self_ty: &hir::Ty<'_>,
@ -206,19 +232,40 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
diag: &mut Diag<'_>, diag: &mut Diag<'_>,
) -> bool { ) -> bool {
let tcx = self.tcx(); 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. // Look at the direct HIR parent, since we care about the relationship between
if let hir::ItemKind::Enum(..) = parent_item.kind { // the type and the thing that directly encloses it.
return false; match tcx.parent_hir_node(self_ty.hir_id) {
} // These are all generally ok. Namely, when a trait object is nested
// into another expression or ty, it's either very certain that they
// missed the ty (e.g. `&Trait`) or it's not really possible to tell
// what their intention is, so let's not give confusing suggestions and
// just mention `dyn`. The user can make up their mind what to do here.
hir::Node::Ty(_)
| hir::Node::Expr(_)
| hir::Node::PatExpr(_)
| hir::Node::PathSegment(_)
| hir::Node::AssocItemConstraint(_)
| hir::Node::TraitRef(_)
| hir::Node::Item(_)
| hir::Node::WherePredicate(_) => {}
// If the parent item is a struct, check if self_ty is the last field. hir::Node::Field(field) => {
if let hir::ItemKind::Struct(variant_data, _) = parent_item.kind { // Enums can't have unsized fields, fields can only have an unsized tail field.
if variant_data.fields().last().unwrap().ty.span != self_ty.span { if let hir::Node::Item(hir::Item {
return false; kind: hir::ItemKind::Struct(variant, _), ..
}) = tcx.parent_hir_node(field.hir_id)
&& variant
.fields()
.last()
.is_some_and(|tail_field| tail_field.hir_id == field.hir_id)
{
// Ok
} else {
return false;
}
} }
_ => return false,
} }
// FIXME: Only emit this suggestion if the trait is dyn-compatible. // FIXME: Only emit this suggestion if the trait is dyn-compatible.

View file

@ -7,6 +7,12 @@ fn function(x: &SomeTrait, y: Box<SomeTrait>) {
//~^ ERROR expected a type, found a trait //~^ ERROR expected a type, found a trait
} }
// Regression test for <https://github.com/rust-lang/rust/issues/138211>.
extern "C" {
fn foo() -> *const SomeTrait;
//~^ ERROR expected a type, found a trait
}
trait SomeTrait {} trait SomeTrait {}
fn main() {} fn main() {}

View file

@ -29,6 +29,17 @@ help: you can add the `dyn` keyword if you want a trait object
LL | fn function(x: &SomeTrait, y: Box<dyn SomeTrait>) { LL | fn function(x: &SomeTrait, y: Box<dyn SomeTrait>) {
| +++ | +++
error[E0782]: expected a type, found a trait
--> $DIR/dyn-2021-edition-error.rs:12:24
|
LL | fn foo() -> *const SomeTrait;
| ^^^^^^^^^
|
help: you can add the `dyn` keyword if you want a trait object
|
LL | fn foo() -> *const dyn SomeTrait;
| +++
error[E0782]: expected a type, found a trait error[E0782]: expected a type, found a trait
--> $DIR/dyn-2021-edition-error.rs:6:14 --> $DIR/dyn-2021-edition-error.rs:6:14
| |
@ -40,6 +51,6 @@ help: you can add the `dyn` keyword if you want a trait object
LL | let _x: &dyn SomeTrait = todo!(); LL | let _x: &dyn SomeTrait = todo!();
| +++ | +++
error: aborting due to 3 previous errors error: aborting due to 4 previous errors
For more information about this error, try `rustc --explain E0782`. For more information about this error, try `rustc --explain E0782`.

View file

@ -13,7 +13,6 @@ struct Foo2 {
//~^ ERROR expected a type, found a trait //~^ ERROR expected a type, found a trait
} }
enum Enum1 { enum Enum1 {
A(Trait), A(Trait),
//~^ ERROR expected a type, found a trait //~^ ERROR expected a type, found a trait
@ -26,5 +25,10 @@ enum Enum2 {
//~^ ERROR expected a type, found a trait //~^ ERROR expected a type, found a trait
} }
// Regression test for <https://github.com/rust-lang/rust/issues/138229>.
pub struct InWhereClause
where
Trait:, {}
//~^ ERROR expected a type, found a trait
fn main() {} fn main() {}

View file

@ -22,7 +22,7 @@ LL | b: dyn Trait,
| +++ | +++
error[E0782]: expected a type, found a trait error[E0782]: expected a type, found a trait
--> $DIR/suggest-struct-or-union-add-generic-impl-trait.rs:18:7 --> $DIR/suggest-struct-or-union-add-generic-impl-trait.rs:17:7
| |
LL | A(Trait), LL | A(Trait),
| ^^^^^ | ^^^^^
@ -34,7 +34,7 @@ LL ~ A(T),
| |
error[E0782]: expected a type, found a trait error[E0782]: expected a type, found a trait
--> $DIR/suggest-struct-or-union-add-generic-impl-trait.rs:25:7 --> $DIR/suggest-struct-or-union-add-generic-impl-trait.rs:24:7
| |
LL | B(Trait), LL | B(Trait),
| ^^^^^ | ^^^^^
@ -46,6 +46,17 @@ LL | A(u32),
LL ~ B(T), LL ~ B(T),
| |
error: aborting due to 4 previous errors error[E0782]: expected a type, found a trait
--> $DIR/suggest-struct-or-union-add-generic-impl-trait.rs:31:5
|
LL | Trait:, {}
| ^^^^^
|
help: you can add the `dyn` keyword if you want a trait object
|
LL | dyn Trait:, {}
| +++
error: aborting due to 5 previous errors
For more information about this error, try `rustc --explain E0782`. For more information about this error, try `rustc --explain E0782`.