Skip to content

Commit ed1edf3

Browse files
Add primitive_method_to_numeric_cast lint
1 parent a9c0e22 commit ed1edf3

File tree

4 files changed

+88
-0
lines changed

4 files changed

+88
-0
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -5950,6 +5950,7 @@ Released 2018-09-13
59505950
[`positional_named_format_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#positional_named_format_parameters
59515951
[`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma
59525952
[`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence
5953+
[`primitive_method_to_numeric_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#primitive_method_to_numeric_cast
59535954
[`print_in_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_in_format_impl
59545955
[`print_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_literal
59555956
[`print_stderr`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stderr

clippy_lints/src/casts/mod.rs

+29
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ mod char_lit_as_u8;
1717
mod fn_to_numeric_cast;
1818
mod fn_to_numeric_cast_any;
1919
mod fn_to_numeric_cast_with_truncation;
20+
mod primitive_method_to_numeric_cast;
2021
mod ptr_as_ptr;
2122
mod ptr_cast_constness;
2223
mod ref_as_ptr;
@@ -754,6 +755,32 @@ declare_clippy_lint! {
754755
"detects `as *mut _` and `as *const _` conversion"
755756
}
756757

758+
declare_clippy_lint! {
759+
/// ### What it does
760+
/// Checks for casts of a primitive method pointer to any integer type.
761+
///
762+
/// ### Why restrict this?
763+
/// Casting a function pointer to an integer can have surprising results and can occur
764+
/// accidentally if parentheses are omitted from a function call. If you aren't doing anything
765+
/// low-level with function pointers then you can opt out of casting functions to integers in
766+
/// order to avoid mistakes. Alternatively, you can use this lint to audit all uses of function
767+
/// pointer casts in your code.
768+
///
769+
/// ### Example
770+
/// ```no_run
771+
/// let _ = u16::max as usize;
772+
/// ```
773+
///
774+
/// Use instead:
775+
/// ```no_run
776+
/// let _ = u16::MAX as usize;
777+
/// ```
778+
#[clippy::version = "1.86.0"]
779+
pub PRIMITIVE_METHOD_TO_NUMERIC_CAST,
780+
suspicious,
781+
"casting a primitive method pointer to any integer type"
782+
}
783+
757784
pub struct Casts {
758785
msrv: Msrv,
759786
}
@@ -792,6 +819,7 @@ impl_lint_pass!(Casts => [
792819
ZERO_PTR,
793820
REF_AS_PTR,
794821
AS_POINTER_UNDERSCORE,
822+
PRIMITIVE_METHOD_TO_NUMERIC_CAST,
795823
]);
796824

797825
impl<'tcx> LateLintPass<'tcx> for Casts {
@@ -816,6 +844,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
816844
ptr_cast_constness::check(cx, expr, cast_from_expr, cast_from, cast_to, &self.msrv);
817845
as_ptr_cast_mut::check(cx, expr, cast_from_expr, cast_to);
818846
fn_to_numeric_cast_any::check(cx, expr, cast_from_expr, cast_from, cast_to);
847+
primitive_method_to_numeric_cast::check(cx, expr, cast_from_expr, cast_from, cast_to);
819848
fn_to_numeric_cast::check(cx, expr, cast_from_expr, cast_from, cast_to);
820849
fn_to_numeric_cast_with_truncation::check(cx, expr, cast_from_expr, cast_from, cast_to);
821850
zero_ptr::check(cx, expr, cast_from_expr, cast_to_hir);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
use clippy_utils::diagnostics::span_lint_and_then;
2+
use clippy_utils::match_def_path;
3+
use clippy_utils::source::snippet_with_applicability;
4+
use rustc_errors::Applicability;
5+
use rustc_hir::Expr;
6+
use rustc_lint::LateContext;
7+
use rustc_middle::ty::{self, Ty};
8+
9+
use super::PRIMITIVE_METHOD_TO_NUMERIC_CAST;
10+
11+
fn get_primitive_ty_name(ty: Ty<'_>) -> Option<&'static str> {
12+
match ty.kind() {
13+
ty::Char => Some("char"),
14+
ty::Int(int) => Some(int.name_str()),
15+
ty::Uint(uint) => Some(uint.name_str()),
16+
ty::Float(float) => Some(float.name_str()),
17+
_ => None,
18+
}
19+
}
20+
21+
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
22+
// We allow casts from any function type to any function type.
23+
match cast_to.kind() {
24+
ty::FnDef(..) | ty::FnPtr(..) => return,
25+
_ => { /* continue to checks */ },
26+
}
27+
28+
if let ty::FnDef(def_id, generics) = cast_from.kind()
29+
&& let Some(method_name) = cx.tcx.opt_item_name(*def_id)
30+
&& let method_name = method_name.as_str()
31+
&& (method_name == "min" || method_name == "max")
32+
// We get the type on which the `min`/`max` method of the `Ord` trait is implemented.
33+
&& let [ty] = generics.as_slice()
34+
&& let Some(ty) = ty.as_type()
35+
// We get its name in case it's a primitive with an associated MIN/MAX constant.
36+
&& let Some(ty_name) = get_primitive_ty_name(ty)
37+
&& match_def_path(cx, *def_id, &["core", "cmp", "Ord", method_name])
38+
{
39+
let mut applicability = Applicability::MaybeIncorrect;
40+
let from_snippet = snippet_with_applicability(cx, cast_expr.span, "..", &mut applicability);
41+
42+
span_lint_and_then(
43+
cx,
44+
PRIMITIVE_METHOD_TO_NUMERIC_CAST,
45+
expr.span,
46+
format!("casting function pointer `{from_snippet}` to `{cast_to}`"),
47+
|diag| {
48+
diag.span_suggestion_verbose(
49+
expr.span,
50+
"did you mean to use the associated constant?",
51+
format!("{ty_name}::{} as {cast_to}", method_name.to_ascii_uppercase()),
52+
applicability,
53+
);
54+
},
55+
);
56+
}
57+
}

clippy_lints/src/declared_lints.rs

+1
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
9696
crate::casts::FN_TO_NUMERIC_CAST_INFO,
9797
crate::casts::FN_TO_NUMERIC_CAST_ANY_INFO,
9898
crate::casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION_INFO,
99+
crate::casts::PRIMITIVE_METHOD_TO_NUMERIC_CAST_INFO,
99100
crate::casts::PTR_AS_PTR_INFO,
100101
crate::casts::PTR_CAST_CONSTNESS_INFO,
101102
crate::casts::REF_AS_PTR_INFO,

0 commit comments

Comments
 (0)