]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/future_not_send.rs
Fix invalid float literal suggestions when recovering an integer
[rust.git] / src / tools / clippy / 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::{self, AliasTy, Clause, EarlyBinder, PredicateKind};
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::TypeErrCtxtExt;
11 use rustc_trait_selection::traits::{self, FulfillmentError};
12
13 declare_clippy_lint! {
14     /// ### What it does
15     /// 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?
21     /// A Future implementation captures some state that it
22     /// needs to eventually produce its final value. When targeting a multithreaded
23     /// executor (which is the norm on non-embedded devices) this means that this
24     /// state may need to be transported to other threads, in other words the
25     /// whole Future needs to implement the `Send` marker trait. If it does not,
26     /// then the resulting Future cannot be submitted to a thread pool in the
27     /// end user’s code.
28     ///
29     /// Especially for generic functions it can be confusing to leave the
30     /// discovery of this problem to the end user: the reported error location
31     /// will be far from its cause and can in many cases not even be fixed without
32     /// modifying the library where the offending Future implementation is
33     /// produced.
34     ///
35     /// ### Example
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     #[clippy::version = "1.44.0"]
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 = return_ty(cx, hir_id);
65         if let ty::Alias(ty::Opaque, AliasTy { def_id, substs, .. }) = *ret_ty.kind() {
66             let preds = cx.tcx.explicit_item_bounds(def_id);
67             let mut is_future = false;
68             for &(p, _span) in preds {
69                 let p = EarlyBinder(p).subst(cx.tcx, substs);
70                 if let Some(trait_pred) = p.to_opt_poly_trait_pred() {
71                     if Some(trait_pred.skip_binder().trait_ref.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).unwrap();
79                 let span = decl.output.span();
80                 let infcx = cx.tcx.infer_ctxt().build();
81                 let cause = traits::ObligationCause::misc(span, hir_id);
82                 let send_errors = traits::fully_solve_bound(&infcx, cause, cx.param_env, ret_ty, send_trait);
83                 if !send_errors.is_empty() {
84                     span_lint_and_then(
85                         cx,
86                         FUTURE_NOT_SEND,
87                         span,
88                         "future cannot be sent between threads safely",
89                         |db| {
90                             for FulfillmentError { obligation, .. } in send_errors {
91                                 infcx
92                                     .err_ctxt()
93                                     .maybe_note_obligation_cause_for_async_await(db, &obligation);
94                                 if let PredicateKind::Clause(Clause::Trait(trait_pred)) =
95                                     obligation.predicate.kind().skip_binder()
96                                 {
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 }