1 use clippy_utils
::diagnostics
::span_lint_and_then
;
2 use clippy_utils
::return_ty
;
3 use rustc_hir
::intravisit
::FnKind
;
4 use rustc_hir
::{Body, FnDecl}
;
5 use rustc_infer
::infer
::TyCtxtInferExt
;
6 use rustc_lint
::{LateContext, LateLintPass}
;
7 use rustc_middle
::ty
::{self, AliasTy, Clause, EarlyBinder, PredicateKind}
;
8 use rustc_session
::{declare_lint_pass, declare_tool_lint}
;
9 use rustc_span
::def_id
::LocalDefId
;
10 use rustc_span
::{sym, Span}
;
11 use rustc_trait_selection
::traits
::error_reporting
::suggestions
::TypeErrCtxtExt
;
12 use rustc_trait_selection
::traits
::{self, FulfillmentError}
;
14 declare_clippy_lint
! {
16 /// This lint requires Future implementations returned from
17 /// functions and methods to implement the `Send` marker trait. It is mostly
18 /// used by library authors (public and internal) that target an audience where
19 /// multithreaded executors are likely to be used for running these Futures.
21 /// ### Why is this bad?
22 /// A Future implementation captures some state that it
23 /// needs to eventually produce its final value. When targeting a multithreaded
24 /// executor (which is the norm on non-embedded devices) this means that this
25 /// state may need to be transported to other threads, in other words the
26 /// whole Future needs to implement the `Send` marker trait. If it does not,
27 /// then the resulting Future cannot be submitted to a thread pool in the
28 /// end user’s code.
30 /// Especially for generic functions it can be confusing to leave the
31 /// discovery of this problem to the end user: the reported error location
32 /// will be far from its cause and can in many cases not even be fixed without
33 /// modifying the library where the offending Future implementation is
38 /// async fn not_send(bytes: std::rc::Rc<[u8]>) {}
42 /// async fn is_send(bytes: std::sync::Arc<[u8]>) {}
44 #[clippy::version = "1.44.0"]
47 "public Futures must be Send"
50 declare_lint_pass
!(FutureNotSend
=> [FUTURE_NOT_SEND
]);
52 impl<'tcx
> LateLintPass
<'tcx
> for FutureNotSend
{
55 cx
: &LateContext
<'tcx
>,
57 decl
: &'tcx FnDecl
<'tcx
>,
60 fn_def_id
: LocalDefId
,
62 if let FnKind
::Closure
= kind
{
65 let ret_ty
= return_ty(cx
, cx
.tcx
.hir().local_def_id_to_hir_id(fn_def_id
).expect_owner());
66 if let ty
::Alias(ty
::Opaque
, AliasTy { def_id, substs, .. }
) = *ret_ty
.kind() {
67 let preds
= cx
.tcx
.explicit_item_bounds(def_id
);
68 let mut is_future
= false;
69 for &(p
, _span
) in preds
{
70 let p
= EarlyBinder(p
).subst(cx
.tcx
, substs
);
71 if let Some(trait_pred
) = p
.to_opt_poly_trait_pred() {
72 if Some(trait_pred
.skip_binder().trait_ref
.def_id
) == cx
.tcx
.lang_items().future_trait() {
79 let send_trait
= cx
.tcx
.get_diagnostic_item(sym
::Send
).unwrap();
80 let span
= decl
.output
.span();
81 let infcx
= cx
.tcx
.infer_ctxt().build();
82 let cause
= traits
::ObligationCause
::misc(span
, fn_def_id
);
83 let send_errors
= traits
::fully_solve_bound(&infcx
, cause
, cx
.param_env
, ret_ty
, send_trait
);
84 if !send_errors
.is_empty() {
89 "future cannot be sent between threads safely",
91 for FulfillmentError { obligation, .. }
in send_errors
{
94 .maybe_note_obligation_cause_for_async_await(db
, &obligation
);
95 if let PredicateKind
::Clause(Clause
::Trait(trait_pred
)) =
96 obligation
.predicate
.kind().skip_binder()
99 "`{}` doesn't implement `{}`",
100 trait_pred
.self_ty(),
101 trait_pred
.trait_ref
.print_only_trait_path(),