1 | use clippy_utils::diagnostics::span_lint_and_sugg; |
2 | use clippy_utils::msrvs::{self, Msrv}; |
3 | use clippy_utils::source::snippet_with_applicability; |
4 | use clippy_utils::sugg::Sugg; |
5 | use rustc_errors::Applicability; |
6 | use rustc_hir::{Expr, ExprKind, Mutability, QPath, TyKind}; |
7 | use rustc_hir_pretty::qpath_to_string; |
8 | use rustc_lint::LateContext; |
9 | use rustc_middle::ty; |
10 | use rustc_span::sym; |
11 | |
12 | use super::PTR_AS_PTR; |
13 | |
14 | enum OmitFollowedCastReason<'a> { |
15 | None, |
16 | Null(&'a QPath<'a>), |
17 | NullMut(&'a QPath<'a>), |
18 | } |
19 | |
20 | impl OmitFollowedCastReason<'_> { |
21 | fn corresponding_item(&self) -> Option<&QPath<'_>> { |
22 | match self { |
23 | OmitFollowedCastReason::None => None, |
24 | OmitFollowedCastReason::Null(x: &&{unknown}) | OmitFollowedCastReason::NullMut(x: &&{unknown}) => Some(*x), |
25 | } |
26 | } |
27 | } |
28 | |
29 | pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: Msrv) { |
30 | if let ExprKind::Cast(cast_expr, cast_to_hir_ty) = expr.kind |
31 | && let (cast_from, cast_to) = (cx.typeck_results().expr_ty(cast_expr), cx.typeck_results().expr_ty(expr)) |
32 | && let ty::RawPtr(_, from_mutbl) = cast_from.kind() |
33 | && let ty::RawPtr(to_pointee_ty, to_mutbl) = cast_to.kind() |
34 | && matches!((from_mutbl, to_mutbl), |
35 | (Mutability::Not, Mutability::Not) | (Mutability::Mut, Mutability::Mut)) |
36 | // The `U` in `pointer::cast` have to be `Sized` |
37 | // as explained here: https://github.com/rust-lang/rust/issues/60602. |
38 | && to_pointee_ty.is_sized(cx.tcx, cx.typing_env()) |
39 | && msrv.meets(cx, msrvs::POINTER_CAST) |
40 | { |
41 | let mut app = Applicability::MachineApplicable; |
42 | let turbofish = match &cast_to_hir_ty.kind { |
43 | TyKind::Infer(()) => String::new(), |
44 | TyKind::Ptr(mut_ty) => { |
45 | if matches!(mut_ty.ty.kind, TyKind::Infer(())) { |
46 | String::new() |
47 | } else { |
48 | format!( |
49 | "::< {}>" , |
50 | snippet_with_applicability(cx, mut_ty.ty.span, "/* type */" , &mut app) |
51 | ) |
52 | } |
53 | }, |
54 | _ => return, |
55 | }; |
56 | |
57 | // following `cast` does not compile because it fails to infer what type is expected |
58 | // as type argument to `std::ptr::ptr_null` or `std::ptr::ptr_null_mut`, so |
59 | // we omit following `cast`: |
60 | let omit_cast = if let ExprKind::Call(func, []) = cast_expr.kind |
61 | && let ExprKind::Path(ref qpath @ QPath::Resolved(None, path)) = func.kind |
62 | && let Some(method_defid) = path.res.opt_def_id() |
63 | { |
64 | if cx.tcx.is_diagnostic_item(sym::ptr_null, method_defid) { |
65 | OmitFollowedCastReason::Null(qpath) |
66 | } else if cx.tcx.is_diagnostic_item(sym::ptr_null_mut, method_defid) { |
67 | OmitFollowedCastReason::NullMut(qpath) |
68 | } else { |
69 | OmitFollowedCastReason::None |
70 | } |
71 | } else { |
72 | OmitFollowedCastReason::None |
73 | }; |
74 | |
75 | let (help, final_suggestion) = if let Some(method) = omit_cast.corresponding_item() { |
76 | // don't force absolute path |
77 | let method = qpath_to_string(&cx.tcx, method); |
78 | ("try call directly" , format!(" {method}{turbofish}()" )) |
79 | } else { |
80 | let cast_expr_sugg = Sugg::hir_with_applicability(cx, cast_expr, "_" , &mut app); |
81 | |
82 | ( |
83 | "try `pointer::cast`, a safer alternative" , |
84 | format!(" {}.cast {turbofish}()" , cast_expr_sugg.maybe_par()), |
85 | ) |
86 | }; |
87 | |
88 | span_lint_and_sugg( |
89 | cx, |
90 | PTR_AS_PTR, |
91 | expr.span, |
92 | "`as` casting between raw pointers without changing their constness" , |
93 | help, |
94 | final_suggestion, |
95 | app, |
96 | ); |
97 | } |
98 | } |
99 | |