Skip to content

Commit 8c0a1b3

Browse files
committed
Add new lint dangling_ptr
1 parent 92fac5c commit 8c0a1b3

File tree

8 files changed

+148
-0
lines changed

8 files changed

+148
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5501,6 +5501,7 @@ Released 2018-09-13
55015501
[`create_dir`]: https://rust-lang.github.io/rust-clippy/master/index.html#create_dir
55025502
[`crosspointer_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#crosspointer_transmute
55035503
[`cyclomatic_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#cyclomatic_complexity
5504+
[`dangling_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#dangling_ptr
55045505
[`dbg_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro
55055506
[`debug_assert_with_mut_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#debug_assert_with_mut_call
55065507
[`decimal_literal_representation`]: https://rust-lang.github.io/rust-clippy/master/index.html#decimal_literal_representation
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
use clippy_utils::diagnostics::span_lint_and_sugg;
2+
use clippy_utils::source::SpanRangeExt;
3+
use clippy_utils::{expr_or_init, is_expr_positive_literal, std_or_core};
4+
use rustc_errors::Applicability;
5+
use rustc_hir::{Expr, Mutability, Ty, TyKind};
6+
use rustc_lint::LateContext;
7+
8+
use super::DANGLING_PTR;
9+
10+
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: &Ty<'_>) {
11+
if let TyKind::Ptr(ref mut_ty) = to.kind {
12+
let init_expr = expr_or_init(cx, from);
13+
if is_expr_positive_literal(init_expr)
14+
&& let Some(std_or_core) = std_or_core(cx)
15+
&& let Some(var) = from.span.get_source_text(cx)
16+
{
17+
let (msg, sugg_fn) = match mut_ty.mutbl {
18+
Mutability::Not => (format!("`{var} as *const _` detected"), "ptr::dangling"),
19+
Mutability::Mut => (format!("`{var} as *mut _` detected"), "ptr::dangling_mut"),
20+
};
21+
22+
let sugg = if let TyKind::Infer = mut_ty.ty.kind {
23+
format!("{std_or_core}::{sugg_fn}()")
24+
} else if let Some(mut_ty_snip) = mut_ty.ty.span.get_source_text(cx) {
25+
format!("{std_or_core}::{sugg_fn}::<{mut_ty_snip}>()")
26+
} else {
27+
return;
28+
};
29+
30+
span_lint_and_sugg(
31+
cx,
32+
DANGLING_PTR,
33+
expr.span,
34+
msg,
35+
"try",
36+
sugg,
37+
Applicability::MachineApplicable,
38+
);
39+
}
40+
}
41+
}

clippy_lints/src/casts/mod.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ mod cast_sign_loss;
1414
mod cast_slice_different_sizes;
1515
mod cast_slice_from_raw_parts;
1616
mod char_lit_as_u8;
17+
mod dangling_ptr;
1718
mod fn_to_numeric_cast;
1819
mod fn_to_numeric_cast_any;
1920
mod fn_to_numeric_cast_with_truncation;
@@ -754,6 +755,28 @@ declare_clippy_lint! {
754755
"detects `as *mut _` and `as *const _` conversion"
755756
}
756757

758+
declare_clippy_lint! {
759+
/// ### What it does
760+
/// Catches casts from a positive integer literal to some pointer type
761+
///
762+
/// ### Why is this bad?
763+
/// This creates a pointer with no provenance which might cause incorrect compiler optimizations.
764+
/// It is better expressed as {`std`, `core`}`::ptr::`{`dangling`, `dangling_mut`}.
765+
///
766+
/// ### Example
767+
/// ```no_run
768+
/// let a = 1 as *const u32;
769+
/// ```
770+
/// Use instead:
771+
/// ```no_run
772+
/// let a = std::ptr::dangling::<u32>();
773+
/// ```
774+
#[clippy::version = "1.86.0"]
775+
pub DANGLING_PTR,
776+
pedantic,
777+
"casting a positive integer literal to a pointer with no provenance"
778+
}
779+
757780
pub struct Casts {
758781
msrv: Msrv,
759782
}
@@ -792,6 +815,7 @@ impl_lint_pass!(Casts => [
792815
ZERO_PTR,
793816
REF_AS_PTR,
794817
AS_POINTER_UNDERSCORE,
818+
DANGLING_PTR,
795819
]);
796820

797821
impl<'tcx> LateLintPass<'tcx> for Casts {
@@ -819,6 +843,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
819843
fn_to_numeric_cast::check(cx, expr, cast_from_expr, cast_from, cast_to);
820844
fn_to_numeric_cast_with_truncation::check(cx, expr, cast_from_expr, cast_from, cast_to);
821845
zero_ptr::check(cx, expr, cast_from_expr, cast_to_hir);
846+
dangling_ptr::check(cx, expr, cast_from_expr, cast_to_hir);
822847

823848
if cast_to.is_numeric() {
824849
cast_possible_truncation::check(cx, expr, cast_from_expr, cast_from, cast_to, cast_to_hir.span);

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
9393
crate::casts::CAST_SLICE_DIFFERENT_SIZES_INFO,
9494
crate::casts::CAST_SLICE_FROM_RAW_PARTS_INFO,
9595
crate::casts::CHAR_LIT_AS_U8_INFO,
96+
crate::casts::DANGLING_PTR_INFO,
9697
crate::casts::FN_TO_NUMERIC_CAST_INFO,
9798
crate::casts::FN_TO_NUMERIC_CAST_ANY_INFO,
9899
crate::casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION_INFO,

clippy_utils/src/lib.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2219,6 +2219,15 @@ pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
22192219
)
22202220
}
22212221

2222+
pub fn is_expr_positive_literal(expr: &Expr<'_>) -> bool {
2223+
if let ExprKind::Lit(spanned) = expr.kind {
2224+
if let LitKind::Int(v, _) = spanned.node {
2225+
return v > 0;
2226+
}
2227+
}
2228+
false
2229+
}
2230+
22222231
/// Checks if the expression is the final expression returned from a block.
22232232
pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
22242233
matches!(tcx.parent_hir_node(expr.hir_id), Node::Block(..))

tests/ui/dangling_ptr.fixed

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#![warn(clippy::dangling_ptr)]
2+
3+
pub fn foo(_const: *const f32, _mut: *mut i64) {}
4+
5+
fn main() {
6+
let _ = std::ptr::dangling::<usize>();
7+
let _ = std::ptr::dangling_mut::<f64>();
8+
let _: *const u8 = std::ptr::dangling();
9+
10+
foo(0 as _, 0 as _);
11+
foo(std::ptr::dangling(), std::ptr::dangling_mut());
12+
13+
let z = 1;
14+
let _ = std::ptr::dangling::<usize>();
15+
}

tests/ui/dangling_ptr.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#![warn(clippy::dangling_ptr)]
2+
3+
pub fn foo(_const: *const f32, _mut: *mut i64) {}
4+
5+
fn main() {
6+
let _ = 1 as *const usize;
7+
let _ = 1 as *mut f64;
8+
let _: *const u8 = 1 as *const _;
9+
10+
foo(0 as _, 0 as _);
11+
foo(1 as *const _, 1 as *mut _);
12+
13+
let z = 1;
14+
let _ = z as *const usize;
15+
}

tests/ui/dangling_ptr.stderr

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
error: `1 as *const _` detected
2+
--> tests/ui/dangling_ptr.rs:6:13
3+
|
4+
LL | let _ = 1 as *const usize;
5+
| ^^^^^^^^^^^^^^^^^ help: try: `std::ptr::dangling::<usize>()`
6+
|
7+
= note: `-D clippy::dangling-ptr` implied by `-D warnings`
8+
= help: to override `-D warnings` add `#[allow(clippy::dangling_ptr)]`
9+
10+
error: `1 as *mut _` detected
11+
--> tests/ui/dangling_ptr.rs:7:13
12+
|
13+
LL | let _ = 1 as *mut f64;
14+
| ^^^^^^^^^^^^^ help: try: `std::ptr::dangling_mut::<f64>()`
15+
16+
error: `1 as *const _` detected
17+
--> tests/ui/dangling_ptr.rs:8:24
18+
|
19+
LL | let _: *const u8 = 1 as *const _;
20+
| ^^^^^^^^^^^^^ help: try: `std::ptr::dangling()`
21+
22+
error: `1 as *const _` detected
23+
--> tests/ui/dangling_ptr.rs:11:9
24+
|
25+
LL | foo(1 as *const _, 1 as *mut _);
26+
| ^^^^^^^^^^^^^ help: try: `std::ptr::dangling()`
27+
28+
error: `1 as *mut _` detected
29+
--> tests/ui/dangling_ptr.rs:11:24
30+
|
31+
LL | foo(1 as *const _, 1 as *mut _);
32+
| ^^^^^^^^^^^ help: try: `std::ptr::dangling_mut()`
33+
34+
error: `z as *const _` detected
35+
--> tests/ui/dangling_ptr.rs:14:13
36+
|
37+
LL | let _ = z as *const usize;
38+
| ^^^^^^^^^^^^^^^^^ help: try: `std::ptr::dangling::<usize>()`
39+
40+
error: aborting due to 6 previous errors
41+

0 commit comments

Comments
 (0)