From 760f70e591c017c79ea692675d8afc07ae0b992d Mon Sep 17 00:00:00 2001 From: mendelsshop Date: Thu, 30 Oct 2025 22:14:14 -0400 Subject: [PATCH 1/3] add lint for transmute from &T to &mut T of a ADT argument --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/transmute/mod.rs | 19 ++++++++++ .../src/transmute/transmute_adt_argument.rs | 35 +++++++++++++++++++ tests/ui/mutable_adt_argument_transmute.rs | 5 +++ 5 files changed, 61 insertions(+) create mode 100644 clippy_lints/src/transmute/transmute_adt_argument.rs create mode 100644 tests/ui/mutable_adt_argument_transmute.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index c04735a495a7..6ec8eead579b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6565,6 +6565,7 @@ Released 2018-09-13 [`mut_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_mut [`mut_mutex_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_mutex_lock [`mut_range_bound`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_range_bound +[`mutable_adt_argument_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutable_adt_argument_transmute [`mutable_key_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key_type [`mutex_atomic`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_atomic [`mutex_integer`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_integer diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index a754eea31165..3ce2b91ae4f4 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -715,6 +715,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::transmute::CROSSPOINTER_TRANSMUTE_INFO, crate::transmute::EAGER_TRANSMUTE_INFO, crate::transmute::MISSING_TRANSMUTE_ANNOTATIONS_INFO, + crate::transmute::MUTABLE_ADT_ARGUMENT_TRANSMUTE_INFO, crate::transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS_INFO, crate::transmute::TRANSMUTE_BYTES_TO_STR_INFO, crate::transmute::TRANSMUTE_INT_TO_BOOL_INFO, diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs index 5fda388259a6..a5a769a57196 100644 --- a/clippy_lints/src/transmute/mod.rs +++ b/clippy_lints/src/transmute/mod.rs @@ -1,6 +1,7 @@ mod crosspointer_transmute; mod eager_transmute; mod missing_transmute_annotations; +mod transmute_adt_argument; mod transmute_int_to_bool; mod transmute_int_to_non_zero; mod transmute_null_to_fn; @@ -44,6 +45,22 @@ declare_clippy_lint! { correctness, "transmutes that are confusing at best, undefined behavior at worst and always useless" } +declare_clippy_lint! { + /// ### What it does + /// Checks for transmutes between the same adt, where at least one of the type argument goes from &T to &mut T. + /// This is an a more complicated version of https://doc.rust-lang.org/rustc/lints/listing/deny-by-default.html#mutable-transmutes. + /// ### Example + /// + /// ```ignore + /// unsafe { + /// std::mem::transmute::, Option<&mut i32>>(&Some(5)); + /// } + /// ``` + #[clippy::version = "1.92.0"] + pub MUTABLE_ADT_ARGUMENT_TRANSMUTE, + correctness, + "transmutes on the same adt where at least one of the type argument goes from &T to &mut T" +} declare_clippy_lint! { /// ### What it does @@ -475,6 +492,7 @@ impl_lint_pass!(Transmute => [ USELESS_TRANSMUTE, WRONG_TRANSMUTE, TRANSMUTE_BYTES_TO_STR, + MUTABLE_ADT_ARGUMENT_TRANSMUTE, TRANSMUTE_INT_TO_BOOL, TRANSMUTE_INT_TO_NON_ZERO, UNSOUND_COLLECTION_TRANSMUTE, @@ -516,6 +534,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { } let linted = wrong_transmute::check(cx, e, from_ty, to_ty) + | transmute_adt_argument::check(cx, e, from_ty, to_ty) | crosspointer_transmute::check(cx, e, from_ty, to_ty) | transmuting_null::check(cx, e, arg, to_ty) | transmute_null_to_fn::check(cx, e, arg, to_ty) diff --git a/clippy_lints/src/transmute/transmute_adt_argument.rs b/clippy_lints/src/transmute/transmute_adt_argument.rs new file mode 100644 index 000000000000..ecf802d36ba0 --- /dev/null +++ b/clippy_lints/src/transmute/transmute_adt_argument.rs @@ -0,0 +1,35 @@ +use super::MUTABLE_ADT_ARGUMENT_TRANSMUTE; +use clippy_utils::diagnostics::span_lint; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, GenericArgKind, Ty}; + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx>, to_ty: Ty<'tcx>) -> bool { + from_ty + .walk() + .zip(to_ty.walk()) + .filter_map(|(from_ty, to_ty)| { + if let (GenericArgKind::Type(from_ty), GenericArgKind::Type(to_ty)) = (from_ty.kind(), to_ty.kind()) { + Some((from_ty, to_ty)) + } else { + None + } + }) + .filter(|(from_ty_inner, to_ty_inner)| { + if let (ty::Ref(_, _, from_mut), ty::Ref(_, _, to_mut)) = (from_ty_inner.kind(), to_ty_inner.kind()) + && from_mut < to_mut + { + span_lint( + cx, + MUTABLE_ADT_ARGUMENT_TRANSMUTE, + e.span, + format!("transmute of type argument {from_ty_inner} to {from_ty_inner}"), + ); + true + } else { + false + } + }) + .count() + > 0 +} diff --git a/tests/ui/mutable_adt_argument_transmute.rs b/tests/ui/mutable_adt_argument_transmute.rs new file mode 100644 index 000000000000..de1ab0b35c8a --- /dev/null +++ b/tests/ui/mutable_adt_argument_transmute.rs @@ -0,0 +1,5 @@ +#![warn(clippy::mutable_adt_argument_transmute)] + +fn main() { + // test code goes here +} From fb142f2dcdc27132f465f20d052e4ed1878aa2fa Mon Sep 17 00:00:00 2001 From: mendelsshop Date: Thu, 30 Oct 2025 22:50:33 -0400 Subject: [PATCH 2/3] adding test for mutable_adt_argument_transmute lint --- clippy_lints/src/transmute/mod.rs | 2 +- tests/ui/mutable_adt_argument_transmute.rs | 5 ++++- tests/ui/mutable_adt_argument_transmute.stderr | 11 +++++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 tests/ui/mutable_adt_argument_transmute.stderr diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs index a5a769a57196..70e0c87d5911 100644 --- a/clippy_lints/src/transmute/mod.rs +++ b/clippy_lints/src/transmute/mod.rs @@ -53,7 +53,7 @@ declare_clippy_lint! { /// /// ```ignore /// unsafe { - /// std::mem::transmute::, Option<&mut i32>>(&Some(5)); + /// std::mem::transmute::, Option<&mut i32>>(Some(&5)); /// } /// ``` #[clippy::version = "1.92.0"] diff --git a/tests/ui/mutable_adt_argument_transmute.rs b/tests/ui/mutable_adt_argument_transmute.rs index de1ab0b35c8a..357ec3f0220a 100644 --- a/tests/ui/mutable_adt_argument_transmute.rs +++ b/tests/ui/mutable_adt_argument_transmute.rs @@ -1,5 +1,8 @@ #![warn(clippy::mutable_adt_argument_transmute)] fn main() { - // test code goes here + unsafe { + let _: Option<&mut i32> = std::mem::transmute(Some(&5i32)); + //~^ mutable_adt_argument_transmute + } } diff --git a/tests/ui/mutable_adt_argument_transmute.stderr b/tests/ui/mutable_adt_argument_transmute.stderr new file mode 100644 index 000000000000..b17537d0e744 --- /dev/null +++ b/tests/ui/mutable_adt_argument_transmute.stderr @@ -0,0 +1,11 @@ +error: transmute of type argument &i32 to &i32 + --> tests/ui/mutable_adt_argument_transmute.rs:5:35 + | +LL | let _: Option<&mut i32> = std::mem::transmute(Some(&5i32)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::mutable-adt-argument-transmute` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::mutable_adt_argument_transmute)]` + +error: aborting due to 1 previous error + From 3b036be60c2da77a5012ba95d9f2cee0b1aa083e Mon Sep 17 00:00:00 2001 From: mendelsshop Date: Fri, 31 Oct 2025 09:35:59 -0400 Subject: [PATCH 3/3] more tests for mutable_adt_argument_transmute --- tests/ui/mutable_adt_argument_transmute.rs | 10 +++++++ .../ui/mutable_adt_argument_transmute.stderr | 26 ++++++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/tests/ui/mutable_adt_argument_transmute.rs b/tests/ui/mutable_adt_argument_transmute.rs index 357ec3f0220a..08f21f4de446 100644 --- a/tests/ui/mutable_adt_argument_transmute.rs +++ b/tests/ui/mutable_adt_argument_transmute.rs @@ -4,5 +4,15 @@ fn main() { unsafe { let _: Option<&mut i32> = std::mem::transmute(Some(&5i32)); //~^ mutable_adt_argument_transmute + let _: Result<&mut i32, ()> = std::mem::transmute(Result::<&i32, ()>::Ok(&5i32)); + //~^ mutable_adt_argument_transmute + let _: Result, ()> = + std::mem::transmute(Result::, ()>::Ok(Some(&"foo".to_string()))); + //~^ mutable_adt_argument_transmute + let _: Result<&mut f32, &usize> = std::mem::transmute(Result::<&f32, &usize>::Ok(&2f32)); + //~^ mutable_adt_argument_transmute + let _: Result<(), &mut usize> = std::mem::transmute(Result::<(), &usize>::Ok(())); + //~^ mutable_adt_argument_transmute + let _: Option<&i32> = std::mem::transmute(Some(&5i32)); } } diff --git a/tests/ui/mutable_adt_argument_transmute.stderr b/tests/ui/mutable_adt_argument_transmute.stderr index b17537d0e744..6804c15ba78c 100644 --- a/tests/ui/mutable_adt_argument_transmute.stderr +++ b/tests/ui/mutable_adt_argument_transmute.stderr @@ -7,5 +7,29 @@ LL | let _: Option<&mut i32> = std::mem::transmute(Some(&5i32)); = note: `-D clippy::mutable-adt-argument-transmute` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::mutable_adt_argument_transmute)]` -error: aborting due to 1 previous error +error: transmute of type argument &i32 to &i32 + --> tests/ui/mutable_adt_argument_transmute.rs:7:39 + | +LL | let _: Result<&mut i32, ()> = std::mem::transmute(Result::<&i32, ()>::Ok(&5i32)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute of type argument &std::string::String to &std::string::String + --> tests/ui/mutable_adt_argument_transmute.rs:10:13 + | +LL | std::mem::transmute(Result::, ()>::Ok(Some(&"foo".to_string()))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute of type argument &f32 to &f32 + --> tests/ui/mutable_adt_argument_transmute.rs:12:43 + | +LL | let _: Result<&mut f32, &usize> = std::mem::transmute(Result::<&f32, &usize>::Ok(&2f32)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: transmute of type argument &usize to &usize + --> tests/ui/mutable_adt_argument_transmute.rs:14:41 + | +LL | let _: Result<(), &mut usize> = std::mem::transmute(Result::<(), &usize>::Ok(())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors