From c0ab8b2531f273f0ce93dc64df83d936fc505604 Mon Sep 17 00:00:00 2001 From: Philipp Hansch Date: Thu, 4 Oct 2018 21:44:16 +0200 Subject: [PATCH] Reimplement the `fn_to_numeric_cast_with_truncation` lint --- CHANGELOG.md | 1 + README.md | 2 +- clippy_lints/src/lib.rs | 2 + clippy_lints/src/types.rs | 40 ++++- tests/ui/fn_to_numeric_cast.rs | 2 +- tests/ui/fn_to_numeric_cast.stderr | 142 ++++++++++++++++++ .../ui/fn_to_numeric_cast_with_truncation.rs | 23 +++ .../fn_to_numeric_cast_with_truncation.stderr | 40 +++++ 8 files changed, 249 insertions(+), 3 deletions(-) create mode 100644 tests/ui/fn_to_numeric_cast.stderr create mode 100644 tests/ui/fn_to_numeric_cast_with_truncation.rs create mode 100644 tests/ui/fn_to_numeric_cast_with_truncation.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 2373cb87eed..c9bea1e8ef5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -689,6 +689,7 @@ All notable changes to this project will be documented in this file. [`float_cmp`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#float_cmp [`float_cmp_const`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#float_cmp_const [`fn_to_numeric_cast`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#fn_to_numeric_cast +[`fn_to_numeric_cast_with_truncation`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#fn_to_numeric_cast_with_truncation [`for_kv_map`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#for_kv_map [`for_loop_over_option`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#for_loop_over_option [`for_loop_over_result`]: https://rust-lang-nursery.github.io/rust-clippy/master/index.html#for_loop_over_result diff --git a/README.md b/README.md index f332a3f645d..40ece34c6fa 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ We are currently in the process of discussing Clippy 1.0 via the RFC process in A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. -[There are 278 lints included in this crate!](https://rust-lang-nursery.github.io/rust-clippy/master/index.html) +[There are 279 lints included in this crate!](https://rust-lang-nursery.github.io/rust-clippy/master/index.html) We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you: diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index b181a4c17ca..3779b09ecf3 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -698,6 +698,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) { types::CAST_PTR_ALIGNMENT, types::CHAR_LIT_AS_U8, types::FN_TO_NUMERIC_CAST, + types::FN_TO_NUMERIC_CAST_WITH_TRUNCATION, types::IMPLICIT_HASHER, types::LET_UNIT_VALUE, types::OPTION_OPTION, @@ -791,6 +792,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) { returns::NEEDLESS_RETURN, strings::STRING_LIT_AS_BYTES, types::FN_TO_NUMERIC_CAST, + types::FN_TO_NUMERIC_CAST_WITH_TRUNCATION, types::IMPLICIT_HASHER, types::LET_UNIT_VALUE, unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME, diff --git a/clippy_lints/src/types.rs b/clippy_lints/src/types.rs index f39a26db509..d3aa8de825b 100644 --- a/clippy_lints/src/types.rs +++ b/clippy_lints/src/types.rs @@ -746,6 +746,32 @@ declare_clippy_lint! { "casting a function pointer to a numeric type other than usize" } +/// **What it does:** Checks for casts of a function pointer to a numeric type not wide enough to +/// store address. +/// +/// **Why is this bad?** +/// Such a cast discards some bits of the function's address. If this is intended, it would be more +/// clearly expressed by casting to usize first, then casting the usize to the intended type (with +/// a comment) to perform the truncation. +/// +/// **Example** +/// +/// ```rust +/// // Bad +/// fn fn1() -> i16 { 1 }; +/// let _ = fn1 as i32; +/// +/// // Better: Cast to usize first, then comment with the reason for the truncation +/// fn fn2() -> i16 { 1 }; +/// let fn_ptr = fn2 as usize; +/// let fn_ptr_truncated = fn_ptr as i32; +/// ``` +declare_clippy_lint! { + pub FN_TO_NUMERIC_CAST_WITH_TRUNCATION, + style, + "casting a function pointer to a numeric type not wide enough to store the address" +} + /// Returns the size in bits of an integral type. /// Will return 0 if the type is not an int or uint variant fn int_ty_to_nbits(typ: Ty<'_>, tcx: TyCtxt<'_, '_, '_>) -> u64 { @@ -1054,7 +1080,19 @@ fn lint_fn_to_numeric_cast(cx: &LateContext<'_, '_>, expr: &Expr, cast_expr: &Ex match cast_from.sty { ty::FnDef(..) | ty::FnPtr(_) => { let from_snippet = snippet(cx, cast_expr.span, "x"); - if cast_to.sty != ty::Uint(UintTy::Usize) { + + let to_nbits = int_ty_to_nbits(cast_to, cx.tcx); + if to_nbits < cx.tcx.data_layout.pointer_size.bits() { + span_lint_and_sugg( + cx, + FN_TO_NUMERIC_CAST_WITH_TRUNCATION, + expr.span, + &format!("casting function pointer `{}` to `{}`, which truncates the value", from_snippet, cast_to), + "try", + format!("{} as usize", from_snippet) + ); + + } else if cast_to.sty != ty::Uint(UintTy::Usize) { span_lint_and_sugg( cx, FN_TO_NUMERIC_CAST, diff --git a/tests/ui/fn_to_numeric_cast.rs b/tests/ui/fn_to_numeric_cast.rs index d250af61847..6d0fd3d8ab8 100644 --- a/tests/ui/fn_to_numeric_cast.rs +++ b/tests/ui/fn_to_numeric_cast.rs @@ -1,6 +1,6 @@ #![feature(tool_lints)] -#[warn(clippy::fn_to_numeric_cast)] +#![warn(clippy::fn_to_numeric_cast)] fn foo() -> String { String::new() } diff --git a/tests/ui/fn_to_numeric_cast.stderr b/tests/ui/fn_to_numeric_cast.stderr new file mode 100644 index 00000000000..be8bc9058b4 --- /dev/null +++ b/tests/ui/fn_to_numeric_cast.stderr @@ -0,0 +1,142 @@ +error: casting function pointer `foo` to `i8` + --> $DIR/fn_to_numeric_cast.rs:8:13 + | +8 | let _ = foo as i8; + | ^^^^^^^^^ help: try: `foo as usize` + | + = note: `-D clippy::fn-to-numeric-cast` implied by `-D warnings` + +error: casting function pointer `foo` to `i16` + --> $DIR/fn_to_numeric_cast.rs:9:13 + | +9 | let _ = foo as i16; + | ^^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `foo` to `i32` + --> $DIR/fn_to_numeric_cast.rs:10:13 + | +10 | let _ = foo as i32; + | ^^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `foo` to `i64` + --> $DIR/fn_to_numeric_cast.rs:11:13 + | +11 | let _ = foo as i64; + | ^^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `foo` to `i128` + --> $DIR/fn_to_numeric_cast.rs:12:13 + | +12 | let _ = foo as i128; + | ^^^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `foo` to `isize` + --> $DIR/fn_to_numeric_cast.rs:13:13 + | +13 | let _ = foo as isize; + | ^^^^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `foo` to `u8` + --> $DIR/fn_to_numeric_cast.rs:15:13 + | +15 | let _ = foo as u8; + | ^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `foo` to `u16` + --> $DIR/fn_to_numeric_cast.rs:16:13 + | +16 | let _ = foo as u16; + | ^^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `foo` to `u32` + --> $DIR/fn_to_numeric_cast.rs:17:13 + | +17 | let _ = foo as u32; + | ^^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `foo` to `u64` + --> $DIR/fn_to_numeric_cast.rs:18:13 + | +18 | let _ = foo as u64; + | ^^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `foo` to `u128` + --> $DIR/fn_to_numeric_cast.rs:19:13 + | +19 | let _ = foo as u128; + | ^^^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `abc` to `i8` + --> $DIR/fn_to_numeric_cast.rs:28:13 + | +28 | let _ = abc as i8; + | ^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `i16` + --> $DIR/fn_to_numeric_cast.rs:29:13 + | +29 | let _ = abc as i16; + | ^^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `i32` + --> $DIR/fn_to_numeric_cast.rs:30:13 + | +30 | let _ = abc as i32; + | ^^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `i64` + --> $DIR/fn_to_numeric_cast.rs:31:13 + | +31 | let _ = abc as i64; + | ^^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `i128` + --> $DIR/fn_to_numeric_cast.rs:32:13 + | +32 | let _ = abc as i128; + | ^^^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `isize` + --> $DIR/fn_to_numeric_cast.rs:33:13 + | +33 | let _ = abc as isize; + | ^^^^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `u8` + --> $DIR/fn_to_numeric_cast.rs:35:13 + | +35 | let _ = abc as u8; + | ^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `u16` + --> $DIR/fn_to_numeric_cast.rs:36:13 + | +36 | let _ = abc as u16; + | ^^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `u32` + --> $DIR/fn_to_numeric_cast.rs:37:13 + | +37 | let _ = abc as u32; + | ^^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `u64` + --> $DIR/fn_to_numeric_cast.rs:38:13 + | +38 | let _ = abc as u64; + | ^^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `abc` to `u128` + --> $DIR/fn_to_numeric_cast.rs:39:13 + | +39 | let _ = abc as u128; + | ^^^^^^^^^^^ help: try: `abc as usize` + +error: casting function pointer `f` to `i32` + --> $DIR/fn_to_numeric_cast.rs:46:5 + | +46 | f as i32 + | ^^^^^^^^ help: try: `f as usize` + +error: aborting due to 23 previous errors + diff --git a/tests/ui/fn_to_numeric_cast_with_truncation.rs b/tests/ui/fn_to_numeric_cast_with_truncation.rs new file mode 100644 index 00000000000..82bbaec201f --- /dev/null +++ b/tests/ui/fn_to_numeric_cast_with_truncation.rs @@ -0,0 +1,23 @@ +#![feature(tool_lints)] + +#![warn(clippy::fn_to_numeric_cast_with_truncation)] +#![allow(clippy::fn_to_numeric_cast)] + +fn foo() -> String { String::new() } + +fn test_fn_to_numeric_cast_with_truncation() { + let _ = foo as i8; + let _ = foo as i16; + let _ = foo as i32; + let _ = foo as u8; + let _ = foo as u16; + let _ = foo as u32; + + // TODO: Is it bad to have these tests? + // Running the tests on a different architechture will + // produce different results + let _ = foo as u64; + let _ = foo as i64; +} + +fn main() {} diff --git a/tests/ui/fn_to_numeric_cast_with_truncation.stderr b/tests/ui/fn_to_numeric_cast_with_truncation.stderr new file mode 100644 index 00000000000..cae4b6fbd40 --- /dev/null +++ b/tests/ui/fn_to_numeric_cast_with_truncation.stderr @@ -0,0 +1,40 @@ +error: casting function pointer `foo` to `i8`, which truncates the value + --> $DIR/fn_to_numeric_cast_with_truncation.rs:9:13 + | +9 | let _ = foo as i8; + | ^^^^^^^^^ help: try: `foo as usize` + | + = note: `-D clippy::fn-to-numeric-cast-with-truncation` implied by `-D warnings` + +error: casting function pointer `foo` to `i16`, which truncates the value + --> $DIR/fn_to_numeric_cast_with_truncation.rs:10:13 + | +10 | let _ = foo as i16; + | ^^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `foo` to `i32`, which truncates the value + --> $DIR/fn_to_numeric_cast_with_truncation.rs:11:13 + | +11 | let _ = foo as i32; + | ^^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `foo` to `u8`, which truncates the value + --> $DIR/fn_to_numeric_cast_with_truncation.rs:12:13 + | +12 | let _ = foo as u8; + | ^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `foo` to `u16`, which truncates the value + --> $DIR/fn_to_numeric_cast_with_truncation.rs:13:13 + | +13 | let _ = foo as u16; + | ^^^^^^^^^^ help: try: `foo as usize` + +error: casting function pointer `foo` to `u32`, which truncates the value + --> $DIR/fn_to_numeric_cast_with_truncation.rs:14:13 + | +14 | let _ = foo as u32; + | ^^^^^^^^^^ help: try: `foo as usize` + +error: aborting due to 6 previous errors +