diff --git a/CHANGELOG.md b/CHANGELOG.md index 3caf78581c1c..77c397f04c6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5790,6 +5790,7 @@ Released 2018-09-13 [`manual_c_str_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_c_str_literals [`manual_clamp`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp [`manual_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_contains +[`manual_dangling_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_dangling_ptr [`manual_div_ceil`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_div_ceil [`manual_filter`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter [`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map diff --git a/clippy_lints/src/casts/manual_dangling_ptr.rs b/clippy_lints/src/casts/manual_dangling_ptr.rs new file mode 100644 index 000000000000..8ace27eca895 --- /dev/null +++ b/clippy_lints/src/casts/manual_dangling_ptr.rs @@ -0,0 +1,82 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::SpanRangeExt; +use clippy_utils::ty::is_normalizable; +use clippy_utils::{expr_or_init, match_def_path, path_def_id, paths, std_or_core}; +use rustc_ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, GenericArg, Mutability, QPath, Ty, TyKind}; +use rustc_lint::LateContext; +use rustc_span::source_map::Spanned; + +use super::MANUAL_DANGLING_PTR; + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: &Ty<'_>) { + if let TyKind::Ptr(ref ptr_ty) = to.kind { + let init_expr = expr_or_init(cx, from); + if is_expr_const_aligned(cx, init_expr, ptr_ty.ty) + && let Some(std_or_core) = std_or_core(cx) + { + let sugg_fn = match ptr_ty.mutbl { + Mutability::Not => "ptr::dangling", + Mutability::Mut => "ptr::dangling_mut", + }; + + let sugg = if let TyKind::Infer(()) = ptr_ty.ty.kind { + format!("{std_or_core}::{sugg_fn}()") + } else if let Some(mut_ty_snip) = ptr_ty.ty.span.get_source_text(cx) { + format!("{std_or_core}::{sugg_fn}::<{mut_ty_snip}>()") + } else { + return; + }; + + span_lint_and_sugg( + cx, + MANUAL_DANGLING_PTR, + expr.span, + "manual creation of a dangling pointer", + "use", + sugg, + Applicability::MachineApplicable, + ); + } + } +} + +// Checks if the given expression is a call to `align_of` whose generic argument matches the target +// type, or a positive constant literal that matches the target type's alignment. +fn is_expr_const_aligned(cx: &LateContext<'_>, expr: &Expr<'_>, to: &Ty<'_>) -> bool { + match expr.kind { + ExprKind::Call(fun, _) => is_align_of_call(cx, fun, to), + ExprKind::Lit(lit) => is_literal_aligned(cx, lit, to), + _ => false, + } +} + +fn is_align_of_call(cx: &LateContext<'_>, fun: &Expr<'_>, to: &Ty<'_>) -> bool { + if let ExprKind::Path(QPath::Resolved(_, path)) = fun.kind + && let Some(fun_id) = path_def_id(cx, fun) + && match_def_path(cx, fun_id, &paths::ALIGN_OF) + && let Some(args) = path.segments.last().and_then(|seg| seg.args) + && let [GenericArg::Type(generic_ty)] = args.args + { + let typeck = cx.typeck_results(); + return typeck.node_type(generic_ty.hir_id) == typeck.node_type(to.hir_id); + } + false +} + +fn is_literal_aligned(cx: &LateContext<'_>, lit: &Spanned, to: &Ty<'_>) -> bool { + let LitKind::Int(val, _) = lit.node else { return false }; + if val == 0 { + return false; + } + let to_mid_ty = cx.typeck_results().node_type(to.hir_id); + is_normalizable(cx, cx.param_env, to_mid_ty) + && cx + .tcx + .layout_of(cx.typing_env().as_query_input(to_mid_ty)) + .is_ok_and(|layout| { + let align = u128::from(layout.align.abi.bytes()); + u128::from(val) <= align + }) +} diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index dc2a1fa85bf5..38352977bde6 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -17,6 +17,7 @@ mod char_lit_as_u8; mod fn_to_numeric_cast; mod fn_to_numeric_cast_any; mod fn_to_numeric_cast_with_truncation; +mod manual_dangling_ptr; mod ptr_as_ptr; mod ptr_cast_constness; mod ref_as_ptr; @@ -759,6 +760,32 @@ declare_clippy_lint! { "detects `as *mut _` and `as *const _` conversion" } +declare_clippy_lint! { + /// ### What it does + /// Checks for casts of small constant literals or `mem::align_of` results to raw pointers. + /// + /// ### Why is this bad? + /// This creates a dangling pointer and is better expressed as + /// {`std`, `core`}`::ptr::`{`dangling`, `dangling_mut`}. + /// + /// ### Example + /// ```no_run + /// let ptr = 4 as *const u32; + /// let aligned = std::mem::align_of::() as *const u32; + /// let mut_ptr: *mut i64 = 8 as *mut _; + /// ``` + /// Use instead: + /// ```no_run + /// let ptr = std::ptr::dangling::(); + /// let aligned = std::ptr::dangling::(); + /// let mut_ptr: *mut i64 = std::ptr::dangling_mut(); + /// ``` + #[clippy::version = "1.87.0"] + pub MANUAL_DANGLING_PTR, + style, + "casting small constant literals to pointers to create dangling pointers" +} + pub struct Casts { msrv: Msrv, } @@ -795,6 +822,7 @@ impl_lint_pass!(Casts => [ ZERO_PTR, REF_AS_PTR, AS_POINTER_UNDERSCORE, + MANUAL_DANGLING_PTR, ]); impl<'tcx> LateLintPass<'tcx> for Casts { @@ -823,6 +851,10 @@ impl<'tcx> LateLintPass<'tcx> for Casts { 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); + if self.msrv.meets(cx, msrvs::MANUAL_DANGLING_PTR) { + manual_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); if cast_from.is_numeric() { diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index a9b6b369c4c3..3bf7da43fce3 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -97,6 +97,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::casts::FN_TO_NUMERIC_CAST_INFO, crate::casts::FN_TO_NUMERIC_CAST_ANY_INFO, crate::casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION_INFO, + crate::casts::MANUAL_DANGLING_PTR_INFO, crate::casts::PTR_AS_PTR_INFO, crate::casts::PTR_CAST_CONSTNESS_INFO, crate::casts::REF_AS_PTR_INFO, diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 86f4f190b950..ab60fefddb44 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -24,7 +24,7 @@ macro_rules! msrv_aliases { msrv_aliases! { 1,87,0 { OS_STR_DISPLAY, INT_MIDPOINT } 1,85,0 { UINT_FLOAT_MIDPOINT } - 1,84,0 { CONST_OPTION_AS_SLICE } + 1,84,0 { CONST_OPTION_AS_SLICE, MANUAL_DANGLING_PTR } 1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY, CONST_MUT_REFS, CONST_UNWRAP } 1,82,0 { IS_NONE_OR, REPEAT_N, RAW_REF_OP } 1,81,0 { LINT_REASONS_STABILIZATION, ERROR_IN_CORE, EXPLICIT_SELF_TYPE_ELISION } diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 51d06ad9b1aa..a960a65ead2f 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -30,6 +30,7 @@ pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"] pub const CHAR_IS_ASCII: [&str; 5] = ["core", "char", "methods", "", "is_ascii"]; pub const IO_ERROR_NEW: [&str; 5] = ["std", "io", "error", "Error", "new"]; pub const IO_ERRORKIND_OTHER: [&str; 5] = ["std", "io", "error", "ErrorKind", "Other"]; +pub const ALIGN_OF: [&str; 3] = ["core", "mem", "align_of"]; // Paths in clippy itself pub const MSRV_STACK: [&str; 3] = ["clippy_utils", "msrvs", "MsrvStack"]; diff --git a/tests/ui/manual_dangling_ptr.fixed b/tests/ui/manual_dangling_ptr.fixed new file mode 100644 index 000000000000..b6afe7898906 --- /dev/null +++ b/tests/ui/manual_dangling_ptr.fixed @@ -0,0 +1,44 @@ +#![warn(clippy::manual_dangling_ptr)] +use std::mem; + +pub fn foo(_const: *const f32, _mut: *mut i32) {} + +fn main() { + let _: *const u8 = std::ptr::dangling(); + //~^ manual_dangling_ptr + let _ = std::ptr::dangling::(); + //~^ manual_dangling_ptr + let _ = std::ptr::dangling_mut::(); + //~^ manual_dangling_ptr + + let _ = std::ptr::dangling::(); + //~^ manual_dangling_ptr + let _ = std::ptr::dangling::(); + //~^ manual_dangling_ptr + let _ = std::ptr::dangling::(); + //~^ manual_dangling_ptr + + foo(std::ptr::dangling(), std::ptr::dangling_mut()); + //~^ manual_dangling_ptr + //~| manual_dangling_ptr +} + +fn should_not_lint() { + let _ = 0x10 as *mut i32; + let _ = mem::align_of::() as *const u8; + + foo(0 as _, 0 as _); +} + +#[clippy::msrv = "1.83"] +fn _msrv_1_83() { + // `{core, std}::ptr::dangling` was stabilized in 1.84. Do not lint this + foo(4 as *const _, 4 as *mut _); +} + +#[clippy::msrv = "1.84"] +fn _msrv_1_84() { + foo(std::ptr::dangling(), std::ptr::dangling_mut()); + //~^ manual_dangling_ptr + //~| manual_dangling_ptr +} diff --git a/tests/ui/manual_dangling_ptr.rs b/tests/ui/manual_dangling_ptr.rs new file mode 100644 index 000000000000..581ad50113e2 --- /dev/null +++ b/tests/ui/manual_dangling_ptr.rs @@ -0,0 +1,44 @@ +#![warn(clippy::manual_dangling_ptr)] +use std::mem; + +pub fn foo(_const: *const f32, _mut: *mut i32) {} + +fn main() { + let _: *const u8 = 1 as *const _; + //~^ manual_dangling_ptr + let _ = 2 as *const u32; + //~^ manual_dangling_ptr + let _ = 4 as *mut f32; + //~^ manual_dangling_ptr + + let _ = mem::align_of::() as *const u8; + //~^ manual_dangling_ptr + let _ = mem::align_of::() as *const u32; + //~^ manual_dangling_ptr + let _ = mem::align_of::() as *const usize; + //~^ manual_dangling_ptr + + foo(4 as *const _, 4 as *mut _); + //~^ manual_dangling_ptr + //~| manual_dangling_ptr +} + +fn should_not_lint() { + let _ = 0x10 as *mut i32; + let _ = mem::align_of::() as *const u8; + + foo(0 as _, 0 as _); +} + +#[clippy::msrv = "1.83"] +fn _msrv_1_83() { + // `{core, std}::ptr::dangling` was stabilized in 1.84. Do not lint this + foo(4 as *const _, 4 as *mut _); +} + +#[clippy::msrv = "1.84"] +fn _msrv_1_84() { + foo(4 as *const _, 4 as *mut _); + //~^ manual_dangling_ptr + //~| manual_dangling_ptr +} diff --git a/tests/ui/manual_dangling_ptr.stderr b/tests/ui/manual_dangling_ptr.stderr new file mode 100644 index 000000000000..e3bc9b16b0d9 --- /dev/null +++ b/tests/ui/manual_dangling_ptr.stderr @@ -0,0 +1,65 @@ +error: manual creation of a dangling pointer + --> tests/ui/manual_dangling_ptr.rs:7:24 + | +LL | let _: *const u8 = 1 as *const _; + | ^^^^^^^^^^^^^ help: use: `std::ptr::dangling()` + | + = note: `-D clippy::manual-dangling-ptr` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_dangling_ptr)]` + +error: manual creation of a dangling pointer + --> tests/ui/manual_dangling_ptr.rs:9:13 + | +LL | let _ = 2 as *const u32; + | ^^^^^^^^^^^^^^^ help: use: `std::ptr::dangling::()` + +error: manual creation of a dangling pointer + --> tests/ui/manual_dangling_ptr.rs:11:13 + | +LL | let _ = 4 as *mut f32; + | ^^^^^^^^^^^^^ help: use: `std::ptr::dangling_mut::()` + +error: manual creation of a dangling pointer + --> tests/ui/manual_dangling_ptr.rs:14:13 + | +LL | let _ = mem::align_of::() as *const u8; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `std::ptr::dangling::()` + +error: manual creation of a dangling pointer + --> tests/ui/manual_dangling_ptr.rs:16:13 + | +LL | let _ = mem::align_of::() as *const u32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `std::ptr::dangling::()` + +error: manual creation of a dangling pointer + --> tests/ui/manual_dangling_ptr.rs:18:13 + | +LL | let _ = mem::align_of::() as *const usize; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `std::ptr::dangling::()` + +error: manual creation of a dangling pointer + --> tests/ui/manual_dangling_ptr.rs:21:9 + | +LL | foo(4 as *const _, 4 as *mut _); + | ^^^^^^^^^^^^^ help: use: `std::ptr::dangling()` + +error: manual creation of a dangling pointer + --> tests/ui/manual_dangling_ptr.rs:21:24 + | +LL | foo(4 as *const _, 4 as *mut _); + | ^^^^^^^^^^^ help: use: `std::ptr::dangling_mut()` + +error: manual creation of a dangling pointer + --> tests/ui/manual_dangling_ptr.rs:41:9 + | +LL | foo(4 as *const _, 4 as *mut _); + | ^^^^^^^^^^^^^ help: use: `std::ptr::dangling()` + +error: manual creation of a dangling pointer + --> tests/ui/manual_dangling_ptr.rs:41:24 + | +LL | foo(4 as *const _, 4 as *mut _); + | ^^^^^^^^^^^ help: use: `std::ptr::dangling_mut()` + +error: aborting due to 10 previous errors + diff --git a/tests/ui/transmute.rs b/tests/ui/transmute.rs index 3aecde398dc3..1ec70db9c3c2 100644 --- a/tests/ui/transmute.rs +++ b/tests/ui/transmute.rs @@ -59,7 +59,7 @@ fn useless() { let _: *const usize = std::mem::transmute(5_isize); //~^ useless_transmute - let _ = 5_isize as *const usize; + let _ = std::ptr::dangling::(); let _: *const usize = std::mem::transmute(1 + 1usize); //~^ useless_transmute diff --git a/tests/ui/transmute_null_to_fn.rs b/tests/ui/transmute_null_to_fn.rs index e88f05bb662e..4712374af934 100644 --- a/tests/ui/transmute_null_to_fn.rs +++ b/tests/ui/transmute_null_to_fn.rs @@ -1,6 +1,7 @@ #![allow(dead_code)] #![warn(clippy::transmute_null_to_fn)] #![allow(clippy::zero_ptr, clippy::missing_transmute_annotations)] +#![allow(clippy::manual_dangling_ptr)] // Easy to lint because these only span one line. fn one_liners() { diff --git a/tests/ui/transmute_null_to_fn.stderr b/tests/ui/transmute_null_to_fn.stderr index f7d80147445d..b5b0d4ecc7c0 100644 --- a/tests/ui/transmute_null_to_fn.stderr +++ b/tests/ui/transmute_null_to_fn.stderr @@ -1,5 +1,5 @@ error: transmuting a known null pointer into a function pointer - --> tests/ui/transmute_null_to_fn.rs:8:23 + --> tests/ui/transmute_null_to_fn.rs:9:23 | LL | let _: fn() = std::mem::transmute(0 as *const ()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this transmute results in undefined behavior @@ -9,7 +9,7 @@ LL | let _: fn() = std::mem::transmute(0 as *const ()); = help: to override `-D warnings` add `#[allow(clippy::transmute_null_to_fn)]` error: transmuting a known null pointer into a function pointer - --> tests/ui/transmute_null_to_fn.rs:11:23 + --> tests/ui/transmute_null_to_fn.rs:12:23 | LL | let _: fn() = std::mem::transmute(std::ptr::null::<()>()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this transmute results in undefined behavior @@ -17,7 +17,7 @@ LL | let _: fn() = std::mem::transmute(std::ptr::null::<()>()); = help: try wrapping your function pointer type in `Option` instead, and using `None` as a null pointer value error: transmuting a known null pointer into a function pointer - --> tests/ui/transmute_null_to_fn.rs:22:23 + --> tests/ui/transmute_null_to_fn.rs:23:23 | LL | let _: fn() = std::mem::transmute(ZPTR); | ^^^^^^^^^^^^^^^^^^^^^^^^^ this transmute results in undefined behavior @@ -25,7 +25,7 @@ LL | let _: fn() = std::mem::transmute(ZPTR); = help: try wrapping your function pointer type in `Option` instead, and using `None` as a null pointer value error: transmuting a known null pointer into a function pointer - --> tests/ui/transmute_null_to_fn.rs:32:23 + --> tests/ui/transmute_null_to_fn.rs:33:23 | LL | let _: fn() = std::mem::transmute(0 as *const u8 as *const ()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this transmute results in undefined behavior @@ -33,7 +33,7 @@ LL | let _: fn() = std::mem::transmute(0 as *const u8 as *const ()); = help: try wrapping your function pointer type in `Option` instead, and using `None` as a null pointer value error: transmuting a known null pointer into a function pointer - --> tests/ui/transmute_null_to_fn.rs:35:23 + --> tests/ui/transmute_null_to_fn.rs:36:23 | LL | let _: fn() = std::mem::transmute(std::ptr::null::<()>() as *const u8); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this transmute results in undefined behavior @@ -41,7 +41,7 @@ LL | let _: fn() = std::mem::transmute(std::ptr::null::<()>() as *const = help: try wrapping your function pointer type in `Option` instead, and using `None` as a null pointer value error: transmuting a known null pointer into a function pointer - --> tests/ui/transmute_null_to_fn.rs:38:23 + --> tests/ui/transmute_null_to_fn.rs:39:23 | LL | let _: fn() = std::mem::transmute(ZPTR as *const u8); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this transmute results in undefined behavior diff --git a/tests/ui/transmuting_null.rs b/tests/ui/transmuting_null.rs index bcd35bbd4e72..f3eb5060cd0d 100644 --- a/tests/ui/transmuting_null.rs +++ b/tests/ui/transmuting_null.rs @@ -3,6 +3,7 @@ #![allow(clippy::zero_ptr)] #![allow(clippy::transmute_ptr_to_ref)] #![allow(clippy::eq_op, clippy::missing_transmute_annotations)] +#![allow(clippy::manual_dangling_ptr)] // Easy to lint because these only span one line. fn one_liners() { diff --git a/tests/ui/transmuting_null.stderr b/tests/ui/transmuting_null.stderr index 84e6e374d525..c68e4102e405 100644 --- a/tests/ui/transmuting_null.stderr +++ b/tests/ui/transmuting_null.stderr @@ -1,5 +1,5 @@ error: transmuting a known null pointer into a reference - --> tests/ui/transmuting_null.rs:10:23 + --> tests/ui/transmuting_null.rs:11:23 | LL | let _: &u64 = std::mem::transmute(0 as *const u64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -8,13 +8,13 @@ LL | let _: &u64 = std::mem::transmute(0 as *const u64); = help: to override `-D warnings` add `#[allow(clippy::transmuting_null)]` error: transmuting a known null pointer into a reference - --> tests/ui/transmuting_null.rs:13:23 + --> tests/ui/transmuting_null.rs:14:23 | LL | let _: &u64 = std::mem::transmute(std::ptr::null::()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmuting a known null pointer into a reference - --> tests/ui/transmuting_null.rs:24:23 + --> tests/ui/transmuting_null.rs:25:23 | LL | let _: &u64 = std::mem::transmute(ZPTR); | ^^^^^^^^^^^^^^^^^^^^^^^^^