@@ -2285,10 +2285,10 @@ impl<'v> Visitor<'v> for FindTypeParam {
22852285 }
22862286}
22872287
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 > ,
22902290 type_def_id : DefId ,
2291- spans : Vec < Span > ,
2291+ spans : Vec < ( Span , Option < hir :: HirId > ) > ,
22922292) {
22932293 assert ! ( type_def_id. is_local( ) ) ;
22942294 let span = tcx. hir ( ) . span_if_local ( type_def_id) . unwrap ( ) ;
@@ -2297,24 +2297,33 @@ pub fn recursive_type_with_infinite_size_error(
22972297 let mut err =
22982298 struct_span_err ! ( tcx. sess, span, E0072 , "recursive type `{}` has infinite size" , path) ;
22992299 err. span_label ( span, "recursive type has infinite size" ) ;
2300- for & span in & spans {
2300+ for & ( span, _ ) in & spans {
23012301 err. span_label ( span, "recursive without indirection" ) ;
23022302 }
23032303 let msg = format ! (
23042304 "insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `{}` representable" ,
23052305 path,
23062306 ) ;
23072307 if spans. len ( ) <= 4 {
2308+ // FIXME(compiler-errors): This suggestion might be erroneous if Box is shadowed
23082309 err. multipart_suggestion (
23092310 & msg,
23102311 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+ }
23182327 } )
23192328 . collect ( ) ,
23202329 Applicability :: HasPlaceholders ,
@@ -2325,6 +2334,34 @@ pub fn recursive_type_with_infinite_size_error(
23252334 err. emit ( ) ;
23262335}
23272336
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+
23282365/// Summarizes information
23292366#[ derive( Clone ) ]
23302367pub enum ArgKind {
0 commit comments