Skip to content

Commit f1fb815

Browse files
committed
Auto merge of rust-lang#5423 - rkuhn:add_futures_not_send, r=flip1995
add lint futures_not_send changelog: add lint futures_not_send fixes rust-lang#5379 ~Remark: one thing that can (should?) still be improved is to directly include the error message from the `Send` check so that the programmer stays in the flow. Currently, getting the actual error message requires a restructuring of the code to make the `Send` constraint explicit.~ It now shows all unmet constraints for allowing the Future to be Send.
2 parents 52dacbc + d2cbbff commit f1fb815

File tree

6 files changed

+329
-0
lines changed

6 files changed

+329
-0
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -1281,6 +1281,7 @@ Released 2018-09-13
12811281
[`for_loop_over_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loop_over_result
12821282
[`forget_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_copy
12831283
[`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref
1284+
[`future_not_send`]: https://rust-lang.github.io/rust-clippy/master/index.html#future_not_send
12841285
[`get_last_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_last_with_len
12851286
[`get_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_unwrap
12861287
[`identity_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_conversion

clippy_lints/src/future_not_send.rs

+113
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
use crate::utils;
2+
use rustc_hir::intravisit::FnKind;
3+
use rustc_hir::{Body, FnDecl, HirId};
4+
use rustc_infer::infer::TyCtxtInferExt;
5+
use rustc_lint::{LateContext, LateLintPass};
6+
use rustc_middle::ty::{Opaque, Predicate::Trait, ToPolyTraitRef};
7+
use rustc_session::{declare_lint_pass, declare_tool_lint};
8+
use rustc_span::{sym, Span};
9+
use rustc_trait_selection::traits::error_reporting::suggestions::InferCtxtExt;
10+
use rustc_trait_selection::traits::{self, FulfillmentError, TraitEngine};
11+
12+
declare_clippy_lint! {
13+
/// **What it does:** This lint requires Future implementations returned from
14+
/// functions and methods to implement the `Send` marker trait. It is mostly
15+
/// used by library authors (public and internal) that target an audience where
16+
/// multithreaded executors are likely to be used for running these Futures.
17+
///
18+
/// **Why is this bad?** A Future implementation captures some state that it
19+
/// needs to eventually produce its final value. When targeting a multithreaded
20+
/// executor (which is the norm on non-embedded devices) this means that this
21+
/// state may need to be transported to other threads, in other words the
22+
/// whole Future needs to implement the `Send` marker trait. If it does not,
23+
/// then the resulting Future cannot be submitted to a thread pool in the
24+
/// end user’s code.
25+
///
26+
/// Especially for generic functions it can be confusing to leave the
27+
/// discovery of this problem to the end user: the reported error location
28+
/// will be far from its cause and can in many cases not even be fixed without
29+
/// modifying the library where the offending Future implementation is
30+
/// produced.
31+
///
32+
/// **Known problems:** None.
33+
///
34+
/// **Example:**
35+
///
36+
/// ```rust
37+
/// async fn not_send(bytes: std::rc::Rc<[u8]>) {}
38+
/// ```
39+
/// Use instead:
40+
/// ```rust
41+
/// async fn is_send(bytes: std::sync::Arc<[u8]>) {}
42+
/// ```
43+
pub FUTURE_NOT_SEND,
44+
nursery,
45+
"public Futures must be Send"
46+
}
47+
48+
declare_lint_pass!(FutureNotSend => [FUTURE_NOT_SEND]);
49+
50+
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for FutureNotSend {
51+
fn check_fn(
52+
&mut self,
53+
cx: &LateContext<'a, 'tcx>,
54+
kind: FnKind<'tcx>,
55+
decl: &'tcx FnDecl<'tcx>,
56+
_: &'tcx Body<'tcx>,
57+
_: Span,
58+
hir_id: HirId,
59+
) {
60+
if let FnKind::Closure(_) = kind {
61+
return;
62+
}
63+
let def_id = cx.tcx.hir().local_def_id(hir_id);
64+
let fn_sig = cx.tcx.fn_sig(def_id);
65+
let fn_sig = cx.tcx.erase_late_bound_regions(&fn_sig);
66+
let ret_ty = fn_sig.output();
67+
if let Opaque(id, subst) = ret_ty.kind {
68+
let preds = cx.tcx.predicates_of(id).instantiate(cx.tcx, subst);
69+
let mut is_future = false;
70+
for p in preds.predicates {
71+
if let Some(trait_ref) = p.to_opt_poly_trait_ref() {
72+
if Some(trait_ref.def_id()) == cx.tcx.lang_items().future_trait() {
73+
is_future = true;
74+
break;
75+
}
76+
}
77+
}
78+
if is_future {
79+
let send_trait = cx.tcx.get_diagnostic_item(sym::send_trait).unwrap();
80+
let span = decl.output.span();
81+
let send_result = cx.tcx.infer_ctxt().enter(|infcx| {
82+
let cause = traits::ObligationCause::misc(span, hir_id);
83+
let mut fulfillment_cx = traits::FulfillmentContext::new();
84+
fulfillment_cx.register_bound(&infcx, cx.param_env, ret_ty, send_trait, cause);
85+
fulfillment_cx.select_all_or_error(&infcx)
86+
});
87+
if let Err(send_errors) = send_result {
88+
utils::span_lint_and_then(
89+
cx,
90+
FUTURE_NOT_SEND,
91+
span,
92+
"future cannot be sent between threads safely",
93+
|db| {
94+
cx.tcx.infer_ctxt().enter(|infcx| {
95+
for FulfillmentError { obligation, .. } in send_errors {
96+
infcx.maybe_note_obligation_cause_for_async_await(db, &obligation);
97+
if let Trait(trait_pred, _) = obligation.predicate {
98+
let trait_ref = trait_pred.to_poly_trait_ref();
99+
db.note(&*format!(
100+
"`{}` doesn't implement `{}`",
101+
trait_ref.self_ty(),
102+
trait_ref.print_only_trait_path(),
103+
));
104+
}
105+
}
106+
})
107+
},
108+
);
109+
}
110+
}
111+
}
112+
}
113+
}

clippy_lints/src/lib.rs

+4
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ mod floating_point_arithmetic;
218218
mod format;
219219
mod formatting;
220220
mod functions;
221+
mod future_not_send;
221222
mod get_last_with_len;
222223
mod identity_conversion;
223224
mod identity_op;
@@ -566,6 +567,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
566567
&functions::NOT_UNSAFE_PTR_ARG_DEREF,
567568
&functions::TOO_MANY_ARGUMENTS,
568569
&functions::TOO_MANY_LINES,
570+
&future_not_send::FUTURE_NOT_SEND,
569571
&get_last_with_len::GET_LAST_WITH_LEN,
570572
&identity_conversion::IDENTITY_CONVERSION,
571573
&identity_op::IDENTITY_OP,
@@ -1045,6 +1047,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
10451047
store.register_late_pass(|| box redundant_pub_crate::RedundantPubCrate::default());
10461048
store.register_late_pass(|| box unnamed_address::UnnamedAddress);
10471049
store.register_late_pass(|| box dereference::Dereferencing);
1050+
store.register_late_pass(|| box future_not_send::FutureNotSend);
10481051

10491052
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
10501053
LintId::of(&arithmetic::FLOAT_ARITHMETIC),
@@ -1689,6 +1692,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
16891692
LintId::of(&fallible_impl_from::FALLIBLE_IMPL_FROM),
16901693
LintId::of(&floating_point_arithmetic::IMPRECISE_FLOPS),
16911694
LintId::of(&floating_point_arithmetic::SUBOPTIMAL_FLOPS),
1695+
LintId::of(&future_not_send::FUTURE_NOT_SEND),
16921696
LintId::of(&missing_const_for_fn::MISSING_CONST_FOR_FN),
16931697
LintId::of(&mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL),
16941698
LintId::of(&mutex_atomic::MUTEX_INTEGER),

src/lintlist/mod.rs

+7
Original file line numberDiff line numberDiff line change
@@ -696,6 +696,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
696696
deprecation: None,
697697
module: "drop_forget_ref",
698698
},
699+
Lint {
700+
name: "future_not_send",
701+
group: "nursery",
702+
desc: "public Futures must be Send",
703+
deprecation: None,
704+
module: "future_not_send",
705+
},
699706
Lint {
700707
name: "get_last_with_len",
701708
group: "complexity",

tests/ui/future_not_send.rs

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// edition:2018
2+
#![warn(clippy::future_not_send)]
3+
4+
use std::cell::Cell;
5+
use std::rc::Rc;
6+
use std::sync::Arc;
7+
8+
async fn private_future(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
9+
async { true }.await
10+
}
11+
12+
pub async fn public_future(rc: Rc<[u8]>) {
13+
async { true }.await;
14+
}
15+
16+
pub async fn public_send(arc: Arc<[u8]>) -> bool {
17+
async { false }.await
18+
}
19+
20+
async fn private_future2(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
21+
true
22+
}
23+
24+
pub async fn public_future2(rc: Rc<[u8]>) {}
25+
26+
pub async fn public_send2(arc: Arc<[u8]>) -> bool {
27+
false
28+
}
29+
30+
struct Dummy {
31+
rc: Rc<[u8]>,
32+
}
33+
34+
impl Dummy {
35+
async fn private_future(&self) -> usize {
36+
async { true }.await;
37+
self.rc.len()
38+
}
39+
40+
pub async fn public_future(&self) {
41+
self.private_future().await;
42+
}
43+
44+
pub fn public_send(&self) -> impl std::future::Future<Output = bool> {
45+
async { false }
46+
}
47+
}
48+
49+
async fn generic_future<T>(t: T) -> T
50+
where
51+
T: Send,
52+
{
53+
let rt = &t;
54+
async { true }.await;
55+
t
56+
}
57+
58+
async fn generic_future_send<T>(t: T)
59+
where
60+
T: Send,
61+
{
62+
async { true }.await;
63+
}
64+
65+
async fn unclear_future<T>(t: T) {}
66+
67+
fn main() {
68+
let rc = Rc::new([1, 2, 3]);
69+
private_future(rc.clone(), &Cell::new(42));
70+
public_future(rc.clone());
71+
let arc = Arc::new([4, 5, 6]);
72+
public_send(arc);
73+
generic_future(42);
74+
generic_future_send(42);
75+
76+
let dummy = Dummy { rc };
77+
dummy.public_future();
78+
dummy.public_send();
79+
}

tests/ui/future_not_send.stderr

+125
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
error: future cannot be sent between threads safely
2+
--> $DIR/future_not_send.rs:8:62
3+
|
4+
LL | async fn private_future(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
5+
| ^^^^ future returned by `private_future` is not `Send`
6+
|
7+
= note: `-D clippy::future-not-send` implied by `-D warnings`
8+
note: future is not `Send` as this value is used across an await
9+
--> $DIR/future_not_send.rs:9:5
10+
|
11+
LL | async fn private_future(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
12+
| -- has type `std::rc::Rc<[u8]>` which is not `Send`
13+
LL | async { true }.await
14+
| ^^^^^^^^^^^^^^^^^^^^ await occurs here, with `rc` maybe used later
15+
LL | }
16+
| - `rc` is later dropped here
17+
= note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send`
18+
note: future is not `Send` as this value is used across an await
19+
--> $DIR/future_not_send.rs:9:5
20+
|
21+
LL | async fn private_future(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
22+
| ---- has type `&std::cell::Cell<usize>` which is not `Send`
23+
LL | async { true }.await
24+
| ^^^^^^^^^^^^^^^^^^^^ await occurs here, with `cell` maybe used later
25+
LL | }
26+
| - `cell` is later dropped here
27+
= note: `std::cell::Cell<usize>` doesn't implement `std::marker::Sync`
28+
29+
error: future cannot be sent between threads safely
30+
--> $DIR/future_not_send.rs:12:42
31+
|
32+
LL | pub async fn public_future(rc: Rc<[u8]>) {
33+
| ^ future returned by `public_future` is not `Send`
34+
|
35+
note: future is not `Send` as this value is used across an await
36+
--> $DIR/future_not_send.rs:13:5
37+
|
38+
LL | pub async fn public_future(rc: Rc<[u8]>) {
39+
| -- has type `std::rc::Rc<[u8]>` which is not `Send`
40+
LL | async { true }.await;
41+
| ^^^^^^^^^^^^^^^^^^^^ await occurs here, with `rc` maybe used later
42+
LL | }
43+
| - `rc` is later dropped here
44+
= note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send`
45+
46+
error: future cannot be sent between threads safely
47+
--> $DIR/future_not_send.rs:20:63
48+
|
49+
LL | async fn private_future2(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
50+
| ^^^^
51+
|
52+
= note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send`
53+
= note: `std::cell::Cell<usize>` doesn't implement `std::marker::Sync`
54+
55+
error: future cannot be sent between threads safely
56+
--> $DIR/future_not_send.rs:24:43
57+
|
58+
LL | pub async fn public_future2(rc: Rc<[u8]>) {}
59+
| ^
60+
|
61+
= note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send`
62+
63+
error: future cannot be sent between threads safely
64+
--> $DIR/future_not_send.rs:35:39
65+
|
66+
LL | async fn private_future(&self) -> usize {
67+
| ^^^^^ future returned by `private_future` is not `Send`
68+
|
69+
note: future is not `Send` as this value is used across an await
70+
--> $DIR/future_not_send.rs:36:9
71+
|
72+
LL | async fn private_future(&self) -> usize {
73+
| ----- has type `&Dummy` which is not `Send`
74+
LL | async { true }.await;
75+
| ^^^^^^^^^^^^^^^^^^^^ await occurs here, with `&self` maybe used later
76+
LL | self.rc.len()
77+
LL | }
78+
| - `&self` is later dropped here
79+
= note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Sync`
80+
81+
error: future cannot be sent between threads safely
82+
--> $DIR/future_not_send.rs:40:39
83+
|
84+
LL | pub async fn public_future(&self) {
85+
| ^ future returned by `public_future` is not `Send`
86+
|
87+
note: future is not `Send` as this value is used across an await
88+
--> $DIR/future_not_send.rs:41:9
89+
|
90+
LL | pub async fn public_future(&self) {
91+
| ----- has type `&Dummy` which is not `Send`
92+
LL | self.private_future().await;
93+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ await occurs here, with `&self` maybe used later
94+
LL | }
95+
| - `&self` is later dropped here
96+
= note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Sync`
97+
98+
error: future cannot be sent between threads safely
99+
--> $DIR/future_not_send.rs:49:37
100+
|
101+
LL | async fn generic_future<T>(t: T) -> T
102+
| ^ future returned by `generic_future` is not `Send`
103+
|
104+
note: future is not `Send` as this value is used across an await
105+
--> $DIR/future_not_send.rs:54:5
106+
|
107+
LL | let rt = &t;
108+
| -- has type `&T` which is not `Send`
109+
LL | async { true }.await;
110+
| ^^^^^^^^^^^^^^^^^^^^ await occurs here, with `rt` maybe used later
111+
LL | t
112+
LL | }
113+
| - `rt` is later dropped here
114+
= note: `T` doesn't implement `std::marker::Sync`
115+
116+
error: future cannot be sent between threads safely
117+
--> $DIR/future_not_send.rs:65:34
118+
|
119+
LL | async fn unclear_future<T>(t: T) {}
120+
| ^
121+
|
122+
= note: `T` doesn't implement `std::marker::Send`
123+
124+
error: aborting due to 8 previous errors
125+

0 commit comments

Comments
 (0)