diff --git a/CHANGELOG.md b/CHANGELOG.md index b87d026fda19..8a6a28531031 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/clippy_lints/src/casts/dangling_ptr.rs b/clippy_lints/src/casts/dangling_ptr.rs new file mode 100644 index 000000000000..9c4dee3bd9ff --- /dev/null +++ b/clippy_lints/src/casts/dangling_ptr.rs @@ -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, + ); + } + } +} diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index d90cf124fe42..888e8e13dca9 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -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; @@ -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::(); + /// ``` + #[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, } @@ -792,6 +815,7 @@ impl_lint_pass!(Casts => [ ZERO_PTR, REF_AS_PTR, AS_POINTER_UNDERSCORE, + DANGLING_PTR, ]); impl<'tcx> LateLintPass<'tcx> for Casts { @@ -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); diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 6d6b415f8cdd..e50bafe6c27f 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -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, diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 6869078ba0ad..270348d4f55d 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -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(..)) diff --git a/tests/ui/dangling_ptr.fixed b/tests/ui/dangling_ptr.fixed new file mode 100644 index 000000000000..1e37f81cbfc3 --- /dev/null +++ b/tests/ui/dangling_ptr.fixed @@ -0,0 +1,15 @@ +#![warn(clippy::dangling_ptr)] + +pub fn foo(_const: *const f32, _mut: *mut i64) {} + +fn main() { + let _ = std::ptr::dangling::(); + let _ = std::ptr::dangling_mut::(); + 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::(); +} diff --git a/tests/ui/dangling_ptr.rs b/tests/ui/dangling_ptr.rs new file mode 100644 index 000000000000..614e545f695b --- /dev/null +++ b/tests/ui/dangling_ptr.rs @@ -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; +} diff --git a/tests/ui/dangling_ptr.stderr b/tests/ui/dangling_ptr.stderr new file mode 100644 index 000000000000..10bb37fbe760 --- /dev/null +++ b/tests/ui/dangling_ptr.stderr @@ -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::()` + | + = 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::()` + +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::()` + +error: aborting due to 6 previous errors +