@@ -4000,14 +4000,6 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
40004000
40014001 // ... whose signature is `async` (i.e. this is an AFIT)
40024002 let ( sig, body) = item. expect_fn ( ) ;
4003- let hir:: IsAsync :: Async ( async_span) = sig. header . asyncness else {
4004- return ;
4005- } ;
4006- let Ok ( async_span) =
4007- self . tcx . sess . source_map ( ) . span_extend_while ( async_span, |c| c. is_whitespace ( ) )
4008- else {
4009- return ;
4010- } ;
40114003 let hir:: FnRetTy :: Return ( hir:: Ty { kind : hir:: TyKind :: OpaqueDef ( def, ..) , .. } ) =
40124004 sig. decl . output
40134005 else {
@@ -4021,55 +4013,17 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
40214013 return ;
40224014 }
40234015
4024- let future = self . tcx . hir ( ) . item ( * def) . expect_opaque_ty ( ) ;
4025- let Some ( hir:: GenericBound :: LangItemTrait ( _, _, _, generics) ) = future. bounds . get ( 0 ) else {
4026- // `async fn` should always lower to a lang item bound... but don't ICE.
4027- return ;
4028- } ;
4029- let Some ( hir:: TypeBindingKind :: Equality { term : hir:: Term :: Ty ( future_output_ty) } ) =
4030- generics. bindings . get ( 0 ) . map ( |binding| binding. kind )
4031- else {
4032- // Also should never happen.
4016+ let Some ( sugg) = suggest_desugaring_async_fn_to_impl_future_in_trait (
4017+ self . tcx ,
4018+ * sig,
4019+ * body,
4020+ opaque_def_id. expect_local ( ) ,
4021+ & format ! ( " + {auto_trait}" ) ,
4022+ ) else {
40334023 return ;
40344024 } ;
40354025
40364026 let function_name = self . tcx . def_path_str ( fn_def_id) ;
4037-
4038- let mut sugg = if future_output_ty. span . is_empty ( ) {
4039- vec ! [
4040- ( async_span, String :: new( ) ) ,
4041- (
4042- future_output_ty. span,
4043- format!( " -> impl std::future::Future<Output = ()> + {auto_trait}" ) ,
4044- ) ,
4045- ]
4046- } else {
4047- vec ! [
4048- (
4049- future_output_ty. span. shrink_to_lo( ) ,
4050- "impl std::future::Future<Output = " . to_owned( ) ,
4051- ) ,
4052- ( future_output_ty. span. shrink_to_hi( ) , format!( "> + {auto_trait}" ) ) ,
4053- ( async_span, String :: new( ) ) ,
4054- ]
4055- } ;
4056-
4057- // If there's a body, we also need to wrap it in `async {}`
4058- if let hir:: TraitFn :: Provided ( body) = body {
4059- let body = self . tcx . hir ( ) . body ( * body) ;
4060- let body_span = body. value . span ;
4061- let body_span_without_braces =
4062- body_span. with_lo ( body_span. lo ( ) + BytePos ( 1 ) ) . with_hi ( body_span. hi ( ) - BytePos ( 1 ) ) ;
4063- if body_span_without_braces. is_empty ( ) {
4064- sugg. push ( ( body_span_without_braces, " async {} " . to_owned ( ) ) ) ;
4065- } else {
4066- sugg. extend ( [
4067- ( body_span_without_braces. shrink_to_lo ( ) , "async {" . to_owned ( ) ) ,
4068- ( body_span_without_braces. shrink_to_hi ( ) , "} " . to_owned ( ) ) ,
4069- ] ) ;
4070- }
4071- }
4072-
40734027 err. multipart_suggestion (
40744028 format ! (
40754029 "`{auto_trait}` can be made part of the associated future's \
@@ -4321,3 +4275,65 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReplaceImplTraitFolder<'tcx> {
43214275 self . tcx
43224276 }
43234277}
4278+
4279+ pub fn suggest_desugaring_async_fn_to_impl_future_in_trait < ' tcx > (
4280+ tcx : TyCtxt < ' tcx > ,
4281+ sig : hir:: FnSig < ' tcx > ,
4282+ body : hir:: TraitFn < ' tcx > ,
4283+ opaque_def_id : LocalDefId ,
4284+ add_bounds : & str ,
4285+ ) -> Option < Vec < ( Span , String ) > > {
4286+ let hir:: IsAsync :: Async ( async_span) = sig. header . asyncness else {
4287+ return None ;
4288+ } ;
4289+ let Ok ( async_span) = tcx. sess . source_map ( ) . span_extend_while ( async_span, |c| c. is_whitespace ( ) )
4290+ else {
4291+ return None ;
4292+ } ;
4293+
4294+ let future = tcx. hir ( ) . get_by_def_id ( opaque_def_id) . expect_item ( ) . expect_opaque_ty ( ) ;
4295+ let Some ( hir:: GenericBound :: LangItemTrait ( _, _, _, generics) ) = future. bounds . get ( 0 ) else {
4296+ // `async fn` should always lower to a lang item bound... but don't ICE.
4297+ return None ;
4298+ } ;
4299+ let Some ( hir:: TypeBindingKind :: Equality { term : hir:: Term :: Ty ( future_output_ty) } ) =
4300+ generics. bindings . get ( 0 ) . map ( |binding| binding. kind )
4301+ else {
4302+ // Also should never happen.
4303+ return None ;
4304+ } ;
4305+
4306+ let mut sugg = if future_output_ty. span . is_empty ( ) {
4307+ vec ! [
4308+ ( async_span, String :: new( ) ) ,
4309+ (
4310+ future_output_ty. span,
4311+ format!( " -> impl std::future::Future<Output = ()>{add_bounds}" ) ,
4312+ ) ,
4313+ ]
4314+ } else {
4315+ vec ! [
4316+ ( future_output_ty. span. shrink_to_lo( ) , "impl std::future::Future<Output = " . to_owned( ) ) ,
4317+ ( future_output_ty. span. shrink_to_hi( ) , format!( ">{add_bounds}" ) ) ,
4318+ ( async_span, String :: new( ) ) ,
4319+ ]
4320+ } ;
4321+
4322+ // If there's a body, we also need to wrap it in `async {}`
4323+ if let hir:: TraitFn :: Provided ( body) = body {
4324+ let body = tcx. hir ( ) . body ( body) ;
4325+ let body_span = body. value . span ;
4326+ let body_span_without_braces =
4327+ body_span. with_lo ( body_span. lo ( ) + BytePos ( 1 ) ) . with_hi ( body_span. hi ( ) - BytePos ( 1 ) ) ;
4328+ if body_span_without_braces. is_empty ( ) {
4329+ sugg. push ( ( body_span_without_braces, " async {} " . to_owned ( ) ) ) ;
4330+ } else {
4331+ sugg. extend ( [
4332+ ( body_span_without_braces. shrink_to_lo ( ) , "async {" . to_owned ( ) ) ,
4333+ ( body_span_without_braces. shrink_to_hi ( ) , "} " . to_owned ( ) ) ,
4334+ ] ) ;
4335+ }
4336+ }
4337+
4338+ Some ( sugg)
4339+ }
0 commit comments