Skip to content

Commit 1e1b4e2

Browse files
authored
Merge pull request #2814 from VKlayd/fn_to_int
WIP: Add lint on cast Fn to all numerical except usize.
2 parents 3d7cdd4 + 24ab207 commit 1e1b4e2

File tree

4 files changed

+158
-1
lines changed

4 files changed

+158
-1
lines changed

clippy_lints/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
678678
types::UNIT_ARG,
679679
types::UNIT_CMP,
680680
types::UNNECESSARY_CAST,
681+
types::FN_TO_NUMERIC_CAST,
681682
unicode::ZERO_WIDTH_SPACE,
682683
unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME,
683684
unused_io_amount::UNUSED_IO_AMOUNT,

clippy_lints/src/types.rs

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -679,6 +679,40 @@ declare_clippy_lint! {
679679
"cast to the same type, e.g. `x as i32` where `x: i32`"
680680
}
681681

682+
/// **What it does:** Checks for casts of a function pointer to a numeric type not enough to store address.
683+
///
684+
/// **Why is this bad?** Casting a function pointer to not eligable type could truncate the address value.
685+
///
686+
/// **Known problems:** None.
687+
///
688+
/// **Example:**
689+
/// ```rust
690+
/// fn test_fn() -> i16;
691+
/// let _ = test_fn as i32
692+
/// ```
693+
declare_clippy_lint! {
694+
pub FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
695+
correctness,
696+
"cast function pointer to the numeric type with value truncation"
697+
}
698+
699+
/// **What it does:** Checks for casts of a function pointer to a numeric type except `usize`.
700+
///
701+
/// **Why is this bad?** Casting a function pointer to something other than `usize` is not a good style.
702+
///
703+
/// **Known problems:** None.
704+
///
705+
/// **Example:**
706+
/// ```rust
707+
/// fn test_fn() -> i16;
708+
/// let _ = test_fn as i128
709+
/// ```
710+
declare_clippy_lint! {
711+
pub FN_TO_NUMERIC_CAST,
712+
style,
713+
"cast function pointer to the numeric type"
714+
}
715+
682716
/// **What it does:** Checks for casts from a less-strictly-aligned pointer to a
683717
/// more-strictly-aligned pointer
684718
///
@@ -891,7 +925,9 @@ impl LintPass for CastPass {
891925
CAST_POSSIBLE_WRAP,
892926
CAST_LOSSLESS,
893927
UNNECESSARY_CAST,
894-
CAST_PTR_ALIGNMENT
928+
CAST_PTR_ALIGNMENT,
929+
FN_TO_NUMERIC_CAST,
930+
FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
895931
)
896932
}
897933
}
@@ -975,6 +1011,38 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CastPass {
9751011
},
9761012
}
9771013
}
1014+
1015+
match &cast_from.sty {
1016+
ty::TyFnDef(..) |
1017+
ty::TyFnPtr(..) => {
1018+
if cast_to.is_numeric() && cast_to.sty != ty::TyUint(UintTy::Usize){
1019+
let to_nbits = int_ty_to_nbits(cast_to, cx.tcx);
1020+
let pointer_nbits = cx.tcx.data_layout.pointer_size.bits();
1021+
if to_nbits < pointer_nbits || (to_nbits == pointer_nbits && cast_to.is_signed()) {
1022+
span_lint_and_sugg(
1023+
cx,
1024+
FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
1025+
expr.span,
1026+
&format!("casting a `{}` to `{}` may truncate the function address value.", cast_from, cast_to),
1027+
"if you need the address of the function, consider",
1028+
format!("{} as usize", &snippet(cx, ex.span, "x"))
1029+
);
1030+
} else {
1031+
span_lint_and_sugg(
1032+
cx,
1033+
FN_TO_NUMERIC_CAST,
1034+
expr.span,
1035+
&format!("casting a `{}` to `{}` is bad style.", cast_from, cast_to),
1036+
"if you need the address of the function, consider",
1037+
format!("{} as usize", &snippet(cx, ex.span, "x"))
1038+
);
1039+
1040+
};
1041+
}
1042+
}
1043+
_ => ()
1044+
}
1045+
9781046
if_chain!{
9791047
if let ty::TyRawPtr(from_ptr_ty) = &cast_from.sty;
9801048
if let ty::TyRawPtr(to_ptr_ty) = &cast_to.sty;

tests/ui/types_fn_to_int.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
enum Foo {
2+
A(usize),
3+
B
4+
}
5+
6+
fn bar() -> i32 {
7+
0i32
8+
}
9+
10+
fn main() {
11+
let x = Foo::A;
12+
let _y = x as i32;
13+
let _y1 = Foo::A as i32;
14+
let _y = x as u32;
15+
let _z = bar as u32;
16+
let _y = bar as i64;
17+
let _y = bar as u64;
18+
let _z = Foo::A as i128;
19+
let _z = Foo::A as u128;
20+
let _z = bar as i128;
21+
let _z = bar as u128;
22+
}

tests/ui/types_fn_to_int.stderr

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
error: casting a `fn(usize) -> Foo {Foo::A}` to `i32` may truncate the function address value.
2+
--> $DIR/types_fn_to_int.rs:12:14
3+
|
4+
12 | let _y = x as i32;
5+
| ^^^^^^^^ help: if you need the address of the function, consider: `x as usize`
6+
|
7+
= note: #[deny(fn_to_numeric_cast_with_truncation)] on by default
8+
9+
error: casting a `fn(usize) -> Foo {Foo::A}` to `i32` may truncate the function address value.
10+
--> $DIR/types_fn_to_int.rs:13:15
11+
|
12+
13 | let _y1 = Foo::A as i32;
13+
| ^^^^^^^^^^^^^ help: if you need the address of the function, consider: `Foo::A as usize`
14+
15+
error: casting a `fn(usize) -> Foo {Foo::A}` to `u32` may truncate the function address value.
16+
--> $DIR/types_fn_to_int.rs:14:14
17+
|
18+
14 | let _y = x as u32;
19+
| ^^^^^^^^ help: if you need the address of the function, consider: `x as usize`
20+
21+
error: casting a `fn() -> i32 {bar}` to `u32` may truncate the function address value.
22+
--> $DIR/types_fn_to_int.rs:15:14
23+
|
24+
15 | let _z = bar as u32;
25+
| ^^^^^^^^^^ help: if you need the address of the function, consider: `bar as usize`
26+
27+
error: casting a `fn() -> i32 {bar}` to `i64` may truncate the function address value.
28+
--> $DIR/types_fn_to_int.rs:16:14
29+
|
30+
16 | let _y = bar as i64;
31+
| ^^^^^^^^^^ help: if you need the address of the function, consider: `bar as usize`
32+
33+
error: casting a `fn() -> i32 {bar}` to `u64` is bad style.
34+
--> $DIR/types_fn_to_int.rs:17:14
35+
|
36+
17 | let _y = bar as u64;
37+
| ^^^^^^^^^^ help: if you need the address of the function, consider: `bar as usize`
38+
|
39+
= note: `-D fn-to-numeric-cast` implied by `-D warnings`
40+
41+
error: casting a `fn(usize) -> Foo {Foo::A}` to `i128` is bad style.
42+
--> $DIR/types_fn_to_int.rs:18:14
43+
|
44+
18 | let _z = Foo::A as i128;
45+
| ^^^^^^^^^^^^^^ help: if you need the address of the function, consider: `Foo::A as usize`
46+
47+
error: casting a `fn(usize) -> Foo {Foo::A}` to `u128` is bad style.
48+
--> $DIR/types_fn_to_int.rs:19:14
49+
|
50+
19 | let _z = Foo::A as u128;
51+
| ^^^^^^^^^^^^^^ help: if you need the address of the function, consider: `Foo::A as usize`
52+
53+
error: casting a `fn() -> i32 {bar}` to `i128` is bad style.
54+
--> $DIR/types_fn_to_int.rs:20:14
55+
|
56+
20 | let _z = bar as i128;
57+
| ^^^^^^^^^^^ help: if you need the address of the function, consider: `bar as usize`
58+
59+
error: casting a `fn() -> i32 {bar}` to `u128` is bad style.
60+
--> $DIR/types_fn_to_int.rs:21:14
61+
|
62+
21 | let _z = bar as u128;
63+
| ^^^^^^^^^^^ help: if you need the address of the function, consider: `bar as usize`
64+
65+
error: aborting due to 10 previous errors
66+

0 commit comments

Comments
 (0)