-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Auto merge of #7705 - michaelsproul:fn_to_numeric_cast_any, r=camsteffen
Restriction lint for function pointer casts The existing lints for function pointer casts cover the cases where a cast is non-portable or would result in truncation, however there's currently no way to forbid numeric function pointer casts entirely. I've added a new lint `fn_to_numeric_cast_any`, which allows one to ban _all_ numeric function pointer casts, including to `usize`. This is useful if you're writing high-level Rust and want to opt-out of potentially surprising behaviour, avoiding silent bugs from forgotten parentheses, e.g. ```rust fn foo() -> u32 { 10 } fn main() { let _ = foo as usize; // oops, forgot to call foo and got a random address instead! } ``` ~~I'm open to suggestions for the naming of the lint, because `fn_to_numeric_cast_any` is a bit clunky. Ideally I'd call this lint `fn_to_numeric_cast`, but that name is already taken for the more specific lint~~. We've stuck with `fn_to_numeric_cast_any` to avoid renaming the existing lint, or choosing a different name that's too generic (like `fn_cast`). I'm also open to changing the suggestion behaviour, as adding parentheses is only one of many possible ways to fix the lint. changelog: add ``[`fn_to_numeric_cast_any`]`` restriction lint
- Loading branch information
Showing
7 changed files
with
258 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
use clippy_utils::diagnostics::span_lint_and_sugg; | ||
use clippy_utils::source::snippet_with_applicability; | ||
use rustc_errors::Applicability; | ||
use rustc_hir::Expr; | ||
use rustc_lint::LateContext; | ||
use rustc_middle::ty::{self, Ty}; | ||
|
||
use super::FN_TO_NUMERIC_CAST_ANY; | ||
|
||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { | ||
// We allow casts from any function type to any function type. | ||
match cast_to.kind() { | ||
ty::FnDef(..) | ty::FnPtr(..) => return, | ||
_ => { /* continue to checks */ }, | ||
} | ||
|
||
match cast_from.kind() { | ||
ty::FnDef(..) | ty::FnPtr(_) => { | ||
let mut applicability = Applicability::MaybeIncorrect; | ||
let from_snippet = snippet_with_applicability(cx, cast_expr.span, "..", &mut applicability); | ||
|
||
span_lint_and_sugg( | ||
cx, | ||
FN_TO_NUMERIC_CAST_ANY, | ||
expr.span, | ||
&format!("casting function pointer `{}` to `{}`", from_snippet, cast_to), | ||
"did you mean to invoke the function?", | ||
format!("{}() as {}", from_snippet, cast_to), | ||
applicability, | ||
); | ||
}, | ||
_ => {}, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
#![warn(clippy::fn_to_numeric_cast_any)] | ||
#![allow(clippy::fn_to_numeric_cast, clippy::fn_to_numeric_cast_with_truncation)] | ||
|
||
fn foo() -> u8 { | ||
0 | ||
} | ||
|
||
fn generic_foo<T>(x: T) -> T { | ||
x | ||
} | ||
|
||
trait Trait { | ||
fn static_method() -> u32 { | ||
2 | ||
} | ||
} | ||
|
||
struct Struct; | ||
|
||
impl Trait for Struct {} | ||
|
||
fn fn_pointer_to_integer() { | ||
let _ = foo as i8; | ||
let _ = foo as i16; | ||
let _ = foo as i32; | ||
let _ = foo as i64; | ||
let _ = foo as i128; | ||
let _ = foo as isize; | ||
|
||
let _ = foo as u8; | ||
let _ = foo as u16; | ||
let _ = foo as u32; | ||
let _ = foo as u64; | ||
let _ = foo as u128; | ||
let _ = foo as usize; | ||
} | ||
|
||
fn static_method_to_integer() { | ||
let _ = Struct::static_method as usize; | ||
} | ||
|
||
fn fn_with_fn_arg(f: fn(i32) -> u32) -> usize { | ||
f as usize | ||
} | ||
|
||
fn fn_with_generic_static_trait_method<T: Trait>() -> usize { | ||
T::static_method as usize | ||
} | ||
|
||
fn closure_to_fn_to_integer() { | ||
let clos = |x| x * 2_u32; | ||
|
||
let _ = (clos as fn(u32) -> u32) as usize; | ||
} | ||
|
||
fn fn_to_raw_ptr() { | ||
let _ = foo as *const (); | ||
} | ||
|
||
fn cast_fn_to_self() { | ||
// Casting to the same function pointer type should be permitted. | ||
let _ = foo as fn() -> u8; | ||
} | ||
|
||
fn cast_generic_to_concrete() { | ||
// Casting to a more concrete function pointer type should be permitted. | ||
let _ = generic_foo as fn(usize) -> usize; | ||
} | ||
|
||
fn cast_closure_to_fn() { | ||
// Casting a closure to a function pointer should be permitted. | ||
let id = |x| x; | ||
let _ = id as fn(usize) -> usize; | ||
} | ||
|
||
fn main() {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
error: casting function pointer `foo` to `i8` | ||
--> $DIR/fn_to_numeric_cast_any.rs:23:13 | ||
| | ||
LL | let _ = foo as i8; | ||
| ^^^^^^^^^ help: did you mean to invoke the function?: `foo() as i8` | ||
| | ||
= note: `-D clippy::fn-to-numeric-cast-any` implied by `-D warnings` | ||
|
||
error: casting function pointer `foo` to `i16` | ||
--> $DIR/fn_to_numeric_cast_any.rs:24:13 | ||
| | ||
LL | let _ = foo as i16; | ||
| ^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as i16` | ||
|
||
error: casting function pointer `foo` to `i32` | ||
--> $DIR/fn_to_numeric_cast_any.rs:25:13 | ||
| | ||
LL | let _ = foo as i32; | ||
| ^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as i32` | ||
|
||
error: casting function pointer `foo` to `i64` | ||
--> $DIR/fn_to_numeric_cast_any.rs:26:13 | ||
| | ||
LL | let _ = foo as i64; | ||
| ^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as i64` | ||
|
||
error: casting function pointer `foo` to `i128` | ||
--> $DIR/fn_to_numeric_cast_any.rs:27:13 | ||
| | ||
LL | let _ = foo as i128; | ||
| ^^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as i128` | ||
|
||
error: casting function pointer `foo` to `isize` | ||
--> $DIR/fn_to_numeric_cast_any.rs:28:13 | ||
| | ||
LL | let _ = foo as isize; | ||
| ^^^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as isize` | ||
|
||
error: casting function pointer `foo` to `u8` | ||
--> $DIR/fn_to_numeric_cast_any.rs:30:13 | ||
| | ||
LL | let _ = foo as u8; | ||
| ^^^^^^^^^ help: did you mean to invoke the function?: `foo() as u8` | ||
|
||
error: casting function pointer `foo` to `u16` | ||
--> $DIR/fn_to_numeric_cast_any.rs:31:13 | ||
| | ||
LL | let _ = foo as u16; | ||
| ^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as u16` | ||
|
||
error: casting function pointer `foo` to `u32` | ||
--> $DIR/fn_to_numeric_cast_any.rs:32:13 | ||
| | ||
LL | let _ = foo as u32; | ||
| ^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as u32` | ||
|
||
error: casting function pointer `foo` to `u64` | ||
--> $DIR/fn_to_numeric_cast_any.rs:33:13 | ||
| | ||
LL | let _ = foo as u64; | ||
| ^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as u64` | ||
|
||
error: casting function pointer `foo` to `u128` | ||
--> $DIR/fn_to_numeric_cast_any.rs:34:13 | ||
| | ||
LL | let _ = foo as u128; | ||
| ^^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as u128` | ||
|
||
error: casting function pointer `foo` to `usize` | ||
--> $DIR/fn_to_numeric_cast_any.rs:35:13 | ||
| | ||
LL | let _ = foo as usize; | ||
| ^^^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as usize` | ||
|
||
error: casting function pointer `Struct::static_method` to `usize` | ||
--> $DIR/fn_to_numeric_cast_any.rs:39:13 | ||
| | ||
LL | let _ = Struct::static_method as usize; | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean to invoke the function?: `Struct::static_method() as usize` | ||
|
||
error: casting function pointer `f` to `usize` | ||
--> $DIR/fn_to_numeric_cast_any.rs:43:5 | ||
| | ||
LL | f as usize | ||
| ^^^^^^^^^^ help: did you mean to invoke the function?: `f() as usize` | ||
|
||
error: casting function pointer `T::static_method` to `usize` | ||
--> $DIR/fn_to_numeric_cast_any.rs:47:5 | ||
| | ||
LL | T::static_method as usize | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean to invoke the function?: `T::static_method() as usize` | ||
|
||
error: casting function pointer `(clos as fn(u32) -> u32)` to `usize` | ||
--> $DIR/fn_to_numeric_cast_any.rs:53:13 | ||
| | ||
LL | let _ = (clos as fn(u32) -> u32) as usize; | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean to invoke the function?: `(clos as fn(u32) -> u32)() as usize` | ||
|
||
error: casting function pointer `foo` to `*const ()` | ||
--> $DIR/fn_to_numeric_cast_any.rs:57:13 | ||
| | ||
LL | let _ = foo as *const (); | ||
| ^^^^^^^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as *const ()` | ||
|
||
error: aborting due to 17 previous errors | ||
|