Skip to content

Commit 3217f8a

Browse files
committed
Auto merge of rust-lang#10942 - Centri3:unnecessary_cast, r=llogiq
Ignore more type aliases in `unnecessary_cast` This is potentially the worst code I've ever written, and even if not, it's very close to being on par with starb. This will ignore `call() as i32` and `local_obtained_from_call as i32` now. This should fix every reasonable way to reproduce rust-lang#10555, but likely not entirely. changelog: Ignore more type aliases in `unnecessary_cast`
2 parents e11f36c + 357e80e commit 3217f8a

File tree

5 files changed

+166
-45
lines changed

5 files changed

+166
-45
lines changed

clippy_lints/src/casts/unnecessary_cast.rs

+68-3
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
use clippy_utils::diagnostics::span_lint_and_sugg;
22
use clippy_utils::numeric_literal::NumericLiteral;
33
use clippy_utils::source::snippet_opt;
4-
use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, is_ty_alias, path_to_local};
4+
use clippy_utils::visitors::{for_each_expr, Visitable};
5+
use clippy_utils::{get_parent_expr, get_parent_node, is_hir_ty_cfg_dependant, is_ty_alias, path_to_local};
56
use if_chain::if_chain;
67
use rustc_ast::{LitFloatType, LitIntType, LitKind};
78
use rustc_errors::Applicability;
8-
use rustc_hir::def::Res;
9+
use rustc_hir::def::{DefKind, Res};
910
use rustc_hir::{Expr, ExprKind, Lit, Node, Path, QPath, TyKind, UnOp};
1011
use rustc_lint::{LateContext, LintContext};
1112
use rustc_middle::lint::in_external_macro;
1213
use rustc_middle::ty::{self, FloatTy, InferTy, Ty};
14+
use std::ops::ControlFlow;
1315

1416
use super::UNNECESSARY_CAST;
1517

@@ -59,7 +61,7 @@ pub(super) fn check<'tcx>(
5961
}
6062
}
6163

62-
// skip cast of local to type alias
64+
// skip cast of local that is a type alias
6365
if let ExprKind::Cast(inner, ..) = expr.kind
6466
&& let ExprKind::Path(qpath) = inner.kind
6567
&& let QPath::Resolved(None, Path { res, .. }) = qpath
@@ -83,6 +85,11 @@ pub(super) fn check<'tcx>(
8385
}
8486
}
8587

88+
// skip cast of fn call that returns type alias
89+
if let ExprKind::Cast(inner, ..) = expr.kind && is_cast_from_ty_alias(cx, inner, cast_from) {
90+
return false;
91+
}
92+
8693
// skip cast to non-primitive type
8794
if_chain! {
8895
if let ExprKind::Cast(_, cast_to) = expr.kind;
@@ -223,3 +230,61 @@ fn fp_ty_mantissa_nbits(typ: Ty<'_>) -> u32 {
223230
_ => 0,
224231
}
225232
}
233+
234+
/// Finds whether an `Expr` returns a type alias.
235+
///
236+
/// TODO: Maybe we should move this to `clippy_utils` so others won't need to go down this dark,
237+
/// dark path reimplementing this (or something similar).
238+
fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx>, cast_from: Ty<'tcx>) -> bool {
239+
for_each_expr(expr, |expr| {
240+
// Calls are a `Path`, and usage of locals are a `Path`. So, this checks
241+
// - call() as i32
242+
// - local as i32
243+
if let ExprKind::Path(qpath) = expr.kind {
244+
let res = cx.qpath_res(&qpath, expr.hir_id);
245+
// Function call
246+
if let Res::Def(DefKind::Fn, def_id) = res {
247+
let Some(snippet) = snippet_opt(cx, cx.tcx.def_span(def_id)) else {
248+
return ControlFlow::Continue(());
249+
};
250+
// This is the worst part of this entire function. This is the only way I know of to
251+
// check whether a function returns a type alias. Sure, you can get the return type
252+
// from a function in the current crate as an hir ty, but how do you get it for
253+
// external functions?? Simple: It's impossible. So, we check whether a part of the
254+
// function's declaration snippet is exactly equal to the `Ty`. That way, we can
255+
// see whether it's a type alias.
256+
//
257+
// Will this work for more complex types? Probably not!
258+
if !snippet
259+
.split("->")
260+
.skip(0)
261+
.map(|s| {
262+
s.trim() == cast_from.to_string()
263+
|| s.split("where").any(|ty| ty.trim() == cast_from.to_string())
264+
})
265+
.any(|a| a)
266+
{
267+
return ControlFlow::Break(());
268+
}
269+
// Local usage
270+
} else if let Res::Local(hir_id) = res
271+
&& let Some(parent) = get_parent_node(cx.tcx, hir_id)
272+
&& let Node::Local(l) = parent
273+
{
274+
if let Some(e) = l.init && is_cast_from_ty_alias(cx, e, cast_from) {
275+
return ControlFlow::Break::<()>(());
276+
}
277+
278+
if let Some(ty) = l.ty
279+
&& let TyKind::Path(qpath) = ty.kind
280+
&& is_ty_alias(&qpath)
281+
{
282+
return ControlFlow::Break::<()>(());
283+
}
284+
}
285+
}
286+
287+
ControlFlow::Continue(())
288+
})
289+
.is_some()
290+
}
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#![allow(nonstandard_style)]
2+
#![allow(clippy::missing_safety_doc, unused)]
3+
4+
type pid_t = i32;
5+
pub unsafe fn getpid() -> pid_t {
6+
pid_t::from(0)
7+
}
8+
pub fn getpid_SAFE_TRUTH() -> pid_t {
9+
unsafe { getpid() }
10+
}

