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