From be405344570f68138ecce86f19f3908d6f481ba9 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 10 Jan 2025 17:44:47 +0100 Subject: [PATCH] Add `primitive_method_to_numeric_cast` lint --- CHANGELOG.md | 1 + clippy_lints/src/casts/mod.rs | 29 ++++++++++ .../casts/primitive_method_to_numeric_cast.rs | 57 +++++++++++++++++++ clippy_lints/src/declared_lints.rs | 1 + 4 files changed, 88 insertions(+) create mode 100644 clippy_lints/src/casts/primitive_method_to_numeric_cast.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b62c9a59aa5..ca2e73897903 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6068,6 +6068,7 @@ Released 2018-09-13 [`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma [`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence [`precedence_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence_bits +[`primitive_method_to_numeric_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#primitive_method_to_numeric_cast [`print_in_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_in_format_impl [`print_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_literal [`print_stderr`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stderr diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index 76931fce209e..7c8fa0b4d921 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -18,6 +18,7 @@ mod fn_to_numeric_cast; mod fn_to_numeric_cast_any; mod fn_to_numeric_cast_with_truncation; mod manual_dangling_ptr; +mod primitive_method_to_numeric_cast; mod ptr_as_ptr; mod ptr_cast_constness; mod ref_as_ptr; @@ -786,6 +787,32 @@ declare_clippy_lint! { "casting small constant literals to pointers to create dangling pointers" } +declare_clippy_lint! { + /// ### What it does + /// Checks for casts of a primitive method pointer to any integer type. + /// + /// ### Why restrict this? + /// Casting a function pointer to an integer can have surprising results and can occur + /// accidentally if parentheses are omitted from a function call. If you aren't doing anything + /// low-level with function pointers then you can opt out of casting functions to integers in + /// order to avoid mistakes. Alternatively, you can use this lint to audit all uses of function + /// pointer casts in your code. + /// + /// ### Example + /// ```no_run + /// let _ = u16::max as usize; + /// ``` + /// + /// Use instead: + /// ```no_run + /// let _ = u16::MAX as usize; + /// ``` + #[clippy::version = "1.86.0"] + pub PRIMITIVE_METHOD_TO_NUMERIC_CAST, + suspicious, + "casting a primitive method pointer to any integer type" +} + pub struct Casts { msrv: Msrv, } @@ -823,6 +850,7 @@ impl_lint_pass!(Casts => [ REF_AS_PTR, AS_POINTER_UNDERSCORE, MANUAL_DANGLING_PTR, + PRIMITIVE_METHOD_TO_NUMERIC_CAST, ]); impl<'tcx> LateLintPass<'tcx> for Casts { @@ -847,6 +875,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { ptr_cast_constness::check(cx, expr, cast_from_expr, cast_from, cast_to, self.msrv); as_ptr_cast_mut::check(cx, expr, cast_from_expr, cast_to); fn_to_numeric_cast_any::check(cx, expr, cast_from_expr, cast_from, cast_to); + primitive_method_to_numeric_cast::check(cx, expr, cast_from_expr, cast_from, cast_to); fn_to_numeric_cast::check(cx, expr, cast_from_expr, cast_from, cast_to); fn_to_numeric_cast_with_truncation::check(cx, expr, cast_from_expr, cast_from, cast_to); zero_ptr::check(cx, expr, cast_from_expr, cast_to_hir); diff --git a/clippy_lints/src/casts/primitive_method_to_numeric_cast.rs b/clippy_lints/src/casts/primitive_method_to_numeric_cast.rs new file mode 100644 index 000000000000..380fa98cd15f --- /dev/null +++ b/clippy_lints/src/casts/primitive_method_to_numeric_cast.rs @@ -0,0 +1,57 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::match_def_path; +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::PRIMITIVE_METHOD_TO_NUMERIC_CAST; + +fn get_primitive_ty_name(ty: Ty<'_>) -> Option<&'static str> { + match ty.kind() { + ty::Char => Some("char"), + ty::Int(int) => Some(int.name_str()), + ty::Uint(uint) => Some(uint.name_str()), + ty::Float(float) => Some(float.name_str()), + _ => None, + } +} + +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 */ }, + } + + if let ty::FnDef(def_id, generics) = cast_from.kind() + && let Some(method_name) = cx.tcx.opt_item_name(*def_id) + && let method_name = method_name.as_str() + && (method_name == "min" || method_name == "max") + // We get the type on which the `min`/`max` method of the `Ord` trait is implemented. + && let [ty] = generics.as_slice() + && let Some(ty) = ty.as_type() + // We get its name in case it's a primitive with an associated MIN/MAX constant. + && let Some(ty_name) = get_primitive_ty_name(ty) + && match_def_path(cx, *def_id, &["core", "cmp", "Ord", method_name]) + { + let mut applicability = Applicability::MaybeIncorrect; + let from_snippet = snippet_with_applicability(cx, cast_expr.span, "..", &mut applicability); + + span_lint_and_then( + cx, + PRIMITIVE_METHOD_TO_NUMERIC_CAST, + expr.span, + format!("casting function pointer `{from_snippet}` to `{cast_to}`"), + |diag| { + diag.span_suggestion_verbose( + expr.span, + "did you mean to use the associated constant?", + format!("{ty_name}::{} as {cast_to}", method_name.to_ascii_uppercase()), + applicability, + ); + }, + ); + } +} diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 2cccd6ba2702..1d4c75dafa31 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -68,6 +68,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::casts::FN_TO_NUMERIC_CAST_ANY_INFO, crate::casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION_INFO, crate::casts::MANUAL_DANGLING_PTR_INFO, + crate::casts::PRIMITIVE_METHOD_TO_NUMERIC_CAST_INFO, crate::casts::PTR_AS_PTR_INFO, crate::casts::PTR_CAST_CONSTNESS_INFO, crate::casts::REF_AS_PTR_INFO,