Skip to content

Commit

Permalink
Add new lint dangling_ptr
Browse files Browse the repository at this point in the history
  • Loading branch information
aaron-ang committed Jan 30, 2025
1 parent 92fac5c commit 8c0a1b3
Show file tree
Hide file tree
Showing 8 changed files with 148 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5501,6 +5501,7 @@ Released 2018-09-13
[`create_dir`]: https://rust-lang.github.io/rust-clippy/master/index.html#create_dir
[`crosspointer_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#crosspointer_transmute
[`cyclomatic_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#cyclomatic_complexity
[`dangling_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#dangling_ptr
[`dbg_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro
[`debug_assert_with_mut_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#debug_assert_with_mut_call
[`decimal_literal_representation`]: https://rust-lang.github.io/rust-clippy/master/index.html#decimal_literal_representation
Expand Down
41 changes: 41 additions & 0 deletions clippy_lints/src/casts/dangling_ptr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::SpanRangeExt;
use clippy_utils::{expr_or_init, is_expr_positive_literal, std_or_core};
use rustc_errors::Applicability;
use rustc_hir::{Expr, Mutability, Ty, TyKind};
use rustc_lint::LateContext;

use super::DANGLING_PTR;

pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: &Ty<'_>) {
if let TyKind::Ptr(ref mut_ty) = to.kind {
let init_expr = expr_or_init(cx, from);
if is_expr_positive_literal(init_expr)
&& let Some(std_or_core) = std_or_core(cx)
&& let Some(var) = from.span.get_source_text(cx)
{
let (msg, sugg_fn) = match mut_ty.mutbl {
Mutability::Not => (format!("`{var} as *const _` detected"), "ptr::dangling"),
Mutability::Mut => (format!("`{var} as *mut _` detected"), "ptr::dangling_mut"),
};

let sugg = if let TyKind::Infer = mut_ty.ty.kind {
format!("{std_or_core}::{sugg_fn}()")
} else if let Some(mut_ty_snip) = mut_ty.ty.span.get_source_text(cx) {
format!("{std_or_core}::{sugg_fn}::<{mut_ty_snip}>()")
} else {
return;
};

span_lint_and_sugg(
cx,
DANGLING_PTR,
expr.span,
msg,
"try",
sugg,
Applicability::MachineApplicable,
);
}
}
}
25 changes: 25 additions & 0 deletions clippy_lints/src/casts/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ mod cast_sign_loss;
mod cast_slice_different_sizes;
mod cast_slice_from_raw_parts;
mod char_lit_as_u8;
mod dangling_ptr;
mod fn_to_numeric_cast;
mod fn_to_numeric_cast_any;
mod fn_to_numeric_cast_with_truncation;
Expand Down Expand Up @@ -754,6 +755,28 @@ declare_clippy_lint! {
"detects `as *mut _` and `as *const _` conversion"
}

declare_clippy_lint! {
/// ### What it does
/// Catches casts from a positive integer literal to some pointer type
///
/// ### Why is this bad?
/// This creates a pointer with no provenance which might cause incorrect compiler optimizations.
/// It is better expressed as {`std`, `core`}`::ptr::`{`dangling`, `dangling_mut`}.
///
/// ### Example
/// ```no_run
/// let a = 1 as *const u32;
/// ```
/// Use instead:
/// ```no_run
/// let a = std::ptr::dangling::<u32>();
/// ```
#[clippy::version = "1.86.0"]
pub DANGLING_PTR,
pedantic,
"casting a positive integer literal to a pointer with no provenance"
}

pub struct Casts {
msrv: Msrv,
}
Expand Down Expand Up @@ -792,6 +815,7 @@ impl_lint_pass!(Casts => [
ZERO_PTR,
REF_AS_PTR,
AS_POINTER_UNDERSCORE,
DANGLING_PTR,
]);

impl<'tcx> LateLintPass<'tcx> for Casts {
Expand Down Expand Up @@ -819,6 +843,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
fn_to_numeric_cast::check(cx, expr, cast_from_expr, cast_from, cast_to);
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);
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);
Expand Down
1 change: 1 addition & 0 deletions clippy_lints/src/declared_lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
crate::casts::CAST_SLICE_DIFFERENT_SIZES_INFO,
crate::casts::CAST_SLICE_FROM_RAW_PARTS_INFO,
crate::casts::CHAR_LIT_AS_U8_INFO,
crate::casts::DANGLING_PTR_INFO,
crate::casts::FN_TO_NUMERIC_CAST_INFO,
crate::casts::FN_TO_NUMERIC_CAST_ANY_INFO,
crate::casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION_INFO,
Expand Down
9 changes: 9 additions & 0 deletions clippy_utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2219,6 +2219,15 @@ pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
)
}

