From dcfc6a20dba6333852c5b55741b47c6f9d2c8db4 Mon Sep 17 00:00:00 2001 From: Centri3 <114838443+Centri3@users.noreply.github.com> Date: Fri, 16 Jun 2023 22:01:15 -0500 Subject: [PATCH 1/2] check for fully qualified paths in `unnecessary_cast` --- clippy_lints/src/casts/unnecessary_cast.rs | 11 ++- tests/ui/unnecessary_cast.fixed | 17 +++++ tests/ui/unnecessary_cast.rs | 17 +++++ tests/ui/unnecessary_cast.stderr | 88 ++++++++++++---------- 4 files changed, 93 insertions(+), 40 deletions(-) diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index 4b45b0c9eac..9130f5df02f 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -254,13 +254,20 @@ fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx // function's declaration snippet is exactly equal to the `Ty`. That way, we can // see whether it's a type alias. // - // Will this work for more complex types? Probably not! + // FIXME: This won't work if the type is given an alias through `use`, should we + // consider this a type alias as well? if !snippet .split("->") .skip(1) .map(|s| { s.trim() == cast_from.to_string() - || s.split("where").any(|ty| ty.trim() == cast_from.to_string()) + || s.trim().contains(&format!("::{cast_from}")) + || s.split("where").any(|ty| { + ty.trim() == cast_from.to_string() + || ty.trim() == cast_from.to_string() + // Fully qualified path, or something silly like `::u32` + || s.trim().contains(&format!("::{cast_from}")) + }) }) .any(|a| a) { diff --git a/tests/ui/unnecessary_cast.fixed b/tests/ui/unnecessary_cast.fixed index 8efd44baf59..2bf02f13414 100644 --- a/tests/ui/unnecessary_cast.fixed +++ b/tests/ui/unnecessary_cast.fixed @@ -38,6 +38,16 @@ mod fake_libc { } } +fn aaa() -> ::std::primitive::u32 { + 0 +} + +use std::primitive::u32 as UnsignedThirtyTwoBitInteger; + +fn bbb() -> UnsignedThirtyTwoBitInteger { + 0 +} + #[rustfmt::skip] fn main() { // Test cast_unnecessary @@ -105,6 +115,13 @@ fn main() { extern_fake_libc::getpid_SAFE_TRUTH() as i32; let pid = unsafe { fake_libc::getpid() }; pid as i32; + aaa(); + let x = aaa(); + aaa(); + // Will not lint currently. + bbb() as u32; + let x = bbb(); + bbb() as u32; let i8_ptr: *const i8 = &1; let u8_ptr: *const u8 = &1; diff --git a/tests/ui/unnecessary_cast.rs b/tests/ui/unnecessary_cast.rs index c7723ef51f9..25b6b0f9b07 100644 --- a/tests/ui/unnecessary_cast.rs +++ b/tests/ui/unnecessary_cast.rs @@ -38,6 +38,16 @@ mod fake_libc { } } +fn aaa() -> ::std::primitive::u32 { + 0 +} + +use std::primitive::u32 as UnsignedThirtyTwoBitInteger; + +fn bbb() -> UnsignedThirtyTwoBitInteger { + 0 +} + #[rustfmt::skip] fn main() { // Test cast_unnecessary @@ -105,6 +115,13 @@ fn main() { extern_fake_libc::getpid_SAFE_TRUTH() as i32; let pid = unsafe { fake_libc::getpid() }; pid as i32; + aaa() as u32; + let x = aaa(); + aaa() as u32; + // Will not lint currently. + bbb() as u32; + let x = bbb(); + bbb() as u32; let i8_ptr: *const i8 = &1; let u8_ptr: *const u8 = &1; diff --git a/tests/ui/unnecessary_cast.stderr b/tests/ui/unnecessary_cast.stderr index f0443556fb4..19411a01b67 100644 --- a/tests/ui/unnecessary_cast.stderr +++ b/tests/ui/unnecessary_cast.stderr @@ -7,226 +7,238 @@ LL | ptr as *const T = note: `-D clippy::unnecessary-cast` implied by `-D warnings` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast.rs:44:5 + --> $DIR/unnecessary_cast.rs:54:5 | LL | 1i32 as i32; | ^^^^^^^^^^^ help: try: `1_i32` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast.rs:45:5 + --> $DIR/unnecessary_cast.rs:55:5 | LL | 1f32 as f32; | ^^^^^^^^^^^ help: try: `1_f32` error: casting to the same type is unnecessary (`bool` -> `bool`) - --> $DIR/unnecessary_cast.rs:46:5 + --> $DIR/unnecessary_cast.rs:56:5 | LL | false as bool; | ^^^^^^^^^^^^^ help: try: `false` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast.rs:49:5 + --> $DIR/unnecessary_cast.rs:59:5 | LL | -1_i32 as i32; | ^^^^^^^^^^^^^ help: try: `-1_i32` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast.rs:50:5 + --> $DIR/unnecessary_cast.rs:60:5 | LL | - 1_i32 as i32; | ^^^^^^^^^^^^^^ help: try: `- 1_i32` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast.rs:51:5 + --> $DIR/unnecessary_cast.rs:61:5 | LL | -1f32 as f32; | ^^^^^^^^^^^^ help: try: `-1_f32` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast.rs:52:5 + --> $DIR/unnecessary_cast.rs:62:5 | LL | 1_i32 as i32; | ^^^^^^^^^^^^ help: try: `1_i32` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast.rs:53:5 + --> $DIR/unnecessary_cast.rs:63:5 | LL | 1_f32 as f32; | ^^^^^^^^^^^^ help: try: `1_f32` error: casting raw pointers to the same type and constness is unnecessary (`*const u8` -> `*const u8`) - --> $DIR/unnecessary_cast.rs:55:22 + --> $DIR/unnecessary_cast.rs:65:22 | LL | let _: *mut u8 = [1u8, 2].as_ptr() as *const u8 as *mut u8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `[1u8, 2].as_ptr()` error: casting raw pointers to the same type and constness is unnecessary (`*const u8` -> `*const u8`) - --> $DIR/unnecessary_cast.rs:57:5 + --> $DIR/unnecessary_cast.rs:67:5 | LL | [1u8, 2].as_ptr() as *const u8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `[1u8, 2].as_ptr()` error: casting raw pointers to the same type and constness is unnecessary (`*mut u8` -> `*mut u8`) - --> $DIR/unnecessary_cast.rs:59:5 + --> $DIR/unnecessary_cast.rs:69:5 | LL | [1u8, 2].as_mut_ptr() as *mut u8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `[1u8, 2].as_mut_ptr()` error: casting raw pointers to the same type and constness is unnecessary (`*const u32` -> `*const u32`) - --> $DIR/unnecessary_cast.rs:70:5 + --> $DIR/unnecessary_cast.rs:80:5 | LL | owo::([1u32].as_ptr()) as *const u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `owo::([1u32].as_ptr())` error: casting raw pointers to the same type and constness is unnecessary (`*const u8` -> `*const u8`) - --> $DIR/unnecessary_cast.rs:71:5 + --> $DIR/unnecessary_cast.rs:81:5 | LL | uwu::([1u32].as_ptr()) as *const u8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `uwu::([1u32].as_ptr())` error: casting raw pointers to the same type and constness is unnecessary (`*const u32` -> `*const u32`) - --> $DIR/unnecessary_cast.rs:73:5 + --> $DIR/unnecessary_cast.rs:83:5 | LL | uwu::([1u32].as_ptr()) as *const u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `uwu::([1u32].as_ptr())` +error: casting to the same type is unnecessary (`u32` -> `u32`) + --> $DIR/unnecessary_cast.rs:118:5 + | +LL | aaa() as u32; + | ^^^^^^^^^^^^ help: try: `aaa()` + +error: casting to the same type is unnecessary (`u32` -> `u32`) + --> $DIR/unnecessary_cast.rs:120:5 + | +LL | aaa() as u32; + | ^^^^^^^^^^^^ help: try: `aaa()` + error: casting integer literal to `f32` is unnecessary - --> $DIR/unnecessary_cast.rs:139:9 + --> $DIR/unnecessary_cast.rs:156:9 | LL | 100 as f32; | ^^^^^^^^^^ help: try: `100_f32` error: casting integer literal to `f64` is unnecessary - --> $DIR/unnecessary_cast.rs:140:9 + --> $DIR/unnecessary_cast.rs:157:9 | LL | 100 as f64; | ^^^^^^^^^^ help: try: `100_f64` error: casting integer literal to `f64` is unnecessary - --> $DIR/unnecessary_cast.rs:141:9 + --> $DIR/unnecessary_cast.rs:158:9 | LL | 100_i32 as f64; | ^^^^^^^^^^^^^^ help: try: `100_f64` error: casting integer literal to `f32` is unnecessary - --> $DIR/unnecessary_cast.rs:142:17 + --> $DIR/unnecessary_cast.rs:159:17 | LL | let _ = -100 as f32; | ^^^^^^^^^^^ help: try: `-100_f32` error: casting integer literal to `f64` is unnecessary - --> $DIR/unnecessary_cast.rs:143:17 + --> $DIR/unnecessary_cast.rs:160:17 | LL | let _ = -100 as f64; | ^^^^^^^^^^^ help: try: `-100_f64` error: casting integer literal to `f64` is unnecessary - --> $DIR/unnecessary_cast.rs:144:17 + --> $DIR/unnecessary_cast.rs:161:17 | LL | let _ = -100_i32 as f64; | ^^^^^^^^^^^^^^^ help: try: `-100_f64` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast.rs:145:9 + --> $DIR/unnecessary_cast.rs:162:9 | LL | 100. as f32; | ^^^^^^^^^^^ help: try: `100_f32` error: casting float literal to `f64` is unnecessary - --> $DIR/unnecessary_cast.rs:146:9 + --> $DIR/unnecessary_cast.rs:163:9 | LL | 100. as f64; | ^^^^^^^^^^^ help: try: `100_f64` error: casting integer literal to `u32` is unnecessary - --> $DIR/unnecessary_cast.rs:158:9 + --> $DIR/unnecessary_cast.rs:175:9 | LL | 1 as u32; | ^^^^^^^^ help: try: `1_u32` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast.rs:159:9 + --> $DIR/unnecessary_cast.rs:176:9 | LL | 0x10 as i32; | ^^^^^^^^^^^ help: try: `0x10_i32` error: casting integer literal to `usize` is unnecessary - --> $DIR/unnecessary_cast.rs:160:9 + --> $DIR/unnecessary_cast.rs:177:9 | LL | 0b10 as usize; | ^^^^^^^^^^^^^ help: try: `0b10_usize` error: casting integer literal to `u16` is unnecessary - --> $DIR/unnecessary_cast.rs:161:9 + --> $DIR/unnecessary_cast.rs:178:9 | LL | 0o73 as u16; | ^^^^^^^^^^^ help: try: `0o73_u16` error: casting integer literal to `u32` is unnecessary - --> $DIR/unnecessary_cast.rs:162:9 + --> $DIR/unnecessary_cast.rs:179:9 | LL | 1_000_000_000 as u32; | ^^^^^^^^^^^^^^^^^^^^ help: try: `1_000_000_000_u32` error: casting float literal to `f64` is unnecessary - --> $DIR/unnecessary_cast.rs:164:9 + --> $DIR/unnecessary_cast.rs:181:9 | LL | 1.0 as f64; | ^^^^^^^^^^ help: try: `1.0_f64` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast.rs:165:9 + --> $DIR/unnecessary_cast.rs:182:9 | LL | 0.5 as f32; | ^^^^^^^^^^ help: try: `0.5_f32` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast.rs:169:17 + --> $DIR/unnecessary_cast.rs:186:17 | LL | let _ = -1 as i32; | ^^^^^^^^^ help: try: `-1_i32` error: casting float literal to `f32` is unnecessary - --> $DIR/unnecessary_cast.rs:170:17 + --> $DIR/unnecessary_cast.rs:187:17 | LL | let _ = -1.0 as f32; | ^^^^^^^^^^^ help: try: `-1.0_f32` error: casting to the same type is unnecessary (`i32` -> `i32`) - --> $DIR/unnecessary_cast.rs:176:18 + --> $DIR/unnecessary_cast.rs:193:18 | LL | let _ = &(x as i32); | ^^^^^^^^^^ help: try: `{ x }` error: casting integer literal to `i32` is unnecessary - --> $DIR/unnecessary_cast.rs:182:22 + --> $DIR/unnecessary_cast.rs:199:22 | LL | let _: i32 = -(1) as i32; | ^^^^^^^^^^^ help: try: `-1_i32` error: casting integer literal to `i64` is unnecessary - --> $DIR/unnecessary_cast.rs:184:22 + --> $DIR/unnecessary_cast.rs:201:22 | LL | let _: i64 = -(1) as i64; | ^^^^^^^^^^^ help: try: `-1_i64` error: casting float literal to `f64` is unnecessary - --> $DIR/unnecessary_cast.rs:191:22 + --> $DIR/unnecessary_cast.rs:208:22 | LL | let _: f64 = (-8.0 as f64).exp(); | ^^^^^^^^^^^^^ help: try: `(-8.0_f64)` error: casting float literal to `f64` is unnecessary - --> $DIR/unnecessary_cast.rs:193:23 + --> $DIR/unnecessary_cast.rs:210:23 | LL | let _: f64 = -(8.0 as f64).exp(); // should suggest `-8.0_f64.exp()` here not to change code behavior | ^^^^^^^^^^^^ help: try: `8.0_f64` error: casting to the same type is unnecessary (`f32` -> `f32`) - --> $DIR/unnecessary_cast.rs:201:20 + --> $DIR/unnecessary_cast.rs:218:20 | LL | let _num = foo() as f32; | ^^^^^^^^^^^^ help: try: `foo()` -error: aborting due to 38 previous errors +error: aborting due to 40 previous errors From 15b68c24b8bccbad6b3c9ec2e60a9540573365ce Mon Sep 17 00:00:00 2001 From: Catherine <114838443+Centri3@users.noreply.github.com> Date: Thu, 29 Jun 2023 04:30:51 -0500 Subject: [PATCH 2/2] Extract the logic for if a snippet equals a type --- clippy_lints/src/casts/mod.rs | 8 +++++++ clippy_lints/src/casts/unnecessary_cast.rs | 25 +++++++++------------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index 0ac6ef6496a..d34de305f5d 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -181,6 +181,14 @@ declare_clippy_lint! { /// ### Why is this bad? /// It's just unnecessary. /// + /// ### Known problems + /// When the expression on the left is a function call, the lint considers the return type to be + /// a type alias if it's aliased through a `use` statement + /// (like `use std::io::Result as IoResult`). It will not lint such cases. + /// + /// This check is also rather primitive. It will only work on primitive types without any + /// intermediate references, raw pointers and trait objects may or may not work. + /// /// ### Example /// ```rust /// let _ = 2i32 as i32; diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index 9130f5df02f..86057bb74ee 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -85,11 +85,6 @@ pub(super) fn check<'tcx>( } } - // skip cast of fn call that returns type alias - if let ExprKind::Cast(inner, ..) = expr.kind && is_cast_from_ty_alias(cx, inner, cast_from) { - return false; - } - // skip cast to non-primitive type if_chain! { if let ExprKind::Cast(_, cast_to) = expr.kind; @@ -101,6 +96,11 @@ pub(super) fn check<'tcx>( } } + // skip cast of fn call that returns type alias + if let ExprKind::Cast(inner, ..) = expr.kind && is_cast_from_ty_alias(cx, inner, cast_from) { + return false; + } + if let Some(lit) = get_numeric_literal(cast_expr) { let literal_str = &cast_str; @@ -259,16 +259,7 @@ fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx if !snippet .split("->") .skip(1) - .map(|s| { - s.trim() == cast_from.to_string() - || s.trim().contains(&format!("::{cast_from}")) - || s.split("where").any(|ty| { - ty.trim() == cast_from.to_string() - || ty.trim() == cast_from.to_string() - // Fully qualified path, or something silly like `::u32` - || s.trim().contains(&format!("::{cast_from}")) - }) - }) + .map(|s| snippet_eq_ty(s, cast_from) || s.split("where").any(|ty| snippet_eq_ty(ty, cast_from))) .any(|a| a) { return ControlFlow::Break(()); @@ -295,3 +286,7 @@ fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx }) .is_some() } + +fn snippet_eq_ty(snippet: &str, ty: Ty<'_>) -> bool { + snippet.trim() == ty.to_string() || snippet.trim().contains(&format!("::{ty}")) +}