about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md1
-rw-r--r--clippy_lints/src/casts/mod.rs29
-rw-r--r--clippy_lints/src/casts/primitive_method_to_numeric_cast.rs57
-rw-r--r--clippy_lints/src/declared_lints.rs1
4 files changed, 88 insertions, 0 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2b62c9a59aa..ca2e7389790 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 76931fce209..7c8fa0b4d92 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 00000000000..380fa98cd15
--- /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 2cccd6ba270..1d4c75dafa3 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,