Skip to content

Commit 131681b

Browse files
committed
Auto merge of rust-lang#13369 - samueltardieu:issue-13361, r=y21
Special-case suggestions for null pointers constness cast This implements the suggestions from rust-lang#13361. It fits into the existing `ptr_cast_constness` lint, as this is a specialized version. However, 1. I have not modified the lint MSRV, so the documentation for this lint will still show that it applies only from Rust 1.72.0. This is true in the general case, but the lint for null pointers will trigger even before this version as `null()` and `null_mut()` were already present in Rust 1.0 and there is no reason not to apply this lint. I guess this is only a minor documentation issue that can be ignored. 2. I have not covered the `core::ptr::null::<T>().cast_mut()` (could be made into `core::ptr::null_mut::<T>()`) and `cotr::ptr::null_mut::<T>().cast_const()` (could be made into `core::ptr::null::<T>()`) cases. Should they be covered? If they should, here or in a separate PR? changelog: [`ptr_cast_constness`]: special-case suggestions for null pointers constness cast Fix rust-lang#13361
2 parents 8be0b36 + 3060873 commit 131681b

File tree

5 files changed

+152
-14
lines changed

5 files changed

+152
-14
lines changed

clippy_lints/src/casts/mod.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -410,19 +410,27 @@ 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;
422+
/// let ptr3 = std::ptr::null::<u32>().cast_mut();
423+
/// let ptr4 = std::ptr::null_mut::<u32>().cast_const();
420424
/// ```
421425
/// Use instead:
422426
/// ```no_run
423427
/// let ptr: *const u32 = &42_u32;
424428
/// let mut_ptr = ptr.cast_mut();
425429
/// let ptr = mut_ptr.cast_const();
430+
/// let ptr1 = std::ptr::null_mut::<u32>();
431+
/// let ptr2 = std::ptr::null::<u32>();
432+
/// let ptr3 = std::ptr::null_mut::<u32>();
433+
/// let ptr4 = std::ptr::null::<u32>();
426434
/// ```
427435
#[clippy::version = "1.72.0"]
428436
pub PTR_CAST_CONSTNESS,
@@ -809,6 +817,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
809817
char_lit_as_u8::check(cx, expr);
810818
ptr_as_ptr::check(cx, expr, &self.msrv);
811819
cast_slice_different_sizes::check(cx, expr, &self.msrv);
820+
ptr_cast_constness::check_null_ptr_cast_method(cx, expr);
812821
}
813822

814823
extract_msrv_attr!(LateContext);
Lines changed: 67 additions & 12 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,74 @@ 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

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+
}
72+
}
73+
}
74+
75+
pub(super) fn check_null_ptr_cast_method(cx: &LateContext<'_>, expr: &Expr<'_>) {
76+
if let ExprKind::MethodCall(method, cast_expr, [], _) = expr.kind
77+
&& let ExprKind::Call(func, []) = cast_expr.kind
78+
&& let ExprKind::Path(QPath::Resolved(None, path)) = func.kind
79+
&& let Some(defid) = path.res.opt_def_id()
80+
&& let method = match (cx.tcx.get_diagnostic_name(defid), method.ident.as_str()) {
81+
(Some(sym::ptr_null), "cast_mut") => "null_mut",
82+
(Some(sym::ptr_null_mut), "cast_const") => "null",
83+
_ => return,
84+
}
85+
&& let Some(prefix) = std_or_core(cx)
86+
&& let mut app = Applicability::MachineApplicable
87+
&& let sugg = format!("{}", Sugg::hir_with_applicability(cx, cast_expr, "_", &mut app))
88+
&& let Some((_, after_lt)) = sugg.split_once("::<")
89+
{
3590
span_lint_and_sugg(
3691
cx,
3792
PTR_CAST_CONSTNESS,
3893
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,
94+
"changing constness of a null pointer",
95+
format!("use `{method}()` directly instead"),
96+
format!("{prefix}::ptr::{method}::<{after_lt}"),
97+
app,
4398
);
4499
}
45100
}

tests/ui/ptr_cast_constness.fixed

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,20 @@ 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+
let _ = std::ptr::null_mut::<u32>();
78+
let _ = std::ptr::null::<u32>();
79+
80+
// Make sure the lint is triggered inside a macro
81+
let _ = inline!(std::ptr::null_mut::<u32>());
82+
let _ = inline!(std::ptr::null_mut::<u32>());
83+
84+
// Do not lint inside macros from external crates
85+
let _ = external!(ptr::null::<u32>() as *mut u32);
86+
let _ = external!(ptr::null::<u32>().cast_mut());
87+
}

tests/ui/ptr_cast_constness.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,20 @@ 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+
let _ = ptr::null::<u32>().cast_mut();
78+
let _ = ptr::null_mut::<u32>().cast_const();
79+
80+
// Make sure the lint is triggered inside a macro
81+
let _ = inline!(ptr::null::<u32>() as *mut u32);
82+
let _ = inline!(ptr::null::<u32>().cast_mut());
83+
84+
// Do not lint inside macros from external crates
85+
let _ = external!(ptr::null::<u32>() as *mut u32);
86+
let _ = external!(ptr::null::<u32>().cast_mut());
87+
}

tests/ui/ptr_cast_constness.stderr

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,5 +43,45 @@ 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: changing constness of a null pointer
59+
--> tests/ui/ptr_cast_constness.rs:77:13
60+
|
61+
LL | let _ = ptr::null::<u32>().cast_mut();
62+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null_mut()` directly instead: `std::ptr::null_mut::<u32>()`
63+
64+
error: changing constness of a null pointer
65+
--> tests/ui/ptr_cast_constness.rs:78:13
66+
|
67+
LL | let _ = ptr::null_mut::<u32>().cast_const();
68+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null()` directly instead: `std::ptr::null::<u32>()`
69+
70+
error: `as` casting to make a const null pointer into a mutable null pointer
71+
--> tests/ui/ptr_cast_constness.rs:81:21
72+
|
73+
LL | let _ = inline!(ptr::null::<u32>() as *mut u32);
74+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null_mut()` directly instead: `std::ptr::null_mut::<u32>()`
75+
|
76+
= note: this error originates in the macro `__inline_mac_fn_null_pointers` (in Nightly builds, run with -Z macro-backtrace for more info)
77+
78+
error: changing constness of a null pointer
79+
--> tests/ui/ptr_cast_constness.rs:82:21
80+
|
81+
LL | let _ = inline!(ptr::null::<u32>().cast_mut());
82+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null_mut()` directly instead: `std::ptr::null_mut::<u32>()`
83+
|
84+
= note: this error originates in the macro `__inline_mac_fn_null_pointers` (in Nightly builds, run with -Z macro-backtrace for more info)
85+
86+
error: aborting due to 13 previous errors
4787

0 commit comments

Comments
 (0)