@@ -4,9 +4,11 @@ use rustc_errors::Applicability;
4
4
use rustc_hir:: intravisit:: FnKind ;
5
5
use rustc_hir:: {
6
6
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 ,
8
8
} ;
9
9
use rustc_lint:: { LateContext , LateLintPass } ;
10
+ use rustc_middle:: middle:: resolve_bound_vars:: ResolvedArg ;
11
+ use rustc_middle:: ty;
10
12
use rustc_session:: declare_lint_pass;
11
13
use rustc_span:: def_id:: LocalDefId ;
12
14
use rustc_span:: { sym, Span } ;
@@ -44,21 +46,22 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn {
44
46
decl : & ' tcx FnDecl < ' _ > ,
45
47
body : & ' tcx Body < ' _ > ,
46
48
span : Span ,
47
- def_id : LocalDefId ,
49
+ fn_def_id : LocalDefId ,
48
50
) {
49
51
if let Some ( header) = kind. header ( )
50
52
&& !header. asyncness . is_async ( )
51
53
// Check that this function returns `impl Future`
52
54
&& 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)
54
57
&& 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 )
56
59
// Check that the body of the function consists of one async block
57
60
&& let ExprKind :: Block ( block, _) = body. value . kind
58
61
&& block. stmts . is_empty ( )
59
62
&& let Some ( closure_body) = desugared_async_block ( cx, block)
60
63
&& 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 )
62
65
{
63
66
let header_span = span. with_hi ( ret_ty. span . hi ( ) ) ;
64
67
@@ -101,12 +104,8 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn {
101
104
}
102
105
}
103
106
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
110
109
&& let Some ( trait_ref) = opaque. bounds . iter ( ) . find_map ( |bound| {
111
110
if let GenericBound :: Trait ( poly, _) = bound {
112
111
Some ( & poly. trait_ref )
@@ -116,9 +115,7 @@ fn future_trait_ref<'tcx>(
116
115
} )
117
116
&& trait_ref. trait_def_id ( ) == cx. tcx . lang_items ( ) . future_trait ( )
118
117
{
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) ;
122
119
}
123
120
124
121
None
@@ -137,26 +134,35 @@ fn future_output_ty<'tcx>(trait_ref: &'tcx TraitRef<'tcx>) -> Option<&'tcx Ty<'t
137
134
None
138
135
}
139
136
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
142
142
. 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 ,
149
163
} )
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
160
166
}
161
167
162
168
fn desugared_async_block < ' tcx > ( cx : & LateContext < ' tcx > , block : & ' tcx Block < ' tcx > ) -> Option < & ' tcx Body < ' tcx > > {
0 commit comments