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
`+
}
`