diff --git a/clippy_lints/src/non_zero_suggestions.rs b/clippy_lints/src/non_zero_suggestions.rs index b9e7053db90..715d9eda6ee 100644 --- a/clippy_lints/src/non_zero_suggestions.rs +++ b/clippy_lints/src/non_zero_suggestions.rs @@ -9,16 +9,34 @@ declare_clippy_lint! { /// ### What it does + /// Checks for conversions from `NonZero` types to regular integer types, + /// and suggests using `NonZero` types for the target as well. /// /// ### Why is this bad? + /// Converting from `NonZero` types to regular integer types and then back to `NonZero` + /// types is less efficient and loses the type-safety guarantees provided by `NonZero` types. + /// Using `NonZero` types consistently can lead to more optimized code and prevent + /// certain classes of errors related to zero values. /// /// ### Example /// ```no_run - /// // example code where clippy issues a warning + /// use std::num::{NonZeroU32, NonZeroU64}; + /// + /// fn example(x: u64, y: NonZeroU32) { + /// // Bad: Converting NonZeroU32 to u64 unnecessarily + /// let r1 = x / u64::from(y.get()); + /// let r2 = x % u64::from(y.get()); + /// } /// ``` /// Use instead: /// ```no_run - /// // example code which does not raise clippy warning + /// use std::num::{NonZeroU32, NonZeroU64}; + /// + /// fn example(x: u64, y: NonZeroU32) { + /// // Good: Preserving the NonZero property + /// let r1 = x / NonZeroU64::from(y); + /// let r2 = x % NonZeroU64::from(y); + /// } /// ``` #[clippy::version = "1.81.0"] pub NON_ZERO_SUGGESTIONS, diff --git a/tests/ui/non_zero_suggestions.fixed b/tests/ui/non_zero_suggestions.fixed new file mode 100644 index 00000000000..b33de1ef03f --- /dev/null +++ b/tests/ui/non_zero_suggestions.fixed @@ -0,0 +1,69 @@ +#![warn(clippy::non_zero_suggestions)] + +use std::num::{NonZeroI16, NonZeroI8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize}; + +fn main() { + // Positive test cases (lint should trigger) + + // U32 -> U64 + let x: u64 = 100; + let y = NonZeroU32::new(10).unwrap(); + let r1 = x / NonZeroU64::from(y); + let r2 = x % NonZeroU64::from(y); + + // U16 -> U32 + let a: u32 = 50; + let b = NonZeroU16::new(5).unwrap(); + let r3 = a / NonZeroU32::from(b); + + // I8 -> I16 + let c: i16 = 25; + let d = NonZeroI8::new(3).unwrap(); + let r4 = NonZeroI16::from(d); + + // Different operations + let m: u64 = 400; + let n = NonZeroU32::new(20).unwrap(); + let r5 = m / NonZeroU64::from(n); + + // Edge cases + + // Using the max value of a type + let max_u32 = NonZeroU32::new(u32::MAX).unwrap(); + let r6 = NonZeroU64::from(max_u32); + + // Chained method calls + let _ = NonZeroU64::from(NonZeroU32::new(10).unwrap()); + + // Negative test cases (lint should not trigger) + + // Same size types + let e: u32 = 200; + let f = NonZeroU32::new(20).unwrap(); + let r10 = e / f.get(); + + // Smaller to larger, but not NonZero + let g: u64 = 1000; + let h: u32 = 50; + let r11 = g / u64::from(h); + + // Using From correctly + let k: u64 = 300; + let l = NonZeroU32::new(15).unwrap(); + let r12 = k / NonZeroU64::from(l); +} + +// Additional function to test the lint in a different context +fn divide_numbers(x: u64, y: NonZeroU32) -> u64 { + x / NonZeroU64::from(y) +} + +struct Calculator { + value: u64, +} + +impl Calculator { + fn divide(&self, divisor: NonZeroU32) -> u64 { + self.value / NonZeroU64::from(divisor) + } +} diff --git a/tests/ui/non_zero_suggestions.rs b/tests/ui/non_zero_suggestions.rs index 1a5ee40dc3d..27089eaf86e 100644 --- a/tests/ui/non_zero_suggestions.rs +++ b/tests/ui/non_zero_suggestions.rs @@ -1,52 +1,69 @@ #![warn(clippy::non_zero_suggestions)] -use std::num::{ - NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128, NonZeroU16, NonZeroU32, - NonZeroU64, NonZeroU8, NonZeroUsize, -}; +use std::num::{NonZeroI16, NonZeroI8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize}; fn main() { - // Basic cases - let _ = u8::try_from(NonZeroU8::new(5).unwrap().get()); + // Positive test cases (lint should trigger) - let _ = u16::from(NonZeroU16::new(10).unwrap().get()); + // U32 -> U64 + let x: u64 = 100; + let y = NonZeroU32::new(10).unwrap(); + let r1 = x / u64::from(y.get()); + let r2 = x % u64::from(y.get()); - // Different integer types - let _ = u32::from(NonZeroU32::new(15).unwrap().get()); + // U16 -> U32 + let a: u32 = 50; + let b = NonZeroU16::new(5).unwrap(); + let r3 = a / u32::from(b.get()); - let _ = u64::from(NonZeroU64::new(20).unwrap().get()); + // I8 -> I16 + let c: i16 = 25; + let d = NonZeroI8::new(3).unwrap(); + let r4 = i16::from(d.get()); - let _ = u128::from(NonZeroU128::new(25).unwrap().get()); - - let _ = usize::from(NonZeroUsize::new(30).unwrap().get()); - - // Signed integer types - let _ = i8::try_from(NonZeroI8::new(-5).unwrap().get()); - - let _ = i16::from(NonZeroI16::new(-10).unwrap().get()); - - let _ = i32::from(NonZeroI32::new(-15).unwrap().get()); + // Different operations + let m: u64 = 400; + let n = NonZeroU32::new(20).unwrap(); + let r5 = m / u64::from(n.get()); // Edge cases - // Complex expression - let _ = u8::from(NonZeroU8::new(5).unwrap().get() + 1); + // Using the max value of a type + let max_u32 = NonZeroU32::new(u32::MAX).unwrap(); + let r6 = u64::from(max_u32.get()); - // Function call - fn get_non_zero() -> NonZeroU8 { - NonZeroU8::new(42).unwrap() - } - let _ = u8::from(get_non_zero().get()); + // Chained method calls + let _ = u64::from(NonZeroU32::new(10).unwrap().get()); - // Method chaining - let _ = u16::from(NonZeroU16::new(100).unwrap().get().checked_add(1).unwrap()); - // This should not trigger the lint + // Negative test cases (lint should not trigger) - // Different conversion methods - let _ = u32::try_from(NonZeroU32::new(200).unwrap().get()).unwrap(); + // Same size types + let e: u32 = 200; + let f = NonZeroU32::new(20).unwrap(); + let r10 = e / f.get(); - // Cases that should not trigger the lint - let _ = u8::from(5); - let _ = u16::from(10u8); - let _ = i32::try_from(40u32).unwrap(); + // Smaller to larger, but not NonZero + let g: u64 = 1000; + let h: u32 = 50; + let r11 = g / u64::from(h); + + // Using From correctly + let k: u64 = 300; + let l = NonZeroU32::new(15).unwrap(); + let r12 = k / NonZeroU64::from(l); +} + +// Additional function to test the lint in a different context +fn divide_numbers(x: u64, y: NonZeroU32) -> u64 { + x / u64::from(y.get()) +} + +struct Calculator { + value: u64, +} + +impl Calculator { + fn divide(&self, divisor: NonZeroU32) -> u64 { + self.value / u64::from(divisor.get()) + } } diff --git a/tests/ui/non_zero_suggestions.stderr b/tests/ui/non_zero_suggestions.stderr new file mode 100644 index 00000000000..b30f9dd1e5c --- /dev/null +++ b/tests/ui/non_zero_suggestions.stderr @@ -0,0 +1,59 @@ +error: Consider using `NonZeroU64::from()` for more efficient and type-safe conversion + --> tests/ui/non_zero_suggestions.rs:11:18 + | +LL | let r1 = x / u64::from(y.get()); + | ^^^^^^^^^^^^^^^^^^ help: Replace with: `NonZeroU64::from(y)` + | + = note: `-D clippy::non-zero-suggestions` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::non_zero_suggestions)]` + +error: Consider using `NonZeroU64::from()` for more efficient and type-safe conversion + --> tests/ui/non_zero_suggestions.rs:12:18 + | +LL | let r2 = x % u64::from(y.get()); + | ^^^^^^^^^^^^^^^^^^ help: Replace with: `NonZeroU64::from(y)` + +error: Consider using `NonZeroU32::from()` for more efficient and type-safe conversion + --> tests/ui/non_zero_suggestions.rs:17:18 + | +LL | let r3 = a / u32::from(b.get()); + | ^^^^^^^^^^^^^^^^^^ help: Replace with: `NonZeroU32::from(b)` + +error: Consider using `NonZeroI16::from()` for more efficient and type-safe conversion + --> tests/ui/non_zero_suggestions.rs:22:14 + | +LL | let r4 = i16::from(d.get()); + | ^^^^^^^^^^^^^^^^^^ help: Replace with: `NonZeroI16::from(d)` + +error: Consider using `NonZeroU64::from()` for more efficient and type-safe conversion + --> tests/ui/non_zero_suggestions.rs:27:18 + | +LL | let r5 = m / u64::from(n.get()); + | ^^^^^^^^^^^^^^^^^^ help: Replace with: `NonZeroU64::from(n)` + +error: Consider using `NonZeroU64::from()` for more efficient and type-safe conversion + --> tests/ui/non_zero_suggestions.rs:33:14 + | +LL | let r6 = u64::from(max_u32.get()); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Replace with: `NonZeroU64::from(max_u32)` + +error: Consider using `NonZeroU64::from()` for more efficient and type-safe conversion + --> tests/ui/non_zero_suggestions.rs:36:13 + | +LL | let _ = u64::from(NonZeroU32::new(10).unwrap().get()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: Replace with: `NonZeroU64::from(NonZeroU32::new(10).unwrap())` + +error: Consider using `NonZeroU64::from()` for more efficient and type-safe conversion + --> tests/ui/non_zero_suggestions.rs:58:9 + | +LL | x / u64::from(y.get()) + | ^^^^^^^^^^^^^^^^^^ help: Replace with: `NonZeroU64::from(y)` + +error: Consider using `NonZeroU64::from()` for more efficient and type-safe conversion + --> tests/ui/non_zero_suggestions.rs:67:22 + | +LL | self.value / u64::from(divisor.get()) + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: Replace with: `NonZeroU64::from(divisor)` + +error: aborting due to 9 previous errors +