Skip to content

Commit 33867dd

Browse files
committed
Add new lint dangling_ptr
1 parent e6d9641 commit 33867dd

File tree

13 files changed

+238
-3
lines changed

13 files changed

+238
-3
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: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
use clippy_utils::diagnostics::span_lint_and_sugg;
2+
use clippy_utils::source::SpanRangeExt;
3+
use clippy_utils::{expr_or_init, is_expr_const_aligned, 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_const_aligned(cx, init_expr)
14+
&& let Some(std_or_core) = std_or_core(cx)
15+
{
16+
let sugg_fn = match mut_ty.mutbl {
17+
Mutability::Not => "ptr::dangling",
18+
Mutability::Mut => "ptr::dangling_mut",
19+
};
20+
21+
let sugg = if let TyKind::Infer(()) = mut_ty.ty.kind {
22+
format!("{std_or_core}::{sugg_fn}()")
23+
} else if let Some(mut_ty_snip) = mut_ty.ty.span.get_source_text(cx) {
24+
format!("{std_or_core}::{sugg_fn}::<{mut_ty_snip}>()")
25+
} else {
26+
return;
27+
};
28+
29+
span_lint_and_sugg(
30+
cx,
31+
DANGLING_PTR,
32+
expr.span,
33+
"manual creation of a dangling pointer",
34+
"try",
35+
sugg,
36+
Applicability::MachineApplicable,
37+
);
38+
}
39+
}
40+
}

clippy_lints/src/casts/mod.rs

Lines changed: 30 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,30 @@ declare_clippy_lint! {
754755
"detects `as *mut _` and `as *const _` conversion"
755756
}
756757

758+
declare_clippy_lint! {
759+
/// ### What it does
760+
/// Checks for 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 = 4 as *const u32;
769+
/// let b = std::mem::align_of::<u32>() as *const u32;
770+
/// ```
771+
/// Use instead:
772+
/// ```no_run
773+
/// let a = std::ptr::dangling::<u32>();
774+
/// let b = std::ptr::dangling::<u32>();
775+
/// ```
776+
#[clippy::version = "1.86.0"]
777+
pub DANGLING_PTR,
778+
style,
779+
"casting a positive integer literal to a pointer with no provenance"
780+
}
781+
757782
pub struct Casts {
758783
msrv: Msrv,
759784
}
@@ -792,6 +817,7 @@ impl_lint_pass!(Casts => [
792817
ZERO_PTR,
793818
REF_AS_PTR,
794819
AS_POINTER_UNDERSCORE,
820+
DANGLING_PTR,
795821
]);
796822

797823
impl<'tcx> LateLintPass<'tcx> for Casts {
@@ -820,6 +846,10 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
820846
fn_to_numeric_cast_with_truncation::check(cx, expr, cast_from_expr, cast_from, cast_to);
821847
zero_ptr::check(cx, expr, cast_from_expr, cast_to_hir);
822848

849+
if self.msrv.meets(msrvs::DANGLING_PTR) {
850+
dangling_ptr::check(cx, expr, cast_from_expr, cast_to_hir);
851+
}
852+
823853
if cast_to.is_numeric() {
824854
cast_possible_truncation::check(cx, expr, cast_from_expr, cast_from, cast_to, cast_to_hir.span);
825855
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: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,15 +120,17 @@ use rustc_middle::ty::{
120120
TyCtxt, TypeVisitableExt, UintTy, UpvarCapture,
121121
};
122122
use rustc_span::hygiene::{ExpnKind, MacroKind};
123-
use rustc_span::source_map::SourceMap;
123+
use rustc_span::source_map::{SourceMap, Spanned};
124124
use rustc_span::symbol::{Ident, Symbol, kw};
125125
use rustc_span::{InnerSpan, Span, sym};
126126
use rustc_target::abi::Integer;
127127
use visitors::{Visitable, for_each_unconsumed_temporary};
128128

129129
use crate::consts::{ConstEvalCtxt, Constant, mir_to_const};
130130
use crate::higher::Range;
131-
use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type};
131+
use crate::ty::{
132+
adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_normalizable, is_recursively_primitive_type,
133+
};
132134
use crate::visitors::for_each_expr_without_closures;
133135
use rustc_middle::hir::nested_filter;
134136

@@ -2226,6 +2228,44 @@ pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
22262228
)
22272229
}
22282230

