Skip to content

Commit dfe23dc

Browse files
committed
Auto merge of rust-lang#10360 - JirkaVebr:transmute_integer_to_non_zero_wrapper, r=llogiq
Add the `transmute_int_to_non_zero` lint Fixes rust-lang#10288 This adds a new complexity lint `transmute_int_to_non_zero` which checks for transmutes to any of the `NonZero*` types, and suggests their `new_unchecked` method instead. r? `@llogiq` changelog: New lint: [`transmute_int_to_non_zero`]
2 parents 52c8b53 + 6d0df84 commit dfe23dc

File tree

6 files changed

+196
-0
lines changed

6 files changed

+196
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4794,6 +4794,7 @@ Released 2018-09-13
47944794
[`transmute_int_to_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_bool
47954795
[`transmute_int_to_char`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_char
47964796
[`transmute_int_to_float`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_float
4797+
[`transmute_int_to_non_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_non_zero
47974798
[`transmute_null_to_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_null_to_fn
47984799
[`transmute_num_to_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_num_to_bytes
47994800
[`transmute_ptr_to_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ptr

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
577577
crate::transmute::TRANSMUTE_INT_TO_BOOL_INFO,
578578
crate::transmute::TRANSMUTE_INT_TO_CHAR_INFO,
579579
crate::transmute::TRANSMUTE_INT_TO_FLOAT_INFO,
580+
crate::transmute::TRANSMUTE_INT_TO_NON_ZERO_INFO,
580581
crate::transmute::TRANSMUTE_NULL_TO_FN_INFO,
581582
crate::transmute::TRANSMUTE_NUM_TO_BYTES_INFO,
582583
crate::transmute::TRANSMUTE_PTR_TO_PTR_INFO,

clippy_lints/src/transmute/mod.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ mod transmute_float_to_int;
33
mod transmute_int_to_bool;
44
mod transmute_int_to_char;
55
mod transmute_int_to_float;
6+
mod transmute_int_to_non_zero;
67
mod transmute_null_to_fn;
78
mod transmute_num_to_bytes;
89
mod transmute_ptr_to_ptr;
@@ -253,6 +254,31 @@ declare_clippy_lint! {
253254
"transmutes from an integer to a float"
254255
}
255256

257+
declare_clippy_lint! {
258+
/// ### What it does
259+
/// Checks for transmutes from integers to `NonZero*` types, and suggests their `new_unchecked`
260+
/// method instead.
261+
///
262+
/// ### Why is this bad?
263+
/// Transmutes work on any types and thus might cause unsoundness when those types change
264+
/// elsewhere. `new_unchecked` only works for the appropriate types instead.
265+
///
266+
/// ### Example
267+
/// ```rust
268+
/// # use core::num::NonZeroU32;
269+
/// let _non_zero: NonZeroU32 = unsafe { std::mem::transmute(123) };
270+
/// ```
271+
/// Use instead:
272+
/// ```rust
273+
/// # use core::num::NonZeroU32;
274+
/// let _non_zero = unsafe { NonZeroU32::new_unchecked(123) };
275+
/// ```
276+
#[clippy::version = "1.69.0"]
277+
pub TRANSMUTE_INT_TO_NON_ZERO,
278+
complexity,
279+
"transmutes from an integer to a non-zero wrapper"
280+
}
281+
256282
declare_clippy_lint! {
257283
/// ### What it does
258284
/// Checks for transmutes from a float to an integer.
@@ -451,6 +477,7 @@ impl_lint_pass!(Transmute => [
451477
TRANSMUTE_BYTES_TO_STR,
452478
TRANSMUTE_INT_TO_BOOL,
453479
TRANSMUTE_INT_TO_FLOAT,
480+
TRANSMUTE_INT_TO_NON_ZERO,
454481
TRANSMUTE_FLOAT_TO_INT,
455482
TRANSMUTE_NUM_TO_BYTES,
456483
UNSOUND_COLLECTION_TRANSMUTE,
@@ -501,6 +528,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute {
501528
| transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg)
502529
| transmute_int_to_bool::check(cx, e, from_ty, to_ty, arg)
503530
| transmute_int_to_float::check(cx, e, from_ty, to_ty, arg, const_context)
531+
| transmute_int_to_non_zero::check(cx, e, from_ty, to_ty, arg)
504532
| transmute_float_to_int::check(cx, e, from_ty, to_ty, arg, const_context)
505533
| transmute_num_to_bytes::check(cx, e, from_ty, to_ty, arg, const_context)
506534
| (
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
use super::TRANSMUTE_INT_TO_NON_ZERO;
2+
use clippy_utils::diagnostics::span_lint_and_then;
3+
use clippy_utils::sugg;
4+
use rustc_errors::Applicability;
5+
use rustc_hir::Expr;
6+
use rustc_lint::LateContext;
7+
use rustc_middle::{
8+
query::Key,
9+
ty::{self, Ty},
10+
};
11+
use rustc_span::symbol::sym;
12+
13+
/// Checks for `transmute_int_to_non_zero` lint.
14+
/// Returns `true` if it's triggered, otherwise returns `false`.
15+
pub(super) fn check<'tcx>(
16+
cx: &LateContext<'tcx>,
17+
e: &'tcx Expr<'_>,
18+
from_ty: Ty<'tcx>,
19+
to_ty: Ty<'tcx>,
20+
arg: &'tcx Expr<'_>,
21+
) -> bool {
22+
let (ty::Int(_) | ty::Uint(_), Some(to_ty_id)) = (&from_ty.kind(), to_ty.ty_adt_id()) else {
23+
return false;
24+
};
25+
let Some(to_type_sym) = cx.tcx.get_diagnostic_name(to_ty_id) else {
26+
return false;
27+
};
28+
29+
if !matches!(
30+
to_type_sym,
31+
sym::NonZeroU8
32+
| sym::NonZeroU16
33+
| sym::NonZeroU32
34+
| sym::NonZeroU64
35+
| sym::NonZeroU128
36+
| sym::NonZeroI8
37+
| sym::NonZeroI16
38+
| sym::NonZeroI32
39+
| sym::NonZeroI64
40+
| sym::NonZeroI128
41+
) {
42+
return false;
43+
}
44+
45+
span_lint_and_then(
46+
cx,
47+
TRANSMUTE_INT_TO_NON_ZERO,
48+
e.span,
49+
&format!("transmute from a `{from_ty}` to a `{to_type_sym}`"),
50+
|diag| {
51+
let arg = sugg::Sugg::hir(cx, arg, "..");
52+
diag.span_suggestion(
53+
e.span,
54+
"consider using",
55+
format!("{to_type_sym}::{}({arg})", sym::new_unchecked),
56+
Applicability::Unspecified,
57+
);
58+
},
59+
);
60+
true
61+
}

tests/ui/transmute_int_to_non_zero.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#![warn(clippy::transmute_int_to_non_zero)]
2+
3+
use core::num::*;
4+
5+
fn main() {
6+
let int_u8: u8 = 1;
7+
let int_u16: u16 = 1;
8+
let int_u32: u32 = 1;
9+
let int_u64: u64 = 1;
10+
let int_u128: u128 = 1;
11+
12+
let int_i8: i8 = 1;
13+
let int_i16: i16 = 1;
14+
let int_i32: i32 = 1;
15+
let int_i64: i64 = 1;
16+
let int_i128: i128 = 1;
17+
18+
let _: NonZeroU8 = unsafe { std::mem::transmute(int_u8) };
19+
let _: NonZeroU16 = unsafe { std::mem::transmute(int_u16) };
20+
let _: NonZeroU32 = unsafe { std::mem::transmute(int_u32) };
21+
let _: NonZeroU64 = unsafe { std::mem::transmute(int_u64) };
22+
let _: NonZeroU128 = unsafe { std::mem::transmute(int_u128) };
23+
24+
let _: NonZeroI8 = unsafe { std::mem::transmute(int_i8) };
25+
let _: NonZeroI16 = unsafe { std::mem::transmute(int_i16) };
26+
let _: NonZeroI32 = unsafe { std::mem::transmute(int_i32) };
27+
let _: NonZeroI64 = unsafe { std::mem::transmute(int_i64) };
28+
let _: NonZeroI128 = unsafe { std::mem::transmute(int_i128) };
29+
30+
let _: NonZeroU8 = unsafe { NonZeroU8::new_unchecked(int_u8) };
31+
let _: NonZeroU16 = unsafe { NonZeroU16::new_unchecked(int_u16) };
32+
let _: NonZeroU32 = unsafe { NonZeroU32::new_unchecked(int_u32) };
33+
let _: NonZeroU64 = unsafe { NonZeroU64::new_unchecked(int_u64) };
34+
let _: NonZeroU128 = unsafe { NonZeroU128::new_unchecked(int_u128) };
35+
36+
let _: NonZeroI8 = unsafe { NonZeroI8::new_unchecked(int_i8) };
37+
let _: NonZeroI16 = unsafe { NonZeroI16::new_unchecked(int_i16) };
38+
let _: NonZeroI32 = unsafe { NonZeroI32::new_unchecked(int_i32) };
39+
let _: NonZeroI64 = unsafe { NonZeroI64::new_unchecked(int_i64) };
40+
let _: NonZeroI128 = unsafe { NonZeroI128::new_unchecked(int_i128) };
41+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
error: transmute from a `u8` to a `NonZeroU8`
2+
--> $DIR/transmute_int_to_non_zero.rs:18:33
3+
|
4+
LL | let _: NonZeroU8 = unsafe { std::mem::transmute(int_u8) };
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroU8::new_unchecked(int_u8)`
6+
|
7+
= note: `-D clippy::transmute-int-to-non-zero` implied by `-D warnings`
8+
9+
error: transmute from a `u16` to a `NonZeroU16`
10+
--> $DIR/transmute_int_to_non_zero.rs:19:34
11+
|
12+
LL | let _: NonZeroU16 = unsafe { std::mem::transmute(int_u16) };
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroU16::new_unchecked(int_u16)`
14+
15+
error: transmute from a `u32` to a `NonZeroU32`
16+
--> $DIR/transmute_int_to_non_zero.rs:20:34
17+
|
18+
LL | let _: NonZeroU32 = unsafe { std::mem::transmute(int_u32) };
19+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroU32::new_unchecked(int_u32)`
20+
21+
error: transmute from a `u64` to a `NonZeroU64`
22+
--> $DIR/transmute_int_to_non_zero.rs:21:34
23+
|
24+
LL | let _: NonZeroU64 = unsafe { std::mem::transmute(int_u64) };
25+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroU64::new_unchecked(int_u64)`
26+
27+
error: transmute from a `u128` to a `NonZeroU128`
28+
--> $DIR/transmute_int_to_non_zero.rs:22:35
29+
|
30+
LL | let _: NonZeroU128 = unsafe { std::mem::transmute(int_u128) };
31+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroU128::new_unchecked(int_u128)`
32+
33+
error: transmute from a `i8` to a `NonZeroI8`
34+
--> $DIR/transmute_int_to_non_zero.rs:24:33
35+
|
36+
LL | let _: NonZeroI8 = unsafe { std::mem::transmute(int_i8) };
37+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroI8::new_unchecked(int_i8)`
38+
39+
error: transmute from a `i16` to a `NonZeroI16`
40+
--> $DIR/transmute_int_to_non_zero.rs:25:34
41+
|
42+
LL | let _: NonZeroI16 = unsafe { std::mem::transmute(int_i16) };
43+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroI16::new_unchecked(int_i16)`
44+
45+
error: transmute from a `i32` to a `NonZeroI32`
46+
--> $DIR/transmute_int_to_non_zero.rs:26:34
47+
|
48+
LL | let _: NonZeroI32 = unsafe { std::mem::transmute(int_i32) };
49+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroI32::new_unchecked(int_i32)`
50+
51+
error: transmute from a `i64` to a `NonZeroI64`
52+
--> $DIR/transmute_int_to_non_zero.rs:27:34
53+
|
54+
LL | let _: NonZeroI64 = unsafe { std::mem::transmute(int_i64) };
55+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroI64::new_unchecked(int_i64)`
56+
57+
error: transmute from a `i128` to a `NonZeroI128`
58+
--> $DIR/transmute_int_to_non_zero.rs:28:35
59+
|
60+
LL | let _: NonZeroI128 = unsafe { std::mem::transmute(int_i128) };
61+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `NonZeroI128::new_unchecked(int_i128)`
62+
63+
error: aborting due to 10 previous errors
64+

0 commit comments

Comments
 (0)