@@ -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:: { Span , sym} ;
@@ -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 < LifetimeName > ) > {
108- if let TyKind :: OpaqueDef ( opaque, bounds) = ty. kind
109- && let Some ( trait_ref) = opaque. bounds . iter ( ) . find_map ( |bound| {
107+ fn future_trait_ref < ' tcx > ( cx : & LateContext < ' tcx > , opaque : & ' tcx OpaqueTy < ' tcx > ) -> Option < & ' tcx TraitRef < ' tcx > > {
108+ if let Some ( trait_ref) = opaque. bounds . iter ( ) . find_map ( |bound| {
110109 if let GenericBound :: Trait ( poly) = bound {
111110 Some ( & poly. trait_ref )
112111 } else {
@@ -115,18 +114,7 @@ fn future_trait_ref<'tcx>(
115114 } )
116115 && trait_ref. trait_def_id ( ) == cx. tcx . lang_items ( ) . future_trait ( )
117116 {
118- let output_lifetimes = bounds
119- . iter ( )
120- . filter_map ( |bound| {
121- if let GenericArg :: Lifetime ( lt) = bound {
122- Some ( lt. res )
123- } else {
124- None
125- }
126- } )
127- . collect ( ) ;
128-
129- return Some ( ( trait_ref, output_lifetimes) ) ;
117+ return Some ( trait_ref) ;
130118 }
131119
132120 None
@@ -145,27 +133,35 @@ fn future_output_ty<'tcx>(trait_ref: &'tcx TraitRef<'tcx>) -> Option<&'tcx Ty<'t
145133 None
146134}
147135
148- fn captures_all_lifetimes ( inputs : & [ Ty < ' _ > ] , output_lifetimes : & [ LifetimeName ] ) -> bool {
149- let input_lifetimes: Vec < LifetimeName > = inputs
136+ fn captures_all_lifetimes ( cx : & LateContext < ' _ > , fn_def_id : LocalDefId , opaque_def_id : LocalDefId ) -> bool {
137+ let early_input_params = ty:: GenericArgs :: identity_for_item ( cx. tcx , fn_def_id) ;
138+ let late_input_params = cx. tcx . late_bound_vars ( cx. tcx . local_def_id_to_hir_id ( fn_def_id) ) ;
139+
140+ let num_early_lifetimes = early_input_params
150141 . iter ( )
151- . filter_map ( |ty| {
152- if let TyKind :: Ref ( lt, _) = ty. kind {
153- Some ( lt. res )
154- } else {
155- None
156- }
142+ . filter ( |param| param. as_region ( ) . is_some ( ) )
143+ . count ( ) ;
144+ let num_late_lifetimes = late_input_params
145+ . iter ( )
146+ . filter ( |param_kind| matches ! ( param_kind, ty:: BoundVariableKind :: Region ( _) ) )
147+ . count ( ) ;
148+
149+ // There is no lifetime, so they are all captured.
150+ if num_early_lifetimes == 0 && num_late_lifetimes == 0 {
151+ return true ;
152+ }
153+
154+ // By construction, each captured lifetime only appears once in `opaque_captured_lifetimes`.
155+ let num_captured_lifetimes = cx
156+ . tcx
157+ . opaque_captured_lifetimes ( opaque_def_id)
158+ . iter ( )
159+ . filter ( |& ( lifetime, _) | match * lifetime {
160+ ResolvedArg :: EarlyBound ( _) | ResolvedArg :: LateBound ( ty:: INNERMOST , _, _) => true ,
161+ _ => false ,
157162 } )
158- . collect ( ) ;
159-
160- // The lint should trigger in one of these cases:
161- // - There are no input lifetimes
162- // - There's only one output lifetime bound using `+ '_`
163- // - All input lifetimes are explicitly bound to the output
164- input_lifetimes. is_empty ( )
165- || ( output_lifetimes. len ( ) == 1 && matches ! ( output_lifetimes[ 0 ] , LifetimeName :: Infer ) )
166- || input_lifetimes
167- . iter ( )
168- . all ( |in_lt| output_lifetimes. iter ( ) . any ( |out_lt| in_lt == out_lt) )
163+ . count ( ) ;
164+ num_captured_lifetimes == num_early_lifetimes + num_late_lifetimes
169165}
170166
171167fn desugared_async_block < ' tcx > ( cx : & LateContext < ' tcx > , block : & ' tcx Block < ' tcx > ) -> Option < & ' tcx Body < ' tcx > > {
0 commit comments