2231+
// Checks if the given expression is a call to `align_of`
2232+
// or casting to a type with the same alignment
2233+
pub fn is_expr_const_aligned(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
2234+
match expr.kind {
2235+
ExprKind::Call(fun, _) => is_align_of_call(cx, fun, expr),
2236+
ExprKind::Lit(lit) => is_align_lit(cx, lit, expr),
2237+
_ => false,
2238+
}
2239+
}
2240+
2241+
fn is_align_of_call(cx: &LateContext<'_>, fun: &Expr<'_>, expr: &Expr<'_>) -> bool {
2242+
let ExprKind::Path(QPath::Resolved(None, path)) = fun.kind else {
2243+
return false;
2244+
};
2245+
let Some(args) = path.segments.last().and_then(|seg| seg.args) else {
2246+
return false;
2247+
};
2248+
let [hir::GenericArg::Type(ty)] = args.args else {
2249+
return false;
2250+
};
2251+
path_def_id(cx, fun).is_some_and(|id| {
2252+
let typeck = cx.typeck_results();
2253+
match_any_def_paths(cx, id, &[&paths::CORE_ALIGN_OF, &paths::STD_ALIGN_OF]).is_some()
2254+
&& typeck.node_type(ty.hir_id) == typeck.expr_ty(expr)
2255+
})
2256+
}
2257+
2258+
fn is_align_lit(cx: &LateContext<'_>, lit: &Spanned<LitKind>, expr: &Expr<'_>) -> bool {
2259+
let LitKind::Int(val, _) = lit.node else { return false };
2260+
let ty = cx.typeck_results().expr_ty(expr);
2261+
if !is_normalizable(cx, cx.param_env, ty) {
2262+
return false;
2263+
}
2264+
cx.tcx
2265+
.layout_of(cx.typing_env().as_query_input(ty))
2266+
.is_ok_and(|layout| val == u128::from(layout.align.abi.bytes()))
2267+
}
2268+
22292269
/// Checks if the expression is the final expression returned from a block.
22302270
pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
22312271
matches!(tcx.parent_hir_node(expr.hir_id), Node::Block(..))

