about summary refs log tree commit diff
diff options
context:
space:
mode:
authorSamuel Tardieu <sam@rfc1149.net>2024-09-07 16:18:45 +0200
committerSamuel Tardieu <sam@rfc1149.net>2024-09-09 10:18:31 +0200
commit9e9526c6ab12fce8113d6c18d348572f4f98492b (patch)
treee3b3d21f0db328d21a06641c66ffec34bfdc563f
parentd9c4523e6cded5fb1fc3cffc8af45a0a003599a9 (diff)
downloadrust-9e9526c6ab12fce8113d6c18d348572f4f98492b.tar.gz
rust-9e9526c6ab12fce8113d6c18d348572f4f98492b.zip
Special-case suggestions for null pointers constness cast
-rw-r--r--clippy_lints/src/casts/mod.rs6
-rw-r--r--clippy_lints/src/casts/ptr_cast_constness.rs62
-rw-r--r--tests/ui/ptr_cast_constness.fixed13
-rw-r--r--tests/ui/ptr_cast_constness.rs13
-rw-r--r--tests/ui/ptr_cast_constness.stderr22
5 files changed, 97 insertions, 19 deletions
diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs
index c31716fbcee..fccddf558ed 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::<u32>() as *mut u32;
+    /// let ptr2 = std::ptr::null_mut::<u32>() 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::<u32>();
+    /// let ptr2 = std::ptr::null::<u32>();
     /// ```
     #[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 7513e18d408..98bc38ed134 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 21ac42196e1..5bf9a30b016 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::<String>();
+    let _ = std::ptr::null::<u32>();
+
+    // Make sure the lint is triggered inside a macro
+    let _ = inline!(std::ptr::null_mut::<u32>());
+
+    // Do not lint inside macros from external crates
+    let _ = external!(ptr::null::<u32>() as *mut u32);
+}
diff --git a/tests/ui/ptr_cast_constness.rs b/tests/ui/ptr_cast_constness.rs
index 5ce590b2b7e..2575a5923e1 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::<String>() as *mut String;
+    let _ = ptr::null_mut::<u32>() as *const u32;
+
+    // Make sure the lint is triggered inside a macro
+    let _ = inline!(ptr::null::<u32>() as *mut u32);
+
+    // Do not lint inside macros from external crates
+    let _ = external!(ptr::null::<u32>() as *mut u32);
+}
diff --git a/tests/ui/ptr_cast_constness.stderr b/tests/ui/ptr_cast_constness.stderr
index 2c52ebd3464..0806ca62806 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::<String>() as *mut String;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null_mut()` directly instead: `std::ptr::null_mut::<String>()`
+
+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::<u32>() as *const u32;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null()` directly instead: `std::ptr::null::<u32>()`
+
+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::<u32>() as *mut u32);
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null_mut()` directly instead: `std::ptr::null_mut::<u32>()`
+   |
+   = 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