| use clippy_utils::diagnostics::span_lint_and_sugg; | |
| use clippy_utils::is_from_proc_macro; | |
| use clippy_utils::msrvs::{self, Msrv}; | |
| use clippy_utils::source::snippet_with_applicability; | |
| use clippy_utils::sugg::Sugg; | |
| use rustc_errors::Applicability; | |
| use rustc_hir::{self as hir, Expr, ExprKind, QPath, TyKind}; | |
| use rustc_lint::LateContext; | |
| use rustc_middle::ty::{self, Ty}; | |
| use rustc_span::{Span, sym}; | |
| use super::PTR_AS_PTR; | |
| enum OmitFollowedCastReason<'a> { | |
| None, | |
| Null(&'a QPath<'a>), | |
| NullMut(&'a QPath<'a>), | |
| } | |
| impl OmitFollowedCastReason<'_> { | |
| fn corresponding_item(&self) -> Option<&QPath<'_>> { | |
| match self { | |
| OmitFollowedCastReason::None => None, | |
| OmitFollowedCastReason::Null(x) | OmitFollowedCastReason::NullMut(x) => Some(*x), | |
| } | |
| } | |
| } | |
| pub(super) fn check<'tcx>( | |
| cx: &LateContext<'tcx>, | |
| expr: &Expr<'tcx>, | |
| cast_from_expr: &Expr<'_>, | |
| cast_from: Ty<'_>, | |
| cast_to_hir: &hir::Ty<'_>, | |
| cast_to: Ty<'tcx>, | |
| msrv: Msrv, | |
| ) { | |
| if let ty::RawPtr(_, from_mutbl) = cast_from.kind() | |
| && let ty::RawPtr(to_pointee_ty, to_mutbl) = cast_to.kind() | |
| && from_mutbl == to_mutbl | |
| // The `U` in `pointer::cast` have to be `Sized` | |
| // as explained here: https://github.com/rust-lang/rust/issues/60602. | |
| && to_pointee_ty.is_sized(cx.tcx, cx.typing_env()) | |
| && msrv.meets(cx, msrvs::POINTER_CAST) | |
| && !is_from_proc_macro(cx, expr) | |
| { | |
| let mut app = Applicability::MachineApplicable; | |
| let turbofish = match &cast_to_hir.kind { | |
| TyKind::Infer(()) => String::new(), | |
| TyKind::Ptr(mut_ty) => { | |
| if matches!(mut_ty.ty.kind, TyKind::Infer(())) { | |
| String::new() | |
| } else { | |
| format!( | |
| "::<{}>", | |
| snippet_with_applicability(cx, mut_ty.ty.span, "/* type */", &mut app) | |
| ) | |
| } | |
| }, | |
| _ => return, | |
| }; | |
| // following `cast` does not compile because it fails to infer what type is expected | |
| // as type argument to `std::ptr::ptr_null` or `std::ptr::ptr_null_mut`, so | |
| // we omit following `cast`: | |
| let omit_cast = if let ExprKind::Call(func, []) = cast_from_expr.kind | |
| && let ExprKind::Path(ref qpath @ QPath::Resolved(None, path)) = func.kind | |
| && let Some(method_defid) = path.res.opt_def_id() | |
| { | |
| match cx.tcx.get_diagnostic_name(method_defid) { | |
| Some(sym::ptr_null) => OmitFollowedCastReason::Null(qpath), | |
| Some(sym::ptr_null_mut) => OmitFollowedCastReason::NullMut(qpath), | |
| _ => OmitFollowedCastReason::None, | |
| } | |
| } else { | |
| OmitFollowedCastReason::None | |
| }; | |
| let (help, final_suggestion) = if let Some(method) = omit_cast.corresponding_item() { | |
| // don't force absolute path | |
| let method = snippet_with_applicability(cx, qpath_span_without_turbofish(method), "..", &mut app); | |
| ("try call directly", format!("{method}{turbofish}()")) | |
| } else { | |
| let cast_expr_sugg = Sugg::hir_with_context(cx, cast_from_expr, expr.span.ctxt(), "_", &mut app); | |
| ( | |
| "try `pointer::cast`, a safer alternative", | |
| format!("{}.cast{turbofish}()", cast_expr_sugg.maybe_paren()), | |
| ) | |
| }; | |
| span_lint_and_sugg( | |
| cx, | |
| PTR_AS_PTR, | |
| expr.span, | |
| "`as` casting between raw pointers without changing their constness", | |
| help, | |
| final_suggestion, | |
| app, | |
| ); | |
| } | |
| } | |
| fn qpath_span_without_turbofish(qpath: &QPath<'_>) -> Span { | |
| if let QPath::Resolved(_, path) = qpath | |
| && let [.., last_ident] = path.segments | |
| && last_ident.args.is_some() | |
| { | |
| return qpath.span().shrink_to_lo().to(last_ident.ident.span); | |
| } | |
| qpath.span() | |
| } |