@@ -123,6 +123,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
123
123
}
124
124
}
125
125
126
+ /// For a struct or enum with an invalid bare trait object field, suggest turning
127
+ /// it into a generic type bound.
126
128
fn maybe_suggest_add_generic_impl_trait (
127
129
& self ,
128
130
self_ty : & hir:: Ty < ' _ > ,
@@ -132,21 +134,38 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
132
134
let msg = "you might be missing a type parameter" ;
133
135
let mut sugg = vec ! [ ] ;
134
136
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
147
148
}
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 ( ) ) ) ;
150
169
diag. multipart_suggestion_verbose ( msg, sugg, Applicability :: MachineApplicable ) ;
151
170
true
152
171
}
@@ -198,6 +217,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
198
217
}
199
218
}
200
219
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.
201
227
fn maybe_suggest_dyn_trait (
202
228
& self ,
203
229
self_ty : & hir:: Ty < ' _ > ,
@@ -206,19 +232,40 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
206
232
diag : & mut Diag < ' _ > ,
207
233
) -> bool {
208
234
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 ( ) ;
211
235
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 ( _) => { }
216
252
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
+ }
221
267
}
268
+ _ => return false ,
222
269
}
223
270
224
271
// FIXME: Only emit this suggestion if the trait is dyn-compatible.
0 commit comments