clippy_utils/src/msrvs.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ macro_rules! msrv_aliases {
1919

2020
// names may refer to stabilized feature flags or library items
2121
msrv_aliases! {
22-
1,84,0 { CONST_OPTION_AS_SLICE }
22+
1,84,0 { CONST_OPTION_AS_SLICE, DANGLING_PTR }
2323
1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY, CONST_MUT_REFS, CONST_UNWRAP }
2424
1,82,0 { IS_NONE_OR, REPEAT_N, RAW_REF_OP }
2525
1,81,0 { LINT_REASONS_STABILIZATION, ERROR_IN_CORE, EXPLICIT_SELF_TYPE_ELISION }

clippy_utils/src/paths.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ pub const CHILD_KILL: [&str; 4] = ["std", "process", "Child", "kill"];
3535
pub const PANIC_ANY: [&str; 3] = ["std", "panic", "panic_any"];
3636
pub const CHAR_IS_ASCII: [&str; 5] = ["core", "char", "methods", "<impl char>", "is_ascii"];
3737
pub const STDIN: [&str; 4] = ["std", "io", "stdio", "Stdin"];
38+
pub const STD_ALIGN_OF: [&str; 3] = ["std", "mem", "align_of"];
39+
pub const CORE_ALIGN_OF: [&str; 3] = ["core", "mem", "align_of"];
3840

3941
// Paths in clippy itself
4042
pub const MSRV: [&str; 3] = ["clippy_utils", "msrvs", "Msrv"];

tests/ui/dangling_ptr.fixed

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#![warn(clippy::dangling_ptr)]
2+
use std::mem;
3+
4+
pub fn foo(_const: *const f32, _mut: *mut i64) {}
5+
6+
fn main() {
7+
let _ = std::ptr::dangling::<u32>();
8+
let _ = std::ptr::dangling_mut::<f64>();
9+
let _: *const u8 = std::ptr::dangling();
10+
11+
let _ = mem::align_of::<u32>() as u32;
12+
let _ = mem::align_of::<u64>() as u64;
13+
14+
foo(std::ptr::dangling(), std::ptr::dangling_mut());
15+
16+
// should not lint
17+
let _ = 1 as *const u32;
18+
let _ = mem::align_of::<u32>() as u32;
19+
20+
foo(0 as _, 0 as _);
21+
}
22+
23+
#[clippy::msrv = "1.83"]
24+
fn _msrv_1_83() {
25+
// `{core, std}::ptr::dangling` was stabilized in 1.84. Do not lint this
26+
foo(8 as *const _, 8 as *mut _);
27+
}
28+
29+
#[clippy::msrv = "1.84"]
30+
fn _msrv_1_84() {
31+
foo(std::ptr::dangling(), std::ptr::dangling_mut());
32+
}

tests/ui/dangling_ptr.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#![warn(clippy::dangling_ptr)]
2+
use std::mem;
3+
4+
pub fn foo(_const: *const f32, _mut: *mut i64) {}
5+
6+
fn main() {
7+
let _ = 8 as *const u32;
8+
let _ = 8 as *mut f64;
9+
let _: *const u8 = 8 as *const _;
10+
11+
let _ = mem::align_of::<u32>() as u32;
12+
let _ = mem::align_of::<u64>() as u64;
13+
14+
foo(8 as *const _, 8 as *mut _);
15+
16+
// should not lint
17+
let _ = 1 as *const u32;
18+
let _ = mem::align_of::<u32>() as u32;
19+
20+
foo(0 as _, 0 as _);
21+
}
22+
23+
#[clippy::msrv = "1.83"]
24+
fn _msrv_1_83() {
25+
// `{core, std}::ptr::dangling` was stabilized in 1.84. Do not lint this
26+
foo(8 as *const _, 8 as *mut _);
27+
}
28+
29+
#[clippy::msrv = "1.84"]
30+
fn _msrv_1_84() {
31+
foo(8 as *const _, 8 as *mut _);
32+
}

tests/ui/dangling_ptr.stderr

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
error: manual creation of a dangling pointer
2+
--> tests/ui/dangling_ptr.rs:7:13
3+
|
4+
LL | let _ = 8 as *const u32;
5+
| ^^^^^^^^^^^^^^^ help: try: `std::ptr::dangling::<u32>()`
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: manual creation of a dangling pointer
11+
--> tests/ui/dangling_ptr.rs:8:13
12+
|
13+
LL | let _ = 8 as *mut f64;
14+
| ^^^^^^^^^^^^^ help: try: `std::ptr::dangling_mut::<f64>()`
15+
16+
error: manual creation of a dangling pointer
17+
--> tests/ui/dangling_ptr.rs:9:24
18+
|
19+
LL | let _: *const u8 = 8 as *const _;
20+
| ^^^^^^^^^^^^^ help: try: `std::ptr::dangling()`
21+
22+
error: manual creation of a dangling pointer
23+
--> tests/ui/dangling_ptr.rs:14:9
24+
|
25+
LL | foo(8 as *const _, 8 as *mut _);
26+
| ^^^^^^^^^^^^^ help: try: `std::ptr::dangling()`
27+
28+
error: manual creation of a dangling pointer
29+
--> tests/ui/dangling_ptr.rs:14:24
30+
|
31+
LL | foo(8 as *const _, 8 as *mut _);
32+
| ^^^^^^^^^^^ help: try: `std::ptr::dangling_mut()`
33+
34+
error: manual creation of a dangling pointer
35+
--> tests/ui/dangling_ptr.rs:31:9
36+
|
37+
LL | foo(8 as *const _, 8 as *mut _);
38+
| ^^^^^^^^^^^^^ help: try: `std::ptr::dangling()`
39+
40+
error: manual creation of a dangling pointer
41+
--> tests/ui/dangling_ptr.rs:31:24
42+
|
43+
LL | foo(8 as *const _, 8 as *mut _);
44+
| ^^^^^^^^^^^ help: try: `std::ptr::dangling_mut()`
45+
46+
error: aborting due to 7 previous errors
47+

tests/ui/ptr_cast_constness.stderr

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,16 @@ error: `as` casting to make a const null pointer into a mutable null pointer
4949
LL | let _ = ptr::null::<String>() as *mut String;
5050
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null_mut()` directly instead: `std::ptr::null_mut::<String>()`
5151

52+
[clippy_utils/src/lib.rs:2251:5] typeck.node_type(generic_ty.hir_id) = std::string::String
53+
[clippy_utils/src/lib.rs:2252:5] typeck.expr_ty(expr) = *const std::string::String
5254
error: `as` casting to make a mutable null pointer into a const null pointer
5355
--> tests/ui/ptr_cast_constness.rs:76:13
5456
|
5557
LL | let _ = ptr::null_mut::<u32>() as *const u32;
5658
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null()` directly instead: `std::ptr::null::<u32>()`
5759

60+
[clippy_utils/src/lib.rs:2251:5] typeck.node_type(generic_ty.hir_id) = u32
61+
[clippy_utils/src/lib.rs:2252:5] typeck.expr_ty(expr) = *mut u32
5862
error: changing constness of a null pointer
5963
--> tests/ui/ptr_cast_constness.rs:77:13
6064
|
@@ -75,6 +79,8 @@ LL | let _ = inline!(ptr::null::<u32>() as *mut u32);
7579
|
7680
= note: this error originates in the macro `__inline_mac_fn_null_pointers` (in Nightly builds, run with -Z macro-backtrace for more info)
7781

82+
[clippy_utils/src/lib.rs:2251:5] typeck.node_type(generic_ty.hir_id) = u32
83+
[clippy_utils/src/lib.rs:2252:5] typeck.expr_ty(expr) = *const u32
7884
error: changing constness of a null pointer
7985
--> tests/ui/ptr_cast_constness.rs:82:21
8086
|

tests/ui/transmute_null_to_fn.stderr

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ LL | let _: fn() = std::mem::transmute(std::ptr::null::<()>() as *const
4040
|
4141
= help: try wrapping your function pointer type in `Option<T>` instead, and using `None` as a null pointer value
4242

43+
[clippy_utils/src/lib.rs:2251:5] typeck.node_type(generic_ty.hir_id) = ()
44+
[clippy_utils/src/lib.rs:2252:5] typeck.expr_ty(expr) = *const ()
4345
error: transmuting a known null pointer into a function pointer
4446
--> tests/ui/transmute_null_to_fn.rs:34:23
4547
|

tests/ui/unnecessary_cast.stderr

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ error: casting raw pointers to the same type and constness is unnecessary (`*con
7979
LL | owo::<u32>([1u32].as_ptr()) as *const u32;
8080
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `owo::<u32>([1u32].as_ptr())`
8181

82+
[clippy_utils/src/lib.rs:2251:5] typeck.node_type(generic_ty.hir_id) = u32
83+
[clippy_utils/src/lib.rs:2252:5] typeck.expr_ty(expr) = *const u32
8284
error: casting raw pointers to the same type and constness is unnecessary (`*const u8` -> `*const u8`)
8385
--> tests/ui/unnecessary_cast.rs:81:5
8486
|

0 commit comments

Comments
 (0)