From e4c3000e5bf254ab19895b7a62413ff0d346d337 Mon Sep 17 00:00:00 2001 From: Yechan Bae Date: Thu, 23 Sep 2021 03:51:10 -0400 Subject: [PATCH 01/12] Initial implementation --- CHANGELOG.md | 1 + clippy_lints/src/lib.mods.rs | 1 + clippy_lints/src/lib.register_lints.rs | 1 + clippy_lints/src/lib.register_nursery.rs | 1 + clippy_lints/src/lib.rs | 1 + clippy_lints/src/non_send_field_in_send_ty.rs | 126 ++++++++++++++++++ tests/ui/non_send_field_in_send_ty.rs | 94 +++++++++++++ 7 files changed, 225 insertions(+) create mode 100644 clippy_lints/src/non_send_field_in_send_ty.rs create mode 100644 tests/ui/non_send_field_in_send_ty.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 58ea0f9ab9d..df3fa2866c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2898,6 +2898,7 @@ Released 2018-09-13 [`no_effect`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect [`non_ascii_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_ascii_literal [`non_octal_unix_permissions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_octal_unix_permissions +[`non_send_field_in_send_ty`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_send_field_in_send_ty [`nonminimal_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonminimal_bool [`nonsensical_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonsensical_open_options [`nonstandard_macro_braces`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonstandard_macro_braces diff --git a/clippy_lints/src/lib.mods.rs b/clippy_lints/src/lib.mods.rs index 2718604f905..1d9d2be3443 100644 --- a/clippy_lints/src/lib.mods.rs +++ b/clippy_lints/src/lib.mods.rs @@ -149,6 +149,7 @@ mod non_copy_const; mod non_expressive_names; mod non_octal_unix_permissions; +mod non_send_field_in_send_ty; mod nonstandard_macro_braces; mod open_options; mod option_env_unwrap; diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 1ad27870b1a..b1cd5aafe4c 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -366,6 +366,7 @@ non_expressive_names::MANY_SINGLE_CHAR_NAMES, non_expressive_names::SIMILAR_NAMES, non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS, + non_send_field_in_send_ty::NON_SEND_FIELD_IN_SEND_TY, nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES, open_options::NONSENSICAL_OPEN_OPTIONS, option_env_unwrap::OPTION_ENV_UNWRAP, diff --git a/clippy_lints/src/lib.register_nursery.rs b/clippy_lints/src/lib.register_nursery.rs index b082f577a52..56b4f9d2391 100644 --- a/clippy_lints/src/lib.register_nursery.rs +++ b/clippy_lints/src/lib.register_nursery.rs @@ -16,6 +16,7 @@ LintId::of(missing_const_for_fn::MISSING_CONST_FOR_FN), LintId::of(mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL), LintId::of(mutex_atomic::MUTEX_INTEGER), + LintId::of(non_send_field_in_send_ty::NON_SEND_FIELD_IN_SEND_TY), LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES), LintId::of(option_if_let_else::OPTION_IF_LET_ELSE), LintId::of(path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 4e21b03217d..93b93bcba48 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -535,6 +535,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || Box::new(feature_name::FeatureName)); store.register_late_pass(move || Box::new(iter_not_returning_iterator::IterNotReturningIterator)); store.register_late_pass(move || Box::new(if_then_panic::IfThenPanic)); + store.register_late_pass(|| Box::new(non_send_field_in_send_ty::NonSendFieldInSendTy)); } #[rustfmt::skip] diff --git a/clippy_lints/src/non_send_field_in_send_ty.rs b/clippy_lints/src/non_send_field_in_send_ty.rs new file mode 100644 index 00000000000..5d6501196cd --- /dev/null +++ b/clippy_lints/src/non_send_field_in_send_ty.rs @@ -0,0 +1,126 @@ +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note}; +use clippy_utils::ty::{implements_trait, is_copy}; +use rustc_ast::ImplPolarity; +use rustc_hir::{Item, ItemKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, subst::GenericArgKind, Ty}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// Warns about a field in a `Send` struct that is neither `Send` nor `Copy`. + /// + /// ### Why is this bad? + /// Sending the struct to another thread and drops it there will also drop + /// the field in the new thread. This effectively changes the ownership of + /// the field type to the new thread and creates a soundness issue by + /// breaking breaks the non-`Send` invariant. + /// + /// ### Known Problems + /// Data structures that contain raw pointers may cause false positives. + /// They are sometimes safe to be sent across threads but do not implement + /// the `Send` trait. This lint has a heuristic to filter out basic cases + /// such as `Vec<*const T>`, but it's not perfect. + /// + /// ### Example + /// ```rust + /// use std::sync::Arc; + /// + /// // There is no `RC: Send` bound here + /// unsafe impl Send for ArcGuard {} + /// + /// #[derive(Debug, Clone)] + /// pub struct ArcGuard { + /// inner: T, + /// // Possibly drops `Arc` (and in turn `RC`) on a wrong thread + /// head: Arc + /// } + /// ``` + pub NON_SEND_FIELD_IN_SEND_TY, + nursery, + "a field in a `Send` struct does not implement `Send`" +} + +declare_lint_pass!(NonSendFieldInSendTy => [NON_SEND_FIELD_IN_SEND_TY]); + +impl<'tcx> LateLintPass<'tcx> for NonSendFieldInSendTy { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + let send_trait = cx.tcx.get_diagnostic_item(sym::send_trait).unwrap(); + + // Check if we are in `Send` impl item + if_chain! { + if let ItemKind::Impl(hir_impl) = &item.kind; + if let Some(trait_ref) = &hir_impl.of_trait; + if let Some(trait_id) = trait_ref.trait_def_id(); + if send_trait == trait_id; + if let ImplPolarity::Positive = hir_impl.polarity; + if let Some(ty_trait_ref) = cx.tcx.impl_trait_ref(item.def_id); + if let self_ty = ty_trait_ref.self_ty(); + if let ty::Adt(adt_def, impl_trait_substs) = self_ty.kind(); + then { + for variant in &adt_def.variants { + for field in &variant.fields { + let field_ty = field.ty(cx.tcx, impl_trait_substs); + + // TODO: substs rebase_onto + + if raw_pointer_in_ty_def(cx, field_ty) + || implements_trait(cx, field_ty, send_trait, &[]) + || is_copy(cx, field_ty) + { + continue; + } + + if let Some(field_span) = cx.tcx.hir().span_if_local(field.did) { + if is_ty_param(field_ty) { + span_lint_and_help( + cx, + NON_SEND_FIELD_IN_SEND_TY, + field_span, + "a field in a `Send` struct does not implement `Send`", + Some(item.span), + &format!("add `{}: Send` in `Send` impl for `{}`", field_ty, self_ty), + ) + } else { + span_lint_and_note( + cx, + NON_SEND_FIELD_IN_SEND_TY, + field_span, + "a field in a `Send` struct does not implement `Send`", + Some(item.span), + &format!( + "type `{}` doesn't implement `Send` when `{}` is `Send`", + field_ty, self_ty + ), + ) + } + } + } + } + } + } + } +} + +/// Returns `true` if the type itself is a raw pointer or has a raw pointer as a +/// generic parameter, e.g., `Vec<*const u8>`. +/// Note that it does not look into enum variants or struct fields. +fn raw_pointer_in_ty_def<'tcx>(cx: &LateContext<'tcx>, target_ty: Ty<'tcx>) -> bool { + for ty_node in target_ty.walk(cx.tcx) { + if_chain! { + if let GenericArgKind::Type(inner_ty) = ty_node.unpack(); + if let ty::RawPtr(_) = inner_ty.kind(); + then { + return true; + } + } + } + + false +} + +/// Returns `true` if the type is a type parameter such as `T`. +fn is_ty_param<'tcx>(target_ty: Ty<'tcx>) -> bool { + matches!(target_ty.kind(), ty::Param(_)) +} diff --git a/tests/ui/non_send_field_in_send_ty.rs b/tests/ui/non_send_field_in_send_ty.rs new file mode 100644 index 00000000000..a0c574f8e59 --- /dev/null +++ b/tests/ui/non_send_field_in_send_ty.rs @@ -0,0 +1,94 @@ +#![warn(clippy::non_send_field_in_send_ty)] +#![feature(extern_types)] + +use std::cell::UnsafeCell; +use std::ptr::NonNull; +use std::sync::{Arc, Mutex}; + +// disrustor / RUSTSEC-2020-0150 +pub struct RingBuffer { + data: Vec>, + capacity: usize, + mask: usize, +} + +unsafe impl Send for RingBuffer {} + +// noise_search / RUSTSEC-2020-0141 +pub struct MvccRwLock { + raw: *const T, + lock: Mutex>, +} + +unsafe impl Send for MvccRwLock {} + +// async-coap / RUSTSEC-2020-0124 +pub struct ArcGuard { + inner: T, + head: Arc, +} + +unsafe impl Send for ArcGuard {} + +// rusb / RUSTSEC-2020-0098 +extern "C" { + type libusb_device_handle; +} + +pub trait UsbContext { + // some user trait that does not guarantee `Send` +} + +pub struct DeviceHandle { + context: T, + handle: NonNull, +} + +unsafe impl Send for DeviceHandle {} + +// Raw pointers are allowed +extern "C" { + type SomeFfiType; +} + +pub struct FpTest { + vec: Vec<*const SomeFfiType>, +} + +unsafe impl Send for FpTest {} + +// Check raw pointer false positive +#[allow(clippy::non_send_field_in_send_ty)] +pub struct AttrTest1(T); + +pub struct AttrTest2 { + #[allow(clippy::non_send_field_in_send_ty)] + field: T, +} + +pub enum AttrTest3 { + #[allow(clippy::non_send_field_in_send_ty)] + Enum1(T), + Enum2(T), +} + +unsafe impl Send for AttrTest1 {} +unsafe impl Send for AttrTest2 {} +unsafe impl Send for AttrTest3 {} + +pub struct MultiField { + field1: T, + field2: T, + field3: T, +} + +unsafe impl Send for MultiField {} + +pub enum MyOption { + MySome(T), + MyNone, +} + +unsafe impl Send for MyOption {} + +fn main() {} From d7a9ec2c5080dc69219e43ba8f221b4e61e47ce4 Mon Sep 17 00:00:00 2001 From: Yechan Bae Date: Thu, 23 Sep 2021 06:45:24 -0400 Subject: [PATCH 02/12] Fix attribute handling --- clippy_lints/src/non_send_field_in_send_ty.rs | 57 +++---- tests/ui/non_send_field_in_send_ty.rs | 38 +++-- tests/ui/non_send_field_in_send_ty.stderr | 142 ++++++++++++++++++ 3 files changed, 197 insertions(+), 40 deletions(-) create mode 100644 tests/ui/non_send_field_in_send_ty.stderr diff --git a/clippy_lints/src/non_send_field_in_send_ty.rs b/clippy_lints/src/non_send_field_in_send_ty.rs index 5d6501196cd..07b50cb8274 100644 --- a/clippy_lints/src/non_send_field_in_send_ty.rs +++ b/clippy_lints/src/non_send_field_in_send_ty.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note}; +use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::ty::{implements_trait, is_copy}; use rustc_ast::ImplPolarity; use rustc_hir::{Item, ItemKind}; @@ -39,17 +39,19 @@ /// ``` pub NON_SEND_FIELD_IN_SEND_TY, nursery, - "a field in a `Send` struct does not implement `Send`" + "there is field that does not implement `Send` in a `Send` struct" } declare_lint_pass!(NonSendFieldInSendTy => [NON_SEND_FIELD_IN_SEND_TY]); impl<'tcx> LateLintPass<'tcx> for NonSendFieldInSendTy { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - let send_trait = cx.tcx.get_diagnostic_item(sym::send_trait).unwrap(); - - // Check if we are in `Send` impl item + // Checks if we are in `Send` impl item. + // We start from `Send` impl instead of `check_field_def()` because + // single `AdtDef` may have multiple `Send` impls due to generic + // parameters, and the lint is much easier to implement in this way. if_chain! { + if let Some(send_trait) = cx.tcx.get_diagnostic_item(sym::send_trait); if let ItemKind::Impl(hir_impl) = &item.kind; if let Some(trait_ref) = &hir_impl.of_trait; if let Some(trait_id) = trait_ref.trait_def_id(); @@ -63,8 +65,6 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { for field in &variant.fields { let field_ty = field.ty(cx.tcx, impl_trait_substs); - // TODO: substs rebase_onto - if raw_pointer_in_ty_def(cx, field_ty) || implements_trait(cx, field_ty, send_trait, &[]) || is_copy(cx, field_ty) @@ -72,28 +72,31 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { continue; } - if let Some(field_span) = cx.tcx.hir().span_if_local(field.did) { - if is_ty_param(field_ty) { - span_lint_and_help( + if let Some(field_hir_id) = field + .did + .as_local() + .map(|local_def_id| cx.tcx.hir().local_def_id_to_hir_id(local_def_id)) + { + if let Some(field_span) = cx.tcx.hir().span_if_local(field.did) { + span_lint_hir_and_then( cx, NON_SEND_FIELD_IN_SEND_TY, + field_hir_id, field_span, - "a field in a `Send` struct does not implement `Send`", - Some(item.span), - &format!("add `{}: Send` in `Send` impl for `{}`", field_ty, self_ty), - ) - } else { - span_lint_and_note( - cx, - NON_SEND_FIELD_IN_SEND_TY, - field_span, - "a field in a `Send` struct does not implement `Send`", - Some(item.span), - &format!( - "type `{}` doesn't implement `Send` when `{}` is `Send`", - field_ty, self_ty - ), - ) + "non-`Send` field found in a `Send` struct", + |diag| { + diag.span_note( + item.span, + &format!( + "type `{}` doesn't implement `Send` when `{}` is `Send`", + field_ty, self_ty + ), + ); + if is_ty_param(field_ty) { + diag.help(&format!("add `{}: Send` bound", field_ty)); + } + }, + ); } } } @@ -121,6 +124,6 @@ fn raw_pointer_in_ty_def<'tcx>(cx: &LateContext<'tcx>, target_ty: Ty<'tcx>) -> b } /// Returns `true` if the type is a type parameter such as `T`. -fn is_ty_param<'tcx>(target_ty: Ty<'tcx>) -> bool { +fn is_ty_param(target_ty: Ty<'_>) -> bool { matches!(target_ty.kind(), ty::Param(_)) } diff --git a/tests/ui/non_send_field_in_send_ty.rs b/tests/ui/non_send_field_in_send_ty.rs index a0c574f8e59..b97501aa457 100644 --- a/tests/ui/non_send_field_in_send_ty.rs +++ b/tests/ui/non_send_field_in_send_ty.rs @@ -3,7 +3,7 @@ use std::cell::UnsafeCell; use std::ptr::NonNull; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, Mutex, MutexGuard}; // disrustor / RUSTSEC-2020-0150 pub struct RingBuffer { @@ -46,6 +46,22 @@ pub struct DeviceHandle { unsafe impl Send for DeviceHandle {} +// Other basic tests +pub struct MultiField { + field1: T, + field2: T, + field3: T, +} + +unsafe impl Send for MultiField {} + +pub enum MyOption { + MySome(T), + MyNone, +} + +unsafe impl Send for MyOption {} + // Raw pointers are allowed extern "C" { type SomeFfiType; @@ -57,7 +73,7 @@ pub struct FpTest { unsafe impl Send for FpTest {} -// Check raw pointer false positive +// Test attributes #[allow(clippy::non_send_field_in_send_ty)] pub struct AttrTest1(T); @@ -76,19 +92,15 @@ unsafe impl Send for AttrTest1 {} unsafe impl Send for AttrTest2 {} unsafe impl Send for AttrTest3 {} -pub struct MultiField { - field1: T, - field2: T, - field3: T, +// Multiple non-overlapping `Send` for a single type +pub struct Complex { + field1: A, + field2: B, } -unsafe impl Send for MultiField {} +unsafe impl

Send for Complex {} -pub enum MyOption { - MySome(T), - MyNone, -} - -unsafe impl Send for MyOption {} +// `MutexGuard` is non-Send +unsafe impl Send for Complex> {} fn main() {} diff --git a/tests/ui/non_send_field_in_send_ty.stderr b/tests/ui/non_send_field_in_send_ty.stderr new file mode 100644 index 00000000000..3a82828e0fe --- /dev/null +++ b/tests/ui/non_send_field_in_send_ty.stderr @@ -0,0 +1,142 @@ +error: non-`Send` field found in a `Send` struct + --> $DIR/non_send_field_in_send_ty.rs:10:5 + | +LL | data: Vec>, + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::non-send-field-in-send-ty` implied by `-D warnings` +note: type `std::vec::Vec>` doesn't implement `Send` when `RingBuffer` is `Send` + --> $DIR/non_send_field_in_send_ty.rs:15:1 + | +LL | unsafe impl Send for RingBuffer {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: non-`Send` field found in a `Send` struct + --> $DIR/non_send_field_in_send_ty.rs:20:5 + | +LL | lock: Mutex>, + | ^^^^^^^^^^^^^^^^^^^ + | +note: type `std::sync::Mutex>` doesn't implement `Send` when `MvccRwLock` is `Send` + --> $DIR/non_send_field_in_send_ty.rs:23:1 + | +LL | unsafe impl Send for MvccRwLock {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: non-`Send` field found in a `Send` struct + --> $DIR/non_send_field_in_send_ty.rs:28:5 + | +LL | head: Arc, + | ^^^^^^^^^^^^^ + | +note: type `std::sync::Arc` doesn't implement `Send` when `ArcGuard` is `Send` + --> $DIR/non_send_field_in_send_ty.rs:31:1 + | +LL | unsafe impl Send for ArcGuard {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: non-`Send` field found in a `Send` struct + --> $DIR/non_send_field_in_send_ty.rs:43:5 + | +LL | context: T, + | ^^^^^^^^^^ + | +note: type `T` doesn't implement `Send` when `DeviceHandle` is `Send` + --> $DIR/non_send_field_in_send_ty.rs:47:1 + | +LL | unsafe impl Send for DeviceHandle {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: add `T: Send` bound + +error: non-`Send` field found in a `Send` struct + --> $DIR/non_send_field_in_send_ty.rs:51:5 + | +LL | field1: T, + | ^^^^^^^^^ + | +note: type `T` doesn't implement `Send` when `MultiField` is `Send` + --> $DIR/non_send_field_in_send_ty.rs:56:1 + | +LL | unsafe impl Send for MultiField {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: add `T: Send` bound + +error: non-`Send` field found in a `Send` struct + --> $DIR/non_send_field_in_send_ty.rs:52:5 + | +LL | field2: T, + | ^^^^^^^^^ + | +note: type `T` doesn't implement `Send` when `MultiField` is `Send` + --> $DIR/non_send_field_in_send_ty.rs:56:1 + | +LL | unsafe impl Send for MultiField {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: add `T: Send` bound + +error: non-`Send` field found in a `Send` struct + --> $DIR/non_send_field_in_send_ty.rs:53:5 + | +LL | field3: T, + | ^^^^^^^^^ + | +note: type `T` doesn't implement `Send` when `MultiField` is `Send` + --> $DIR/non_send_field_in_send_ty.rs:56:1 + | +LL | unsafe impl Send for MultiField {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: add `T: Send` bound + +error: non-`Send` field found in a `Send` struct + --> $DIR/non_send_field_in_send_ty.rs:59:12 + | +LL | MySome(T), + | ^ + | +note: type `T` doesn't implement `Send` when `MyOption` is `Send` + --> $DIR/non_send_field_in_send_ty.rs:63:1 + | +LL | unsafe impl Send for MyOption {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: add `T: Send` bound + +error: non-`Send` field found in a `Send` struct + --> $DIR/non_send_field_in_send_ty.rs:88:11 + | +LL | Enum2(T), + | ^ + | +note: type `T` doesn't implement `Send` when `AttrTest3` is `Send` + --> $DIR/non_send_field_in_send_ty.rs:93:1 + | +LL | unsafe impl Send for AttrTest3 {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: add `T: Send` bound + +error: non-`Send` field found in a `Send` struct + --> $DIR/non_send_field_in_send_ty.rs:97:5 + | +LL | field1: A, + | ^^^^^^^^^ + | +note: type `P` doesn't implement `Send` when `Complex` is `Send` + --> $DIR/non_send_field_in_send_ty.rs:101:1 + | +LL | unsafe impl

Send for Complex {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: add `P: Send` bound + +error: non-`Send` field found in a `Send` struct + --> $DIR/non_send_field_in_send_ty.rs:98:5 + | +LL | field2: B, + | ^^^^^^^^^ + | +note: type `std::sync::MutexGuard<'static, bool>` doesn't implement `Send` when `Complex>` is `Send` + --> $DIR/non_send_field_in_send_ty.rs:104:1 + | +LL | unsafe impl Send for Complex> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 11 previous errors + From 6458630e1aad3ef325b5839f5c3af665d837fa84 Mon Sep 17 00:00:00 2001 From: Yechan Bae Date: Thu, 23 Sep 2021 06:49:23 -0400 Subject: [PATCH 03/12] typo --- clippy_lints/src/non_send_field_in_send_ty.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/non_send_field_in_send_ty.rs b/clippy_lints/src/non_send_field_in_send_ty.rs index 07b50cb8274..7b81119448f 100644 --- a/clippy_lints/src/non_send_field_in_send_ty.rs +++ b/clippy_lints/src/non_send_field_in_send_ty.rs @@ -15,7 +15,7 @@ /// Sending the struct to another thread and drops it there will also drop /// the field in the new thread. This effectively changes the ownership of /// the field type to the new thread and creates a soundness issue by - /// breaking breaks the non-`Send` invariant. + /// breaking the non-`Send` invariant. /// /// ### Known Problems /// Data structures that contain raw pointers may cause false positives. From a81a5ad1a87b0fd92d9d0310ae44ac582c814387 Mon Sep 17 00:00:00 2001 From: Yechan Bae Date: Tue, 28 Sep 2021 13:46:13 -0400 Subject: [PATCH 04/12] Update documentation --- clippy_lints/src/non_send_field_in_send_ty.rs | 35 ++++++++++--------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/clippy_lints/src/non_send_field_in_send_ty.rs b/clippy_lints/src/non_send_field_in_send_ty.rs index 7b81119448f..d141cd2199f 100644 --- a/clippy_lints/src/non_send_field_in_send_ty.rs +++ b/clippy_lints/src/non_send_field_in_send_ty.rs @@ -12,31 +12,34 @@ /// Warns about a field in a `Send` struct that is neither `Send` nor `Copy`. /// /// ### Why is this bad? - /// Sending the struct to another thread and drops it there will also drop - /// the field in the new thread. This effectively changes the ownership of - /// the field type to the new thread and creates a soundness issue by - /// breaking the non-`Send` invariant. + /// Sending the struct to another thread will transfer the ownership to + /// the new thread by dropping in the current thread during the transfer. + /// This causes soundness issues for non-`Send` fields, as they are also + /// dropped and might not be set up to handle this. + /// + /// See: + /// * [*The Rustonomicon* about *Send and Sync*](https://doc.rust-lang.org/nomicon/send-and-sync.html) + /// * [The documentation of `Send`](https://doc.rust-lang.org/std/marker/trait.Send.html) /// /// ### Known Problems /// Data structures that contain raw pointers may cause false positives. /// They are sometimes safe to be sent across threads but do not implement /// the `Send` trait. This lint has a heuristic to filter out basic cases - /// such as `Vec<*const T>`, but it's not perfect. + /// such as `Vec<*const T>`, but it's not perfect. Feel free to create an + /// issue if you have a suggestion on how this heuristic can be improved. /// /// ### Example - /// ```rust - /// use std::sync::Arc; - /// - /// // There is no `RC: Send` bound here - /// unsafe impl Send for ArcGuard {} - /// - /// #[derive(Debug, Clone)] - /// pub struct ArcGuard { - /// inner: T, - /// // Possibly drops `Arc` (and in turn `RC`) on a wrong thread - /// head: Arc + /// ```rust,ignore + /// struct ExampleStruct { + /// rc_is_not_send: Rc, + /// unbounded_generic_field: T, /// } + /// + /// // This impl is unsound because it allows sending `!Send` types through `ExampleStruct` + /// unsafe impl Send for ExampleStruct {} /// ``` + /// Use thread-safe types like [`std::sync::Arc`](https://doc.rust-lang.org/std/sync/struct.Arc.html) + /// and specify correct bounds on generic type parameters (`T: Send`). pub NON_SEND_FIELD_IN_SEND_TY, nursery, "there is field that does not implement `Send` in a `Send` struct" From ee74574876e74a00eeae9c36f662d20fd6c61c0a Mon Sep 17 00:00:00 2001 From: Yechan Bae Date: Tue, 28 Sep 2021 15:23:39 -0400 Subject: [PATCH 05/12] Emit one report for all fields in the same ADT --- clippy_lints/src/non_send_field_in_send_ty.rs | 122 ++++++++--- tests/ui/non_send_field_in_send_ty.rs | 14 ++ tests/ui/non_send_field_in_send_ty.stderr | 206 ++++++++++-------- 3 files changed, 211 insertions(+), 131 deletions(-) diff --git a/clippy_lints/src/non_send_field_in_send_ty.rs b/clippy_lints/src/non_send_field_in_send_ty.rs index d141cd2199f..0fa994cacb1 100644 --- a/clippy_lints/src/non_send_field_in_send_ty.rs +++ b/clippy_lints/src/non_send_field_in_send_ty.rs @@ -1,11 +1,14 @@ -use clippy_utils::diagnostics::span_lint_hir_and_then; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::is_lint_allowed; use clippy_utils::ty::{implements_trait, is_copy}; use rustc_ast::ImplPolarity; +use rustc_hir::def_id::DefId; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, subst::GenericArgKind, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::sym; +use rustc_span::symbol::Symbol; +use rustc_span::{sym, Span}; declare_clippy_lint! { /// ### What it does @@ -64,51 +67,98 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if let self_ty = ty_trait_ref.self_ty(); if let ty::Adt(adt_def, impl_trait_substs) = self_ty.kind(); then { + let mut non_send_fields = Vec::new(); + + let hir_map = cx.tcx.hir(); for variant in &adt_def.variants { for field in &variant.fields { - let field_ty = field.ty(cx.tcx, impl_trait_substs); - - if raw_pointer_in_ty_def(cx, field_ty) - || implements_trait(cx, field_ty, send_trait, &[]) - || is_copy(cx, field_ty) - { - continue; - } - - if let Some(field_hir_id) = field - .did - .as_local() - .map(|local_def_id| cx.tcx.hir().local_def_id_to_hir_id(local_def_id)) - { - if let Some(field_span) = cx.tcx.hir().span_if_local(field.did) { - span_lint_hir_and_then( - cx, - NON_SEND_FIELD_IN_SEND_TY, - field_hir_id, - field_span, - "non-`Send` field found in a `Send` struct", - |diag| { - diag.span_note( - item.span, - &format!( - "type `{}` doesn't implement `Send` when `{}` is `Send`", - field_ty, self_ty - ), - ); - if is_ty_param(field_ty) { - diag.help(&format!("add `{}: Send` bound", field_ty)); - } - }, - ); + if_chain! { + if let Some(field_hir_id) = field + .did + .as_local() + .map(|local_def_id| hir_map.local_def_id_to_hir_id(local_def_id)); + if !is_lint_allowed(cx, NON_SEND_FIELD_IN_SEND_TY, field_hir_id); + if let field_ty = field.ty(cx.tcx, impl_trait_substs); + if !ty_allowed_in_send(cx, field_ty, send_trait); + if let Some(field_span) = hir_map.span_if_local(field.did); + then { + non_send_fields.push(NonSendField { + name: hir_map.name(field_hir_id), + span: field_span, + ty: field_ty, + generic_params: collect_generic_params(cx, field_ty), + }) } } } } + + if !non_send_fields.is_empty() { + span_lint_and_then( + cx, + NON_SEND_FIELD_IN_SEND_TY, + item.span, + &format!( + "this implementation is unsound, as some fields in `{}` are `!Send`", + self_ty + ), + |diag| { + for field in non_send_fields { + diag.span_note( + field.span, + &format!("the field `{}` has type `{}` which is not `Send`", field.name, field.ty), + ); + + match field.generic_params.len() { + 0 => diag.help("use a thread-safe type that implements `Send`"), + 1 if is_ty_param(field.ty) => diag.help(&format!("add `{}: Send` bound in `Send` impl", field.ty)), + _ => diag.help(&format!( + "add bounds on type parameter{} `{}` that satisfy `{}: Send`", + if field.generic_params.len() > 1 { "s" } else { "" }, + field.generic_params_string(), + field.ty + )), + }; + } + }, + ) + } } } } } +struct NonSendField<'tcx> { + name: Symbol, + span: Span, + ty: Ty<'tcx>, + generic_params: Vec>, +} + +impl<'tcx> NonSendField<'tcx> { + fn generic_params_string(&self) -> String { + self.generic_params + .iter() + .map(ToString::to_string) + .collect::>() + .join(", ") + } +} + +fn collect_generic_params<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Vec> { + ty.walk(cx.tcx) + .filter_map(|inner| match inner.unpack() { + GenericArgKind::Type(inner_ty) => Some(inner_ty), + _ => None, + }) + .filter(|&inner_ty| is_ty_param(inner_ty)) + .collect() +} + +fn ty_allowed_in_send<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, send_trait: DefId) -> bool { + raw_pointer_in_ty_def(cx, ty) || implements_trait(cx, ty, send_trait, &[]) || is_copy(cx, ty) +} + /// Returns `true` if the type itself is a raw pointer or has a raw pointer as a /// generic parameter, e.g., `Vec<*const u8>`. /// Note that it does not look into enum variants or struct fields. diff --git a/tests/ui/non_send_field_in_send_ty.rs b/tests/ui/non_send_field_in_send_ty.rs index b97501aa457..cf4fc1fd9b8 100644 --- a/tests/ui/non_send_field_in_send_ty.rs +++ b/tests/ui/non_send_field_in_send_ty.rs @@ -3,6 +3,7 @@ use std::cell::UnsafeCell; use std::ptr::NonNull; +use std::rc::Rc; use std::sync::{Arc, Mutex, MutexGuard}; // disrustor / RUSTSEC-2020-0150 @@ -47,6 +48,12 @@ pub struct DeviceHandle { unsafe impl Send for DeviceHandle {} // Other basic tests +pub struct NoGeneric { + rc_is_not_send: Rc, +} + +unsafe impl Send for NoGeneric {} + pub struct MultiField { field1: T, field2: T, @@ -62,6 +69,13 @@ pub enum MyOption { unsafe impl Send for MyOption {} +// Multiple type parameters +pub struct MultiParam { + vec: Vec<(A, B)>, +} + +unsafe impl Send for MultiParam {} + // Raw pointers are allowed extern "C" { type SomeFfiType; diff --git a/tests/ui/non_send_field_in_send_ty.stderr b/tests/ui/non_send_field_in_send_ty.stderr index 3a82828e0fe..52cc038b658 100644 --- a/tests/ui/non_send_field_in_send_ty.stderr +++ b/tests/ui/non_send_field_in_send_ty.stderr @@ -1,142 +1,158 @@ -error: non-`Send` field found in a `Send` struct - --> $DIR/non_send_field_in_send_ty.rs:10:5 - | -LL | data: Vec>, - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::non-send-field-in-send-ty` implied by `-D warnings` -note: type `std::vec::Vec>` doesn't implement `Send` when `RingBuffer` is `Send` - --> $DIR/non_send_field_in_send_ty.rs:15:1 +error: this implementation is unsound, as some fields in `RingBuffer` are `!Send` + --> $DIR/non_send_field_in_send_ty.rs:16:1 | LL | unsafe impl Send for RingBuffer {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::non-send-field-in-send-ty` implied by `-D warnings` +note: the field `data` has type `std::vec::Vec>` which is not `Send` + --> $DIR/non_send_field_in_send_ty.rs:11:5 + | +LL | data: Vec>, + | ^^^^^^^^^^^^^^^^^^^^^^^^ + = help: add bounds on type parameter `T` that satisfy `std::vec::Vec>: Send` -error: non-`Send` field found in a `Send` struct - --> $DIR/non_send_field_in_send_ty.rs:20:5 - | -LL | lock: Mutex>, - | ^^^^^^^^^^^^^^^^^^^ - | -note: type `std::sync::Mutex>` doesn't implement `Send` when `MvccRwLock` is `Send` - --> $DIR/non_send_field_in_send_ty.rs:23:1 +error: this implementation is unsound, as some fields in `MvccRwLock` are `!Send` + --> $DIR/non_send_field_in_send_ty.rs:24:1 | LL | unsafe impl Send for MvccRwLock {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the field `lock` has type `std::sync::Mutex>` which is not `Send` + --> $DIR/non_send_field_in_send_ty.rs:21:5 + | +LL | lock: Mutex>, + | ^^^^^^^^^^^^^^^^^^^ + = help: add bounds on type parameter `T` that satisfy `std::sync::Mutex>: Send` -error: non-`Send` field found in a `Send` struct - --> $DIR/non_send_field_in_send_ty.rs:28:5 - | -LL | head: Arc, - | ^^^^^^^^^^^^^ - | -note: type `std::sync::Arc` doesn't implement `Send` when `ArcGuard` is `Send` - --> $DIR/non_send_field_in_send_ty.rs:31:1 +error: this implementation is unsound, as some fields in `ArcGuard` are `!Send` + --> $DIR/non_send_field_in_send_ty.rs:32:1 | LL | unsafe impl Send for ArcGuard {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the field `head` has type `std::sync::Arc` which is not `Send` + --> $DIR/non_send_field_in_send_ty.rs:29:5 + | +LL | head: Arc, + | ^^^^^^^^^^^^^ + = help: add bounds on type parameter `RC` that satisfy `std::sync::Arc: Send` -error: non-`Send` field found in a `Send` struct - --> $DIR/non_send_field_in_send_ty.rs:43:5 - | -LL | context: T, - | ^^^^^^^^^^ - | -note: type `T` doesn't implement `Send` when `DeviceHandle` is `Send` - --> $DIR/non_send_field_in_send_ty.rs:47:1 +error: this implementation is unsound, as some fields in `DeviceHandle` are `!Send` + --> $DIR/non_send_field_in_send_ty.rs:48:1 | LL | unsafe impl Send for DeviceHandle {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = help: add `T: Send` bound + | +note: the field `context` has type `T` which is not `Send` + --> $DIR/non_send_field_in_send_ty.rs:44:5 + | +LL | context: T, + | ^^^^^^^^^^ + = help: add `T: Send` bound in `Send` impl -error: non-`Send` field found in a `Send` struct - --> $DIR/non_send_field_in_send_ty.rs:51:5 +error: this implementation is unsound, as some fields in `NoGeneric` are `!Send` + --> $DIR/non_send_field_in_send_ty.rs:55:1 + | +LL | unsafe impl Send for NoGeneric {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the field `rc_is_not_send` has type `std::rc::Rc` which is not `Send` + --> $DIR/non_send_field_in_send_ty.rs:52:5 + | +LL | rc_is_not_send: Rc, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: use a thread-safe type that implements `Send` + +error: this implementation is unsound, as some fields in `MultiField` are `!Send` + --> $DIR/non_send_field_in_send_ty.rs:63:1 + | +LL | unsafe impl Send for MultiField {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the field `field1` has type `T` which is not `Send` + --> $DIR/non_send_field_in_send_ty.rs:58:5 | LL | field1: T, | ^^^^^^^^^ - | -note: type `T` doesn't implement `Send` when `MultiField` is `Send` - --> $DIR/non_send_field_in_send_ty.rs:56:1 - | -LL | unsafe impl Send for MultiField {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = help: add `T: Send` bound - -error: non-`Send` field found in a `Send` struct - --> $DIR/non_send_field_in_send_ty.rs:52:5 + = help: add `T: Send` bound in `Send` impl +note: the field `field2` has type `T` which is not `Send` + --> $DIR/non_send_field_in_send_ty.rs:59:5 | LL | field2: T, | ^^^^^^^^^ - | -note: type `T` doesn't implement `Send` when `MultiField` is `Send` - --> $DIR/non_send_field_in_send_ty.rs:56:1 - | -LL | unsafe impl Send for MultiField {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = help: add `T: Send` bound - -error: non-`Send` field found in a `Send` struct - --> $DIR/non_send_field_in_send_ty.rs:53:5 + = help: add `T: Send` bound in `Send` impl +note: the field `field3` has type `T` which is not `Send` + --> $DIR/non_send_field_in_send_ty.rs:60:5 | LL | field3: T, | ^^^^^^^^^ - | -note: type `T` doesn't implement `Send` when `MultiField` is `Send` - --> $DIR/non_send_field_in_send_ty.rs:56:1 - | -LL | unsafe impl Send for MultiField {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = help: add `T: Send` bound + = help: add `T: Send` bound in `Send` impl -error: non-`Send` field found in a `Send` struct - --> $DIR/non_send_field_in_send_ty.rs:59:12 - | -LL | MySome(T), - | ^ - | -note: type `T` doesn't implement `Send` when `MyOption` is `Send` - --> $DIR/non_send_field_in_send_ty.rs:63:1 +error: this implementation is unsound, as some fields in `MyOption` are `!Send` + --> $DIR/non_send_field_in_send_ty.rs:70:1 | LL | unsafe impl Send for MyOption {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = help: add `T: Send` bound + | +note: the field `0` has type `T` which is not `Send` + --> $DIR/non_send_field_in_send_ty.rs:66:12 + | +LL | MySome(T), + | ^ + = help: add `T: Send` bound in `Send` impl -error: non-`Send` field found in a `Send` struct - --> $DIR/non_send_field_in_send_ty.rs:88:11 +error: this implementation is unsound, as some fields in `MultiParam` are `!Send` + --> $DIR/non_send_field_in_send_ty.rs:77:1 | -LL | Enum2(T), - | ^ +LL | unsafe impl Send for MultiParam {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: type `T` doesn't implement `Send` when `AttrTest3` is `Send` - --> $DIR/non_send_field_in_send_ty.rs:93:1 +note: the field `vec` has type `std::vec::Vec<(A, B)>` which is not `Send` + --> $DIR/non_send_field_in_send_ty.rs:74:5 + | +LL | vec: Vec<(A, B)>, + | ^^^^^^^^^^^^^^^^ + = help: add bounds on type parameters `A, B` that satisfy `std::vec::Vec<(A, B)>: Send` + +error: this implementation is unsound, as some fields in `AttrTest3` are `!Send` + --> $DIR/non_send_field_in_send_ty.rs:107:1 | LL | unsafe impl Send for AttrTest3 {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = help: add `T: Send` bound + | +note: the field `0` has type `T` which is not `Send` + --> $DIR/non_send_field_in_send_ty.rs:102:11 + | +LL | Enum2(T), + | ^ + = help: add `T: Send` bound in `Send` impl -error: non-`Send` field found in a `Send` struct - --> $DIR/non_send_field_in_send_ty.rs:97:5 - | -LL | field1: A, - | ^^^^^^^^^ - | -note: type `P` doesn't implement `Send` when `Complex` is `Send` - --> $DIR/non_send_field_in_send_ty.rs:101:1 +error: this implementation is unsound, as some fields in `Complex` are `!Send` + --> $DIR/non_send_field_in_send_ty.rs:115:1 | LL | unsafe impl

Send for Complex {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = help: add `P: Send` bound - -error: non-`Send` field found in a `Send` struct - --> $DIR/non_send_field_in_send_ty.rs:98:5 | -LL | field2: B, +note: the field `field1` has type `P` which is not `Send` + --> $DIR/non_send_field_in_send_ty.rs:111:5 + | +LL | field1: A, | ^^^^^^^^^ - | -note: type `std::sync::MutexGuard<'static, bool>` doesn't implement `Send` when `Complex>` is `Send` - --> $DIR/non_send_field_in_send_ty.rs:104:1 + = help: add `P: Send` bound in `Send` impl + +error: this implementation is unsound, as some fields in `Complex>` are `!Send` + --> $DIR/non_send_field_in_send_ty.rs:118:1 | LL | unsafe impl Send for Complex> {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the field `field2` has type `std::sync::MutexGuard<'static, bool>` which is not `Send` + --> $DIR/non_send_field_in_send_ty.rs:112:5 + | +LL | field2: B, + | ^^^^^^^^^ + = help: use a thread-safe type that implements `Send` error: aborting due to 11 previous errors From d413e157a5410b40eaa42decad6bf9d85a577a2d Mon Sep 17 00:00:00 2001 From: Yechan Bae Date: Tue, 28 Sep 2021 16:42:27 -0400 Subject: [PATCH 06/12] Look into tuple, array, ADT args in raw pointer heuristic --- clippy_lints/src/non_send_field_in_send_ty.rs | 49 ++++++++++++++++--- tests/ui/non_send_field_in_send_ty.rs | 17 +++++-- tests/ui/non_send_field_in_send_ty.stderr | 27 +++++++--- 3 files changed, 74 insertions(+), 19 deletions(-) diff --git a/clippy_lints/src/non_send_field_in_send_ty.rs b/clippy_lints/src/non_send_field_in_send_ty.rs index 0fa994cacb1..f800d3ff7e1 100644 --- a/clippy_lints/src/non_send_field_in_send_ty.rs +++ b/clippy_lints/src/non_send_field_in_send_ty.rs @@ -121,7 +121,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { }; } }, - ) + ); } } } @@ -145,6 +145,8 @@ fn generic_params_string(&self) -> String { } } +/// Given a type, collect all of its generic parameters. +/// Example: MyStruct> => vec![P, Q, R] fn collect_generic_params<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Vec> { ty.walk(cx.tcx) .filter_map(|inner| match inner.unpack() { @@ -155,14 +157,47 @@ fn collect_generic_params<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Vec(cx: &LateContext<'tcx>, ty: Ty<'tcx>, send_trait: DefId) -> bool { - raw_pointer_in_ty_def(cx, ty) || implements_trait(cx, ty, send_trait, &[]) || is_copy(cx, ty) +/// Determine if the given type is allowed in an ADT that implements `Send` +fn ty_allowed_in_send(cx: &LateContext<'tcx>, ty: Ty<'tcx>, send_trait: DefId) -> bool { + // TODO: check configuration and call `ty_implements_send_or_copy()` or + // `ty_allowed_with_raw_pointer_heuristic()` + ty_allowed_with_raw_pointer_heuristic(cx, ty, send_trait) } -/// Returns `true` if the type itself is a raw pointer or has a raw pointer as a -/// generic parameter, e.g., `Vec<*const u8>`. -/// Note that it does not look into enum variants or struct fields. -fn raw_pointer_in_ty_def<'tcx>(cx: &LateContext<'tcx>, target_ty: Ty<'tcx>) -> bool { +/// Determine if the given type is `Send` or `Copy` +fn ty_implements_send_or_copy(cx: &LateContext<'tcx>, ty: Ty<'tcx>, send_trait: DefId) -> bool { + implements_trait(cx, ty, send_trait, &[]) || is_copy(cx, ty) +} + +/// Heuristic to allow cases like `Vec<*const u8>` +fn ty_allowed_with_raw_pointer_heuristic<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, send_trait: DefId) -> bool { + if ty_implements_send_or_copy(cx, ty, send_trait) { + true + } else { + // The type is known to be `!Send` and `!Copy` + match ty.kind() { + ty::Tuple(_) => ty + .tuple_fields() + .all(|ty| ty_allowed_with_raw_pointer_heuristic(cx, ty, send_trait)), + ty::Array(ty, _) | ty::Slice(ty) => ty_allowed_with_raw_pointer_heuristic(cx, ty, send_trait), + ty::Adt(_, substs) => { + if contains_raw_pointer(cx, ty) { + // descends only if ADT contains any raw pointers + substs.iter().all(|generic_arg| match generic_arg.unpack() { + GenericArgKind::Type(ty) => ty_allowed_with_raw_pointer_heuristic(cx, ty, send_trait), + GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => true, + }) + } else { + false + } + }, + ty::RawPtr(_) => true, + _ => false, + } + } +} + +fn contains_raw_pointer<'tcx>(cx: &LateContext<'tcx>, target_ty: Ty<'tcx>) -> bool { for ty_node in target_ty.walk(cx.tcx) { if_chain! { if let GenericArgKind::Type(inner_ty) = ty_node.unpack(); diff --git a/tests/ui/non_send_field_in_send_ty.rs b/tests/ui/non_send_field_in_send_ty.rs index cf4fc1fd9b8..f5e7abdecd1 100644 --- a/tests/ui/non_send_field_in_send_ty.rs +++ b/tests/ui/non_send_field_in_send_ty.rs @@ -76,16 +76,23 @@ pub struct MultiParam { unsafe impl Send for MultiParam {} -// Raw pointers are allowed +// Tests for raw pointer heuristic extern "C" { - type SomeFfiType; + type NonSend; } -pub struct FpTest { - vec: Vec<*const SomeFfiType>, +pub struct HeuristicTest { + // raw pointers are allowed + field1: Vec<*const NonSend>, + field2: [*const NonSend; 3], + field3: (*const NonSend, *const NonSend, *const NonSend), + // not allowed when it contains concrete `!Send` field + field4: (*const NonSend, Rc), + // nested raw pointer is also allowed + field5: Vec>, } -unsafe impl Send for FpTest {} +unsafe impl Send for HeuristicTest {} // Test attributes #[allow(clippy::non_send_field_in_send_ty)] diff --git a/tests/ui/non_send_field_in_send_ty.stderr b/tests/ui/non_send_field_in_send_ty.stderr index 52cc038b658..327ef9fc2d6 100644 --- a/tests/ui/non_send_field_in_send_ty.stderr +++ b/tests/ui/non_send_field_in_send_ty.stderr @@ -115,44 +115,57 @@ LL | vec: Vec<(A, B)>, | ^^^^^^^^^^^^^^^^ = help: add bounds on type parameters `A, B` that satisfy `std::vec::Vec<(A, B)>: Send` +error: this implementation is unsound, as some fields in `HeuristicTest` are `!Send` + --> $DIR/non_send_field_in_send_ty.rs:95:1 + | +LL | unsafe impl Send for HeuristicTest {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the field `field4` has type `(*const NonSend, std::rc::Rc)` which is not `Send` + --> $DIR/non_send_field_in_send_ty.rs:90:5 + | +LL | field4: (*const NonSend, Rc), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: use a thread-safe type that implements `Send` + error: this implementation is unsound, as some fields in `AttrTest3` are `!Send` - --> $DIR/non_send_field_in_send_ty.rs:107:1 + --> $DIR/non_send_field_in_send_ty.rs:114:1 | LL | unsafe impl Send for AttrTest3 {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: the field `0` has type `T` which is not `Send` - --> $DIR/non_send_field_in_send_ty.rs:102:11 + --> $DIR/non_send_field_in_send_ty.rs:109:11 | LL | Enum2(T), | ^ = help: add `T: Send` bound in `Send` impl error: this implementation is unsound, as some fields in `Complex` are `!Send` - --> $DIR/non_send_field_in_send_ty.rs:115:1 + --> $DIR/non_send_field_in_send_ty.rs:122:1 | LL | unsafe impl

Send for Complex {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: the field `field1` has type `P` which is not `Send` - --> $DIR/non_send_field_in_send_ty.rs:111:5 + --> $DIR/non_send_field_in_send_ty.rs:118:5 | LL | field1: A, | ^^^^^^^^^ = help: add `P: Send` bound in `Send` impl error: this implementation is unsound, as some fields in `Complex>` are `!Send` - --> $DIR/non_send_field_in_send_ty.rs:118:1 + --> $DIR/non_send_field_in_send_ty.rs:125:1 | LL | unsafe impl Send for Complex> {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: the field `field2` has type `std::sync::MutexGuard<'static, bool>` which is not `Send` - --> $DIR/non_send_field_in_send_ty.rs:112:5 + --> $DIR/non_send_field_in_send_ty.rs:119:5 | LL | field2: B, | ^^^^^^^^^ = help: use a thread-safe type that implements `Send` -error: aborting due to 11 previous errors +error: aborting due to 12 previous errors From 427a09ba7bb91cc692cd3101d3c8d84a21a64d1c Mon Sep 17 00:00:00 2001 From: Yechan Bae Date: Tue, 28 Sep 2021 16:55:36 -0400 Subject: [PATCH 07/12] Add configuration for raw pointer heuristic --- clippy_lints/src/lib.rs | 3 +- clippy_lints/src/non_send_field_in_send_ty.rs | 30 +++++++++++++------ clippy_lints/src/utils/conf.rs | 4 +++ .../toml_unknown_key/conf_unknown_key.stderr | 2 +- 4 files changed, 28 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 93b93bcba48..f9d660972cb 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -535,7 +535,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || Box::new(feature_name::FeatureName)); store.register_late_pass(move || Box::new(iter_not_returning_iterator::IterNotReturningIterator)); store.register_late_pass(move || Box::new(if_then_panic::IfThenPanic)); - store.register_late_pass(|| Box::new(non_send_field_in_send_ty::NonSendFieldInSendTy)); + let enable_raw_pointer_heuristic = conf.enable_raw_pointer_heuristic; + store.register_late_pass(move || Box::new(non_send_field_in_send_ty::NonSendFieldInSendTy::new(enable_raw_pointer_heuristic))); } #[rustfmt::skip] diff --git a/clippy_lints/src/non_send_field_in_send_ty.rs b/clippy_lints/src/non_send_field_in_send_ty.rs index f800d3ff7e1..706214cb3d5 100644 --- a/clippy_lints/src/non_send_field_in_send_ty.rs +++ b/clippy_lints/src/non_send_field_in_send_ty.rs @@ -6,7 +6,7 @@ use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, subst::GenericArgKind, Ty}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::Symbol; use rustc_span::{sym, Span}; @@ -48,10 +48,29 @@ "there is field that does not implement `Send` in a `Send` struct" } -declare_lint_pass!(NonSendFieldInSendTy => [NON_SEND_FIELD_IN_SEND_TY]); +#[derive(Copy, Clone)] +pub struct NonSendFieldInSendTy { + enable_raw_pointer_heuristic: bool, +} + +impl NonSendFieldInSendTy { + pub fn new(enable_raw_pointer_heuristic: bool) -> Self { + Self { + enable_raw_pointer_heuristic, + } + } +} + +impl_lint_pass!(NonSendFieldInSendTy => [NON_SEND_FIELD_IN_SEND_TY]); impl<'tcx> LateLintPass<'tcx> for NonSendFieldInSendTy { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + let ty_allowed_in_send = if self.enable_raw_pointer_heuristic { + ty_allowed_with_raw_pointer_heuristic + } else { + ty_implements_send_or_copy + }; + // Checks if we are in `Send` impl item. // We start from `Send` impl instead of `check_field_def()` because // single `AdtDef` may have multiple `Send` impls due to generic @@ -157,13 +176,6 @@ fn collect_generic_params<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Vec, ty: Ty<'tcx>, send_trait: DefId) -> bool { - // TODO: check configuration and call `ty_implements_send_or_copy()` or - // `ty_allowed_with_raw_pointer_heuristic()` - ty_allowed_with_raw_pointer_heuristic(cx, ty, send_trait) -} - /// Determine if the given type is `Send` or `Copy` fn ty_implements_send_or_copy(cx: &LateContext<'tcx>, ty: Ty<'tcx>, send_trait: DefId) -> bool { implements_trait(cx, ty, send_trait, &[]) || is_copy(cx, ty) diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 1e0447239be..177c0fd375c 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -284,6 +284,10 @@ pub(crate) fn get_configuration_metadata() -> Vec { /// /// The list of unicode scripts allowed to be used in the scope. (allowed_scripts: Vec = vec!["Latin".to_string()]), + /// Lint: NON_SEND_FIELD_IN_SEND_TY. + /// + /// Whether to apply the raw pointer heuristic in `non_send_field_in_send_ty` lint. + (enable_raw_pointer_heuristic: bool = true), } /// Search for the configuration file. diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index e0029ebeb27..0eff2be7960 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -1,4 +1,4 @@ -error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `standard-macro-braces`, `enforced-import-renames`, `allowed-scripts`, `third-party` at line 5 column 1 +error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `standard-macro-braces`, `enforced-import-renames`, `allowed-scripts`, `enable-raw-pointer-heuristic`, `third-party` at line 5 column 1 error: aborting due to previous error From 08f0aecffd4e8522bf51a7b9d74386a36834d4a9 Mon Sep 17 00:00:00 2001 From: Yechan Bae Date: Fri, 1 Oct 2021 12:47:54 -0400 Subject: [PATCH 08/12] Minor changes from PR feedback --- clippy_lints/src/lib.rs | 4 +- clippy_lints/src/non_send_field_in_send_ty.rs | 53 ++++++++++--------- clippy_lints/src/utils/conf.rs | 2 +- tests/ui/non_send_field_in_send_ty.stderr | 28 +++++----- 4 files changed, 45 insertions(+), 42 deletions(-) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index f9d660972cb..50b55e5f79a 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -535,8 +535,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || Box::new(feature_name::FeatureName)); store.register_late_pass(move || Box::new(iter_not_returning_iterator::IterNotReturningIterator)); store.register_late_pass(move || Box::new(if_then_panic::IfThenPanic)); - let enable_raw_pointer_heuristic = conf.enable_raw_pointer_heuristic; - store.register_late_pass(move || Box::new(non_send_field_in_send_ty::NonSendFieldInSendTy::new(enable_raw_pointer_heuristic))); + let enable_raw_pointer_heuristic_for_send = conf.enable_raw_pointer_heuristic_for_send; + store.register_late_pass(move || Box::new(non_send_field_in_send_ty::NonSendFieldInSendTy::new(enable_raw_pointer_heuristic_for_send))); } #[rustfmt::skip] diff --git a/clippy_lints/src/non_send_field_in_send_ty.rs b/clippy_lints/src/non_send_field_in_send_ty.rs index 706214cb3d5..83f6f786d26 100644 --- a/clippy_lints/src/non_send_field_in_send_ty.rs +++ b/clippy_lints/src/non_send_field_in_send_ty.rs @@ -42,7 +42,7 @@ /// unsafe impl Send for ExampleStruct {} /// ``` /// Use thread-safe types like [`std::sync::Arc`](https://doc.rust-lang.org/std/sync/struct.Arc.html) - /// and specify correct bounds on generic type parameters (`T: Send`). + /// or specify correct bounds on generic type parameters (`T: Send`). pub NON_SEND_FIELD_IN_SEND_TY, nursery, "there is field that does not implement `Send` in a `Send` struct" @@ -125,7 +125,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { for field in non_send_fields { diag.span_note( field.span, - &format!("the field `{}` has type `{}` which is not `Send`", field.name, field.ty), + &format!("the field `{}` has type `{}` which is `!Send`", field.name, field.ty), ); match field.generic_params.len() { @@ -165,7 +165,7 @@ fn generic_params_string(&self) -> String { } /// Given a type, collect all of its generic parameters. -/// Example: MyStruct> => vec![P, Q, R] +/// Example: `MyStruct>` => `vec![P, Q, R]` fn collect_generic_params<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Vec> { ty.walk(cx.tcx) .filter_map(|inner| match inner.unpack() { @@ -184,31 +184,34 @@ fn ty_implements_send_or_copy(cx: &LateContext<'tcx>, ty: Ty<'tcx>, send_trait: /// Heuristic to allow cases like `Vec<*const u8>` fn ty_allowed_with_raw_pointer_heuristic<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, send_trait: DefId) -> bool { if ty_implements_send_or_copy(cx, ty, send_trait) { - true - } else { - // The type is known to be `!Send` and `!Copy` - match ty.kind() { - ty::Tuple(_) => ty - .tuple_fields() - .all(|ty| ty_allowed_with_raw_pointer_heuristic(cx, ty, send_trait)), - ty::Array(ty, _) | ty::Slice(ty) => ty_allowed_with_raw_pointer_heuristic(cx, ty, send_trait), - ty::Adt(_, substs) => { - if contains_raw_pointer(cx, ty) { - // descends only if ADT contains any raw pointers - substs.iter().all(|generic_arg| match generic_arg.unpack() { - GenericArgKind::Type(ty) => ty_allowed_with_raw_pointer_heuristic(cx, ty, send_trait), - GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => true, - }) - } else { - false - } - }, - ty::RawPtr(_) => true, - _ => false, - } + return true; + } + + // The type is known to be `!Send` and `!Copy` + match ty.kind() { + ty::Tuple(_) => ty + .tuple_fields() + .all(|ty| ty_allowed_with_raw_pointer_heuristic(cx, ty, send_trait)), + ty::Array(ty, _) | ty::Slice(ty) => ty_allowed_with_raw_pointer_heuristic(cx, ty, send_trait), + ty::Adt(_, substs) => { + if contains_raw_pointer(cx, ty) { + // descends only if ADT contains any raw pointers + substs.iter().all(|generic_arg| match generic_arg.unpack() { + GenericArgKind::Type(ty) => ty_allowed_with_raw_pointer_heuristic(cx, ty, send_trait), + // Lifetimes and const generics are not solid part of ADT and ignored + GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => true, + }) + } else { + false + } + }, + // Raw pointers are `!Send` but allowed by the heuristic + ty::RawPtr(_) => true, + _ => false, } } +/// Checks if the type contains any raw pointers in substs (including nested ones). fn contains_raw_pointer<'tcx>(cx: &LateContext<'tcx>, target_ty: Ty<'tcx>) -> bool { for ty_node in target_ty.walk(cx.tcx) { if_chain! { diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 177c0fd375c..e6233f73a57 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -287,7 +287,7 @@ pub(crate) fn get_configuration_metadata() -> Vec { /// Lint: NON_SEND_FIELD_IN_SEND_TY. /// /// Whether to apply the raw pointer heuristic in `non_send_field_in_send_ty` lint. - (enable_raw_pointer_heuristic: bool = true), + (enable_raw_pointer_heuristic_for_send: bool = true), } /// Search for the configuration file. diff --git a/tests/ui/non_send_field_in_send_ty.stderr b/tests/ui/non_send_field_in_send_ty.stderr index 327ef9fc2d6..6c7312d0152 100644 --- a/tests/ui/non_send_field_in_send_ty.stderr +++ b/tests/ui/non_send_field_in_send_ty.stderr @@ -5,7 +5,7 @@ LL | unsafe impl Send for RingBuffer {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::non-send-field-in-send-ty` implied by `-D warnings` -note: the field `data` has type `std::vec::Vec>` which is not `Send` +note: the field `data` has type `std::vec::Vec>` which is `!Send` --> $DIR/non_send_field_in_send_ty.rs:11:5 | LL | data: Vec>, @@ -18,7 +18,7 @@ error: this implementation is unsound, as some fields in `MvccRwLock` are `!S LL | unsafe impl Send for MvccRwLock {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: the field `lock` has type `std::sync::Mutex>` which is not `Send` +note: the field `lock` has type `std::sync::Mutex>` which is `!Send` --> $DIR/non_send_field_in_send_ty.rs:21:5 | LL | lock: Mutex>, @@ -31,7 +31,7 @@ error: this implementation is unsound, as some fields in `ArcGuard` are ` LL | unsafe impl Send for ArcGuard {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: the field `head` has type `std::sync::Arc` which is not `Send` +note: the field `head` has type `std::sync::Arc` which is `!Send` --> $DIR/non_send_field_in_send_ty.rs:29:5 | LL | head: Arc, @@ -44,7 +44,7 @@ error: this implementation is unsound, as some fields in `DeviceHandle` are ` LL | unsafe impl Send for DeviceHandle {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: the field `context` has type `T` which is not `Send` +note: the field `context` has type `T` which is `!Send` --> $DIR/non_send_field_in_send_ty.rs:44:5 | LL | context: T, @@ -57,7 +57,7 @@ error: this implementation is unsound, as some fields in `NoGeneric` are `!Send` LL | unsafe impl Send for NoGeneric {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: the field `rc_is_not_send` has type `std::rc::Rc` which is not `Send` +note: the field `rc_is_not_send` has type `std::rc::Rc` which is `!Send` --> $DIR/non_send_field_in_send_ty.rs:52:5 | LL | rc_is_not_send: Rc, @@ -70,19 +70,19 @@ error: this implementation is unsound, as some fields in `MultiField` are `!S LL | unsafe impl Send for MultiField {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: the field `field1` has type `T` which is not `Send` +note: the field `field1` has type `T` which is `!Send` --> $DIR/non_send_field_in_send_ty.rs:58:5 | LL | field1: T, | ^^^^^^^^^ = help: add `T: Send` bound in `Send` impl -note: the field `field2` has type `T` which is not `Send` +note: the field `field2` has type `T` which is `!Send` --> $DIR/non_send_field_in_send_ty.rs:59:5 | LL | field2: T, | ^^^^^^^^^ = help: add `T: Send` bound in `Send` impl -note: the field `field3` has type `T` which is not `Send` +note: the field `field3` has type `T` which is `!Send` --> $DIR/non_send_field_in_send_ty.rs:60:5 | LL | field3: T, @@ -95,7 +95,7 @@ error: this implementation is unsound, as some fields in `MyOption` are `!Sen LL | unsafe impl Send for MyOption {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: the field `0` has type `T` which is not `Send` +note: the field `0` has type `T` which is `!Send` --> $DIR/non_send_field_in_send_ty.rs:66:12 | LL | MySome(T), @@ -108,7 +108,7 @@ error: this implementation is unsound, as some fields in `MultiParam` are LL | unsafe impl Send for MultiParam {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: the field `vec` has type `std::vec::Vec<(A, B)>` which is not `Send` +note: the field `vec` has type `std::vec::Vec<(A, B)>` which is `!Send` --> $DIR/non_send_field_in_send_ty.rs:74:5 | LL | vec: Vec<(A, B)>, @@ -121,7 +121,7 @@ error: this implementation is unsound, as some fields in `HeuristicTest` are `!S LL | unsafe impl Send for HeuristicTest {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: the field `field4` has type `(*const NonSend, std::rc::Rc)` which is not `Send` +note: the field `field4` has type `(*const NonSend, std::rc::Rc)` which is `!Send` --> $DIR/non_send_field_in_send_ty.rs:90:5 | LL | field4: (*const NonSend, Rc), @@ -134,7 +134,7 @@ error: this implementation is unsound, as some fields in `AttrTest3` are `!Se LL | unsafe impl Send for AttrTest3 {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: the field `0` has type `T` which is not `Send` +note: the field `0` has type `T` which is `!Send` --> $DIR/non_send_field_in_send_ty.rs:109:11 | LL | Enum2(T), @@ -147,7 +147,7 @@ error: this implementation is unsound, as some fields in `Complex` are ` LL | unsafe impl

Send for Complex {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: the field `field1` has type `P` which is not `Send` +note: the field `field1` has type `P` which is `!Send` --> $DIR/non_send_field_in_send_ty.rs:118:5 | LL | field1: A, @@ -160,7 +160,7 @@ error: this implementation is unsound, as some fields in `Complex Send for Complex> {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: the field `field2` has type `std::sync::MutexGuard<'static, bool>` which is not `Send` +note: the field `field2` has type `std::sync::MutexGuard<'static, bool>` which is `!Send` --> $DIR/non_send_field_in_send_ty.rs:119:5 | LL | field2: B, From 4f01656a7d5d206488f9f3de879d43a637f943a1 Mon Sep 17 00:00:00 2001 From: Yechan Bae Date: Fri, 1 Oct 2021 13:07:25 -0400 Subject: [PATCH 09/12] Add ui-test for enable-raw-pointer-heuristic-for-send config --- clippy_lints/src/non_send_field_in_send_ty.rs | 18 +++- .../clippy.toml | 1 + .../strict_non_send_field_in_send_ty/test.rs | 43 +++++++++ .../test.stderr | 91 +++++++++++++++++++ .../toml_unknown_key/conf_unknown_key.stderr | 2 +- 5 files changed, 149 insertions(+), 6 deletions(-) create mode 100644 tests/ui-toml/strict_non_send_field_in_send_ty/clippy.toml create mode 100644 tests/ui-toml/strict_non_send_field_in_send_ty/test.rs create mode 100644 tests/ui-toml/strict_non_send_field_in_send_ty/test.stderr diff --git a/clippy_lints/src/non_send_field_in_send_ty.rs b/clippy_lints/src/non_send_field_in_send_ty.rs index 83f6f786d26..b43fb02d329 100644 --- a/clippy_lints/src/non_send_field_in_send_ty.rs +++ b/clippy_lints/src/non_send_field_in_send_ty.rs @@ -68,7 +68,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { let ty_allowed_in_send = if self.enable_raw_pointer_heuristic { ty_allowed_with_raw_pointer_heuristic } else { - ty_implements_send_or_copy + ty_allowed_without_raw_pointer_heuristic }; // Checks if we are in `Send` impl item. @@ -176,14 +176,22 @@ fn collect_generic_params<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Vec, ty: Ty<'tcx>, send_trait: DefId) -> bool { - implements_trait(cx, ty, send_trait, &[]) || is_copy(cx, ty) +/// Be more strict when the heuristic is disabled +fn ty_allowed_without_raw_pointer_heuristic<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, send_trait: DefId) -> bool { + if implements_trait(cx, ty, send_trait, &[]) { + return true; + } + + if is_copy(cx, ty) && !contains_raw_pointer(cx, ty) { + return true; + } + + false } /// Heuristic to allow cases like `Vec<*const u8>` fn ty_allowed_with_raw_pointer_heuristic<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, send_trait: DefId) -> bool { - if ty_implements_send_or_copy(cx, ty, send_trait) { + if implements_trait(cx, ty, send_trait, &[]) || is_copy(cx, ty) { return true; } diff --git a/tests/ui-toml/strict_non_send_field_in_send_ty/clippy.toml b/tests/ui-toml/strict_non_send_field_in_send_ty/clippy.toml new file mode 100644 index 00000000000..a942709d14a --- /dev/null +++ b/tests/ui-toml/strict_non_send_field_in_send_ty/clippy.toml @@ -0,0 +1 @@ +enable-raw-pointer-heuristic-for-send = false diff --git a/tests/ui-toml/strict_non_send_field_in_send_ty/test.rs b/tests/ui-toml/strict_non_send_field_in_send_ty/test.rs new file mode 100644 index 00000000000..6306c9c9f6d --- /dev/null +++ b/tests/ui-toml/strict_non_send_field_in_send_ty/test.rs @@ -0,0 +1,43 @@ +#![warn(clippy::non_send_field_in_send_ty)] +#![feature(extern_types)] + +use std::rc::Rc; + +// Basic tests should not be affected +pub struct NoGeneric { + rc_is_not_send: Rc, +} + +unsafe impl Send for NoGeneric {} + +pub struct MultiField { + field1: T, + field2: T, + field3: T, +} + +unsafe impl Send for MultiField {} + +pub enum MyOption { + MySome(T), + MyNone, +} + +unsafe impl Send for MyOption {} + +// All fields are disallowed when raw pointer heuristic is off +extern "C" { + type NonSend; +} + +pub struct HeuristicTest { + field1: Vec<*const NonSend>, + field2: [*const NonSend; 3], + field3: (*const NonSend, *const NonSend, *const NonSend), + field4: (*const NonSend, Rc), + field5: Vec>, +} + +unsafe impl Send for HeuristicTest {} + +fn main() {} diff --git a/tests/ui-toml/strict_non_send_field_in_send_ty/test.stderr b/tests/ui-toml/strict_non_send_field_in_send_ty/test.stderr new file mode 100644 index 00000000000..093c9320945 --- /dev/null +++ b/tests/ui-toml/strict_non_send_field_in_send_ty/test.stderr @@ -0,0 +1,91 @@ +error: this implementation is unsound, as some fields in `NoGeneric` are `!Send` + --> $DIR/test.rs:11:1 + | +LL | unsafe impl Send for NoGeneric {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::non-send-field-in-send-ty` implied by `-D warnings` +note: the field `rc_is_not_send` has type `std::rc::Rc` which is `!Send` + --> $DIR/test.rs:8:5 + | +LL | rc_is_not_send: Rc, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: use a thread-safe type that implements `Send` + +error: this implementation is unsound, as some fields in `MultiField` are `!Send` + --> $DIR/test.rs:19:1 + | +LL | unsafe impl Send for MultiField {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the field `field1` has type `T` which is `!Send` + --> $DIR/test.rs:14:5 + | +LL | field1: T, + | ^^^^^^^^^ + = help: add `T: Send` bound in `Send` impl +note: the field `field2` has type `T` which is `!Send` + --> $DIR/test.rs:15:5 + | +LL | field2: T, + | ^^^^^^^^^ + = help: add `T: Send` bound in `Send` impl +note: the field `field3` has type `T` which is `!Send` + --> $DIR/test.rs:16:5 + | +LL | field3: T, + | ^^^^^^^^^ + = help: add `T: Send` bound in `Send` impl + +error: this implementation is unsound, as some fields in `MyOption` are `!Send` + --> $DIR/test.rs:26:1 + | +LL | unsafe impl Send for MyOption {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the field `0` has type `T` which is `!Send` + --> $DIR/test.rs:22:12 + | +LL | MySome(T), + | ^ + = help: add `T: Send` bound in `Send` impl + +error: this implementation is unsound, as some fields in `HeuristicTest` are `!Send` + --> $DIR/test.rs:41:1 + | +LL | unsafe impl Send for HeuristicTest {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the field `field1` has type `std::vec::Vec<*const NonSend>` which is `!Send` + --> $DIR/test.rs:34:5 + | +LL | field1: Vec<*const NonSend>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: use a thread-safe type that implements `Send` +note: the field `field2` has type `[*const NonSend; 3]` which is `!Send` + --> $DIR/test.rs:35:5 + | +LL | field2: [*const NonSend; 3], + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: use a thread-safe type that implements `Send` +note: the field `field3` has type `(*const NonSend, *const NonSend, *const NonSend)` which is `!Send` + --> $DIR/test.rs:36:5 + | +LL | field3: (*const NonSend, *const NonSend, *const NonSend), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: use a thread-safe type that implements `Send` +note: the field `field4` has type `(*const NonSend, std::rc::Rc)` which is `!Send` + --> $DIR/test.rs:37:5 + | +LL | field4: (*const NonSend, Rc), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: use a thread-safe type that implements `Send` +note: the field `field5` has type `std::vec::Vec>` which is `!Send` + --> $DIR/test.rs:38:5 + | +LL | field5: Vec>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: use a thread-safe type that implements `Send` + +error: aborting due to 4 previous errors + diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 0eff2be7960..97bab1308aa 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -1,4 +1,4 @@ -error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `standard-macro-braces`, `enforced-import-renames`, `allowed-scripts`, `enable-raw-pointer-heuristic`, `third-party` at line 5 column 1 +error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `standard-macro-braces`, `enforced-import-renames`, `allowed-scripts`, `enable-raw-pointer-heuristic-for-send`, `third-party` at line 5 column 1 error: aborting due to previous error From dfed2e31d5caa0ff7e53b442e8bd3585e78aba09 Mon Sep 17 00:00:00 2001 From: Yechan Bae Date: Fri, 1 Oct 2021 14:00:50 -0400 Subject: [PATCH 10/12] Do not use full type path in help message --- clippy_lints/src/non_send_field_in_send_ty.rs | 22 +++++------ tests/ui/non_send_field_in_send_ty.stderr | 38 +++++++++---------- 2 files changed, 29 insertions(+), 31 deletions(-) diff --git a/clippy_lints/src/non_send_field_in_send_ty.rs b/clippy_lints/src/non_send_field_in_send_ty.rs index b43fb02d329..399b1a929e2 100644 --- a/clippy_lints/src/non_send_field_in_send_ty.rs +++ b/clippy_lints/src/non_send_field_in_send_ty.rs @@ -1,14 +1,14 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_lint_allowed; +use clippy_utils::source::snippet; use clippy_utils::ty::{implements_trait, is_copy}; use rustc_ast::ImplPolarity; use rustc_hir::def_id::DefId; -use rustc_hir::{Item, ItemKind}; +use rustc_hir::{FieldDef, Item, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, subst::GenericArgKind, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::symbol::Symbol; -use rustc_span::{sym, Span}; +use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -99,11 +99,10 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if !is_lint_allowed(cx, NON_SEND_FIELD_IN_SEND_TY, field_hir_id); if let field_ty = field.ty(cx.tcx, impl_trait_substs); if !ty_allowed_in_send(cx, field_ty, send_trait); - if let Some(field_span) = hir_map.span_if_local(field.did); + if let Node::Field(field_def) = hir_map.get(field_hir_id); then { non_send_fields.push(NonSendField { - name: hir_map.name(field_hir_id), - span: field_span, + def: field_def, ty: field_ty, generic_params: collect_generic_params(cx, field_ty), }) @@ -119,13 +118,13 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { item.span, &format!( "this implementation is unsound, as some fields in `{}` are `!Send`", - self_ty + snippet(cx, hir_impl.self_ty.span, "Unknown") ), |diag| { for field in non_send_fields { diag.span_note( - field.span, - &format!("the field `{}` has type `{}` which is `!Send`", field.name, field.ty), + field.def.span, + &format!("the type of field `{}` is `!Send`", field.def.ident.name), ); match field.generic_params.len() { @@ -135,7 +134,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { "add bounds on type parameter{} `{}` that satisfy `{}: Send`", if field.generic_params.len() > 1 { "s" } else { "" }, field.generic_params_string(), - field.ty + snippet(cx, field.def.ty.span, "Unknown"), )), }; } @@ -148,8 +147,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { } struct NonSendField<'tcx> { - name: Symbol, - span: Span, + def: &'tcx FieldDef<'tcx>, ty: Ty<'tcx>, generic_params: Vec>, } diff --git a/tests/ui/non_send_field_in_send_ty.stderr b/tests/ui/non_send_field_in_send_ty.stderr index 6c7312d0152..f49a7355b53 100644 --- a/tests/ui/non_send_field_in_send_ty.stderr +++ b/tests/ui/non_send_field_in_send_ty.stderr @@ -5,12 +5,12 @@ LL | unsafe impl Send for RingBuffer {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::non-send-field-in-send-ty` implied by `-D warnings` -note: the field `data` has type `std::vec::Vec>` which is `!Send` +note: the type of field `data` is `!Send` --> $DIR/non_send_field_in_send_ty.rs:11:5 | LL | data: Vec>, | ^^^^^^^^^^^^^^^^^^^^^^^^ - = help: add bounds on type parameter `T` that satisfy `std::vec::Vec>: Send` + = help: add bounds on type parameter `T` that satisfy `Vec>: Send` error: this implementation is unsound, as some fields in `MvccRwLock` are `!Send` --> $DIR/non_send_field_in_send_ty.rs:24:1 @@ -18,12 +18,12 @@ error: this implementation is unsound, as some fields in `MvccRwLock` are `!S LL | unsafe impl Send for MvccRwLock {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: the field `lock` has type `std::sync::Mutex>` which is `!Send` +note: the type of field `lock` is `!Send` --> $DIR/non_send_field_in_send_ty.rs:21:5 | LL | lock: Mutex>, | ^^^^^^^^^^^^^^^^^^^ - = help: add bounds on type parameter `T` that satisfy `std::sync::Mutex>: Send` + = help: add bounds on type parameter `T` that satisfy `Mutex>: Send` error: this implementation is unsound, as some fields in `ArcGuard` are `!Send` --> $DIR/non_send_field_in_send_ty.rs:32:1 @@ -31,12 +31,12 @@ error: this implementation is unsound, as some fields in `ArcGuard` are ` LL | unsafe impl Send for ArcGuard {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: the field `head` has type `std::sync::Arc` which is `!Send` +note: the type of field `head` is `!Send` --> $DIR/non_send_field_in_send_ty.rs:29:5 | LL | head: Arc, | ^^^^^^^^^^^^^ - = help: add bounds on type parameter `RC` that satisfy `std::sync::Arc: Send` + = help: add bounds on type parameter `RC` that satisfy `Arc: Send` error: this implementation is unsound, as some fields in `DeviceHandle` are `!Send` --> $DIR/non_send_field_in_send_ty.rs:48:1 @@ -44,7 +44,7 @@ error: this implementation is unsound, as some fields in `DeviceHandle` are ` LL | unsafe impl Send for DeviceHandle {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: the field `context` has type `T` which is `!Send` +note: the type of field `context` is `!Send` --> $DIR/non_send_field_in_send_ty.rs:44:5 | LL | context: T, @@ -57,7 +57,7 @@ error: this implementation is unsound, as some fields in `NoGeneric` are `!Send` LL | unsafe impl Send for NoGeneric {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: the field `rc_is_not_send` has type `std::rc::Rc` which is `!Send` +note: the type of field `rc_is_not_send` is `!Send` --> $DIR/non_send_field_in_send_ty.rs:52:5 | LL | rc_is_not_send: Rc, @@ -70,19 +70,19 @@ error: this implementation is unsound, as some fields in `MultiField` are `!S LL | unsafe impl Send for MultiField {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: the field `field1` has type `T` which is `!Send` +note: the type of field `field1` is `!Send` --> $DIR/non_send_field_in_send_ty.rs:58:5 | LL | field1: T, | ^^^^^^^^^ = help: add `T: Send` bound in `Send` impl -note: the field `field2` has type `T` which is `!Send` +note: the type of field `field2` is `!Send` --> $DIR/non_send_field_in_send_ty.rs:59:5 | LL | field2: T, | ^^^^^^^^^ = help: add `T: Send` bound in `Send` impl -note: the field `field3` has type `T` which is `!Send` +note: the type of field `field3` is `!Send` --> $DIR/non_send_field_in_send_ty.rs:60:5 | LL | field3: T, @@ -95,7 +95,7 @@ error: this implementation is unsound, as some fields in `MyOption` are `!Sen LL | unsafe impl Send for MyOption {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: the field `0` has type `T` which is `!Send` +note: the type of field `0` is `!Send` --> $DIR/non_send_field_in_send_ty.rs:66:12 | LL | MySome(T), @@ -108,12 +108,12 @@ error: this implementation is unsound, as some fields in `MultiParam` are LL | unsafe impl Send for MultiParam {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: the field `vec` has type `std::vec::Vec<(A, B)>` which is `!Send` +note: the type of field `vec` is `!Send` --> $DIR/non_send_field_in_send_ty.rs:74:5 | LL | vec: Vec<(A, B)>, | ^^^^^^^^^^^^^^^^ - = help: add bounds on type parameters `A, B` that satisfy `std::vec::Vec<(A, B)>: Send` + = help: add bounds on type parameters `A, B` that satisfy `Vec<(A, B)>: Send` error: this implementation is unsound, as some fields in `HeuristicTest` are `!Send` --> $DIR/non_send_field_in_send_ty.rs:95:1 @@ -121,7 +121,7 @@ error: this implementation is unsound, as some fields in `HeuristicTest` are `!S LL | unsafe impl Send for HeuristicTest {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: the field `field4` has type `(*const NonSend, std::rc::Rc)` which is `!Send` +note: the type of field `field4` is `!Send` --> $DIR/non_send_field_in_send_ty.rs:90:5 | LL | field4: (*const NonSend, Rc), @@ -134,7 +134,7 @@ error: this implementation is unsound, as some fields in `AttrTest3` are `!Se LL | unsafe impl Send for AttrTest3 {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: the field `0` has type `T` which is `!Send` +note: the type of field `0` is `!Send` --> $DIR/non_send_field_in_send_ty.rs:109:11 | LL | Enum2(T), @@ -147,20 +147,20 @@ error: this implementation is unsound, as some fields in `Complex` are ` LL | unsafe impl

Send for Complex {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: the field `field1` has type `P` which is `!Send` +note: the type of field `field1` is `!Send` --> $DIR/non_send_field_in_send_ty.rs:118:5 | LL | field1: A, | ^^^^^^^^^ = help: add `P: Send` bound in `Send` impl -error: this implementation is unsound, as some fields in `Complex>` are `!Send` +error: this implementation is unsound, as some fields in `Complex>` are `!Send` --> $DIR/non_send_field_in_send_ty.rs:125:1 | LL | unsafe impl Send for Complex> {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: the field `field2` has type `std::sync::MutexGuard<'static, bool>` which is `!Send` +note: the type of field `field2` is `!Send` --> $DIR/non_send_field_in_send_ty.rs:119:5 | LL | field2: B, From ef8df9df6811fc6597a40cf4cb853c30e9329206 Mon Sep 17 00:00:00 2001 From: Yechan Bae Date: Fri, 1 Oct 2021 14:26:04 -0400 Subject: [PATCH 11/12] Forgot to bless ui-toml test --- .../test.stderr | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/ui-toml/strict_non_send_field_in_send_ty/test.stderr b/tests/ui-toml/strict_non_send_field_in_send_ty/test.stderr index 093c9320945..74ec93cfa55 100644 --- a/tests/ui-toml/strict_non_send_field_in_send_ty/test.stderr +++ b/tests/ui-toml/strict_non_send_field_in_send_ty/test.stderr @@ -5,7 +5,7 @@ LL | unsafe impl Send for NoGeneric {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::non-send-field-in-send-ty` implied by `-D warnings` -note: the field `rc_is_not_send` has type `std::rc::Rc` which is `!Send` +note: the type of field `rc_is_not_send` is `!Send` --> $DIR/test.rs:8:5 | LL | rc_is_not_send: Rc, @@ -18,19 +18,19 @@ error: this implementation is unsound, as some fields in `MultiField` are `!S LL | unsafe impl Send for MultiField {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: the field `field1` has type `T` which is `!Send` +note: the type of field `field1` is `!Send` --> $DIR/test.rs:14:5 | LL | field1: T, | ^^^^^^^^^ = help: add `T: Send` bound in `Send` impl -note: the field `field2` has type `T` which is `!Send` +note: the type of field `field2` is `!Send` --> $DIR/test.rs:15:5 | LL | field2: T, | ^^^^^^^^^ = help: add `T: Send` bound in `Send` impl -note: the field `field3` has type `T` which is `!Send` +note: the type of field `field3` is `!Send` --> $DIR/test.rs:16:5 | LL | field3: T, @@ -43,7 +43,7 @@ error: this implementation is unsound, as some fields in `MyOption` are `!Sen LL | unsafe impl Send for MyOption {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: the field `0` has type `T` which is `!Send` +note: the type of field `0` is `!Send` --> $DIR/test.rs:22:12 | LL | MySome(T), @@ -56,31 +56,31 @@ error: this implementation is unsound, as some fields in `HeuristicTest` are `!S LL | unsafe impl Send for HeuristicTest {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | -note: the field `field1` has type `std::vec::Vec<*const NonSend>` which is `!Send` +note: the type of field `field1` is `!Send` --> $DIR/test.rs:34:5 | LL | field1: Vec<*const NonSend>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: use a thread-safe type that implements `Send` -note: the field `field2` has type `[*const NonSend; 3]` which is `!Send` +note: the type of field `field2` is `!Send` --> $DIR/test.rs:35:5 | LL | field2: [*const NonSend; 3], | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: use a thread-safe type that implements `Send` -note: the field `field3` has type `(*const NonSend, *const NonSend, *const NonSend)` which is `!Send` +note: the type of field `field3` is `!Send` --> $DIR/test.rs:36:5 | LL | field3: (*const NonSend, *const NonSend, *const NonSend), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: use a thread-safe type that implements `Send` -note: the field `field4` has type `(*const NonSend, std::rc::Rc)` which is `!Send` +note: the type of field `field4` is `!Send` --> $DIR/test.rs:37:5 | LL | field4: (*const NonSend, Rc), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: use a thread-safe type that implements `Send` -note: the field `field5` has type `std::vec::Vec>` which is `!Send` +note: the type of field `field5` is `!Send` --> $DIR/test.rs:38:5 | LL | field5: Vec>, From fb0353b28d746108932ffb0a9ce8535c115c0acf Mon Sep 17 00:00:00 2001 From: Yechan Bae Date: Sat, 2 Oct 2021 19:22:37 -0400 Subject: [PATCH 12/12] Update documentation and name for non_send_fields_in_send_ty lint --- CHANGELOG.md | 2 +- clippy_lints/src/lib.mods.rs | 2 +- clippy_lints/src/lib.register_lints.rs | 2 +- clippy_lints/src/lib.register_nursery.rs | 2 +- clippy_lints/src/lib.rs | 2 +- ...nd_ty.rs => non_send_fields_in_send_ty.rs} | 10 ++-- clippy_lints/src/utils/conf.rs | 4 +- .../clippy.toml | 0 .../test.rs | 2 +- .../test.stderr | 2 +- ...nd_ty.rs => non_send_fields_in_send_ty.rs} | 8 +-- ...derr => non_send_fields_in_send_ty.stderr} | 54 +++++++++---------- 12 files changed, 45 insertions(+), 45 deletions(-) rename clippy_lints/src/{non_send_field_in_send_ty.rs => non_send_fields_in_send_ty.rs} (97%) rename tests/ui-toml/{strict_non_send_field_in_send_ty => strict_non_send_fields_in_send_ty}/clippy.toml (100%) rename tests/ui-toml/{strict_non_send_field_in_send_ty => strict_non_send_fields_in_send_ty}/test.rs (94%) rename tests/ui-toml/{strict_non_send_field_in_send_ty => strict_non_send_fields_in_send_ty}/test.stderr (97%) rename tests/ui/{non_send_field_in_send_ty.rs => non_send_fields_in_send_ty.rs} (92%) rename tests/ui/{non_send_field_in_send_ty.stderr => non_send_fields_in_send_ty.stderr} (78%) diff --git a/CHANGELOG.md b/CHANGELOG.md index df3fa2866c2..9a4424f5061 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2898,7 +2898,7 @@ Released 2018-09-13 [`no_effect`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect [`non_ascii_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_ascii_literal [`non_octal_unix_permissions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_octal_unix_permissions -[`non_send_field_in_send_ty`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_send_field_in_send_ty +[`non_send_fields_in_send_ty`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_send_fields_in_send_ty [`nonminimal_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonminimal_bool [`nonsensical_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonsensical_open_options [`nonstandard_macro_braces`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonstandard_macro_braces diff --git a/clippy_lints/src/lib.mods.rs b/clippy_lints/src/lib.mods.rs index 1d9d2be3443..7fd84783c22 100644 --- a/clippy_lints/src/lib.mods.rs +++ b/clippy_lints/src/lib.mods.rs @@ -149,7 +149,7 @@ mod non_copy_const; mod non_expressive_names; mod non_octal_unix_permissions; -mod non_send_field_in_send_ty; +mod non_send_fields_in_send_ty; mod nonstandard_macro_braces; mod open_options; mod option_env_unwrap; diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index b1cd5aafe4c..28d54246fbb 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -366,7 +366,7 @@ non_expressive_names::MANY_SINGLE_CHAR_NAMES, non_expressive_names::SIMILAR_NAMES, non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS, - non_send_field_in_send_ty::NON_SEND_FIELD_IN_SEND_TY, + non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY, nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES, open_options::NONSENSICAL_OPEN_OPTIONS, option_env_unwrap::OPTION_ENV_UNWRAP, diff --git a/clippy_lints/src/lib.register_nursery.rs b/clippy_lints/src/lib.register_nursery.rs index 56b4f9d2391..32606e570d8 100644 --- a/clippy_lints/src/lib.register_nursery.rs +++ b/clippy_lints/src/lib.register_nursery.rs @@ -16,7 +16,7 @@ LintId::of(missing_const_for_fn::MISSING_CONST_FOR_FN), LintId::of(mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL), LintId::of(mutex_atomic::MUTEX_INTEGER), - LintId::of(non_send_field_in_send_ty::NON_SEND_FIELD_IN_SEND_TY), + LintId::of(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY), LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES), LintId::of(option_if_let_else::OPTION_IF_LET_ELSE), LintId::of(path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 50b55e5f79a..c3be6db6ffa 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -536,7 +536,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || Box::new(iter_not_returning_iterator::IterNotReturningIterator)); store.register_late_pass(move || Box::new(if_then_panic::IfThenPanic)); let enable_raw_pointer_heuristic_for_send = conf.enable_raw_pointer_heuristic_for_send; - store.register_late_pass(move || Box::new(non_send_field_in_send_ty::NonSendFieldInSendTy::new(enable_raw_pointer_heuristic_for_send))); + store.register_late_pass(move || Box::new(non_send_fields_in_send_ty::NonSendFieldInSendTy::new(enable_raw_pointer_heuristic_for_send))); } #[rustfmt::skip] diff --git a/clippy_lints/src/non_send_field_in_send_ty.rs b/clippy_lints/src/non_send_fields_in_send_ty.rs similarity index 97% rename from clippy_lints/src/non_send_field_in_send_ty.rs rename to clippy_lints/src/non_send_fields_in_send_ty.rs index 399b1a929e2..0dbf296c714 100644 --- a/clippy_lints/src/non_send_field_in_send_ty.rs +++ b/clippy_lints/src/non_send_fields_in_send_ty.rs @@ -12,7 +12,7 @@ declare_clippy_lint! { /// ### What it does - /// Warns about a field in a `Send` struct that is neither `Send` nor `Copy`. + /// Warns about fields in struct implementing `Send` that are neither `Send` nor `Copy`. /// /// ### Why is this bad? /// Sending the struct to another thread will transfer the ownership to @@ -43,7 +43,7 @@ /// ``` /// Use thread-safe types like [`std::sync::Arc`](https://doc.rust-lang.org/std/sync/struct.Arc.html) /// or specify correct bounds on generic type parameters (`T: Send`). - pub NON_SEND_FIELD_IN_SEND_TY, + pub NON_SEND_FIELDS_IN_SEND_TY, nursery, "there is field that does not implement `Send` in a `Send` struct" } @@ -61,7 +61,7 @@ pub fn new(enable_raw_pointer_heuristic: bool) -> Self { } } -impl_lint_pass!(NonSendFieldInSendTy => [NON_SEND_FIELD_IN_SEND_TY]); +impl_lint_pass!(NonSendFieldInSendTy => [NON_SEND_FIELDS_IN_SEND_TY]); impl<'tcx> LateLintPass<'tcx> for NonSendFieldInSendTy { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { @@ -96,7 +96,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { .did .as_local() .map(|local_def_id| hir_map.local_def_id_to_hir_id(local_def_id)); - if !is_lint_allowed(cx, NON_SEND_FIELD_IN_SEND_TY, field_hir_id); + if !is_lint_allowed(cx, NON_SEND_FIELDS_IN_SEND_TY, field_hir_id); if let field_ty = field.ty(cx.tcx, impl_trait_substs); if !ty_allowed_in_send(cx, field_ty, send_trait); if let Node::Field(field_def) = hir_map.get(field_hir_id); @@ -114,7 +114,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if !non_send_fields.is_empty() { span_lint_and_then( cx, - NON_SEND_FIELD_IN_SEND_TY, + NON_SEND_FIELDS_IN_SEND_TY, item.span, &format!( "this implementation is unsound, as some fields in `{}` are `!Send`", diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index e6233f73a57..6cbada4c150 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -284,9 +284,9 @@ pub(crate) fn get_configuration_metadata() -> Vec { /// /// The list of unicode scripts allowed to be used in the scope. (allowed_scripts: Vec = vec!["Latin".to_string()]), - /// Lint: NON_SEND_FIELD_IN_SEND_TY. + /// Lint: NON_SEND_FIELDS_IN_SEND_TY. /// - /// Whether to apply the raw pointer heuristic in `non_send_field_in_send_ty` lint. + /// Whether to apply the raw pointer heuristic to determine if a type is `Send`. (enable_raw_pointer_heuristic_for_send: bool = true), } diff --git a/tests/ui-toml/strict_non_send_field_in_send_ty/clippy.toml b/tests/ui-toml/strict_non_send_fields_in_send_ty/clippy.toml similarity index 100% rename from tests/ui-toml/strict_non_send_field_in_send_ty/clippy.toml rename to tests/ui-toml/strict_non_send_fields_in_send_ty/clippy.toml diff --git a/tests/ui-toml/strict_non_send_field_in_send_ty/test.rs b/tests/ui-toml/strict_non_send_fields_in_send_ty/test.rs similarity index 94% rename from tests/ui-toml/strict_non_send_field_in_send_ty/test.rs rename to tests/ui-toml/strict_non_send_fields_in_send_ty/test.rs index 6306c9c9f6d..90c2439dc34 100644 --- a/tests/ui-toml/strict_non_send_field_in_send_ty/test.rs +++ b/tests/ui-toml/strict_non_send_fields_in_send_ty/test.rs @@ -1,4 +1,4 @@ -#![warn(clippy::non_send_field_in_send_ty)] +#![warn(clippy::non_send_fields_in_send_ty)] #![feature(extern_types)] use std::rc::Rc; diff --git a/tests/ui-toml/strict_non_send_field_in_send_ty/test.stderr b/tests/ui-toml/strict_non_send_fields_in_send_ty/test.stderr similarity index 97% rename from tests/ui-toml/strict_non_send_field_in_send_ty/test.stderr rename to tests/ui-toml/strict_non_send_fields_in_send_ty/test.stderr index 74ec93cfa55..b07f9dd3df3 100644 --- a/tests/ui-toml/strict_non_send_field_in_send_ty/test.stderr +++ b/tests/ui-toml/strict_non_send_fields_in_send_ty/test.stderr @@ -4,7 +4,7 @@ error: this implementation is unsound, as some fields in `NoGeneric` are `!Send` LL | unsafe impl Send for NoGeneric {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: `-D clippy::non-send-field-in-send-ty` implied by `-D warnings` + = note: `-D clippy::non-send-fields-in-send-ty` implied by `-D warnings` note: the type of field `rc_is_not_send` is `!Send` --> $DIR/test.rs:8:5 | diff --git a/tests/ui/non_send_field_in_send_ty.rs b/tests/ui/non_send_fields_in_send_ty.rs similarity index 92% rename from tests/ui/non_send_field_in_send_ty.rs rename to tests/ui/non_send_fields_in_send_ty.rs index f5e7abdecd1..eca7f5e5655 100644 --- a/tests/ui/non_send_field_in_send_ty.rs +++ b/tests/ui/non_send_fields_in_send_ty.rs @@ -1,4 +1,4 @@ -#![warn(clippy::non_send_field_in_send_ty)] +#![warn(clippy::non_send_fields_in_send_ty)] #![feature(extern_types)] use std::cell::UnsafeCell; @@ -95,16 +95,16 @@ pub struct HeuristicTest { unsafe impl Send for HeuristicTest {} // Test attributes -#[allow(clippy::non_send_field_in_send_ty)] +#[allow(clippy::non_send_fields_in_send_ty)] pub struct AttrTest1(T); pub struct AttrTest2 { - #[allow(clippy::non_send_field_in_send_ty)] + #[allow(clippy::non_send_fields_in_send_ty)] field: T, } pub enum AttrTest3 { - #[allow(clippy::non_send_field_in_send_ty)] + #[allow(clippy::non_send_fields_in_send_ty)] Enum1(T), Enum2(T), } diff --git a/tests/ui/non_send_field_in_send_ty.stderr b/tests/ui/non_send_fields_in_send_ty.stderr similarity index 78% rename from tests/ui/non_send_field_in_send_ty.stderr rename to tests/ui/non_send_fields_in_send_ty.stderr index f49a7355b53..8b8a1d16d9b 100644 --- a/tests/ui/non_send_field_in_send_ty.stderr +++ b/tests/ui/non_send_fields_in_send_ty.stderr @@ -1,167 +1,167 @@ error: this implementation is unsound, as some fields in `RingBuffer` are `!Send` - --> $DIR/non_send_field_in_send_ty.rs:16:1 + --> $DIR/non_send_fields_in_send_ty.rs:16:1 | LL | unsafe impl Send for RingBuffer {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: `-D clippy::non-send-field-in-send-ty` implied by `-D warnings` + = note: `-D clippy::non-send-fields-in-send-ty` implied by `-D warnings` note: the type of field `data` is `!Send` - --> $DIR/non_send_field_in_send_ty.rs:11:5 + --> $DIR/non_send_fields_in_send_ty.rs:11:5 | LL | data: Vec>, | ^^^^^^^^^^^^^^^^^^^^^^^^ = help: add bounds on type parameter `T` that satisfy `Vec>: Send` error: this implementation is unsound, as some fields in `MvccRwLock` are `!Send` - --> $DIR/non_send_field_in_send_ty.rs:24:1 + --> $DIR/non_send_fields_in_send_ty.rs:24:1 | LL | unsafe impl Send for MvccRwLock {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: the type of field `lock` is `!Send` - --> $DIR/non_send_field_in_send_ty.rs:21:5 + --> $DIR/non_send_fields_in_send_ty.rs:21:5 | LL | lock: Mutex>, | ^^^^^^^^^^^^^^^^^^^ = help: add bounds on type parameter `T` that satisfy `Mutex>: Send` error: this implementation is unsound, as some fields in `ArcGuard` are `!Send` - --> $DIR/non_send_field_in_send_ty.rs:32:1 + --> $DIR/non_send_fields_in_send_ty.rs:32:1 | LL | unsafe impl Send for ArcGuard {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: the type of field `head` is `!Send` - --> $DIR/non_send_field_in_send_ty.rs:29:5 + --> $DIR/non_send_fields_in_send_ty.rs:29:5 | LL | head: Arc, | ^^^^^^^^^^^^^ = help: add bounds on type parameter `RC` that satisfy `Arc: Send` error: this implementation is unsound, as some fields in `DeviceHandle` are `!Send` - --> $DIR/non_send_field_in_send_ty.rs:48:1 + --> $DIR/non_send_fields_in_send_ty.rs:48:1 | LL | unsafe impl Send for DeviceHandle {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: the type of field `context` is `!Send` - --> $DIR/non_send_field_in_send_ty.rs:44:5 + --> $DIR/non_send_fields_in_send_ty.rs:44:5 | LL | context: T, | ^^^^^^^^^^ = help: add `T: Send` bound in `Send` impl error: this implementation is unsound, as some fields in `NoGeneric` are `!Send` - --> $DIR/non_send_field_in_send_ty.rs:55:1 + --> $DIR/non_send_fields_in_send_ty.rs:55:1 | LL | unsafe impl Send for NoGeneric {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: the type of field `rc_is_not_send` is `!Send` - --> $DIR/non_send_field_in_send_ty.rs:52:5 + --> $DIR/non_send_fields_in_send_ty.rs:52:5 | LL | rc_is_not_send: Rc, | ^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: use a thread-safe type that implements `Send` error: this implementation is unsound, as some fields in `MultiField` are `!Send` - --> $DIR/non_send_field_in_send_ty.rs:63:1 + --> $DIR/non_send_fields_in_send_ty.rs:63:1 | LL | unsafe impl Send for MultiField {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: the type of field `field1` is `!Send` - --> $DIR/non_send_field_in_send_ty.rs:58:5 + --> $DIR/non_send_fields_in_send_ty.rs:58:5 | LL | field1: T, | ^^^^^^^^^ = help: add `T: Send` bound in `Send` impl note: the type of field `field2` is `!Send` - --> $DIR/non_send_field_in_send_ty.rs:59:5 + --> $DIR/non_send_fields_in_send_ty.rs:59:5 | LL | field2: T, | ^^^^^^^^^ = help: add `T: Send` bound in `Send` impl note: the type of field `field3` is `!Send` - --> $DIR/non_send_field_in_send_ty.rs:60:5 + --> $DIR/non_send_fields_in_send_ty.rs:60:5 | LL | field3: T, | ^^^^^^^^^ = help: add `T: Send` bound in `Send` impl error: this implementation is unsound, as some fields in `MyOption` are `!Send` - --> $DIR/non_send_field_in_send_ty.rs:70:1 + --> $DIR/non_send_fields_in_send_ty.rs:70:1 | LL | unsafe impl Send for MyOption {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: the type of field `0` is `!Send` - --> $DIR/non_send_field_in_send_ty.rs:66:12 + --> $DIR/non_send_fields_in_send_ty.rs:66:12 | LL | MySome(T), | ^ = help: add `T: Send` bound in `Send` impl error: this implementation is unsound, as some fields in `MultiParam` are `!Send` - --> $DIR/non_send_field_in_send_ty.rs:77:1 + --> $DIR/non_send_fields_in_send_ty.rs:77:1 | LL | unsafe impl Send for MultiParam {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: the type of field `vec` is `!Send` - --> $DIR/non_send_field_in_send_ty.rs:74:5 + --> $DIR/non_send_fields_in_send_ty.rs:74:5 | LL | vec: Vec<(A, B)>, | ^^^^^^^^^^^^^^^^ = help: add bounds on type parameters `A, B` that satisfy `Vec<(A, B)>: Send` error: this implementation is unsound, as some fields in `HeuristicTest` are `!Send` - --> $DIR/non_send_field_in_send_ty.rs:95:1 + --> $DIR/non_send_fields_in_send_ty.rs:95:1 | LL | unsafe impl Send for HeuristicTest {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: the type of field `field4` is `!Send` - --> $DIR/non_send_field_in_send_ty.rs:90:5 + --> $DIR/non_send_fields_in_send_ty.rs:90:5 | LL | field4: (*const NonSend, Rc), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: use a thread-safe type that implements `Send` error: this implementation is unsound, as some fields in `AttrTest3` are `!Send` - --> $DIR/non_send_field_in_send_ty.rs:114:1 + --> $DIR/non_send_fields_in_send_ty.rs:114:1 | LL | unsafe impl Send for AttrTest3 {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: the type of field `0` is `!Send` - --> $DIR/non_send_field_in_send_ty.rs:109:11 + --> $DIR/non_send_fields_in_send_ty.rs:109:11 | LL | Enum2(T), | ^ = help: add `T: Send` bound in `Send` impl error: this implementation is unsound, as some fields in `Complex` are `!Send` - --> $DIR/non_send_field_in_send_ty.rs:122:1 + --> $DIR/non_send_fields_in_send_ty.rs:122:1 | LL | unsafe impl

Send for Complex {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: the type of field `field1` is `!Send` - --> $DIR/non_send_field_in_send_ty.rs:118:5 + --> $DIR/non_send_fields_in_send_ty.rs:118:5 | LL | field1: A, | ^^^^^^^^^ = help: add `P: Send` bound in `Send` impl error: this implementation is unsound, as some fields in `Complex>` are `!Send` - --> $DIR/non_send_field_in_send_ty.rs:125:1 + --> $DIR/non_send_fields_in_send_ty.rs:125:1 | LL | unsafe impl Send for Complex> {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: the type of field `field2` is `!Send` - --> $DIR/non_send_field_in_send_ty.rs:119:5 + --> $DIR/non_send_fields_in_send_ty.rs:119:5 | LL | field2: B, | ^^^^^^^^^