Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 9e9526c

Browse files
committed
Special-case suggestions for null pointers constness cast
1 parent d9c4523 commit 9e9526c

File tree

5 files changed

+97
-19
lines changed

5 files changed

+97
-19
lines changed

clippy_lints/src/casts/mod.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -410,19 +410,23 @@ declare_clippy_lint! {
410410
/// ### Why is this bad?
411411
/// Though `as` casts between raw pointers are not terrible, `pointer::cast_mut` and
412412
/// `pointer::cast_const` are safer because they cannot accidentally cast the pointer to another
413-
/// type.
413+
/// type. Or, when null pointers are involved, `null()` and `null_mut()` can be used directly.
414414
///
415415
/// ### Example
416416
/// ```no_run
417417
/// let ptr: *const u32 = &42_u32;
418418
/// let mut_ptr = ptr as *mut u32;
419419
/// let ptr = mut_ptr as *const u32;
420+
/// let ptr1 = std::ptr::null::<u32>() as *mut u32;
421+
/// let ptr2 = std::ptr::null_mut::<u32>() as *const u32;
420422
/// ```
421423
/// Use instead:
422424
/// ```no_run
423425
/// let ptr: *const u32 = &42_u32;
424426
/// let mut_ptr = ptr.cast_mut();
425427
/// let ptr = mut_ptr.cast_const();
428+
/// let ptr1 = std::ptr::null_mut::<u32>();
429+
/// let ptr2 = std::ptr::null::<u32>();
426430
/// ```
427431
#[clippy::version = "1.72.0"]
428432
pub PTR_CAST_CONSTNESS,
Lines changed: 45 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
use clippy_config::msrvs::{self, Msrv};
22
use clippy_utils::diagnostics::span_lint_and_sugg;
3+
use clippy_utils::std_or_core;
34
use clippy_utils::sugg::Sugg;
45
use rustc_errors::Applicability;
5-
use rustc_hir::{Expr, Mutability};
6+
use rustc_hir::{Expr, ExprKind, Mutability, QPath};
67
use rustc_lint::LateContext;
78
use rustc_middle::ty::{self, Ty, TypeVisitableExt};
9+
use rustc_span::sym;
810

911
use super::PTR_CAST_CONSTNESS;
1012

@@ -16,8 +18,7 @@ pub(super) fn check<'tcx>(
1618
cast_to: Ty<'tcx>,
1719
msrv: &Msrv,
1820
) {
19-
if msrv.meets(msrvs::POINTER_CAST_CONSTNESS)
20-
&& let ty::RawPtr(from_ty, from_mutbl) = cast_from.kind()
21+
if let ty::RawPtr(from_ty, from_mutbl) = cast_from.kind()
2122
&& let ty::RawPtr(to_ty, to_mutbl) = cast_to.kind()
2223
&& matches!(
2324
(from_mutbl, to_mutbl),
@@ -26,20 +27,47 @@ pub(super) fn check<'tcx>(
2627
&& from_ty == to_ty
2728
&& !from_ty.has_erased_regions()
2829
{
29-
let sugg = Sugg::hir(cx, cast_expr, "_");
30-
let constness = match *to_mutbl {
31-
Mutability::Not => "const",
32-
Mutability::Mut => "mut",
33-
};
30+
if let ExprKind::Call(func, []) = cast_expr.kind
31+
&& let ExprKind::Path(QPath::Resolved(None, path)) = func.kind
32+
&& let Some(defid) = path.res.opt_def_id()
33+
&& let Some(prefix) = std_or_core(cx)
34+
&& let mut app = Applicability::MachineApplicable
35+
&& let sugg = format!("{}", Sugg::hir_with_applicability(cx, cast_expr, "_", &mut app))
36+
&& let Some((_, after_lt)) = sugg.split_once("::<")
37+
&& let Some((source, target, target_func)) = match cx.tcx.get_diagnostic_name(defid) {
38+
Some(sym::ptr_null) => Some(("const", "mutable", "null_mut")),
39+
Some(sym::ptr_null_mut) => Some(("mutable", "const", "null")),
40+
_ => None,
41+
}
42+
{
43+
span_lint_and_sugg(
44+
cx,
45+
PTR_CAST_CONSTNESS,
46+
expr.span,
47+
format!("`as` casting to make a {source} null pointer into a {target} null pointer"),
48+
format!("use `{target_func}()` directly instead"),
49+
format!("{prefix}::ptr::{target_func}::<{after_lt}"),
50+
app,
51+
);
52+
return;
53+
}
3454

35-
span_lint_and_sugg(
36-
cx,
37-
PTR_CAST_CONSTNESS,
38-
expr.span,
39-
"`as` casting between raw pointers while changing only its constness",
40-
format!("try `pointer::cast_{constness}`, a safer alternative"),
41-
format!("{}.cast_{constness}()", sugg.maybe_par()),
42-
Applicability::MachineApplicable,
43-
);
55+
if msrv.meets(msrvs::POINTER_CAST_CONSTNESS) {
56+
let sugg = Sugg::hir(cx, cast_expr, "_");
57+
let constness = match *to_mutbl {
58+
Mutability::Not => "const",
59+
Mutability::Mut => "mut",
60+
};
61+
62+
span_lint_and_sugg(
63+
cx,
64+
PTR_CAST_CONSTNESS,
65+
expr.span,
66+
"`as` casting between raw pointers while changing only its constness",
67+
format!("try `pointer::cast_{constness}`, a safer alternative"),
68+
format!("{}.cast_{constness}()", sugg.maybe_par()),
69+
Applicability::MachineApplicable,
70+
);
71+
}
4472
}
4573
}

tests/ui/ptr_cast_constness.fixed

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,16 @@ fn _msrv_1_65() {
6868
let _ = ptr.cast_mut();
6969
let _ = mut_ptr.cast_const();
7070
}
71+
72+
#[inline_macros]
73+
fn null_pointers() {
74+
use std::ptr;
75+
let _ = std::ptr::null_mut::<String>();
76+
let _ = std::ptr::null::<u32>();
77+
78+
// Make sure the lint is triggered inside a macro
79+
let _ = inline!(std::ptr::null_mut::<u32>());
80+
81+
// Do not lint inside macros from external crates
82+
let _ = external!(ptr::null::<u32>() as *mut u32);
83+
}

tests/ui/ptr_cast_constness.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,16 @@ fn _msrv_1_65() {
6868
let _ = ptr as *mut u32;
6969
let _ = mut_ptr as *const u32;
7070
}
71+
72+
#[inline_macros]
73+
fn null_pointers() {
74+
use std::ptr;
75+
let _ = ptr::null::<String>() as *mut String;
76+
let _ = ptr::null_mut::<u32>() as *const u32;
77+
78+
// Make sure the lint is triggered inside a macro
79+
let _ = inline!(ptr::null::<u32>() as *mut u32);
80+
81+
// Do not lint inside macros from external crates
82+
let _ = external!(ptr::null::<u32>() as *mut u32);
83+
}

tests/ui/ptr_cast_constness.stderr

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,5 +43,25 @@ error: `as` casting between raw pointers while changing only its constness
4343
LL | let _ = mut_ptr as *const u32;
4444
| ^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast_const`, a safer alternative: `mut_ptr.cast_const()`
4545

46-
error: aborting due to 7 previous errors
46+
error: `as` casting to make a const null pointer into a mutable null pointer
47+
--> tests/ui/ptr_cast_constness.rs:75:13
48+
|
49+
LL | let _ = ptr::null::<String>() as *mut String;
50+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null_mut()` directly instead: `std::ptr::null_mut::<String>()`
51+
52+
error: `as` casting to make a mutable null pointer into a const null pointer
53+
--> tests/ui/ptr_cast_constness.rs:76:13
54+
|
55+
LL | let _ = ptr::null_mut::<u32>() as *const u32;
56+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null()` directly instead: `std::ptr::null::<u32>()`
57+
58+
error: `as` casting to make a const null pointer into a mutable null pointer
59+
--> tests/ui/ptr_cast_constness.rs:79:21
60+
|
61+
LL | let _ = inline!(ptr::null::<u32>() as *mut u32);
62+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null_mut()` directly instead: `std::ptr::null_mut::<u32>()`
63+
|
64+
= note: this error originates in the macro `__inline_mac_fn_null_pointers` (in Nightly builds, run with -Z macro-backtrace for more info)
65+
66+
error: aborting due to 10 previous errors
4767

0 commit comments

Comments
 (0)