add manual_dangling_ptr lint (#14107)
close #2177 changelog: [`manual_dangling_ptr`]: new lint
This commit is contained in:
commit
b46b311ee7
14 changed files with 283 additions and 11 deletions
82
clippy_lints/src/casts/manual_dangling_ptr.rs
Normal file
82
clippy_lints/src/casts/manual_dangling_ptr.rs
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::SpanRangeExt;
|
||||
use clippy_utils::ty::is_normalizable;
|
||||
use clippy_utils::{expr_or_init, match_def_path, path_def_id, paths, std_or_core};
|
||||
use rustc_ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, GenericArg, Mutability, QPath, Ty, TyKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::source_map::Spanned;
|
||||
|
||||
use super::MANUAL_DANGLING_PTR;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: &Ty<'_>) {
|
||||
if let TyKind::Ptr(ref ptr_ty) = to.kind {
|
||||
let init_expr = expr_or_init(cx, from);
|
||||
if is_expr_const_aligned(cx, init_expr, ptr_ty.ty)
|
||||
&& let Some(std_or_core) = std_or_core(cx)
|
||||
{
|
||||
let sugg_fn = match ptr_ty.mutbl {
|
||||
Mutability::Not => "ptr::dangling",
|
||||
Mutability::Mut => "ptr::dangling_mut",
|
||||
};
|
||||
|
||||
let sugg = if let TyKind::Infer(()) = ptr_ty.ty.kind {
|
||||
format!("{std_or_core}::{sugg_fn}()")
|
||||
} else if let Some(mut_ty_snip) = ptr_ty.ty.span.get_source_text(cx) {
|
||||
format!("{std_or_core}::{sugg_fn}::<{mut_ty_snip}>()")
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MANUAL_DANGLING_PTR,
|
||||
expr.span,
|
||||
"manual creation of a dangling pointer",
|
||||
"use",
|
||||
sugg,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Checks if the given expression is a call to `align_of` whose generic argument matches the target
|
||||
// type, or a positive constant literal that matches the target type's alignment.
|
||||
fn is_expr_const_aligned(cx: &LateContext<'_>, expr: &Expr<'_>, to: &Ty<'_>) -> bool {
|
||||
match expr.kind {
|
||||
ExprKind::Call(fun, _) => is_align_of_call(cx, fun, to),
|
||||
ExprKind::Lit(lit) => is_literal_aligned(cx, lit, to),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_align_of_call(cx: &LateContext<'_>, fun: &Expr<'_>, to: &Ty<'_>) -> bool {
|
||||
if let ExprKind::Path(QPath::Resolved(_, path)) = fun.kind
|
||||
&& let Some(fun_id) = path_def_id(cx, fun)
|
||||
&& match_def_path(cx, fun_id, &paths::ALIGN_OF)
|
||||
&& let Some(args) = path.segments.last().and_then(|seg| seg.args)
|
||||
&& let [GenericArg::Type(generic_ty)] = args.args
|
||||
{
|
||||
let typeck = cx.typeck_results();
|
||||
return typeck.node_type(generic_ty.hir_id) == typeck.node_type(to.hir_id);
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn is_literal_aligned(cx: &LateContext<'_>, lit: &Spanned<LitKind>, to: &Ty<'_>) -> bool {
|
||||
let LitKind::Int(val, _) = lit.node else { return false };
|
||||
if val == 0 {
|
||||
return false;
|
||||
}
|
||||
let to_mid_ty = cx.typeck_results().node_type(to.hir_id);
|
||||
is_normalizable(cx, cx.param_env, to_mid_ty)
|
||||
&& cx
|
||||
.tcx
|
||||
.layout_of(cx.typing_env().as_query_input(to_mid_ty))
|
||||
.is_ok_and(|layout| {
|
||||
let align = u128::from(layout.align.abi.bytes());
|
||||
u128::from(val) <= align
|
||||
})
|
||||
}
|
||||
|
|
@ -17,6 +17,7 @@ mod char_lit_as_u8;
|
|||
mod fn_to_numeric_cast;
|
||||
mod fn_to_numeric_cast_any;
|
||||
mod fn_to_numeric_cast_with_truncation;
|
||||
mod manual_dangling_ptr;
|
||||
mod ptr_as_ptr;
|
||||
mod ptr_cast_constness;
|
||||
mod ref_as_ptr;
|
||||
|
|
@ -759,6 +760,32 @@ declare_clippy_lint! {
|
|||
"detects `as *mut _` and `as *const _` conversion"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for casts of small constant literals or `mem::align_of` results to raw pointers.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This creates a dangling pointer and is better expressed as
|
||||
/// {`std`, `core`}`::ptr::`{`dangling`, `dangling_mut`}.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let ptr = 4 as *const u32;
|
||||
/// let aligned = std::mem::align_of::<u32>() as *const u32;
|
||||
/// let mut_ptr: *mut i64 = 8 as *mut _;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let ptr = std::ptr::dangling::<u32>();
|
||||
/// let aligned = std::ptr::dangling::<u32>();
|
||||
/// let mut_ptr: *mut i64 = std::ptr::dangling_mut();
|
||||
/// ```
|
||||
#[clippy::version = "1.87.0"]
|
||||
pub MANUAL_DANGLING_PTR,
|
||||
style,
|
||||
"casting small constant literals to pointers to create dangling pointers"
|
||||
}
|
||||
|
||||
pub struct Casts {
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
|
@ -795,6 +822,7 @@ impl_lint_pass!(Casts => [
|
|||
ZERO_PTR,
|
||||
REF_AS_PTR,
|
||||
AS_POINTER_UNDERSCORE,
|
||||
MANUAL_DANGLING_PTR,
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Casts {
|
||||
|
|
@ -823,6 +851,10 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
|
|||
fn_to_numeric_cast_with_truncation::check(cx, expr, cast_from_expr, cast_from, cast_to);
|
||||
zero_ptr::check(cx, expr, cast_from_expr, cast_to_hir);
|
||||
|
||||
if self.msrv.meets(cx, msrvs::MANUAL_DANGLING_PTR) {
|
||||
manual_dangling_ptr::check(cx, expr, cast_from_expr, cast_to_hir);
|
||||
}
|
||||
|
||||
if cast_to.is_numeric() {
|
||||
cast_possible_truncation::check(cx, expr, cast_from_expr, cast_from, cast_to, cast_to_hir.span);
|
||||
if cast_from.is_numeric() {
|
||||
|
|
|
|||
|
|
@ -97,6 +97,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::casts::FN_TO_NUMERIC_CAST_INFO,
|
||||
crate::casts::FN_TO_NUMERIC_CAST_ANY_INFO,
|
||||
crate::casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION_INFO,
|
||||
crate::casts::MANUAL_DANGLING_PTR_INFO,
|
||||
crate::casts::PTR_AS_PTR_INFO,
|
||||
crate::casts::PTR_CAST_CONSTNESS_INFO,
|
||||
crate::casts::REF_AS_PTR_INFO,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue