| use clippy_utils::diagnostics::span_lint_and_sugg; | |
| use clippy_utils::msrvs::{self, Msrv}; | |
| use clippy_utils::source::snippet_with_applicability; | |
| use clippy_utils::sugg::Sugg; | |
| use clippy_utils::{std_or_core, sym}; | |
| use rustc_errors::Applicability; | |
| use rustc_hir::{self as hir, Expr, ExprKind, QPath}; | |
| use rustc_lint::LateContext; | |
| use rustc_middle::ty::{self, Ty, TypeVisitableExt}; | |
| use super::PTR_CAST_CONSTNESS; | |
| pub(super) fn check<'tcx>( | |
| cx: &LateContext<'_>, | |
| expr: &Expr<'_>, | |
| cast_from_expr: &Expr<'_>, | |
| cast_from: Ty<'tcx>, | |
| cast_to: Ty<'tcx>, | |
| msrv: Msrv, | |
| ) { | |
| if let ty::RawPtr(from_ty, from_mutbl) = cast_from.kind() | |
| && let ty::RawPtr(to_ty, to_mutbl) = cast_to.kind() | |
| && from_mutbl != to_mutbl | |
| && from_ty == to_ty | |
| && !from_ty.has_erased_regions() | |
| { | |
| if let ExprKind::Call(func, []) = cast_from_expr.kind | |
| && let ExprKind::Path(QPath::Resolved(None, path)) = func.kind | |
| && let Some(defid) = path.res.opt_def_id() | |
| && let Some(prefix) = std_or_core(cx) | |
| && let mut app = Applicability::MachineApplicable | |
| && let sugg = snippet_with_applicability(cx, cast_from_expr.span, "_", &mut app) | |
| && let Some((_, after_lt)) = sugg.split_once("::<") | |
| && let Some((source, target, target_func)) = match cx.tcx.get_diagnostic_name(defid) { | |
| Some(sym::ptr_null) => Some(("const", "mutable", "null_mut")), | |
| Some(sym::ptr_null_mut) => Some(("mutable", "const", "null")), | |
| _ => None, | |
| } | |
| { | |
| span_lint_and_sugg( | |
| cx, | |
| PTR_CAST_CONSTNESS, | |
| expr.span, | |
| format!("`as` casting to make a {source} null pointer into a {target} null pointer"), | |
| format!("use `{target_func}()` directly instead"), | |
| format!("{prefix}::ptr::{target_func}::<{after_lt}"), | |
| app, | |
| ); | |
| return; | |
| } | |
| if msrv.meets(cx, msrvs::POINTER_CAST_CONSTNESS) { | |
| let mut app = Applicability::MachineApplicable; | |
| let sugg = if let ExprKind::Cast(nested_from, nested_hir_ty) = cast_from_expr.kind | |
| && let hir::TyKind::Ptr(ptr_ty) = nested_hir_ty.kind | |
| && let hir::TyKind::Infer(()) = ptr_ty.ty.kind | |
| { | |
| // `(foo as *const _).cast_mut()` fails method name resolution | |
| // avoid this by `as`-ing the full type | |
| Sugg::hir_with_context(cx, nested_from, expr.span.ctxt(), "_", &mut app).as_ty(cast_from) | |
| } else { | |
| Sugg::hir_with_context(cx, cast_from_expr, expr.span.ctxt(), "_", &mut app) | |
| }; | |
| let constness = to_mutbl.ptr_str(); | |
| span_lint_and_sugg( | |
| cx, | |
| PTR_CAST_CONSTNESS, | |
| expr.span, | |
| "`as` casting between raw pointers while changing only its constness", | |
| format!("try `pointer::cast_{constness}`, a safer alternative"), | |
| format!("{}.cast_{constness}()", sugg.maybe_paren()), | |
| app, | |
| ); | |
| } | |
| } | |
| } | |
| pub(super) fn check_null_ptr_cast_method(cx: &LateContext<'_>, expr: &Expr<'_>) { | |
| if let ExprKind::MethodCall(method, cast_from_expr, [], _) = expr.kind | |
| && let ExprKind::Call(func, []) = cast_from_expr.kind | |
| && let ExprKind::Path(QPath::Resolved(None, path)) = func.kind | |
| && let Some(defid) = path.res.opt_def_id() | |
| && let method = match (cx.tcx.get_diagnostic_name(defid), method.ident.name) { | |
| (Some(sym::ptr_null), sym::cast_mut) => "null_mut", | |
| (Some(sym::ptr_null_mut), sym::cast_const) => "null", | |
| _ => return, | |
| } | |
| && let Some(prefix) = std_or_core(cx) | |
| && let mut app = Applicability::MachineApplicable | |
| && let sugg = snippet_with_applicability(cx, cast_from_expr.span, "_", &mut app) | |
| && let Some((_, after_lt)) = sugg.split_once("::<") | |
| { | |
| span_lint_and_sugg( | |
| cx, | |
| PTR_CAST_CONSTNESS, | |
| expr.span, | |
| "changing constness of a null pointer", | |
| format!("use `{method}()` directly instead"), | |
| format!("{prefix}::ptr::{method}::<{after_lt}"), | |
| app, | |
| ); | |
| } | |
| } |