| use clippy_utils::diagnostics::span_lint_and_then; |
| use clippy_utils::source::snippet_with_applicability; |
| use clippy_utils::sym; |
| use rustc_errors::Applicability; |
| use rustc_hir::Expr; |
| use rustc_lint::LateContext; |
| use rustc_middle::ty::{self, GenericArg, Ty}; |
| use rustc_span::Symbol; |
| use rustc_span::def_id::DefId; |
| |
| use super::CONFUSING_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, |
| } |
| } |
| |
| fn get_const_name_and_ty_name( |
| cx: &LateContext<'_>, |
| method_name: Symbol, |
| method_def_id: DefId, |
| generics: &[GenericArg<'_>], |
| ) -> Option<(&'static str, &'static str)> { |
| let diagnostic_name = cx.tcx.get_diagnostic_name(method_def_id); |
| |
| let ty_name = if diagnostic_name.is_some_and(|diag| diag == sym::cmp_ord_min || diag == sym::cmp_ord_max) { |
| // We get the type on which the `min`/`max` method of the `Ord` trait is implemented. |
| if let [ty] = generics |
| && let Some(ty) = ty.as_type() |
| { |
| get_primitive_ty_name(ty)? |
| } else { |
| return None; |
| } |
| } else if let Some(impl_id) = cx.tcx.impl_of_assoc(method_def_id) |
| && let Some(ty_name) = get_primitive_ty_name(cx.tcx.type_of(impl_id).instantiate_identity()) |
| && matches!( |
| method_name, |
| sym::min | sym::max | sym::minimum | sym::maximum | sym::min_value | sym::max_value |
| ) |
| { |
| ty_name |
| } else { |
| return None; |
| }; |
| |
| let const_name = if matches!(method_name, sym::max | sym::maximum | sym::max_value) { |
| "MAX" |
| } else { |
| "MIN" |
| }; |
| Some((const_name, ty_name)) |
| } |
| |
| 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. |
| if cast_to.is_fn() { |
| return; |
| } |
| |
| if let ty::FnDef(def_id, generics) = cast_from.kind() |
| && let Some(method_name) = cx.tcx.opt_item_name(*def_id) |
| && let Some((const_name, ty_name)) = get_const_name_and_ty_name(cx, method_name, *def_id, generics.as_slice()) |
| { |
| let mut applicability = Applicability::MaybeIncorrect; |
| let from_snippet = snippet_with_applicability(cx, cast_expr.span, "..", &mut applicability); |
| |
| span_lint_and_then( |
| cx, |
| CONFUSING_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}::{const_name} as {cast_to}"), |
| applicability, |
| ); |
| }, |
| ); |
| } |
| } |