Add support for ptr::write for the invalid_reference_casting lint
This commit is contained in:
parent
b4d09f3b81
commit
7ee77b5d1b
@ -56,20 +56,7 @@ fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx rustc_hir::Stmt<'tc
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||||
// &mut <expr>
|
let Some((is_assignment, e)) = is_operation_we_care_about(cx, expr) else {
|
||||||
let inner = if let ExprKind::AddrOf(_, Mutability::Mut, expr) = expr.kind {
|
|
||||||
expr
|
|
||||||
// <expr> = ...
|
|
||||||
} else if let ExprKind::Assign(expr, _, _) = expr.kind {
|
|
||||||
expr
|
|
||||||
// <expr> += ...
|
|
||||||
} else if let ExprKind::AssignOp(_, expr, _) = expr.kind {
|
|
||||||
expr
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
let ExprKind::Unary(UnOp::Deref, e) = &inner.kind else {
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -86,15 +73,58 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
|||||||
cx.emit_spanned_lint(
|
cx.emit_spanned_lint(
|
||||||
INVALID_REFERENCE_CASTING,
|
INVALID_REFERENCE_CASTING,
|
||||||
expr.span,
|
expr.span,
|
||||||
if matches!(expr.kind, ExprKind::AddrOf(..)) {
|
if is_assignment {
|
||||||
InvalidReferenceCastingDiag::BorrowAsMut { orig_cast }
|
|
||||||
} else {
|
|
||||||
InvalidReferenceCastingDiag::AssignToRef { orig_cast }
|
InvalidReferenceCastingDiag::AssignToRef { orig_cast }
|
||||||
|
} else {
|
||||||
|
InvalidReferenceCastingDiag::BorrowAsMut { orig_cast }
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_operation_we_care_about<'tcx>(
|
||||||
|
cx: &LateContext<'tcx>,
|
||||||
|
e: &'tcx Expr<'tcx>,
|
||||||
|
) -> Option<(bool, &'tcx Expr<'tcx>)> {
|
||||||
|
fn deref_assign_or_addr_of<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<(bool, &'tcx Expr<'tcx>)> {
|
||||||
|
// &mut <expr>
|
||||||
|
let inner = if let ExprKind::AddrOf(_, Mutability::Mut, expr) = expr.kind {
|
||||||
|
expr
|
||||||
|
// <expr> = ...
|
||||||
|
} else if let ExprKind::Assign(expr, _, _) = expr.kind {
|
||||||
|
expr
|
||||||
|
// <expr> += ...
|
||||||
|
} else if let ExprKind::AssignOp(_, expr, _) = expr.kind {
|
||||||
|
expr
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
if let ExprKind::Unary(UnOp::Deref, e) = &inner.kind {
|
||||||
|
Some((!matches!(expr.kind, ExprKind::AddrOf(..)), e))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ptr_write<'tcx>(
|
||||||
|
cx: &LateContext<'tcx>,
|
||||||
|
e: &'tcx Expr<'tcx>,
|
||||||
|
) -> Option<(bool, &'tcx Expr<'tcx>)> {
|
||||||
|
if let ExprKind::Call(path, [arg_ptr, _arg_val]) = e.kind
|
||||||
|
&& let ExprKind::Path(ref qpath) = path.kind
|
||||||
|
&& let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
|
||||||
|
&& matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::ptr_write | sym::ptr_write_volatile | sym::ptr_write_unaligned))
|
||||||
|
{
|
||||||
|
Some((true, arg_ptr))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deref_assign_or_addr_of(e).or_else(|| ptr_write(cx, e))
|
||||||
|
}
|
||||||
|
|
||||||
fn is_cast_from_const_to_mut<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
|
fn is_cast_from_const_to_mut<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
|
||||||
let e = e.peel_blocks();
|
let e = e.peel_blocks();
|
||||||
|
|
||||||
|
@ -1179,6 +1179,9 @@
|
|||||||
ptr_offset_from,
|
ptr_offset_from,
|
||||||
ptr_offset_from_unsigned,
|
ptr_offset_from_unsigned,
|
||||||
ptr_unique,
|
ptr_unique,
|
||||||
|
ptr_write,
|
||||||
|
ptr_write_unaligned,
|
||||||
|
ptr_write_volatile,
|
||||||
pub_macro_rules,
|
pub_macro_rules,
|
||||||
pub_restricted,
|
pub_restricted,
|
||||||
public,
|
public,
|
||||||
|
@ -1357,6 +1357,7 @@ macro_rules! attempt_swap_as_chunks {
|
|||||||
#[inline]
|
#[inline]
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
#[rustc_const_unstable(feature = "const_ptr_write", issue = "86302")]
|
#[rustc_const_unstable(feature = "const_ptr_write", issue = "86302")]
|
||||||
|
#[rustc_diagnostic_item = "ptr_write"]
|
||||||
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
|
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
|
||||||
pub const unsafe fn write<T>(dst: *mut T, src: T) {
|
pub const unsafe fn write<T>(dst: *mut T, src: T) {
|
||||||
// Semantically, it would be fine for this to be implemented as a
|
// Semantically, it would be fine for this to be implemented as a
|
||||||
@ -1459,6 +1460,7 @@ macro_rules! attempt_swap_as_chunks {
|
|||||||
#[inline]
|
#[inline]
|
||||||
#[stable(feature = "ptr_unaligned", since = "1.17.0")]
|
#[stable(feature = "ptr_unaligned", since = "1.17.0")]
|
||||||
#[rustc_const_unstable(feature = "const_ptr_write", issue = "86302")]
|
#[rustc_const_unstable(feature = "const_ptr_write", issue = "86302")]
|
||||||
|
#[rustc_diagnostic_item = "ptr_write_unaligned"]
|
||||||
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
|
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
|
||||||
pub const unsafe fn write_unaligned<T>(dst: *mut T, src: T) {
|
pub const unsafe fn write_unaligned<T>(dst: *mut T, src: T) {
|
||||||
// SAFETY: the caller must guarantee that `dst` is valid for writes.
|
// SAFETY: the caller must guarantee that `dst` is valid for writes.
|
||||||
@ -1607,6 +1609,7 @@ pub unsafe fn read_volatile<T>(src: *const T) -> T {
|
|||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
#[stable(feature = "volatile", since = "1.9.0")]
|
#[stable(feature = "volatile", since = "1.9.0")]
|
||||||
|
#[rustc_diagnostic_item = "ptr_write_volatile"]
|
||||||
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
|
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
|
||||||
pub unsafe fn write_volatile<T>(dst: *mut T, src: T) {
|
pub unsafe fn write_volatile<T>(dst: *mut T, src: T) {
|
||||||
// SAFETY: the caller must uphold the safety contract for `volatile_store`.
|
// SAFETY: the caller must uphold the safety contract for `volatile_store`.
|
||||||
|
@ -71,6 +71,11 @@ unsafe fn assign_to_ref() {
|
|||||||
//~^ ERROR assigning to `&T` is undefined behavior
|
//~^ ERROR assigning to `&T` is undefined behavior
|
||||||
*std::mem::transmute::<_, *mut i32>(num) += 1;
|
*std::mem::transmute::<_, *mut i32>(num) += 1;
|
||||||
//~^ ERROR assigning to `&T` is undefined behavior
|
//~^ ERROR assigning to `&T` is undefined behavior
|
||||||
|
std::ptr::write(
|
||||||
|
//~^ ERROR assigning to `&T` is undefined behavior
|
||||||
|
std::mem::transmute::<*const i32, *mut i32>(num),
|
||||||
|
-1i32,
|
||||||
|
);
|
||||||
|
|
||||||
let value = num as *const i32 as *mut i32;
|
let value = num as *const i32 as *mut i32;
|
||||||
*value = 1;
|
*value = 1;
|
||||||
@ -79,6 +84,12 @@ unsafe fn assign_to_ref() {
|
|||||||
//~^ ERROR assigning to `&T` is undefined behavior
|
//~^ ERROR assigning to `&T` is undefined behavior
|
||||||
*(num as *const _ as usize as *mut i32) = 2;
|
*(num as *const _ as usize as *mut i32) = 2;
|
||||||
//~^ ERROR assigning to `&T` is undefined behavior
|
//~^ ERROR assigning to `&T` is undefined behavior
|
||||||
|
std::ptr::write(value, 2);
|
||||||
|
//~^ ERROR assigning to `&T` is undefined behavior
|
||||||
|
std::ptr::write_unaligned(value, 2);
|
||||||
|
//~^ ERROR assigning to `&T` is undefined behavior
|
||||||
|
std::ptr::write_volatile(value, 2);
|
||||||
|
//~^ ERROR assigning to `&T` is undefined behavior
|
||||||
|
|
||||||
unsafe fn generic_assign_to_ref<T>(this: &T, a: T) {
|
unsafe fn generic_assign_to_ref<T>(this: &T, a: T) {
|
||||||
*(this as *const _ as *mut _) = a;
|
*(this as *const _ as *mut _) = a;
|
||||||
|
@ -131,7 +131,17 @@ LL | *std::mem::transmute::<_, *mut i32>(num) += 1;
|
|||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
|
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
|
||||||
--> $DIR/reference_casting.rs:76:5
|
--> $DIR/reference_casting.rs:74:5
|
||||||
|
|
|
||||||
|
LL | / std::ptr::write(
|
||||||
|
LL | |
|
||||||
|
LL | | std::mem::transmute::<*const i32, *mut i32>(num),
|
||||||
|
LL | | -1i32,
|
||||||
|
LL | | );
|
||||||
|
| |_____^
|
||||||
|
|
||||||
|
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
|
||||||
|
--> $DIR/reference_casting.rs:81:5
|
||||||
|
|
|
|
||||||
LL | let value = num as *const i32 as *mut i32;
|
LL | let value = num as *const i32 as *mut i32;
|
||||||
| ----------------------------- casting happend here
|
| ----------------------------- casting happend here
|
||||||
@ -139,22 +149,49 @@ LL | *value = 1;
|
|||||||
| ^^^^^^^^^^
|
| ^^^^^^^^^^
|
||||||
|
|
||||||
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
|
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
|
||||||
--> $DIR/reference_casting.rs:78:5
|
--> $DIR/reference_casting.rs:83:5
|
||||||
|
|
|
|
||||||
LL | *(num as *const i32).cast::<i32>().cast_mut() = 2;
|
LL | *(num as *const i32).cast::<i32>().cast_mut() = 2;
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
|
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
|
||||||
--> $DIR/reference_casting.rs:80:5
|
--> $DIR/reference_casting.rs:85:5
|
||||||
|
|
|
|
||||||
LL | *(num as *const _ as usize as *mut i32) = 2;
|
LL | *(num as *const _ as usize as *mut i32) = 2;
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
|
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
|
||||||
--> $DIR/reference_casting.rs:84:9
|
--> $DIR/reference_casting.rs:87:5
|
||||||
|
|
|
||||||
|
LL | let value = num as *const i32 as *mut i32;
|
||||||
|
| ----------------------------- casting happend here
|
||||||
|
...
|
||||||
|
LL | std::ptr::write(value, 2);
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
|
||||||
|
--> $DIR/reference_casting.rs:89:5
|
||||||
|
|
|
||||||
|
LL | let value = num as *const i32 as *mut i32;
|
||||||
|
| ----------------------------- casting happend here
|
||||||
|
...
|
||||||
|
LL | std::ptr::write_unaligned(value, 2);
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
|
||||||
|
--> $DIR/reference_casting.rs:91:5
|
||||||
|
|
|
||||||
|
LL | let value = num as *const i32 as *mut i32;
|
||||||
|
| ----------------------------- casting happend here
|
||||||
|
...
|
||||||
|
LL | std::ptr::write_volatile(value, 2);
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
|
||||||
|
--> $DIR/reference_casting.rs:95:9
|
||||||
|
|
|
|
||||||
LL | *(this as *const _ as *mut _) = a;
|
LL | *(this as *const _ as *mut _) = a;
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: aborting due to 25 previous errors
|
error: aborting due to 29 previous errors
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user