about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJirka Vebr <jirkavebr@gmail.com>2023-02-16 16:08:28 +0100
committerJirka Vebr <jirkavebr@gmail.com>2023-02-16 16:58:05 +0100
commit6d0df84f6f879feb2211f8f61351a4669d2397e0 (patch)
tree4e7114cdedd4427253cdfa8fc480d43b8ede3d9d
parent52c8b536c954bc543290f36f2d90f3e75facaa0b (diff)
downloadrust-6d0df84f6f879feb2211f8f61351a4669d2397e0.tar.gz
rust-6d0df84f6f879feb2211f8f61351a4669d2397e0.zip
Add the `transmute_int_to_non_zero` lint
-rw-r--r--CHANGELOG.md1
-rw-r--r--clippy_lints/src/declared_lints.rs1
-rw-r--r--clippy_lints/src/transmute/mod.rs28
-rw-r--r--clippy_lints/src/transmute/transmute_int_to_non_zero.rs61
-rw-r--r--tests/ui/transmute_int_to_non_zero.rs41
-rw-r--r--tests/ui/transmute_int_to_non_zero.stderr64
6 files changed, 196 insertions, 0 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 73ae215ce1b..9138a4fb9c5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4794,6 +4794,7 @@ Released 2018-09-13
 [`transmute_int_to_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_bool
 [`transmute_int_to_char`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_char
 [`transmute_int_to_float`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_float
+[`transmute_int_to_non_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_non_zero
 [`transmute_null_to_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_null_to_fn
 [`transmute_num_to_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_num_to_bytes
 [`transmute_ptr_to_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ptr
diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs
index 0dab6b5f3f1..2577cdc8c66 100644
--- a/clippy_lints/src/declared_lints.rs
+++ b/clippy_lints/src/declared_lints.rs
@@ -577,6 +577,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::transmute::TRANSMUTE_INT_TO_BOOL_INFO,
     crate::transmute::TRANSMUTE_INT_TO_CHAR_INFO,
     crate::transmute::TRANSMUTE_INT_TO_FLOAT_INFO,
+    crate::transmute::TRANSMUTE_INT_TO_NON_ZERO_INFO,
     crate::transmute::TRANSMUTE_NULL_TO_FN_INFO,
     crate::transmute::TRANSMUTE_NUM_TO_BYTES_INFO,
     crate::transmute::TRANSMUTE_PTR_TO_PTR_INFO,
diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs
index c0d290b5adc..c01cbe5090f 100644
--- a/clippy_lints/src/transmute/mod.rs
+++ b/clippy_lints/src/transmute/mod.rs
@@ -3,6 +3,7 @@ mod transmute_float_to_int;
 mod transmute_int_to_bool;
 mod transmute_int_to_char;
 mod transmute_int_to_float;
+mod transmute_int_to_non_zero;
 mod transmute_null_to_fn;
 mod transmute_num_to_bytes;
 mod transmute_ptr_to_ptr;
@@ -255,6 +256,31 @@ declare_clippy_lint! {
 
 declare_clippy_lint! {
     /// ### What it does
+    /// Checks for transmutes from integers to `NonZero*` types, and suggests their `new_unchecked`
+    /// method instead.
+    ///
+    /// ### Why is this bad?
+    /// Transmutes work on any types and thus might cause unsoundness when those types change
+    /// elsewhere. `new_unchecked` only works for the appropriate types instead.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # use core::num::NonZeroU32;
+    /// let _non_zero: NonZeroU32 = unsafe { std::mem::transmute(123) };
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// # use core::num::NonZeroU32;
+    /// let _non_zero = unsafe { NonZeroU32::new_unchecked(123) };
+    /// ```
+    #[clippy::version = "1.69.0"]
+    pub TRANSMUTE_INT_TO_NON_ZERO,
+    complexity,
+    "transmutes from an integer to a non-zero wrapper"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
     /// Checks for transmutes from a float to an integer.
     ///
     /// ### Why is this bad?
@@ -451,6 +477,7 @@ impl_lint_pass!(Transmute => [
     TRANSMUTE_BYTES_TO_STR,
     TRANSMUTE_INT_TO_BOOL,
     TRANSMUTE_INT_TO_FLOAT,
+    TRANSMUTE_INT_TO_NON_ZERO,
     TRANSMUTE_FLOAT_TO_INT,
     TRANSMUTE_NUM_TO_BYTES,
     UNSOUND_COLLECTION_TRANSMUTE,
@@ -501,6 +528,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute {
                     | transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg)
                     | transmute_int_to_bool::check(cx, e, from_ty, to_ty, arg)
                     | transmute_int_to_float::check(cx, e, from_ty, to_ty, arg, const_context)
+                    | transmute_int_to_non_zero::check(cx, e, from_ty, to_ty, arg)
                     | transmute_float_to_int::check(cx, e, from_ty, to_ty, arg, const_context)
                     | transmute_num_to_bytes::check(cx, e, from_ty, to_ty, arg, const_context)
                     | (
diff --git a/clippy_lints/src/transmute/transmute_int_to_non_zero.rs b/clippy_lints/src/transmute/transmute_int_to_non_zero.rs
new file mode 100644
index 00000000000..5503653253c
--- /dev/null
+++ b/clippy_lints/src/transmute/transmute_int_to_non_zero.rs
@@ -0,0 +1,61 @@
+use super::TRANSMUTE_INT_TO_NON_ZERO;
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::sugg;
+use rustc_errors::Applicability;
+use rustc_hir::Expr;
+use rustc_lint::LateContext;
+use rustc_middle::{
+    query::Key,
+    ty::{self, Ty},
+};
+use rustc_span::symbol::sym;
+
+/// Checks for `transmute_int_to_non_zero` lint.
+/// Returns `true` if it's triggered, otherwise returns `false`.
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    e: &'tcx Expr<'_>,
+    from_ty: Ty<'tcx>,
+    to_ty: Ty<'tcx>,
+    arg: &'tcx Expr<'_>,
+) -> bool {
+    let (ty::Int(_) | ty::Uint(_), Some(to_ty_id)) = (&from_ty.kind(), to_ty.ty_adt_id()) else {
+        return false;
+    };
+    let Some(to_type_sym) = cx.tcx.get_diagnostic_name(to_ty_id) else {
+        return false;
+    };
+
+    if !matches!(
+        to_type_sym,
+        sym::NonZeroU8
+            | sym::NonZeroU16
+            | sym::NonZeroU32
+            | sym::NonZeroU64
+            | sym::NonZeroU128
+            | sym::NonZeroI8
+            | sym::NonZeroI16
+            | sym::NonZeroI32
+            | sym::NonZeroI64
+            | sym::NonZeroI128
+    ) {
+        return false;
+    }
+
+    span_lint_and_then(
+        cx,
+        TRANSMUTE_INT_TO_NON_ZERO,
+        e.span,
+        &format!("transmute from a `{from_ty}` to a `{to_type_sym}`"),
+        |diag| {
+            let arg = sugg::Sugg::hir(cx, arg, "..");
+            diag.span_suggestion(
+                e.span,
+                "consider using",
+                format!("{to_type_sym}::{}({arg})", sym::new_unchecked),
+                Applicability::Unspecified,
+            );
+        },
+    );
+    true
+}
diff --git a/tests/ui/transmute_int_to_non_zero.rs b/tests/ui/transmute_int_to_non_zero.rs
new file mode 100644
index 00000000000..a3840678250
--- /dev/null
+++ b/tests/ui/transmute_int_to_non_zero.rs
@@ -0,0 +1,41 @@
+#![warn(clippy::transmute_int_to_non_zero)]
+
+use core::num::*;
+
+fn main() {
+    let int_u8: u8 = 1;
+    let int_u16: u16 = 1;
+    let int_u32: u32 = 1;
+    let int_u64: u64 = 1;
+    let int_u128: u128 = 1;
+
+    let int_i8: i8 = 1;
+    let int_i16: i16 = 1;
+    let int_i32: i32 = 1;
+    let int_i64: i64 = 1;
+    let int_i128: i128 = 1;
+
+    let _: NonZeroU8 = unsafe { std::mem::transmute(int_u8) };
+    let _: NonZeroU16 = unsafe { std::mem::transmute(int_u16) };
+    let _: NonZeroU32 = unsafe { std::mem::transmute(int_u32) };
+    let _: NonZeroU64 = unsafe { std::mem::transmute(int_u64) };
+    let _: NonZeroU128 = unsafe { std::mem::transmute(int_u128) };
+
+    let _: NonZeroI8 = unsafe { std::mem::transmute(int_i8) };
+    let _: NonZeroI16 = unsafe { std::mem::transmute(int_i16) };
+    let _: NonZeroI32 = unsafe { std::mem::transmute(int_i32) };
+    let _: NonZeroI64 = unsafe { std::mem::transmute(int_i64) };
+    let _: NonZeroI128 = unsafe { std::mem::transmute(int_i128) };
+
+    let _: NonZeroU8 = unsafe { NonZeroU8::new_unchecked(int_u8) };
+    let _: NonZeroU16 = unsafe { NonZeroU16::new_unchecked(int_u16) };
+    let _: NonZeroU32 = unsafe { NonZeroU32::new_unchecked(int_u32) };
+    let _: NonZeroU64 = unsafe { NonZeroU64::new_unchecked(int_u64) };
+    let _: NonZeroU128 = unsafe { NonZeroU128::new_unchecked(int_u128) };
+
+    let _: NonZeroI8 = unsafe { NonZeroI8::new_unchecked(int_i8) };
+    let _: NonZeroI16 = unsafe { NonZeroI16::new_unchecked(int_i16) };
+    let _: NonZeroI32 = unsafe { NonZeroI32::new_unchecked(int_i32) };
+    let _: NonZeroI64 = unsafe { NonZeroI64::new_unchecked(int_i64) };
+    let _: NonZeroI128 = unsafe { NonZeroI128::new_unchecked(int_i128) };
+}
diff --git a/tests/ui/transmute_int_to_non_zero.stderr b/tests/ui/transmute_int_to_non_zero.stderr
new file mode 100644
index 00000000000..33f8ce79ea7
--- /dev/null
+++ b/tests/ui/transmute_int_to_non_zero.stderr
@@ -0,0 +1,64 @@
+error: transmute from a `u8` to a `NonZeroU8`
+  --> $DIR/transmute_int_to_non_zero.rs:18:33
+   |
+LL |     let _: NonZeroU8 = unsafe { std::mem::transmute(int_u8) };
+   |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroU8::new_unchecked(int_u8)`
+   |
+   = note: `-D clippy::transmute-int-to-non-zero` implied by `-D warnings`
+
+error: transmute from a `u16` to a `NonZeroU16`
+  --> $DIR/transmute_int_to_non_zero.rs:19:34
+   |
+LL |     let _: NonZeroU16 = unsafe { std::mem::transmute(int_u16) };
+   |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroU16::new_unchecked(int_u16)`
+
+error: transmute from a `u32` to a `NonZeroU32`
+  --> $DIR/transmute_int_to_non_zero.rs:20:34
+   |
+LL |     let _: NonZeroU32 = unsafe { std::mem::transmute(int_u32) };
+   |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroU32::new_unchecked(int_u32)`
+
+error: transmute from a `u64` to a `NonZeroU64`
+  --> $DIR/transmute_int_to_non_zero.rs:21:34
+   |
+LL |     let _: NonZeroU64 = unsafe { std::mem::transmute(int_u64) };
+   |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroU64::new_unchecked(int_u64)`
+
+error: transmute from a `u128` to a `NonZeroU128`
+  --> $DIR/transmute_int_to_non_zero.rs:22:35
+   |
+LL |     let _: NonZeroU128 = unsafe { std::mem::transmute(int_u128) };
+   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroU128::new_unchecked(int_u128)`
+
+error: transmute from a `i8` to a `NonZeroI8`
+  --> $DIR/transmute_int_to_non_zero.rs:24:33
+   |
+LL |     let _: NonZeroI8 = unsafe { std::mem::transmute(int_i8) };
+   |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroI8::new_unchecked(int_i8)`
+
+error: transmute from a `i16` to a `NonZeroI16`
+  --> $DIR/transmute_int_to_non_zero.rs:25:34
+   |
+LL |     let _: NonZeroI16 = unsafe { std::mem::transmute(int_i16) };
+   |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroI16::new_unchecked(int_i16)`
+
+error: transmute from a `i32` to a `NonZeroI32`
+  --> $DIR/transmute_int_to_non_zero.rs:26:34
+   |
+LL |     let _: NonZeroI32 = unsafe { std::mem::transmute(int_i32) };
+   |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroI32::new_unchecked(int_i32)`
+
+error: transmute from a `i64` to a `NonZeroI64`
+  --> $DIR/transmute_int_to_non_zero.rs:27:34
+   |
+LL |     let _: NonZeroI64 = unsafe { std::mem::transmute(int_i64) };
+   |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroI64::new_unchecked(int_i64)`
+
+error: transmute from a `i128` to a `NonZeroI128`
+  --> $DIR/transmute_int_to_non_zero.rs:28:35
+   |
+LL |     let _: NonZeroI128 = unsafe { std::mem::transmute(int_i128) };
+   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroI128::new_unchecked(int_i128)`
+
+error: aborting due to 10 previous errors
+