Emit one report for all fields in the same ADT

This commit is contained in:
Yechan Bae 2021-09-28 15:23:39 -04:00
parent a81a5ad1a8
commit ee74574876
3 changed files with 211 additions and 131 deletions

View file

@ -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 @@ impl<'tcx> LateLintPass<'tcx> for NonSendFieldInSendTy {
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<Ty<'tcx>>,
}
impl<'tcx> NonSendField<'tcx> {
fn generic_params_string(&self) -> String {
self.generic_params
.iter()
.map(ToString::to_string)
.collect::<Vec<_>>()
.join(", ")
}
}
fn collect_generic_params<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Vec<Ty<'tcx>> {
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.

View file

@ -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<T: UsbContext> {
unsafe impl<T: UsbContext> Send for DeviceHandle<T> {}
// Other basic tests
pub struct NoGeneric {
rc_is_not_send: Rc<String>,
}
unsafe impl Send for NoGeneric {}
pub struct MultiField<T> {
field1: T,
field2: T,
@ -62,6 +69,13 @@ pub enum MyOption<T> {
unsafe impl<T> Send for MyOption<T> {}
// Multiple type parameters
pub struct MultiParam<A, B> {
vec: Vec<(A, B)>,
}
unsafe impl<A, B> Send for MultiParam<A, B> {}
// Raw pointers are allowed
extern "C" {
type SomeFfiType;

View file

@ -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<UnsafeCell<T>>,
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::non-send-field-in-send-ty` implied by `-D warnings`
note: type `std::vec::Vec<std::cell::UnsafeCell<T>>` doesn't implement `Send` when `RingBuffer<T>` is `Send`
--> $DIR/non_send_field_in_send_ty.rs:15:1
error: this implementation is unsound, as some fields in `RingBuffer<T>` are `!Send`
--> $DIR/non_send_field_in_send_ty.rs:16:1
|
LL | unsafe impl<T> Send for RingBuffer<T> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::non-send-field-in-send-ty` implied by `-D warnings`
note: the field `data` has type `std::vec::Vec<std::cell::UnsafeCell<T>>` which is not `Send`
--> $DIR/non_send_field_in_send_ty.rs:11:5
|
LL | data: Vec<UnsafeCell<T>>,
| ^^^^^^^^^^^^^^^^^^^^^^^^
= help: add bounds on type parameter `T` that satisfy `std::vec::Vec<std::cell::UnsafeCell<T>>: Send`
error: non-`Send` field found in a `Send` struct
--> $DIR/non_send_field_in_send_ty.rs:20:5
|
LL | lock: Mutex<Box<T>>,
| ^^^^^^^^^^^^^^^^^^^
|
note: type `std::sync::Mutex<std::boxed::Box<T>>` doesn't implement `Send` when `MvccRwLock<T>` is `Send`
--> $DIR/non_send_field_in_send_ty.rs:23:1
error: this implementation is unsound, as some fields in `MvccRwLock<T>` are `!Send`
--> $DIR/non_send_field_in_send_ty.rs:24:1
|
LL | unsafe impl<T> Send for MvccRwLock<T> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: the field `lock` has type `std::sync::Mutex<std::boxed::Box<T>>` which is not `Send`
--> $DIR/non_send_field_in_send_ty.rs:21:5
|
LL | lock: Mutex<Box<T>>,
| ^^^^^^^^^^^^^^^^^^^
= help: add bounds on type parameter `T` that satisfy `std::sync::Mutex<std::boxed::Box<T>>: Send`
error: non-`Send` field found in a `Send` struct
--> $DIR/non_send_field_in_send_ty.rs:28:5
|
LL | head: Arc<RC>,
| ^^^^^^^^^^^^^
|
note: type `std::sync::Arc<RC>` doesn't implement `Send` when `ArcGuard<RC, T>` is `Send`
--> $DIR/non_send_field_in_send_ty.rs:31:1
error: this implementation is unsound, as some fields in `ArcGuard<RC, T>` are `!Send`
--> $DIR/non_send_field_in_send_ty.rs:32:1
|
LL | unsafe impl<RC, T: Send> Send for ArcGuard<RC, T> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: the field `head` has type `std::sync::Arc<RC>` which is not `Send`
--> $DIR/non_send_field_in_send_ty.rs:29:5
|
LL | head: Arc<RC>,
| ^^^^^^^^^^^^^
= help: add bounds on type parameter `RC` that satisfy `std::sync::Arc<RC>: 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<T>` is `Send`
--> $DIR/non_send_field_in_send_ty.rs:47:1
error: this implementation is unsound, as some fields in `DeviceHandle<T>` are `!Send`
--> $DIR/non_send_field_in_send_ty.rs:48:1
|
LL | unsafe impl<T: UsbContext> Send for DeviceHandle<T> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= 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<std::string::String>` which is not `Send`
--> $DIR/non_send_field_in_send_ty.rs:52:5
|
LL | rc_is_not_send: Rc<String>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: use a thread-safe type that implements `Send`
error: this implementation is unsound, as some fields in `MultiField<T>` are `!Send`
--> $DIR/non_send_field_in_send_ty.rs:63:1
|
LL | unsafe impl<T> Send for MultiField<T> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
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<T>` is `Send`
--> $DIR/non_send_field_in_send_ty.rs:56:1
|
LL | unsafe impl<T> Send for MultiField<T> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= 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<T>` is `Send`
--> $DIR/non_send_field_in_send_ty.rs:56:1
|
LL | unsafe impl<T> Send for MultiField<T> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= 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<T>` is `Send`
--> $DIR/non_send_field_in_send_ty.rs:56:1
|
LL | unsafe impl<T> Send for MultiField<T> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= 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<T>` is `Send`
--> $DIR/non_send_field_in_send_ty.rs:63:1
error: this implementation is unsound, as some fields in `MyOption<T>` are `!Send`
--> $DIR/non_send_field_in_send_ty.rs:70:1
|
LL | unsafe impl<T> Send for MyOption<T> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= 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<A, B>` are `!Send`
--> $DIR/non_send_field_in_send_ty.rs:77:1
|
LL | Enum2(T),
| ^
LL | unsafe impl<A, B> Send for MultiParam<A, B> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: type `T` doesn't implement `Send` when `AttrTest3<T>` 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<T>` are `!Send`
--> $DIR/non_send_field_in_send_ty.rs:107:1
|
LL | unsafe impl<T> Send for AttrTest3<T> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= 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<P, u32>` is `Send`
--> $DIR/non_send_field_in_send_ty.rs:101:1
error: this implementation is unsound, as some fields in `Complex<P, u32>` are `!Send`
--> $DIR/non_send_field_in_send_ty.rs:115:1
|
LL | unsafe impl<P> Send for Complex<P, u32> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= 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<Q, std::sync::MutexGuard<'static, bool>>` 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<Q, std::sync::MutexGuard<'static, bool>>` are `!Send`
--> $DIR/non_send_field_in_send_ty.rs:118:1
|
LL | unsafe impl<Q: Send> Send for Complex<Q, MutexGuard<'static, bool>> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
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