@@ -2285,10 +2285,10 @@ impl<'v> Visitor<'v> for FindTypeParam {
2285
2285
}
2286
2286
}
2287
2287
2288
- pub fn recursive_type_with_infinite_size_error (
2289
- tcx : TyCtxt < ' _ > ,
2288
+ pub fn recursive_type_with_infinite_size_error < ' tcx > (
2289
+ tcx : TyCtxt < ' tcx > ,
2290
2290
type_def_id : DefId ,
2291
- spans : Vec < Span > ,
2291
+ spans : Vec < ( Span , Option < hir :: HirId > ) > ,
2292
2292
) {
2293
2293
assert ! ( type_def_id. is_local( ) ) ;
2294
2294
let span = tcx. hir ( ) . span_if_local ( type_def_id) . unwrap ( ) ;
@@ -2297,24 +2297,33 @@ pub fn recursive_type_with_infinite_size_error(
2297
2297
let mut err =
2298
2298
struct_span_err ! ( tcx. sess, span, E0072 , "recursive type `{}` has infinite size" , path) ;
2299
2299
err. span_label ( span, "recursive type has infinite size" ) ;
2300
- for & span in & spans {
2300
+ for & ( span, _ ) in & spans {
2301
2301
err. span_label ( span, "recursive without indirection" ) ;
2302
2302
}
2303
2303
let msg = format ! (
2304
2304
"insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `{}` representable" ,
2305
2305
path,
2306
2306
) ;
2307
2307
if spans. len ( ) <= 4 {
2308
+ // FIXME(compiler-errors): This suggestion might be erroneous if Box is shadowed
2308
2309
err. multipart_suggestion (
2309
2310
& msg,
2310
2311
spans
2311
- . iter ( )
2312
- . flat_map ( |& span| {
2313
- [
2314
- ( span. shrink_to_lo ( ) , "Box<" . to_string ( ) ) ,
2315
- ( span. shrink_to_hi ( ) , ">" . to_string ( ) ) ,
2316
- ]
2317
- . into_iter ( )
2312
+ . into_iter ( )
2313
+ . flat_map ( |( span, field_id) | {
2314
+ if let Some ( generic_span) = get_option_generic_from_field_id ( tcx, field_id) {
2315
+ // If we match an `Option` and can grab the span of the Option's generic, then
2316
+ // suggest boxing the generic arg for a non-null niche optimization.
2317
+ vec ! [
2318
+ ( generic_span. shrink_to_lo( ) , "Box<" . to_string( ) ) ,
2319
+ ( generic_span. shrink_to_hi( ) , ">" . to_string( ) ) ,
2320
+ ]
2321
+ } else {
2322
+ vec ! [
2323
+ ( span. shrink_to_lo( ) , "Box<" . to_string( ) ) ,
2324
+ ( span. shrink_to_hi( ) , ">" . to_string( ) ) ,
2325
+ ]
2326
+ }
2318
2327
} )
2319
2328
. collect ( ) ,
2320
2329
Applicability :: HasPlaceholders ,
@@ -2325,6 +2334,34 @@ pub fn recursive_type_with_infinite_size_error(
2325
2334
err. emit ( ) ;
2326
2335
}
2327
2336
2337
+ /// Extract the span for the generic type `T` of `Option<T>` in a field definition
2338
+ fn get_option_generic_from_field_id ( tcx : TyCtxt < ' _ > , field_id : Option < hir:: HirId > ) -> Option < Span > {
2339
+ let node = tcx. hir ( ) . find ( field_id?) ;
2340
+
2341
+ // Expect a field from our field_id
2342
+ let Some ( hir:: Node :: Field ( field_def) ) = node
2343
+ else { bug ! ( "Expected HirId corresponding to FieldDef, found: {:?}" , node) } ;
2344
+
2345
+ // Match a type that is a simple QPath with no Self
2346
+ let hir:: TyKind :: Path ( hir:: QPath :: Resolved ( None , path) ) = & field_def. ty . kind
2347
+ else { return None } ;
2348
+
2349
+ // Check if the path we're checking resolves to Option
2350
+ let hir:: def:: Res :: Def ( _, did) = path. res
2351
+ else { return None } ;
2352
+
2353
+ // Bail if this path doesn't describe `::core::option::Option`
2354
+ if !tcx. is_diagnostic_item ( sym:: Option , did) {
2355
+ return None ;
2356
+ }
2357
+
2358
+ // Match a single generic arg in the 0th path segment
2359
+ let generic_arg = path. segments . last ( ) ?. args ?. args . get ( 0 ) ?;
2360
+
2361
+ // Take the span out of the type, if it's a type
2362
+ if let hir:: GenericArg :: Type ( generic_ty) = generic_arg { Some ( generic_ty. span ) } else { None }
2363
+ }
2364
+
2328
2365
/// Summarizes information
2329
2366
#[ derive( Clone ) ]
2330
2367
pub enum ArgKind {
0 commit comments