Add the transmute_int_to_non_zero lint

This commit is contained in:
Jirka Vebr 2023-02-16 16:08:28 +01:00
parent 52c8b536c9
commit 6d0df84f6f
No known key found for this signature in database
GPG key ID: 5D98B205D843DF83
6 changed files with 196 additions and 0 deletions

View file

@ -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

View file

@ -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,

View file

@ -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;
@ -253,6 +254,31 @@ declare_clippy_lint! {
"transmutes from an integer to a float"
}
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.
@ -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)
| (

View file

@ -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
}

View file

@ -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) };
}

View file

@ -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