]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/future_not_send.rs
04730ace887c92868297202034a18da178571f8e
[rust.git] / clippy_lints / src / future_not_send.rs
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, HirId};
5 use rustc_infer::infer::TyCtxtInferExt;
6 use rustc_lint::{LateContext, LateLintPass};
7 use rustc_middle::ty::subst::Subst;
8 use rustc_middle::ty::{Opaque, PredicateKind::Trait};
9 use rustc_session::{declare_lint_pass, declare_tool_lint};
10 use rustc_span::{sym, Span};
11 use rustc_trait_selection::traits::error_reporting::suggestions::InferCtxtExt;
12 use rustc_trait_selection::traits::{self, FulfillmentError, TraitEngine};
13
14 declare_clippy_lint! {
15     /// **What it does:** This lint requires Future implementations returned from
16     /// functions and methods to implement the `Send` marker trait. It is mostly
17     /// used by library authors (public and internal) that target an audience where
18     /// multithreaded executors are likely to be used for running these Futures.
19     ///
20     /// **Why is this bad?** A Future implementation captures some state that it
21     /// needs to eventually produce its final value. When targeting a multithreaded
22     /// executor (which is the norm on non-embedded devices) this means that this
23     /// state may need to be transported to other threads, in other words the
24     /// whole Future needs to implement the `Send` marker trait. If it does not,
25     /// then the resulting Future cannot be submitted to a thread pool in the
26     /// end user’s code.
27     ///
28     /// Especially for generic functions it can be confusing to leave the
29     /// discovery of this problem to the end user: the reported error location
30     /// will be far from its cause and can in many cases not even be fixed without
31     /// modifying the library where the offending Future implementation is
32     /// produced.
33     ///
34     /// **Known problems:** None.
35     ///
36     /// **Example:**
37     ///
38     /// ```rust
39     /// async fn not_send(bytes: std::rc::Rc<[u8]>) {}
40     /// ```
41     /// Use instead:
42     /// ```rust
43     /// async fn is_send(bytes: std::sync::Arc<[u8]>) {}
44     /// ```
45     pub FUTURE_NOT_SEND,
46     nursery,
47     "public Futures must be Send"
48 }
49
50 declare_lint_pass!(FutureNotSend => [FUTURE_NOT_SEND]);
51
52 impl<'tcx> LateLintPass<'tcx> for FutureNotSend {
53     fn check_fn(
54         &mut self,
55         cx: &LateContext<'tcx>,
56         kind: FnKind<'tcx>,
57         decl: &'tcx FnDecl<'tcx>,
58         _: &'tcx Body<'tcx>,
59         _: Span,
60         hir_id: HirId,
61     ) {
62         if let FnKind::Closure = kind {
63             return;
64         }
65         let ret_ty = return_ty(cx, hir_id);
66         if let Opaque(id, subst) = *ret_ty.kind() {
67             let preds = cx.tcx.explicit_item_bounds(id);
68             let mut is_future = false;
69             for &(p, _span) in preds {
70                 let p = p.subst(cx.tcx, subst);
71                 if let Some(trait_ref) = p.to_opt_poly_trait_ref() {
72                     if Some(trait_ref.value.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                     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.kind().skip_binder() {
98                                         db.note(&format!(
99                                             "`{}` doesn't implement `{}`",
100                                             trait_pred.self_ty(),
101                                             trait_pred.trait_ref.print_only_trait_path(),
102                                         ));
103                                     }
104                                 }
105                             })
106                         },
107                     );
108                 }
109             }
110         }
111     }
112 }