-use crate::utils;
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::return_ty;
use rustc_hir::intravisit::FnKind;
use rustc_hir::{Body, FnDecl, HirId};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_trait_selection::traits::{self, FulfillmentError, TraitEngine};
declare_clippy_lint! {
- /// **What it does:** This lint requires Future implementations returned from
+ /// ### 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
+ /// ### 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
/// modifying the library where the offending Future implementation is
/// produced.
///
- /// **Known problems:** None.
- ///
- /// **Example:**
- ///
+ /// ### Example
/// ```rust
/// async fn not_send(bytes: std::rc::Rc<[u8]>) {}
/// ```
/// ```rust
/// async fn is_send(bytes: std::sync::Arc<[u8]>) {}
/// ```
+ #[clippy::version = "1.44.0"]
pub FUTURE_NOT_SEND,
nursery,
"public Futures must be Send"
_: Span,
hir_id: HirId,
) {
- if let FnKind::Closure(_) = kind {
+ if let FnKind::Closure = kind {
return;
}
- let ret_ty = utils::return_ty(cx, hir_id);
+ let ret_ty = return_ty(cx, hir_id);
if let Opaque(id, subst) = *ret_ty.kind() {
let preds = cx.tcx.explicit_item_bounds(id);
let mut is_future = false;
for &(p, _span) in preds {
let p = p.subst(cx.tcx, subst);
- if let Some(trait_ref) = p.to_opt_poly_trait_ref() {
- if Some(trait_ref.value.def_id()) == cx.tcx.lang_items().future_trait() {
+ if let Some(trait_pred) = p.to_opt_poly_trait_pred() {
+ if Some(trait_pred.skip_binder().trait_ref.def_id) == cx.tcx.lang_items().future_trait() {
is_future = true;
break;
}
}
}
if is_future {
- let send_trait = cx.tcx.get_diagnostic_item(sym::send_trait).unwrap();
+ let send_trait = cx.tcx.get_diagnostic_item(sym::Send).unwrap();
let span = decl.output.span();
- let send_result = cx.tcx.infer_ctxt().enter(|infcx| {
+ let send_errors = 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(
+ if !send_errors.is_empty() {
+ span_lint_and_then(
cx,
FUTURE_NOT_SEND,
span,
cx.tcx.infer_ctxt().enter(|infcx| {
for FulfillmentError { obligation, .. } in send_errors {
infcx.maybe_note_obligation_cause_for_async_await(db, &obligation);
- if let Trait(trait_pred, _) = obligation.predicate.kind().skip_binder() {
+ if let Trait(trait_pred) = obligation.predicate.kind().skip_binder() {
db.note(&format!(
"`{}` doesn't implement `{}`",
trait_pred.self_ty(),
));
}
}
- })
+ });
},
);
}