Skip to content

Commit cbf3ef8

Browse files
committed
Restriction lint for casts from function pointer to *any* numeric type
1 parent ef2e2f0 commit cbf3ef8

File tree

6 files changed

+214
-0
lines changed

6 files changed

+214
-0
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -2671,6 +2671,7 @@ Released 2018-09-13
26712671
[`fn_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_address_comparisons
26722672
[`fn_params_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_params_excessive_bools
26732673
[`fn_to_numeric_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast
2674+
[`fn_to_numeric_cast_any`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast_any
26742675
[`fn_to_numeric_cast_with_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast_with_truncation
26752676
[`for_kv_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_kv_map
26762677
[`for_loops_over_fallibles`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loops_over_fallibles
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
use clippy_utils::diagnostics::span_lint_and_sugg;
2+
use clippy_utils::source::snippet_with_applicability;
3+
use rustc_errors::Applicability;
4+
use rustc_hir::Expr;
5+
use rustc_lint::LateContext;
6+
use rustc_middle::ty::{self, Ty};
7+
8+
use super::FN_TO_NUMERIC_CAST_ANY;
9+
10+
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
11+
// We only want to check casts to `ty::Uint` or `ty::Int`
12+
match cast_to.kind() {
13+
ty::Uint(_) | ty::Int(..) => { /* continue on */ },
14+
_ => return,
15+
}
16+
17+
match cast_from.kind() {
18+
ty::FnDef(..) | ty::FnPtr(_) => {
19+
let mut applicability = Applicability::MaybeIncorrect;
20+
let from_snippet = snippet_with_applicability(cx, cast_expr.span, "x", &mut applicability);
21+
22+
span_lint_and_sugg(
23+
cx,
24+
FN_TO_NUMERIC_CAST_ANY,
25+
expr.span,
26+
&format!("casting function pointer `{}` to `{}`", from_snippet, cast_to),
27+
"try",
28+
format!("{}() as {}", from_snippet, cast_to),
29+
applicability,
30+
);
31+
},
32+
_ => {},
33+
}
34+
}

clippy_lints/src/casts/mod.rs

+33
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ mod cast_ref_to_mut;
77
mod cast_sign_loss;
88
mod char_lit_as_u8;
99
mod fn_to_numeric_cast;
10+
mod fn_to_numeric_cast_any;
1011
mod fn_to_numeric_cast_with_truncation;
1112
mod ptr_as_ptr;
1213
mod unnecessary_cast;
@@ -251,6 +252,36 @@ declare_clippy_lint! {
251252
"casting a function pointer to a numeric type not wide enough to store the address"
252253
}
253254

255+
declare_clippy_lint! {
256+
/// ### What it does
257+
/// Checks for casts of a function pointer to **any** numeric type.
258+
///
259+
/// ### Why is this bad?
260+
/// Casting a function pointer to an integer can have surprising results and can occur
261+
/// accidentally if parantheses are omitted from a function call. If you aren't doing anything
262+
/// low-level with function pointers then you can opt-out of casting functions to integers to
263+
/// avoid mistakes. Alternatively, you can use this lint to audit all uses of function pointer
264+
/// to integer casts in your code.
265+
///
266+
/// ### Example
267+
/// ```rust
268+
/// // Bad: forgot to call `fn1` before casting to `usize`
269+
/// fn fn1() -> u16 {
270+
/// 1
271+
/// };
272+
/// let _ = fn1 as usize;
273+
///
274+
/// // Good: lint ensures that you remember to call `fn2`
275+
/// fn fn2() -> u16 {
276+
/// 1
277+
/// };
278+
/// let _ = fn2() as usize;
279+
/// ```
280+
pub FN_TO_NUMERIC_CAST_ANY,
281+
restriction,
282+
"casting a function pointer to any numeric type"
283+
}
284+
254285
declare_clippy_lint! {
255286
/// ### What it does
256287
/// Checks for casts of `&T` to `&mut T` anywhere in the code.
@@ -361,6 +392,7 @@ impl_lint_pass!(Casts => [
361392
CAST_PTR_ALIGNMENT,
362393
UNNECESSARY_CAST,
363394
FN_TO_NUMERIC_CAST,
395+
FN_TO_NUMERIC_CAST_ANY,
364396
FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
365397
CHAR_LIT_AS_U8,
366398
PTR_AS_PTR,
@@ -385,6 +417,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
385417
return;
386418
}
387419

420+
fn_to_numeric_cast_any::check(cx, expr, cast_expr, cast_from, cast_to);
388421
fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to);
389422
fn_to_numeric_cast_with_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
390423
if cast_from.is_numeric() && cast_to.is_numeric() && !in_external_macro(cx.sess(), expr.span) {

clippy_lints/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
570570
casts::CAST_SIGN_LOSS,
571571
casts::CHAR_LIT_AS_U8,
572572
casts::FN_TO_NUMERIC_CAST,
573+
casts::FN_TO_NUMERIC_CAST_ANY,
573574
casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
574575
casts::PTR_AS_PTR,
575576
casts::UNNECESSARY_CAST,
@@ -1016,6 +1017,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
10161017
LintId::of(as_conversions::AS_CONVERSIONS),
10171018
LintId::of(asm_syntax::INLINE_ASM_X86_ATT_SYNTAX),
10181019
LintId::of(asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX),
1020+
LintId::of(casts::FN_TO_NUMERIC_CAST_ANY),
10191021
LintId::of(create_dir::CREATE_DIR),
10201022
LintId::of(dbg_macro::DBG_MACRO),
10211023
LintId::of(default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK),

tests/ui/fn_to_numeric_cast_any.rs

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#![warn(clippy::fn_to_numeric_cast_any)]
2+
#![allow(clippy::fn_to_numeric_cast, clippy::fn_to_numeric_cast_with_truncation)]
3+
4+
fn foo() -> u8 {
5+
0
6+
}
7+
8+
trait Trait {
9+
fn static_method() -> u32 {
10+
2
11+
}
12+
}
13+
14+
struct Struct;
15+
16+
impl Trait for Struct {}
17+
18+
fn test_function_to_numeric_cast_any() {
19+
let _ = foo as i8;
20+
let _ = foo as i16;
21+
let _ = foo as i32;
22+
let _ = foo as i64;
23+
let _ = foo as i128;
24+
let _ = foo as isize;
25+
26+
let _ = foo as u8;
27+
let _ = foo as u16;
28+
let _ = foo as u32;
29+
let _ = foo as u64;
30+
let _ = foo as u128;
31+
let _ = foo as usize;
32+
33+
// Cast `f` (a `FnDef`) to `fn()` should not warn
34+
fn f() {}
35+
let _ = f as fn();
36+
}
37+
38+
fn test_trait_static_method_to_numeric_cast_any() {
39+
let _ = Struct::static_method as usize;
40+
}
41+
42+
fn fn_with_fn_args(f: fn(i32) -> u32) -> usize {
43+
f as usize
44+
}
45+
46+
fn fn_with_generic_static_trait_method<T: Trait>() -> usize {
47+
T::static_method as usize
48+
}
49+
50+
fn main() {}
+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
error: casting function pointer `foo` to `i8`
2+
--> $DIR/fn_to_numeric_cast_any.rs:19:13
3+
|
4+
LL | let _ = foo as i8;
5+
| ^^^^^^^^^ help: try: `foo() as i8`
6+
|
7+
= note: `-D clippy::fn-to-numeric-cast-any` implied by `-D warnings`
8+
9+
error: casting function pointer `foo` to `i16`
10+
--> $DIR/fn_to_numeric_cast_any.rs:20:13
11+
|
12+
LL | let _ = foo as i16;
13+
| ^^^^^^^^^^ help: try: `foo() as i16`
14+
15+
error: casting function pointer `foo` to `i32`
16+
--> $DIR/fn_to_numeric_cast_any.rs:21:13
17+
|
18+
LL | let _ = foo as i32;
19+
| ^^^^^^^^^^ help: try: `foo() as i32`
20+
21+
error: casting function pointer `foo` to `i64`
22+
--> $DIR/fn_to_numeric_cast_any.rs:22:13
23+
|
24+
LL | let _ = foo as i64;
25+
| ^^^^^^^^^^ help: try: `foo() as i64`
26+
27+
error: casting function pointer `foo` to `i128`
28+
--> $DIR/fn_to_numeric_cast_any.rs:23:13
29+
|
30+
LL | let _ = foo as i128;
31+
| ^^^^^^^^^^^ help: try: `foo() as i128`
32+
33+
error: casting function pointer `foo` to `isize`
34+
--> $DIR/fn_to_numeric_cast_any.rs:24:13
35+
|
36+
LL | let _ = foo as isize;
37+
| ^^^^^^^^^^^^ help: try: `foo() as isize`
38+
39+
error: casting function pointer `foo` to `u8`
40+
--> $DIR/fn_to_numeric_cast_any.rs:26:13
41+
|
42+
LL | let _ = foo as u8;
43+
| ^^^^^^^^^ help: try: `foo() as u8`
44+
45+
error: casting function pointer `foo` to `u16`
46+
--> $DIR/fn_to_numeric_cast_any.rs:27:13
47+
|
48+
LL | let _ = foo as u16;
49+
| ^^^^^^^^^^ help: try: `foo() as u16`
50+
51+
error: casting function pointer `foo` to `u32`
52+
--> $DIR/fn_to_numeric_cast_any.rs:28:13
53+
|
54+
LL | let _ = foo as u32;
55+
| ^^^^^^^^^^ help: try: `foo() as u32`
56+
57+
error: casting function pointer `foo` to `u64`
58+
--> $DIR/fn_to_numeric_cast_any.rs:29:13
59+
|
60+
LL | let _ = foo as u64;
61+
| ^^^^^^^^^^ help: try: `foo() as u64`
62+
63+
error: casting function pointer `foo` to `u128`
64+
--> $DIR/fn_to_numeric_cast_any.rs:30:13
65+
|
66+
LL | let _ = foo as u128;
67+
| ^^^^^^^^^^^ help: try: `foo() as u128`
68+
69+
error: casting function pointer `foo` to `usize`
70+
--> $DIR/fn_to_numeric_cast_any.rs:31:13
71+
|
72+
LL | let _ = foo as usize;
73+
| ^^^^^^^^^^^^ help: try: `foo() as usize`
74+
75+
error: casting function pointer `Struct::static_method` to `usize`
76+
--> $DIR/fn_to_numeric_cast_any.rs:39:13
77+
|
78+
LL | let _ = Struct::static_method as usize;
79+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Struct::static_method() as usize`
80+
81+
error: casting function pointer `f` to `usize`
82+
--> $DIR/fn_to_numeric_cast_any.rs:43:5
83+
|
84+
LL | f as usize
85+
| ^^^^^^^^^^ help: try: `f() as usize`
86+
87+
error: casting function pointer `T::static_method` to `usize`
88+
--> $DIR/fn_to_numeric_cast_any.rs:47:5
89+
|
90+
LL | T::static_method as usize
91+
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `T::static_method() as usize`
92+
93+
error: aborting due to 15 previous errors
94+

0 commit comments

Comments
 (0)