Skip to content

Commit 604b1c8

Browse files
committed
Add new lint dangling_ptr
1 parent ad05bc0 commit 604b1c8

File tree

9 files changed

+186
-0
lines changed

9 files changed

+186
-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: 28 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 {
@@ -820,6 +844,10 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
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);
822846

847+
if self.msrv.meets(msrvs::DANGLING_PTR) {
848+
dangling_ptr::check(cx, expr, cast_from_expr, cast_to_hir);
849+
}
850+
823851
if cast_to.is_numeric() {
824852
cast_possible_truncation::check(cx, expr, cast_from_expr, cast_from, cast_to, cast_to_hir.span);
825853
if cast_from.is_numeric() {

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
@@ -2218,6 +2218,15 @@ pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
22182218
)
22192219
}
22202220

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

clippy_utils/src/msrvs.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ macro_rules! msrv_aliases {
1818

1919
// names may refer to stabilized feature flags or library items
2020
msrv_aliases! {
21+
1,84,0 { DANGLING_PTR }
2122
1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY, CONST_MUT_REFS, CONST_UNWRAP }
2223
1,82,0 { IS_NONE_OR, REPEAT_N, RAW_REF_OP }
2324
1,81,0 { LINT_REASONS_STABILIZATION, ERROR_IN_CORE, EXPLICIT_SELF_TYPE_ELISION }

tests/ui/dangling_ptr.fixed

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
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+
}
16+
17+
#[clippy::msrv = "1.83"]
18+
fn _msrv_1_83() {
19+
// `{core, std}::ptr::dangling` was stabilized in 1.84. Do not lint this
20+
foo(1 as *const _, 1 as *mut _);
21+
}
22+
23+
#[clippy::msrv = "1.84"]
24+
fn _msrv_1_84() {
25+
foo(std::ptr::dangling(), std::ptr::dangling_mut());
26+
}

tests/ui/dangling_ptr.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
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+
}
16+
17+
#[clippy::msrv = "1.83"]
18+
fn _msrv_1_83() {
19+
// `{core, std}::ptr::dangling` was stabilized in 1.84. Do not lint this
20+
foo(1 as *const _, 1 as *mut _);
21+
}
22+
23+
#[clippy::msrv = "1.84"]
24+
fn _msrv_1_84() {
25+
foo(1 as *const _, 1 as *mut _);
26+
}

tests/ui/dangling_ptr.stderr

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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: `1 as *const _` detected
41+
--> tests/ui/dangling_ptr.rs:25:9
42+
|
43+
LL | foo(1 as *const _, 1 as *mut _);
44+
| ^^^^^^^^^^^^^ help: try: `std::ptr::dangling()`
45+
46+
error: `1 as *mut _` detected
47+
--> tests/ui/dangling_ptr.rs:25:24
48+
|
49+
LL | foo(1 as *const _, 1 as *mut _);
50+
| ^^^^^^^^^^^ help: try: `std::ptr::dangling_mut()`
51+
52+
error: aborting due to 8 previous errors
53+

0 commit comments

Comments
 (0)