pub fn is_expr_positive_literal(expr: &Expr<'_>) -> bool {
if let ExprKind::Lit(spanned) = expr.kind {
if let LitKind::Int(v, _) = spanned.node {
return v > 0;
}
}
false
}

/// Checks if the expression is the final expression returned from a block.
pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
matches!(tcx.parent_hir_node(expr.hir_id), Node::Block(..))
Expand Down
15 changes: 15 additions & 0 deletions tests/ui/dangling_ptr.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#![warn(clippy::dangling_ptr)]

pub fn foo(_const: *const f32, _mut: *mut i64) {}

fn main() {
let _ = std::ptr::dangling::<usize>();
let _ = std::ptr::dangling_mut::<f64>();
let _: *const u8 = std::ptr::dangling();

foo(0 as _, 0 as _);
foo(std::ptr::dangling(), std::ptr::dangling_mut());

let z = 1;
let _ = std::ptr::dangling::<usize>();
}
15 changes: 15 additions & 0 deletions tests/ui/dangling_ptr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#![warn(clippy::dangling_ptr)]

pub fn foo(_const: *const f32, _mut: *mut i64) {}

fn main() {
let _ = 1 as *const usize;
let _ = 1 as *mut f64;
let _: *const u8 = 1 as *const _;

foo(0 as _, 0 as _);
foo(1 as *const _, 1 as *mut _);

let z = 1;
let _ = z as *const usize;
}
41 changes: 41 additions & 0 deletions tests/ui/dangling_ptr.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
error: `1 as *const _` detected
--> tests/ui/dangling_ptr.rs:6:13
|
LL | let _ = 1 as *const usize;
| ^^^^^^^^^^^^^^^^^ help: try: `std::ptr::dangling::<usize>()`
|
= note: `-D clippy::dangling-ptr` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::dangling_ptr)]`

error: `1 as *mut _` detected
--> tests/ui/dangling_ptr.rs:7:13
|
LL | let _ = 1 as *mut f64;
| ^^^^^^^^^^^^^ help: try: `std::ptr::dangling_mut::<f64>()`

error: `1 as *const _` detected
--> tests/ui/dangling_ptr.rs:8:24
|
LL | let _: *const u8 = 1 as *const _;
| ^^^^^^^^^^^^^ help: try: `std::ptr::dangling()`

error: `1 as *const _` detected
--> tests/ui/dangling_ptr.rs:11:9
|
LL | foo(1 as *const _, 1 as *mut _);
| ^^^^^^^^^^^^^ help: try: `std::ptr::dangling()`

error: `1 as *mut _` detected
--> tests/ui/dangling_ptr.rs:11:24
|
LL | foo(1 as *const _, 1 as *mut _);
| ^^^^^^^^^^^ help: try: `std::ptr::dangling_mut()`

error: `z as *const _` detected
--> tests/ui/dangling_ptr.rs:14:13
|
LL | let _ = z as *const usize;
| ^^^^^^^^^^^^^^^^^ help: try: `std::ptr::dangling::<usize>()`

error: aborting due to 6 previous errors

0 comments on commit 8c0a1b3

Please sign in to comment.