@@ -4,9 +4,11 @@ use rustc_errors::Applicability;
44use rustc_hir:: intravisit:: FnKind ;
55use rustc_hir:: {
66 Block , Body , Closure , ClosureKind , CoroutineDesugaring , CoroutineKind , CoroutineSource , Expr , ExprKind , FnDecl ,
7- FnRetTy , GenericArg , GenericBound , ImplItem , Item , LifetimeName , Node , TraitRef , Ty , TyKind ,
7+ FnRetTy , GenericBound , ImplItem , Item , Node , OpaqueTy , TraitRef , Ty , TyKind ,
88} ;
99use rustc_lint:: { LateContext , LateLintPass } ;
10+ use rustc_middle:: middle:: resolve_bound_vars:: ResolvedArg ;
11+ use rustc_middle:: ty;
1012use rustc_session:: declare_lint_pass;
1113use rustc_span:: def_id:: LocalDefId ;
1214use rustc_span:: { sym, Span } ;
@@ -44,21 +46,22 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn {
4446 decl : & ' tcx FnDecl < ' _ > ,
4547 body : & ' tcx Body < ' _ > ,
4648 span : Span ,
47- def_id : LocalDefId ,
49+ fn_def_id : LocalDefId ,
4850 ) {
4951 if let Some ( header) = kind. header ( )
5052 && !header. asyncness . is_async ( )
5153 // Check that this function returns `impl Future`
5254 && let FnRetTy :: Return ( ret_ty) = decl. output
53- && let Some ( ( trait_ref, output_lifetimes) ) = future_trait_ref ( cx, ret_ty)
55+ && let TyKind :: OpaqueDef ( opaque) = ret_ty. kind
56+ && let Some ( trait_ref) = future_trait_ref ( cx, opaque)
5457 && let Some ( output) = future_output_ty ( trait_ref)
55- && captures_all_lifetimes ( decl . inputs , & output_lifetimes )
58+ && captures_all_lifetimes ( cx , fn_def_id , opaque . def_id )
5659 // Check that the body of the function consists of one async block
5760 && let ExprKind :: Block ( block, _) = body. value . kind
5861 && block. stmts . is_empty ( )
5962 && let Some ( closure_body) = desugared_async_block ( cx, block)
6063 && let Node :: Item ( Item { vis_span, ..} ) | Node :: ImplItem ( ImplItem { vis_span, ..} ) =
61- cx. tcx . hir_node_by_def_id ( def_id )
64+ cx. tcx . hir_node_by_def_id ( fn_def_id )
6265 {
6366 let header_span = span. with_hi ( ret_ty. span . hi ( ) ) ;
6467
@@ -101,12 +104,8 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn {
101104 }
102105}
103106
104- fn future_trait_ref < ' tcx > (
105- cx : & LateContext < ' tcx > ,
106- ty : & ' tcx Ty < ' tcx > ,
107- ) -> Option < ( & ' tcx TraitRef < ' tcx > , Vec < ResolvedArg > ) > {
108- if let TyKind :: OpaqueDef ( opaque) = ty. kind
109- && !opaque. in_trait
107+ fn future_trait_ref < ' tcx > ( cx : & LateContext < ' tcx > , opaque : & ' tcx OpaqueTy < ' tcx > ) -> Option < & ' tcx TraitRef < ' tcx > > {
108+ if !opaque. in_trait
110109 && let Some ( trait_ref) = opaque. bounds . iter ( ) . find_map ( |bound| {
111110 if let GenericBound :: Trait ( poly, _) = bound {
112111 Some ( & poly. trait_ref )
@@ -116,9 +115,7 @@ fn future_trait_ref<'tcx>(
116115 } )
117116 && trait_ref. trait_def_id ( ) == cx. tcx . lang_items ( ) . future_trait ( )
118117 {
119- let output_lifetimes = cx. tcx . opaque_captured_lifetimes ( opaque. def_id ) .
120- iter ( ) . map ( |( capture, _) | capture) . collect ( ) ;
121- return Some ( ( trait_ref, output_lifetimes) ) ;
118+ return Some ( trait_ref) ;
122119 }
123120
124121 None
@@ -137,26 +134,35 @@ fn future_output_ty<'tcx>(trait_ref: &'tcx TraitRef<'tcx>) -> Option<&'tcx Ty<'t
137134 None
138135}
139136
140- fn captures_all_lifetimes ( inputs : & [ Ty < ' _ > ] , output_lifetimes : & [ ResolvedArg ] ) -> bool {
141- let input_lifetimes: Vec < LifetimeName > = inputs
137+ fn captures_all_lifetimes ( cx : & LateContext < ' _ > , fn_def_id : LocalDefId , opaque_def_id : LocalDefId ) -> bool {
138+ let early_input_params = ty:: GenericArgs :: identity_for_item ( cx. tcx , fn_def_id) ;
139+ let late_input_params = cx. tcx . late_bound_vars ( cx. tcx . local_def_id_to_hir_id ( fn_def_id) ) ;
140+
141+ let num_early_lifetimes = early_input_params
142142 . iter ( )
143- . filter_map ( |ty| {
144- if let TyKind :: Ref ( lt, _) = ty. kind {
145- Some ( lt. res )
146- } else {
147- None
148- }
143+ . filter ( |param| param. as_region ( ) . is_some ( ) )
144+ . count ( ) ;
145+ let num_late_lifetimes = late_input_params
146+ . iter ( )
147+ . filter ( |param_kind| matches ! ( param_kind, ty:: BoundVariableKind :: Region ( _) ) )
148+ . count ( ) ;
149+
150+ // There is no lifetime, so they are all captured.
151+ if num_early_lifetimes == 0 && num_late_lifetimes == 0 {
152+ return true ;
153+ }
154+
155+ // By construction, each captured lifetime only appears once in `opaque_captured_lifetimes`.
156+ let num_captured_lifetimes = cx
157+ . tcx
158+ . opaque_captured_lifetimes ( opaque_def_id)
159+ . iter ( )
160+ . filter ( |& ( lifetime, _) | match * lifetime {
161+ ResolvedArg :: EarlyBound ( _) | ResolvedArg :: LateBound ( ty:: INNERMOST , _, _) => true ,
162+ _ => false ,
149163 } )
150- . collect ( ) ;
151-
152- // The lint should trigger in one of these cases:
153- // - There are no input lifetimes
154- // - There's only one output lifetime bound using `+ '_`
155- // - All input lifetimes are explicitly bound to the output
156- input_lifetimes. is_empty ( )
157- || input_lifetimes
158- . iter ( )
159- . all ( |in_lt| output_lifetimes. iter ( ) . any ( |out_lt| in_lt == out_lt) )
164+ . count ( ) ;
165+ num_captured_lifetimes == num_early_lifetimes + num_late_lifetimes
160166}
161167
162168fn desugared_async_block < ' tcx > ( cx : & LateContext < ' tcx > , block : & ' tcx Block < ' tcx > ) -> Option < & ' tcx Body < ' tcx > > {
0 commit comments