tests/ui/unnecessary_cast.fixed

+25-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
//@run-rustfix
2+
//@aux-build:extern_fake_libc.rs
23
#![warn(clippy::unnecessary_cast)]
34
#![allow(
4-
unused,
55
clippy::borrow_as_ptr,
66
clippy::no_effect,
77
clippy::nonstandard_macro_braces,
8-
clippy::unnecessary_operation
8+
clippy::unnecessary_operation,
9+
nonstandard_style,
10+
unused
911
)]
1012

13+
extern crate extern_fake_libc;
14+
1115
type PtrConstU8 = *const u8;
1216
type PtrMutU8 = *mut u8;
1317

@@ -19,6 +23,21 @@ fn uwu<T, U>(ptr: *const T) -> *const U {
1923
ptr as *const U
2024
}
2125

26+
mod fake_libc {
27+
type pid_t = i32;
28+
pub unsafe fn getpid() -> pid_t {
29+
pid_t::from(0)
30+
}
31+
// Make sure a where clause does not break it
32+
pub fn getpid_SAFE_TRUTH<T: Clone>(t: &T) -> pid_t
33+
where
34+
T: Clone,
35+
{
36+
t;
37+
unsafe { getpid() }
38+
}
39+
}
40+
2241
#[rustfmt::skip]
2342
fn main() {
2443
// Test cast_unnecessary
@@ -82,6 +101,10 @@ fn main() {
82101
// or from
83102
let x: I32Alias = 1;
84103
let y = x as u64;
104+
fake_libc::getpid_SAFE_TRUTH(&0u32) as i32;
105+
extern_fake_libc::getpid_SAFE_TRUTH() as i32;
106+
let pid = unsafe { fake_libc::getpid() };
107+
pid as i32;
85108

86109
let i8_ptr: *const i8 = &1;
87110
let u8_ptr: *const u8 = &1;

tests/ui/unnecessary_cast.rs

+25-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
//@run-rustfix
2+
//@aux-build:extern_fake_libc.rs
23
#![warn(clippy::unnecessary_cast)]
34
#![allow(
4-
unused,
55
clippy::borrow_as_ptr,
66
clippy::no_effect,
77
clippy::nonstandard_macro_braces,
8-
clippy::unnecessary_operation
8+
clippy::unnecessary_operation,
9+
nonstandard_style,
10+
unused
911
)]
1012

13+
extern crate extern_fake_libc;
14+
1115
type PtrConstU8 = *const u8;
1216
type PtrMutU8 = *mut u8;
1317

@@ -19,6 +23,21 @@ fn uwu<T, U>(ptr: *const T) -> *const U {
1923
ptr as *const U
2024
}
2125

26+
mod fake_libc {
27+
type pid_t = i32;
28+
pub unsafe fn getpid() -> pid_t {
29+
pid_t::from(0)
30+
}
31+
// Make sure a where clause does not break it
32+
pub fn getpid_SAFE_TRUTH<T: Clone>(t: &T) -> pid_t
33+
where
34+
T: Clone,
35+
{
36+
t;
37+
unsafe { getpid() }
38+
}
39+
}
40+
2241
#[rustfmt::skip]
2342
fn main() {
2443
// Test cast_unnecessary
@@ -82,6 +101,10 @@ fn main() {
82101
// or from
83102
let x: I32Alias = 1;
84103
let y = x as u64;
104+
fake_libc::getpid_SAFE_TRUTH(&0u32) as i32;
105+
extern_fake_libc::getpid_SAFE_TRUTH() as i32;
106+
let pid = unsafe { fake_libc::getpid() };
107+
pid as i32;
85108

86109
let i8_ptr: *const i8 = &1;
87110
let u8_ptr: *const u8 = &1;

0 commit comments

Comments
 (0)