@@ -4,8 +4,8 @@ use if_chain::if_chain;
4
4
use rustc_errors:: Applicability ;
5
5
use rustc_hir:: intravisit:: FnKind ;
6
6
use 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 ,
9
9
} ;
10
10
use rustc_lint:: { LateContext , LateLintPass } ;
11
11
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
@@ -27,8 +27,6 @@ declare_clippy_lint! {
27
27
/// ```
28
28
/// Use instead:
29
29
/// ```rust
30
- /// use std::future::Future;
31
- ///
32
30
/// async fn foo() -> i32 { 42 }
33
31
/// ```
34
32
pub MANUAL_ASYNC_FN ,
@@ -53,8 +51,9 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn {
53
51
if let IsAsync :: NotAsync = header. asyncness;
54
52
// Check that this function returns `impl Future`
55
53
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) ;
57
55
if let Some ( output) = future_output_ty( trait_ref) ;
56
+ if captures_all_lifetimes( decl. inputs, & output_lifetimes) ;
58
57
// Check that the body of the function consists of one async block
59
58
if let ExprKind :: Block ( block, _) = body. value. kind;
60
59
if block. stmts. is_empty( ) ;
@@ -97,16 +96,35 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn {
97
96
}
98
97
}
99
98
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 > ) > {
101
103
if_chain ! {
102
- if let TyKind :: OpaqueDef ( item_id, _ ) = ty. kind;
104
+ if let TyKind :: OpaqueDef ( item_id, bounds ) = ty. kind;
103
105
let item = cx. tcx. hir( ) . item( item_id. id) ;
104
106
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( ) ;
108
115
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) ) ;
110
128
}
111
129
}
112
130
@@ -129,6 +147,29 @@ fn future_output_ty<'tcx>(trait_ref: &'tcx TraitRef<'tcx>) -> Option<&'tcx Ty<'t
129
147
None
130
148
}
131
149
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
+
132
173
fn desugared_async_block < ' tcx > ( cx : & LateContext < ' tcx > , block : & ' tcx Block < ' tcx > ) -> Option < & ' tcx Body < ' tcx > > {
133
174
if_chain ! {
134
175
if let Some ( block_expr) = block. expr;
0 commit comments