1use clippy_utils::diagnostics::span_lint_and_sugg;
2use clippy_utils::msrvs::{self, Msrv};
3use clippy_utils::source::snippet_with_applicability;
4use clippy_utils::sugg::Sugg;
5use rustc_errors::Applicability;
6use rustc_hir::{Expr, ExprKind, Mutability, QPath, TyKind};
7use rustc_hir_pretty::qpath_to_string;
8use rustc_lint::LateContext;
9use rustc_middle::ty;
10use rustc_span::sym;
11
12use super::PTR_AS_PTR;
13
14enum OmitFollowedCastReason<'a> {
15 None,
16 Null(&'a QPath<'a>),
17 NullMut(&'a QPath<'a>),
18}
19
20impl 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
29pub(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

Provided by KDAB

Privacy Policy
Learn Rust with the experts
Find out more