1
1
use std:: ops:: ControlFlow ;
2
2
3
- use clippy_config:: Conf ;
4
3
use clippy_utils:: diagnostics:: span_lint_and_then;
5
4
use clippy_utils:: return_ty;
6
5
use rustc_hir:: intravisit:: FnKind ;
@@ -11,7 +10,7 @@ use rustc_middle::ty::print::PrintTraitRefExt;
11
10
use rustc_middle:: ty:: {
12
11
self , AliasTy , Binder , ClauseKind , PredicateKind , Ty , TyCtxt , TypeVisitable , TypeVisitableExt , TypeVisitor ,
13
12
} ;
14
- use rustc_session:: impl_lint_pass ;
13
+ use rustc_session:: declare_lint_pass ;
15
14
use rustc_span:: def_id:: LocalDefId ;
16
15
use rustc_span:: { Span , sym} ;
17
16
use rustc_trait_selection:: error_reporting:: InferCtxtErrorExt ;
@@ -20,23 +19,17 @@ use rustc_trait_selection::traits::{self, FulfillmentError, ObligationCtxt};
20
19
declare_clippy_lint ! {
21
20
/// ### What it does
22
21
/// This lint requires Future implementations returned from
23
- /// functions and methods to implement the `Send` marker trait.
22
+ /// functions and methods to implement the `Send` marker trait,
23
+ /// ignoring type parameters.
24
+ ///
25
+ /// If a function is generic and its Future conditionally implements `Send`
26
+ /// based on a generic parameter then it is considered `Send` and no warning is emitted.
24
27
///
25
- /// The default configuration of this lint only emits warnings for futures
26
- /// that are unconditionally `!Send`, ignoring generic parameters.
27
28
/// This can be used by library authors (public and internal) to ensure
28
- /// their functions are compatible with multi-threaded runtimes that require `Send` futures,
29
+ /// their functions are compatible with both multi-threaded runtimes that require `Send` futures,
29
30
/// as well as single-threaded runtimes where callers may choose `!Send` types
30
31
/// for generic parameters.
31
32
///
32
- /// A more strict version can be enabled through the `unconditional_send_futures` configuration,
33
- /// which requires that futures must always unconditionally implement `Send`,
34
- /// even if whether the future is `Send` or not is determined at call site for generic functions.
35
- /// This can be useful for binary crates that always use a multi-threaded runtime to find `!Send` futures
36
- /// as early as possible and keep errors contained in the most relevant place,
37
- /// instead of propagating `!Send` and be left with a hard-to-debug error in an
38
- /// unrelated place (e.g. the final future passed to `tokio::spawn()`).
39
- ///
40
33
/// ### Why is this bad?
41
34
/// A Future implementation captures some state that it
42
35
/// needs to eventually produce its final value. When targeting a multithreaded
@@ -66,19 +59,7 @@ declare_clippy_lint! {
66
59
"public Futures must be Send"
67
60
}
68
61
69
- pub struct FutureNotSend {
70
- unconditionally_not_send : bool ,
71
- }
72
-
73
- impl FutureNotSend {
74
- pub fn new ( conf : & ' static Conf ) -> Self {
75
- Self {
76
- unconditionally_not_send : conf. unconditional_send_futures ,
77
- }
78
- }
79
- }
80
-
81
- impl_lint_pass ! ( FutureNotSend => [ FUTURE_NOT_SEND ] ) ;
62
+ declare_lint_pass ! ( FutureNotSend => [ FUTURE_NOT_SEND ] ) ;
82
63
83
64
impl < ' tcx > LateLintPass < ' tcx > for FutureNotSend {
84
65
fn check_fn (
@@ -111,43 +92,27 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend {
111
92
ocx. register_bound ( cause, cx. param_env , ret_ty, send_trait) ;
112
93
let send_errors = ocx. select_all_or_error ( ) ;
113
94
114
- let is_send = if self . unconditionally_not_send {
115
- send_errors. is_empty ( )
116
- } else {
117
- // Allow errors that try to prove `Send` for types that "mention" a generic parameter at the "top
118
- // level".
119
- // For example, allow errors that `T: Send` can't be proven, but reject `Rc<T>: Send` errors,
120
- // which is always unconditionally `!Send` for any possible type `T`.
121
- //
122
- // We also allow associated type projections if the self type is either itself a projection or a
123
- // type parameter.
124
- // This is to prevent emitting warnings for e.g. holding a `<Fut as Future>::Output` across await
125
- // points, where `Fut` is a type parameter.
126
-
127
- struct TypeWalker ;
128
- impl < ' tcx > TypeVisitor < TyCtxt < ' tcx > > for TypeWalker {
129
- type Result = ControlFlow < bool > ;
130
- fn visit_ty ( & mut self , ty : Ty < ' tcx > ) -> Self :: Result {
131
- match ty. kind ( ) {
132
- ty:: Param ( _) => ControlFlow :: Break ( true ) ,
133
- ty:: Alias ( ty:: AliasTyKind :: Projection , ty) => ty. visit_with ( self ) ,
134
- _ => ControlFlow :: Break ( false ) ,
135
- }
136
- }
137
- }
95
+ // Allow errors that try to prove `Send` for types that "mention" a generic parameter at the "top
96
+ // level".
97
+ // For example, allow errors that `T: Send` can't be proven, but reject `Rc<T>: Send` errors,
98
+ // which is always unconditionally `!Send` for any possible type `T`.
99
+ //
100
+ // We also allow associated type projections if the self type is either itself a projection or a
101
+ // type parameter.
102
+ // This is to prevent emitting warnings for e.g. holding a `<Fut as Future>::Output` across await
103
+ // points, where `Fut` is a type parameter.
138
104
139
- send_errors. iter ( ) . all ( |err| {
140
- err. obligation
141
- . predicate
142
- . as_trait_clause ( )
143
- . map ( Binder :: skip_binder)
144
- . is_some_and ( |pred| {
145
- pred. def_id ( ) == send_trait
146
- && pred. self_ty ( ) . has_param ( )
147
- && TypeWalker . visit_ty ( pred. self_ty ( ) ) == ControlFlow :: Break ( true )
148
- } )
149
- } )
150
- } ;
105
+ let is_send = send_errors. iter ( ) . all ( |err| {
106
+ err. obligation
107
+ . predicate
108
+ . as_trait_clause ( )
109
+ . map ( Binder :: skip_binder)
110
+ . is_some_and ( |pred| {
111
+ pred. def_id ( ) == send_trait
112
+ && pred. self_ty ( ) . has_param ( )
113
+ && TyParamAtTopLevelVisitor . visit_ty ( pred. self_ty ( ) ) == ControlFlow :: Break ( true )
114
+ } )
115
+ } ) ;
151
116
152
117
if !is_send {
153
118
span_lint_and_then (
@@ -177,3 +142,15 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend {
177
142
}
178
143
}
179
144
}
145
+
146
+ struct TyParamAtTopLevelVisitor ;
147
+ impl < ' tcx > TypeVisitor < TyCtxt < ' tcx > > for TyParamAtTopLevelVisitor {
148
+ type Result = ControlFlow < bool > ;
149
+ fn visit_ty ( & mut self , ty : Ty < ' tcx > ) -> Self :: Result {
150
+ match ty. kind ( ) {
151
+ ty:: Param ( _) => ControlFlow :: Break ( true ) ,
152
+ ty:: Alias ( ty:: AliasTyKind :: Projection , ty) => ty. visit_with ( self ) ,
153
+ _ => ControlFlow :: Break ( false ) ,
154
+ }
155
+ }
156
+ }
0 commit comments