2021-03-25 13:29:11 -05:00
|
|
|
|
use clippy_utils::diagnostics::span_lint_and_then;
|
|
|
|
|
use clippy_utils::return_ty;
|
2020-04-07 08:39:07 -05:00
|
|
|
|
use rustc_hir::intravisit::FnKind;
|
2023-01-22 12:00:33 -06:00
|
|
|
|
use rustc_hir::{Body, FnDecl};
|
2020-04-07 08:39:07 -05:00
|
|
|
|
use rustc_infer::infer::TyCtxtInferExt;
|
|
|
|
|
use rustc_lint::{LateContext, LateLintPass};
|
2023-06-16 00:59:42 -05:00
|
|
|
|
use rustc_middle::ty::{self, AliasTy, ClauseKind, PredicateKind};
|
2020-04-07 08:39:07 -05:00
|
|
|
|
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
2023-01-22 12:00:33 -06:00
|
|
|
|
use rustc_span::def_id::LocalDefId;
|
2020-04-07 08:39:07 -05:00
|
|
|
|
use rustc_span::{sym, Span};
|
2022-09-09 15:08:06 -05:00
|
|
|
|
use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt;
|
2023-03-14 08:19:06 -05:00
|
|
|
|
use rustc_trait_selection::traits::{self, FulfillmentError, ObligationCtxt};
|
2020-04-07 08:39:07 -05:00
|
|
|
|
|
|
|
|
|
declare_clippy_lint! {
|
2021-07-29 05:16:06 -05:00
|
|
|
|
/// ### What it does
|
|
|
|
|
/// This lint requires Future implementations returned from
|
2020-04-07 08:39:07 -05:00
|
|
|
|
/// 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.
|
|
|
|
|
///
|
2021-07-29 05:16:06 -05:00
|
|
|
|
/// ### Why is this bad?
|
|
|
|
|
/// A Future implementation captures some state that it
|
2020-04-07 08:39:07 -05:00
|
|
|
|
/// 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.
|
|
|
|
|
///
|
2021-07-29 05:16:06 -05:00
|
|
|
|
/// ### Example
|
2023-11-02 11:35:56 -05:00
|
|
|
|
/// ```no_run
|
2020-04-07 08:39:07 -05:00
|
|
|
|
/// async fn not_send(bytes: std::rc::Rc<[u8]>) {}
|
|
|
|
|
/// ```
|
|
|
|
|
/// Use instead:
|
2023-11-02 11:35:56 -05:00
|
|
|
|
/// ```no_run
|
2020-04-07 08:39:07 -05:00
|
|
|
|
/// async fn is_send(bytes: std::sync::Arc<[u8]>) {}
|
|
|
|
|
/// ```
|
2021-12-06 05:33:31 -06:00
|
|
|
|
#[clippy::version = "1.44.0"]
|
2020-04-07 08:39:07 -05:00
|
|
|
|
pub FUTURE_NOT_SEND,
|
|
|
|
|
nursery,
|
|
|
|
|
"public Futures must be Send"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
declare_lint_pass!(FutureNotSend => [FUTURE_NOT_SEND]);
|
|
|
|
|
|
2020-06-25 15:41:36 -05:00
|
|
|
|
impl<'tcx> LateLintPass<'tcx> for FutureNotSend {
|
2020-04-07 08:39:07 -05:00
|
|
|
|
fn check_fn(
|
|
|
|
|
&mut self,
|
2020-06-25 15:41:36 -05:00
|
|
|
|
cx: &LateContext<'tcx>,
|
2020-04-07 08:39:07 -05:00
|
|
|
|
kind: FnKind<'tcx>,
|
|
|
|
|
decl: &'tcx FnDecl<'tcx>,
|
|
|
|
|
_: &'tcx Body<'tcx>,
|
|
|
|
|
_: Span,
|
2023-01-22 12:00:33 -06:00
|
|
|
|
fn_def_id: LocalDefId,
|
2020-04-07 08:39:07 -05:00
|
|
|
|
) {
|
2020-11-27 02:24:42 -06:00
|
|
|
|
if let FnKind::Closure = kind {
|
2020-04-07 08:39:07 -05:00
|
|
|
|
return;
|
|
|
|
|
}
|
2023-01-22 12:00:33 -06:00
|
|
|
|
let ret_ty = return_ty(cx, cx.tcx.hir().local_def_id_to_hir_id(fn_def_id).expect_owner());
|
2023-07-11 16:35:29 -05:00
|
|
|
|
if let ty::Alias(ty::Opaque, AliasTy { def_id, args, .. }) = *ret_ty.kind() {
|
2023-04-17 17:43:46 -05:00
|
|
|
|
let preds = cx.tcx.explicit_item_bounds(def_id);
|
2020-04-07 08:39:07 -05:00
|
|
|
|
let mut is_future = false;
|
2023-07-17 12:49:47 -05:00
|
|
|
|
for (p, _span) in preds.iter_instantiated_copied(cx.tcx, args) {
|
2023-06-19 15:46:46 -05:00
|
|
|
|
if let Some(trait_pred) = p.as_trait_clause() {
|
2021-12-11 22:34:46 -06:00
|
|
|
|
if Some(trait_pred.skip_binder().trait_ref.def_id) == cx.tcx.lang_items().future_trait() {
|
2020-04-07 08:39:07 -05:00
|
|
|
|
is_future = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if is_future {
|
2021-10-02 18:51:01 -05:00
|
|
|
|
let send_trait = cx.tcx.get_diagnostic_item(sym::Send).unwrap();
|
2020-04-07 08:39:07 -05:00
|
|
|
|
let span = decl.output.span();
|
2022-09-19 22:03:59 -05:00
|
|
|
|
let infcx = cx.tcx.infer_ctxt().build();
|
2023-03-14 08:19:06 -05:00
|
|
|
|
let ocx = ObligationCtxt::new(&infcx);
|
2023-01-22 12:00:33 -06:00
|
|
|
|
let cause = traits::ObligationCause::misc(span, fn_def_id);
|
2023-03-14 08:19:06 -05:00
|
|
|
|
ocx.register_bound(cause, cx.param_env, ret_ty, send_trait);
|
|
|
|
|
let send_errors = ocx.select_all_or_error();
|
2021-11-08 09:35:23 -06:00
|
|
|
|
if !send_errors.is_empty() {
|
2021-03-25 13:29:11 -05:00
|
|
|
|
span_lint_and_then(
|
2020-04-07 08:39:07 -05:00
|
|
|
|
cx,
|
|
|
|
|
FUTURE_NOT_SEND,
|
|
|
|
|
span,
|
|
|
|
|
"future cannot be sent between threads safely",
|
|
|
|
|
|db| {
|
2022-09-19 22:03:59 -05:00
|
|
|
|
for FulfillmentError { obligation, .. } in send_errors {
|
|
|
|
|
infcx
|
|
|
|
|
.err_ctxt()
|
|
|
|
|
.maybe_note_obligation_cause_for_async_await(db, &obligation);
|
2023-06-16 00:59:42 -05:00
|
|
|
|
if let PredicateKind::Clause(ClauseKind::Trait(trait_pred)) =
|
2022-12-01 11:29:38 -06:00
|
|
|
|
obligation.predicate.kind().skip_binder()
|
|
|
|
|
{
|
Restrict `From<S>` for `{D,Subd}iagnosticMessage`.
Currently a `{D,Subd}iagnosticMessage` can be created from any type that
impls `Into<String>`. That includes `&str`, `String`, and `Cow<'static,
str>`, which are reasonable. It also includes `&String`, which is pretty
weird, and results in many places making unnecessary allocations for
patterns like this:
```
self.fatal(&format!(...))
```
This creates a string with `format!`, takes a reference, passes the
reference to `fatal`, which does an `into()`, which clones the
reference, doing a second allocation. Two allocations for a single
string, bleh.
This commit changes the `From` impls so that you can only create a
`{D,Subd}iagnosticMessage` from `&str`, `String`, or `Cow<'static,
str>`. This requires changing all the places that currently create one
from a `&String`. Most of these are of the `&format!(...)` form
described above; each one removes an unnecessary static `&`, plus an
allocation when executed. There are also a few places where the existing
use of `&String` was more reasonable; these now just use `clone()` at
the call site.
As well as making the code nicer and more efficient, this is a step
towards possibly using `Cow<'static, str>` in
`{D,Subd}iagnosticMessage::{Str,Eager}`. That would require changing
the `From<&'a str>` impls to `From<&'static str>`, which is doable, but
I'm not yet sure if it's worthwhile.
2023-04-19 22:26:58 -05:00
|
|
|
|
db.note(format!(
|
2022-09-19 22:03:59 -05:00
|
|
|
|
"`{}` doesn't implement `{}`",
|
|
|
|
|
trait_pred.self_ty(),
|
|
|
|
|
trait_pred.trait_ref.print_only_trait_path(),
|
|
|
|
|
));
|
2020-04-07 08:39:07 -05:00
|
|
|
|
}
|
2022-09-19 22:03:59 -05:00
|
|
|
|
}
|
2020-04-07 08:39:07 -05:00
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|