@@ -123,6 +123,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
123123 }
124124 }
125125
126+ /// For a struct or enum with an invalid bare trait object field, suggest turning
127+ /// it into a generic type bound.
126128 fn maybe_suggest_add_generic_impl_trait (
127129 & self ,
128130 self_ty : & hir:: Ty < ' _ > ,
@@ -132,21 +134,38 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
132134 let msg = "you might be missing a type parameter" ;
133135 let mut sugg = vec ! [ ] ;
134136
135- let parent_id = tcx. hir_get_parent_item ( self_ty. hir_id ) . def_id ;
136- let parent_item = tcx. hir_node_by_def_id ( parent_id) . expect_item ( ) ;
137- match parent_item. kind {
138- hir:: ItemKind :: Struct ( _, generics) | hir:: ItemKind :: Enum ( _, generics) => {
139- sugg. push ( (
140- generics. where_clause_span ,
141- format ! (
142- "<T: {}>" ,
143- self . tcx( ) . sess. source_map( ) . span_to_snippet( self_ty. span) . unwrap( )
144- ) ,
145- ) ) ;
146- sugg. push ( ( self_ty. span , "T" . to_string ( ) ) ) ;
137+ let parent_hir_id = tcx. parent_hir_id ( self_ty. hir_id ) ;
138+ let parent_item = tcx. hir_get_parent_item ( self_ty. hir_id ) . def_id ;
139+
140+ let generics = match tcx. hir_node_by_def_id ( parent_item) {
141+ hir:: Node :: Item ( hir:: Item {
142+ kind : hir:: ItemKind :: Struct ( variant, generics) , ..
143+ } ) => {
144+ if !variant. fields ( ) . iter ( ) . any ( |field| field. hir_id == parent_hir_id) {
145+ return false ;
146+ }
147+ generics
147148 }
148- _ => { }
149- }
149+ hir:: Node :: Item ( hir:: Item { kind : hir:: ItemKind :: Enum ( def, generics) , .. } ) => {
150+ if !def
151+ . variants
152+ . iter ( )
153+ . flat_map ( |variant| variant. data . fields ( ) . iter ( ) )
154+ . any ( |field| field. hir_id == parent_hir_id)
155+ {
156+ return false ;
157+ }
158+ generics
159+ }
160+ _ => return false ,
161+ } ;
162+
163+ // FIXME: `T` may already be taken.
164+ sugg. push ( (
165+ generics. where_clause_span ,
166+ format ! ( "<T: {}>" , self . tcx( ) . sess. source_map( ) . span_to_snippet( self_ty. span) . unwrap( ) ) ,
167+ ) ) ;
168+ sugg. push ( ( self_ty. span , "T" . to_string ( ) ) ) ;
150169 diag. multipart_suggestion_verbose ( msg, sugg, Applicability :: MachineApplicable ) ;
151170 true
152171 }
@@ -198,6 +217,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
198217 }
199218 }
200219
220+ /// Try our best to approximate when adding `dyn` would be helpful for a bare
221+ /// trait object.
222+ ///
223+ /// Right now, this is if the type is either directly nested in another ty,
224+ /// or if it's in the tail field within a struct. This approximates what the
225+ /// user would've gotten on edition 2015, except for the case where we have
226+ /// an *obvious* knock-on `Sized` error.
201227 fn maybe_suggest_dyn_trait (
202228 & self ,
203229 self_ty : & hir:: Ty < ' _ > ,
@@ -206,19 +232,40 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
206232 diag : & mut Diag < ' _ > ,
207233 ) -> bool {
208234 let tcx = self . tcx ( ) ;
209- let parent_id = tcx. hir_get_parent_item ( self_ty. hir_id ) . def_id ;
210- let parent_item = tcx. hir_node_by_def_id ( parent_id) . expect_item ( ) ;
211235
212- // If the parent item is an enum, don't suggest the dyn trait.
213- if let hir:: ItemKind :: Enum ( ..) = parent_item. kind {
214- return false ;
215- }
236+ // Look at the direct HIR parent, since we care about the relationship between
237+ // the type and the thing that directly encloses it.
238+ match tcx. parent_hir_node ( self_ty. hir_id ) {
239+ // These are all generally ok. Namely, when a trait object is nested
240+ // into another expression or ty, it's either very certain that they
241+ // missed the ty (e.g. `&Trait`) or it's not really possible to tell
242+ // what their intention is, so let's not give confusing suggestions and
243+ // just mention `dyn`. The user can make up their mind what to do here.
244+ hir:: Node :: Ty ( _)
245+ | hir:: Node :: Expr ( _)
246+ | hir:: Node :: PatExpr ( _)
247+ | hir:: Node :: PathSegment ( _)
248+ | hir:: Node :: AssocItemConstraint ( _)
249+ | hir:: Node :: TraitRef ( _)
250+ | hir:: Node :: Item ( _)
251+ | hir:: Node :: WherePredicate ( _) => { }
216252
217- // If the parent item is a struct, check if self_ty is the last field.
218- if let hir:: ItemKind :: Struct ( variant_data, _) = parent_item. kind {
219- if variant_data. fields ( ) . last ( ) . unwrap ( ) . ty . span != self_ty. span {
220- return false ;
253+ hir:: Node :: Field ( field) => {
254+ // Enums can't have unsized fields, fields can only have an unsized tail field.
255+ if let hir:: Node :: Item ( hir:: Item {
256+ kind : hir:: ItemKind :: Struct ( variant, _) , ..
257+ } ) = tcx. parent_hir_node ( field. hir_id )
258+ && variant
259+ . fields ( )
260+ . last ( )
261+ . is_some_and ( |tail_field| tail_field. hir_id == field. hir_id )
262+ {
263+ // Ok
264+ } else {
265+ return false ;
266+ }
221267 }
268+ _ => return false ,
222269 }
223270
224271 // FIXME: Only emit this suggestion if the trait is dyn-compatible.
0 commit comments