From 9e9526c6ab12fce8113d6c18d348572f4f98492b Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sat, 7 Sep 2024 16:18:45 +0200 Subject: [PATCH 1/2] Special-case suggestions for null pointers constness cast --- clippy_lints/src/casts/mod.rs | 6 +- clippy_lints/src/casts/ptr_cast_constness.rs | 62 ++++++++++++++------ tests/ui/ptr_cast_constness.fixed | 13 ++++ tests/ui/ptr_cast_constness.rs | 13 ++++ tests/ui/ptr_cast_constness.stderr | 22 ++++++- 5 files changed, 97 insertions(+), 19 deletions(-) diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index c31716fbcee1..fccddf558ed8 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -410,19 +410,23 @@ declare_clippy_lint! { /// ### Why is this bad? /// Though `as` casts between raw pointers are not terrible, `pointer::cast_mut` and /// `pointer::cast_const` are safer because they cannot accidentally cast the pointer to another - /// type. + /// type. Or, when null pointers are involved, `null()` and `null_mut()` can be used directly. /// /// ### Example /// ```no_run /// let ptr: *const u32 = &42_u32; /// let mut_ptr = ptr as *mut u32; /// let ptr = mut_ptr as *const u32; + /// let ptr1 = std::ptr::null::() as *mut u32; + /// let ptr2 = std::ptr::null_mut::() as *const u32; /// ``` /// Use instead: /// ```no_run /// let ptr: *const u32 = &42_u32; /// let mut_ptr = ptr.cast_mut(); /// let ptr = mut_ptr.cast_const(); + /// let ptr1 = std::ptr::null_mut::(); + /// let ptr2 = std::ptr::null::(); /// ``` #[clippy::version = "1.72.0"] pub PTR_CAST_CONSTNESS, diff --git a/clippy_lints/src/casts/ptr_cast_constness.rs b/clippy_lints/src/casts/ptr_cast_constness.rs index 7513e18d408b..98bc38ed134d 100644 --- a/clippy_lints/src/casts/ptr_cast_constness.rs +++ b/clippy_lints/src/casts/ptr_cast_constness.rs @@ -1,10 +1,12 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::std_or_core; use clippy_utils::sugg::Sugg; use rustc_errors::Applicability; -use rustc_hir::{Expr, Mutability}; +use rustc_hir::{Expr, ExprKind, Mutability, QPath}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty, TypeVisitableExt}; +use rustc_span::sym; use super::PTR_CAST_CONSTNESS; @@ -16,8 +18,7 @@ pub(super) fn check<'tcx>( cast_to: Ty<'tcx>, msrv: &Msrv, ) { - if msrv.meets(msrvs::POINTER_CAST_CONSTNESS) - && let ty::RawPtr(from_ty, from_mutbl) = cast_from.kind() + if let ty::RawPtr(from_ty, from_mutbl) = cast_from.kind() && let ty::RawPtr(to_ty, to_mutbl) = cast_to.kind() && matches!( (from_mutbl, to_mutbl), @@ -26,20 +27,47 @@ pub(super) fn check<'tcx>( && from_ty == to_ty && !from_ty.has_erased_regions() { - let sugg = Sugg::hir(cx, cast_expr, "_"); - let constness = match *to_mutbl { - Mutability::Not => "const", - Mutability::Mut => "mut", - }; + if let ExprKind::Call(func, []) = cast_expr.kind + && let ExprKind::Path(QPath::Resolved(None, path)) = func.kind + && let Some(defid) = path.res.opt_def_id() + && let Some(prefix) = std_or_core(cx) + && let mut app = Applicability::MachineApplicable + && let sugg = format!("{}", Sugg::hir_with_applicability(cx, cast_expr, "_", &mut app)) + && let Some((_, after_lt)) = sugg.split_once("::<") + && let Some((source, target, target_func)) = match cx.tcx.get_diagnostic_name(defid) { + Some(sym::ptr_null) => Some(("const", "mutable", "null_mut")), + Some(sym::ptr_null_mut) => Some(("mutable", "const", "null")), + _ => None, + } + { + span_lint_and_sugg( + cx, + PTR_CAST_CONSTNESS, + expr.span, + format!("`as` casting to make a {source} null pointer into a {target} null pointer"), + format!("use `{target_func}()` directly instead"), + format!("{prefix}::ptr::{target_func}::<{after_lt}"), + app, + ); + return; + } - span_lint_and_sugg( - cx, - PTR_CAST_CONSTNESS, - expr.span, - "`as` casting between raw pointers while changing only its constness", - format!("try `pointer::cast_{constness}`, a safer alternative"), - format!("{}.cast_{constness}()", sugg.maybe_par()), - Applicability::MachineApplicable, - ); + if msrv.meets(msrvs::POINTER_CAST_CONSTNESS) { + let sugg = Sugg::hir(cx, cast_expr, "_"); + let constness = match *to_mutbl { + Mutability::Not => "const", + Mutability::Mut => "mut", + }; + + span_lint_and_sugg( + cx, + PTR_CAST_CONSTNESS, + expr.span, + "`as` casting between raw pointers while changing only its constness", + format!("try `pointer::cast_{constness}`, a safer alternative"), + format!("{}.cast_{constness}()", sugg.maybe_par()), + Applicability::MachineApplicable, + ); + } } } diff --git a/tests/ui/ptr_cast_constness.fixed b/tests/ui/ptr_cast_constness.fixed index 21ac42196e1b..5bf9a30b0161 100644 --- a/tests/ui/ptr_cast_constness.fixed +++ b/tests/ui/ptr_cast_constness.fixed @@ -68,3 +68,16 @@ fn _msrv_1_65() { let _ = ptr.cast_mut(); let _ = mut_ptr.cast_const(); } + +#[inline_macros] +fn null_pointers() { + use std::ptr; + let _ = std::ptr::null_mut::(); + let _ = std::ptr::null::(); + + // Make sure the lint is triggered inside a macro + let _ = inline!(std::ptr::null_mut::()); + + // Do not lint inside macros from external crates + let _ = external!(ptr::null::() as *mut u32); +} diff --git a/tests/ui/ptr_cast_constness.rs b/tests/ui/ptr_cast_constness.rs index 5ce590b2b7e4..2575a5923e12 100644 --- a/tests/ui/ptr_cast_constness.rs +++ b/tests/ui/ptr_cast_constness.rs @@ -68,3 +68,16 @@ fn _msrv_1_65() { let _ = ptr as *mut u32; let _ = mut_ptr as *const u32; } + +#[inline_macros] +fn null_pointers() { + use std::ptr; + let _ = ptr::null::() as *mut String; + let _ = ptr::null_mut::() as *const u32; + + // Make sure the lint is triggered inside a macro + let _ = inline!(ptr::null::() as *mut u32); + + // Do not lint inside macros from external crates + let _ = external!(ptr::null::() as *mut u32); +} diff --git a/tests/ui/ptr_cast_constness.stderr b/tests/ui/ptr_cast_constness.stderr index 2c52ebd3464d..0806ca62806d 100644 --- a/tests/ui/ptr_cast_constness.stderr +++ b/tests/ui/ptr_cast_constness.stderr @@ -43,5 +43,25 @@ error: `as` casting between raw pointers while changing only its constness LL | let _ = mut_ptr as *const u32; | ^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast_const`, a safer alternative: `mut_ptr.cast_const()` -error: aborting due to 7 previous errors +error: `as` casting to make a const null pointer into a mutable null pointer + --> tests/ui/ptr_cast_constness.rs:75:13 + | +LL | let _ = ptr::null::() as *mut String; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null_mut()` directly instead: `std::ptr::null_mut::()` + +error: `as` casting to make a mutable null pointer into a const null pointer + --> tests/ui/ptr_cast_constness.rs:76:13 + | +LL | let _ = ptr::null_mut::() as *const u32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null()` directly instead: `std::ptr::null::()` + +error: `as` casting to make a const null pointer into a mutable null pointer + --> tests/ui/ptr_cast_constness.rs:79:21 + | +LL | let _ = inline!(ptr::null::() as *mut u32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null_mut()` directly instead: `std::ptr::null_mut::()` + | + = note: this error originates in the macro `__inline_mac_fn_null_pointers` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 10 previous errors From 30608732c2f68c423e4a62e6c20cdf28f6d38559 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sat, 7 Sep 2024 21:57:29 +0200 Subject: [PATCH 2/2] Handle null pointer constness cast through methods This covers two cases: - `core::ptr::null::().cast_mut()` -> `core::ptr::null_mut::()` - `core::ptr::null_mut::().cast_const()` -> `core::ptr::null::()` --- clippy_lints/src/casts/mod.rs | 5 ++++ clippy_lints/src/casts/ptr_cast_constness.rs | 27 ++++++++++++++++++++ tests/ui/ptr_cast_constness.fixed | 4 +++ tests/ui/ptr_cast_constness.rs | 4 +++ tests/ui/ptr_cast_constness.stderr | 24 +++++++++++++++-- 5 files changed, 62 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index fccddf558ed8..39f50a347c06 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -419,6 +419,8 @@ declare_clippy_lint! { /// let ptr = mut_ptr as *const u32; /// let ptr1 = std::ptr::null::() as *mut u32; /// let ptr2 = std::ptr::null_mut::() as *const u32; + /// let ptr3 = std::ptr::null::().cast_mut(); + /// let ptr4 = std::ptr::null_mut::().cast_const(); /// ``` /// Use instead: /// ```no_run @@ -427,6 +429,8 @@ declare_clippy_lint! { /// let ptr = mut_ptr.cast_const(); /// let ptr1 = std::ptr::null_mut::(); /// let ptr2 = std::ptr::null::(); + /// let ptr3 = std::ptr::null_mut::(); + /// let ptr4 = std::ptr::null::(); /// ``` #[clippy::version = "1.72.0"] pub PTR_CAST_CONSTNESS, @@ -813,6 +817,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { char_lit_as_u8::check(cx, expr); ptr_as_ptr::check(cx, expr, &self.msrv); cast_slice_different_sizes::check(cx, expr, &self.msrv); + ptr_cast_constness::check_null_ptr_cast_method(cx, expr); } extract_msrv_attr!(LateContext); diff --git a/clippy_lints/src/casts/ptr_cast_constness.rs b/clippy_lints/src/casts/ptr_cast_constness.rs index 98bc38ed134d..7518dd2435ae 100644 --- a/clippy_lints/src/casts/ptr_cast_constness.rs +++ b/clippy_lints/src/casts/ptr_cast_constness.rs @@ -71,3 +71,30 @@ pub(super) fn check<'tcx>( } } } + +pub(super) fn check_null_ptr_cast_method(cx: &LateContext<'_>, expr: &Expr<'_>) { + if let ExprKind::MethodCall(method, cast_expr, [], _) = expr.kind + && let ExprKind::Call(func, []) = cast_expr.kind + && let ExprKind::Path(QPath::Resolved(None, path)) = func.kind + && let Some(defid) = path.res.opt_def_id() + && let method = match (cx.tcx.get_diagnostic_name(defid), method.ident.as_str()) { + (Some(sym::ptr_null), "cast_mut") => "null_mut", + (Some(sym::ptr_null_mut), "cast_const") => "null", + _ => return, + } + && let Some(prefix) = std_or_core(cx) + && let mut app = Applicability::MachineApplicable + && let sugg = format!("{}", Sugg::hir_with_applicability(cx, cast_expr, "_", &mut app)) + && let Some((_, after_lt)) = sugg.split_once("::<") + { + span_lint_and_sugg( + cx, + PTR_CAST_CONSTNESS, + expr.span, + "changing constness of a null pointer", + format!("use `{method}()` directly instead"), + format!("{prefix}::ptr::{method}::<{after_lt}"), + app, + ); + } +} diff --git a/tests/ui/ptr_cast_constness.fixed b/tests/ui/ptr_cast_constness.fixed index 5bf9a30b0161..9a5272c7adc3 100644 --- a/tests/ui/ptr_cast_constness.fixed +++ b/tests/ui/ptr_cast_constness.fixed @@ -74,10 +74,14 @@ fn null_pointers() { use std::ptr; let _ = std::ptr::null_mut::(); let _ = std::ptr::null::(); + let _ = std::ptr::null_mut::(); + let _ = std::ptr::null::(); // Make sure the lint is triggered inside a macro let _ = inline!(std::ptr::null_mut::()); + let _ = inline!(std::ptr::null_mut::()); // Do not lint inside macros from external crates let _ = external!(ptr::null::() as *mut u32); + let _ = external!(ptr::null::().cast_mut()); } diff --git a/tests/ui/ptr_cast_constness.rs b/tests/ui/ptr_cast_constness.rs index 2575a5923e12..43ab5f5ba7cc 100644 --- a/tests/ui/ptr_cast_constness.rs +++ b/tests/ui/ptr_cast_constness.rs @@ -74,10 +74,14 @@ fn null_pointers() { use std::ptr; let _ = ptr::null::() as *mut String; let _ = ptr::null_mut::() as *const u32; + let _ = ptr::null::().cast_mut(); + let _ = ptr::null_mut::().cast_const(); // Make sure the lint is triggered inside a macro let _ = inline!(ptr::null::() as *mut u32); + let _ = inline!(ptr::null::().cast_mut()); // Do not lint inside macros from external crates let _ = external!(ptr::null::() as *mut u32); + let _ = external!(ptr::null::().cast_mut()); } diff --git a/tests/ui/ptr_cast_constness.stderr b/tests/ui/ptr_cast_constness.stderr index 0806ca62806d..a693793a4ae9 100644 --- a/tests/ui/ptr_cast_constness.stderr +++ b/tests/ui/ptr_cast_constness.stderr @@ -55,13 +55,33 @@ error: `as` casting to make a mutable null pointer into a const null pointer LL | let _ = ptr::null_mut::() as *const u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null()` directly instead: `std::ptr::null::()` +error: changing constness of a null pointer + --> tests/ui/ptr_cast_constness.rs:77:13 + | +LL | let _ = ptr::null::().cast_mut(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null_mut()` directly instead: `std::ptr::null_mut::()` + +error: changing constness of a null pointer + --> tests/ui/ptr_cast_constness.rs:78:13 + | +LL | let _ = ptr::null_mut::().cast_const(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null()` directly instead: `std::ptr::null::()` + error: `as` casting to make a const null pointer into a mutable null pointer - --> tests/ui/ptr_cast_constness.rs:79:21 + --> tests/ui/ptr_cast_constness.rs:81:21 | LL | let _ = inline!(ptr::null::() as *mut u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null_mut()` directly instead: `std::ptr::null_mut::()` | = note: this error originates in the macro `__inline_mac_fn_null_pointers` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 10 previous errors +error: changing constness of a null pointer + --> tests/ui/ptr_cast_constness.rs:82:21 + | +LL | let _ = inline!(ptr::null::().cast_mut()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null_mut()` directly instead: `std::ptr::null_mut::()` + | + = note: this error originates in the macro `__inline_mac_fn_null_pointers` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 13 previous errors