2020-04-07 13:39:07 +00:00
|
|
|
|
use crate::utils;
|
|
|
|
|
use rustc_hir::intravisit::FnKind;
|
|
|
|
|
use rustc_hir::{Body, FnDecl, HirId};
|
|
|
|
|
use rustc_infer::infer::TyCtxtInferExt;
|
|
|
|
|
use rustc_lint::{LateContext, LateLintPass};
|
2020-06-30 21:41:57 +00:00
|
|
|
|
use rustc_middle::ty::subst::Subst;
|
2021-01-07 16:20:28 +00:00
|
|
|
|
use rustc_middle::ty::{Opaque, PredicateKind::Trait};
|
2020-04-07 13:39:07 +00:00
|
|
|
|
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
|
|
|
|
use rustc_span::{sym, Span};
|
|
|
|
|
use rustc_trait_selection::traits::error_reporting::suggestions::InferCtxtExt;
|
|
|
|
|
use rustc_trait_selection::traits::{self, FulfillmentError, TraitEngine};
|
|
|
|
|
|
|
|
|
|
declare_clippy_lint! {
|
|
|
|
|
/// **What it does:** This lint requires Future implementations returned from
|
|
|
|
|
/// functions and methods to implement the `Send` marker trait. It is mostly
|
|
|
|
|
/// used by library authors (public and internal) that target an audience where
|
|
|
|
|
/// multithreaded executors are likely to be used for running these Futures.
|
|
|
|
|
///
|
|
|
|
|
/// **Why is this bad?** A Future implementation captures some state that it
|
|
|
|
|
/// needs to eventually produce its final value. When targeting a multithreaded
|
|
|
|
|
/// executor (which is the norm on non-embedded devices) this means that this
|
|
|
|
|
/// state may need to be transported to other threads, in other words the
|
|
|
|
|
/// whole Future needs to implement the `Send` marker trait. If it does not,
|
|
|
|
|
/// then the resulting Future cannot be submitted to a thread pool in the
|
|
|
|
|
/// end user’s code.
|
|
|
|
|
///
|
|
|
|
|
/// Especially for generic functions it can be confusing to leave the
|
|
|
|
|
/// discovery of this problem to the end user: the reported error location
|
|
|
|
|
/// will be far from its cause and can in many cases not even be fixed without
|
|
|
|
|
/// modifying the library where the offending Future implementation is
|
|
|
|
|
/// produced.
|
|
|
|
|
///
|
|
|
|
|
/// **Known problems:** None.
|
|
|
|
|
///
|
|
|
|
|
/// **Example:**
|
|
|
|
|
///
|
|
|
|
|
/// ```rust
|
|
|
|
|
/// async fn not_send(bytes: std::rc::Rc<[u8]>) {}
|
|
|
|
|
/// ```
|
|
|
|
|
/// Use instead:
|
|
|
|
|
/// ```rust
|
|
|
|
|
/// async fn is_send(bytes: std::sync::Arc<[u8]>) {}
|
|
|
|
|
/// ```
|
|
|
|
|
pub FUTURE_NOT_SEND,
|
|
|
|
|
nursery,
|
|
|
|
|
"public Futures must be Send"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
declare_lint_pass!(FutureNotSend => [FUTURE_NOT_SEND]);
|
|
|
|
|
|
2020-06-25 20:41:36 +00:00
|
|
|
|
impl<'tcx> LateLintPass<'tcx> for FutureNotSend {
|
2020-04-07 13:39:07 +00:00
|
|
|
|
fn check_fn(
|
|
|
|
|
&mut self,
|
2020-06-25 20:41:36 +00:00
|
|
|
|
cx: &LateContext<'tcx>,
|
2020-04-07 13:39:07 +00:00
|
|
|
|
kind: FnKind<'tcx>,
|
|
|
|
|
decl: &'tcx FnDecl<'tcx>,
|
|
|
|
|
_: &'tcx Body<'tcx>,
|
|
|
|
|
_: Span,
|
|
|
|
|
hir_id: HirId,
|
|
|
|
|
) {
|
|
|
|
|
if let FnKind::Closure(_) = kind {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2020-04-19 12:00:03 +00:00
|
|
|
|
let ret_ty = utils::return_ty(cx, hir_id);
|
2020-08-03 22:18:29 +00:00
|
|
|
|
if let Opaque(id, subst) = *ret_ty.kind() {
|
2020-06-30 21:41:57 +00:00
|
|
|
|
let preds = cx.tcx.explicit_item_bounds(id);
|
2020-04-07 13:39:07 +00:00
|
|
|
|
let mut is_future = false;
|
2020-06-30 21:41:57 +00:00
|
|
|
|
for &(p, _span) in preds {
|
|
|
|
|
let p = p.subst(cx.tcx, subst);
|
2020-04-07 13:39:07 +00:00
|
|
|
|
if let Some(trait_ref) = p.to_opt_poly_trait_ref() {
|
2020-11-22 01:13:53 +00:00
|
|
|
|
if Some(trait_ref.value.def_id()) == cx.tcx.lang_items().future_trait() {
|
2020-04-07 13:39:07 +00:00
|
|
|
|
is_future = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if is_future {
|
|
|
|
|
let send_trait = cx.tcx.get_diagnostic_item(sym::send_trait).unwrap();
|
|
|
|
|
let span = decl.output.span();
|
|
|
|
|
let send_result = cx.tcx.infer_ctxt().enter(|infcx| {
|
|
|
|
|
let cause = traits::ObligationCause::misc(span, hir_id);
|
|
|
|
|
let mut fulfillment_cx = traits::FulfillmentContext::new();
|
|
|
|
|
fulfillment_cx.register_bound(&infcx, cx.param_env, ret_ty, send_trait, cause);
|
|
|
|
|
fulfillment_cx.select_all_or_error(&infcx)
|
|
|
|
|
});
|
|
|
|
|
if let Err(send_errors) = send_result {
|
|
|
|
|
utils::span_lint_and_then(
|
|
|
|
|
cx,
|
|
|
|
|
FUTURE_NOT_SEND,
|
|
|
|
|
span,
|
|
|
|
|
"future cannot be sent between threads safely",
|
|
|
|
|
|db| {
|
|
|
|
|
cx.tcx.infer_ctxt().enter(|infcx| {
|
|
|
|
|
for FulfillmentError { obligation, .. } in send_errors {
|
2021-01-30 17:06:34 +00:00
|
|
|
|
infcx.maybe_note_obligation_cause_for_async_await(db, &obligation);
|
|
|
|
|
if let Trait(trait_pred, _) = obligation.predicate.kind().skip_binder() {
|
2020-06-19 08:22:25 +00:00
|
|
|
|
db.note(&format!(
|
2020-04-07 13:39:07 +00:00
|
|
|
|
"`{}` doesn't implement `{}`",
|
2020-06-19 08:22:25 +00:00
|
|
|
|
trait_pred.self_ty(),
|
|
|
|
|
trait_pred.trait_ref.print_only_trait_path(),
|
2020-04-07 13:39:07 +00:00
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|