blob: eb75d5576f5cf3852861ed2ffcc6ec938d7b8d36 [file] [log] [blame] [edit]
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::msrvs::Msrv;
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
use clippy_utils::sugg::has_enclosing_paren;
use clippy_utils::{get_parent_expr, is_expr_temporary_value, is_from_proc_macro, is_lint_allowed, msrvs, std_or_core};
use rustc_errors::Applicability;
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Ty, TyKind};
use rustc_lint::LateContext;
use rustc_middle::ty::adjustment::{Adjust, AutoBorrow};
use rustc_span::BytePos;
use super::BORROW_AS_PTR;
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'_>,
cast_expr: &'tcx Expr<'_>,
cast_to: &'tcx Ty<'_>,
msrv: Msrv,
) -> bool {
if let TyKind::Ptr(target) = cast_to.kind
&& !matches!(target.ty.kind, TyKind::TraitObject(..))
&& let ExprKind::AddrOf(BorrowKind::Ref, mutability, e) = cast_expr.kind
&& !is_lint_allowed(cx, BORROW_AS_PTR, expr.hir_id)
// Fix #9884
&& !is_expr_temporary_value(cx, e)
&& !is_from_proc_macro(cx, expr)
{
let mut app = Applicability::MachineApplicable;
let snip = snippet_with_context(cx, e.span, cast_expr.span.ctxt(), "..", &mut app).0;
let (suggestion, span) = if msrv.meets(cx, msrvs::RAW_REF_OP) {
// Make sure that the span to be replaced doesn't include parentheses, that could break the
// suggestion.
let span = if has_enclosing_paren(snippet_with_applicability(cx, expr.span, "", &mut app)) {
expr.span
.with_lo(expr.span.lo() + BytePos(1))
.with_hi(expr.span.hi() - BytePos(1))
} else {
expr.span
};
(format!("&raw {} {snip}", mutability.ptr_str()), span)
} else {
let Some(std_or_core) = std_or_core(cx) else {
return false;
};
let macro_name = match mutability {
Mutability::Not => "addr_of",
Mutability::Mut => "addr_of_mut",
};
(format!("{std_or_core}::ptr::{macro_name}!({snip})"), expr.span)
};
span_lint_and_sugg(cx, BORROW_AS_PTR, span, "borrow as raw pointer", "try", suggestion, app);
return true;
}
false
}
/// Check for an implicit cast from reference to raw pointer outside an explicit `as`.
pub(super) fn check_implicit_cast(cx: &LateContext<'_>, expr: &Expr<'_>) {
if !expr.span.from_expansion()
&& let ExprKind::AddrOf(BorrowKind::Ref, _, pointee) = expr.kind
&& !matches!(get_parent_expr(cx, expr).map(|e| e.kind), Some(ExprKind::Cast(..)))
&& let [deref, borrow] = cx.typeck_results().expr_adjustments(expr)
&& matches!(deref.kind, Adjust::Deref(..))
&& let Adjust::Borrow(AutoBorrow::RawPtr(mutability)) = borrow.kind
// Do not suggest taking a raw pointer to a temporary value
&& !is_expr_temporary_value(cx, pointee)
{
span_lint_and_then(cx, BORROW_AS_PTR, expr.span, "implicit borrow as raw pointer", |diag| {
diag.span_suggestion_verbose(
expr.span.until(pointee.span),
"use a raw pointer instead",
format!("&raw {} ", mutability.ptr_str()),
Applicability::MachineApplicable,
);
});
}
}