@@ -4,8 +4,8 @@ use if_chain::if_chain;
44use rustc_errors:: Applicability ;
55use rustc_hir:: intravisit:: FnKind ;
66use rustc_hir:: {
7- AsyncGeneratorKind , Block , Body , Expr , ExprKind , FnDecl , FnRetTy , GeneratorKind , GenericBound , HirId , IsAsync ,
8- ItemKind , TraitRef , Ty , TyKind , TypeBindingKind ,
7+ AsyncGeneratorKind , Block , Body , Expr , ExprKind , FnDecl , FnRetTy , GeneratorKind , GenericArg , GenericBound , HirId ,
8+ IsAsync , ItemKind , LifetimeName , TraitRef , Ty , TyKind , TypeBindingKind ,
99} ;
1010use rustc_lint:: { LateContext , LateLintPass } ;
1111use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
@@ -27,8 +27,6 @@ declare_clippy_lint! {
2727 /// ```
2828 /// Use instead:
2929 /// ```rust
30- /// use std::future::Future;
31- ///
3230 /// async fn foo() -> i32 { 42 }
3331 /// ```
3432 pub MANUAL_ASYNC_FN ,
@@ -53,8 +51,9 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn {
5351 if let IsAsync :: NotAsync = header. asyncness;
5452 // Check that this function returns `impl Future`
5553 if let FnRetTy :: Return ( ret_ty) = decl. output;
56- if let Some ( trait_ref) = future_trait_ref( cx, ret_ty) ;
54+ if let Some ( ( trait_ref, output_lifetimes ) ) = future_trait_ref( cx, ret_ty) ;
5755 if let Some ( output) = future_output_ty( trait_ref) ;
56+ if captures_all_lifetimes( decl. inputs, & output_lifetimes) ;
5857 // Check that the body of the function consists of one async block
5958 if let ExprKind :: Block ( block, _) = body. value. kind;
6059 if block. stmts. is_empty( ) ;
@@ -97,16 +96,35 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn {
9796 }
9897}
9998
100- fn future_trait_ref < ' tcx > ( cx : & LateContext < ' tcx > , ty : & ' tcx Ty < ' tcx > ) -> Option < & ' tcx TraitRef < ' tcx > > {
99+ fn future_trait_ref < ' tcx > (
100+ cx : & LateContext < ' tcx > ,
101+ ty : & ' tcx Ty < ' tcx > ,
102+ ) -> Option < ( & ' tcx TraitRef < ' tcx > , Vec < LifetimeName > ) > {
101103 if_chain ! {
102- if let TyKind :: OpaqueDef ( item_id, _ ) = ty. kind;
104+ if let TyKind :: OpaqueDef ( item_id, bounds ) = ty. kind;
103105 let item = cx. tcx. hir( ) . item( item_id. id) ;
104106 if let ItemKind :: OpaqueTy ( opaque) = & item. kind;
105- if opaque. bounds. len( ) == 1 ;
106- if let GenericBound :: Trait ( poly, _) = & opaque. bounds[ 0 ] ;
107- if poly. trait_ref. trait_def_id( ) == cx. tcx. lang_items( ) . future_trait( ) ;
107+ if let Some ( trait_ref) = opaque. bounds. iter( ) . find_map( |bound| {
108+ if let GenericBound :: Trait ( poly, _) = bound {
109+ Some ( & poly. trait_ref)
110+ } else {
111+ None
112+ }
113+ } ) ;
114+ if trait_ref. trait_def_id( ) == cx. tcx. lang_items( ) . future_trait( ) ;
108115 then {
109- return Some ( & poly. trait_ref) ;
116+ let output_lifetimes = bounds
117+ . iter( )
118+ . filter_map( |bound| {
119+ if let GenericArg :: Lifetime ( lt) = bound {
120+ Some ( lt. name)
121+ } else {
122+ None
123+ }
124+ } )
125+ . collect( ) ;
126+
127+ return Some ( ( trait_ref, output_lifetimes) ) ;
110128 }
111129 }
112130
@@ -129,6 +147,29 @@ fn future_output_ty<'tcx>(trait_ref: &'tcx TraitRef<'tcx>) -> Option<&'tcx Ty<'t
129147 None
130148}
131149
150+ fn captures_all_lifetimes ( inputs : & [ Ty < ' _ > ] , output_lifetimes : & [ LifetimeName ] ) -> bool {
151+ let input_lifetimes: Vec < LifetimeName > = inputs
152+ . iter ( )
153+ . filter_map ( |ty| {
154+ if let TyKind :: Rptr ( lt, _) = ty. kind {
155+ Some ( lt. name )
156+ } else {
157+ None
158+ }
159+ } )
160+ . collect ( ) ;
161+
162+ // The lint should trigger in one of these cases:
163+ // - There are no input lifetimes
164+ // - There's only one output lifetime bound using `+ '_`
165+ // - All input lifetimes are explicitly bound to the output
166+ input_lifetimes. is_empty ( )
167+ || ( output_lifetimes. len ( ) == 1 && matches ! ( output_lifetimes[ 0 ] , LifetimeName :: Underscore ) )
168+ || input_lifetimes
169+ . iter ( )
170+ . all ( |in_lt| output_lifetimes. iter ( ) . any ( |out_lt| in_lt == out_lt) )
171+ }
172+
132173fn desugared_async_block < ' tcx > ( cx : & LateContext < ' tcx > , block : & ' tcx Block < ' tcx > ) -> Option < & ' tcx Body < ' tcx > > {
133174 if_chain ! {
134175 if let Some ( block_expr) = block. expr;
0 commit comments