Add async_fn_in_trait lint · rust-lang/rust@ec79720 (original) (raw)

`@@ -4000,14 +4000,6 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {

`

4000

4000

``

4001

4001

`` // ... whose signature is async (i.e. this is an AFIT)

``

4002

4002

`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

``

`-

};

`

4011

4003

`let hir::FnRetTy::Return(hir::Ty { kind: hir::TyKind::OpaqueDef(def, ..), .. }) =

`

4012

4004

` sig.decl.output

`

4013

4005

`else {

`

`@@ -4021,55 +4013,17 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {

`

4021

4013

`return;

`

4022

4014

`}

`

4023

4015

``

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 {

`

4033

4023

`return;

`

4034

4024

`};

`

4035

4025

``

4036

4026

`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

``

-

4073

4027

` err.multipart_suggestion(

`

4074

4028

`format!(

`

4075

4029

`` "{auto_trait} can be made part of the associated future's \

``

`@@ -4321,3 +4275,65 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReplaceImplTraitFolder<'tcx> {

`

4321

4275

`self.tcx

`

4322

4276

`}

`

4323

4277

`}

`

``

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

`+

}

`