blob: c0f13f5e5782314dcd0c0e8a370f812c20ead92e [file] [log] [blame] [edit]
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,
);
}
}