]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/future_not_send.rs
Add 'src/tools/clippy/' from commit 'd2708873ef711ec8ab45df1e984ecf24a96cd369'
[rust.git] / src / tools / clippy / clippy_lints / src / future_not_send.rs
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 ret_ty = utils::return_ty(cx, hir_id);
64         if let Opaque(id, subst) = ret_ty.kind {
65             let preds = cx.tcx.predicates_of(id).instantiate(cx.tcx, subst);
66             let mut is_future = false;
67             for p in preds.predicates {
68                 if let Some(trait_ref) = p.to_opt_poly_trait_ref() {
69                     if Some(trait_ref.def_id()) == cx.tcx.lang_items().future_trait() {
70                         is_future = true;
71                         break;
72                     }
73                 }
74             }
75             if is_future {
76                 let send_trait = cx.tcx.get_diagnostic_item(sym::send_trait).unwrap();
77                 let span = decl.output.span();
78                 let send_result = cx.tcx.infer_ctxt().enter(|infcx| {
79                     let cause = traits::ObligationCause::misc(span, hir_id);
80                     let mut fulfillment_cx = traits::FulfillmentContext::new();
81                     fulfillment_cx.register_bound(&infcx, cx.param_env, ret_ty, send_trait, cause);
82                     fulfillment_cx.select_all_or_error(&infcx)
83                 });
84                 if let Err(send_errors) = send_result {
85                     utils::span_lint_and_then(
86                         cx,
87                         FUTURE_NOT_SEND,
88                         span,
89                         "future cannot be sent between threads safely",
90                         |db| {
91                             cx.tcx.infer_ctxt().enter(|infcx| {
92                                 for FulfillmentError { obligation, .. } in send_errors {
93                                     infcx.maybe_note_obligation_cause_for_async_await(db, &obligation);
94                                     if let Trait(trait_pred, _) = obligation.predicate {
95                                         let trait_ref = trait_pred.to_poly_trait_ref();
96                                         db.note(&*format!(
97                                             "`{}` doesn't implement `{}`",
98                                             trait_ref.self_ty(),
99                                             trait_ref.print_only_trait_path(),
100                                         ));
101                                     }
102                                 }
103                             })
104                         },
105                     );
106                 }
107             }
108         }
109     }
110 }