]>
Commit | Line | Data |
---|---|---|
f20569fa XL |
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 | } |