Improve diagnostics of the invalid_reference_casting lint

This commit is contained in:
Urgau 2023-07-14 22:25:47 +02:00
parent 50a46710a9
commit 20a6b57106
5 changed files with 151 additions and 76 deletions

View file

@ -318,7 +318,10 @@ lint_invalid_nan_comparisons_eq_ne = incorrect NaN comparison, NaN cannot be dir
lint_invalid_nan_comparisons_lt_le_gt_ge = incorrect NaN comparison, NaN is not orderable
lint_invalid_reference_casting = casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
lint_invalid_reference_casting_assign_to_ref = assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
.label = casting happend here
lint_invalid_reference_casting_borrow_as_mut = casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
.label = casting happend here
lint_lintpass_by_hand = implementing `LintPass` by hand

View file

@ -745,10 +745,17 @@ pub enum InvalidFromUtf8Diag {
// reference_casting.rs
#[derive(LintDiagnostic)]
#[diag(lint_invalid_reference_casting)]
pub struct InvalidReferenceCastingDiag {
#[label]
pub orig_cast: Option<Span>,
pub enum InvalidReferenceCastingDiag {
#[diag(lint_invalid_reference_casting_borrow_as_mut)]
BorrowAsMut {
#[label]
orig_cast: Option<Span>,
},
#[diag(lint_invalid_reference_casting_assign_to_ref)]
AssignToRef {
#[label]
orig_cast: Option<Span>,
},
}
// hidden_unicode_codepoints.rs

View file

@ -73,13 +73,25 @@ impl<'tcx> LateLintPass<'tcx> for InvalidReferenceCasting {
return;
};
if is_cast_from_const_to_mut(cx, e) {
cx.emit_spanned_lint(INVALID_REFERENCE_CASTING, expr.span, InvalidReferenceCastingDiag { orig_cast: None });
let orig_cast = if is_cast_from_const_to_mut(cx, e) {
None
} else if let ExprKind::Path(QPath::Resolved(_, path)) = e.kind
&& let Res::Local(hir_id) = &path.res
&& let Some(orig_cast) = self.casted.get(hir_id) {
cx.emit_spanned_lint(INVALID_REFERENCE_CASTING, expr.span, InvalidReferenceCastingDiag { orig_cast: Some(*orig_cast) });
}
Some(*orig_cast)
} else {
return;
};
cx.emit_spanned_lint(
INVALID_REFERENCE_CASTING,
expr.span,
if matches!(expr.kind, ExprKind::AddrOf(..)) {
InvalidReferenceCastingDiag::BorrowAsMut { orig_cast }
} else {
InvalidReferenceCastingDiag::AssignToRef { orig_cast }
},
);
}
}

View file

@ -9,42 +9,63 @@ extern "C" {
fn int_ffi(c: *mut i32);
}
fn main() {
unsafe fn ref_to_mut() {
let num = &3i32;
let _num = &mut *(num as *const i32 as *mut i32);
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
let _num = &mut *(num as *const i32).cast_mut();
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
let _num = &mut *std::ptr::from_ref(num).cast_mut();
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
let _num = &mut *std::ptr::from_ref({ num }).cast_mut();
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
let _num = &mut *{ std::ptr::from_ref(num) }.cast_mut();
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
let _num = &mut *(std::ptr::from_ref({ num }) as *mut i32);
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
let deferred = num as *const i32 as *mut i32;
let _num = &mut *deferred;
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
}
unsafe fn assign_to_ref() {
let s = String::from("Hello");
let a = &s;
unsafe {
let num = &3i32;
let mut_num = &mut 3i32;
let num = &3i32;
*(a as *const _ as *mut _) = String::from("Replaced");
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
*(a as *const _ as *mut String) += " world";
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
let _num = &mut *(num as *const i32 as *mut i32);
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
let _num = &mut *(num as *const i32).cast_mut();
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
*std::ptr::from_ref(num).cast_mut() += 1;
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
*std::ptr::from_ref({ num }).cast_mut() += 1;
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
*{ std::ptr::from_ref(num) }.cast_mut() += 1;
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
*(std::ptr::from_ref({ num }) as *mut i32) += 1;
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
let value = num as *const i32 as *mut i32;
*value = 1;
//~^ ERROR casting `&T` to `&mut T` is undefined behavior
// Shouldn't be warned against
*(num as *const i32 as *mut i32);
println!("{}", *(num as *const _ as *const i16));
println!("{}", *(mut_num as *mut _ as *mut i16));
ffi(a.as_ptr() as *mut _);
int_ffi(num as *const _ as *mut _);
int_ffi(&3 as *const _ as *mut _);
let mut value = 3;
let value: *const i32 = &mut value;
*(value as *const i16 as *mut i16) = 42;
}
*(a as *const _ as *mut _) = String::from("Replaced");
//~^ ERROR assigning to `&T` is undefined behavior
*(a as *const _ as *mut String) += " world";
//~^ ERROR assigning to `&T` is undefined behavior
*std::ptr::from_ref(num).cast_mut() += 1;
//~^ ERROR assigning to `&T` is undefined behavior
*std::ptr::from_ref({ num }).cast_mut() += 1;
//~^ ERROR assigning to `&T` is undefined behavior
*{ std::ptr::from_ref(num) }.cast_mut() += 1;
//~^ ERROR assigning to `&T` is undefined behavior
*(std::ptr::from_ref({ num }) as *mut i32) += 1;
//~^ ERROR assigning to `&T` is undefined behavior
let value = num as *const i32 as *mut i32;
*value = 1;
//~^ ERROR assigning to `&T` is undefined behavior
}
unsafe fn no_warn() {
let num = &3i32;
let mut_num = &mut 3i32;
let a = &String::from("ffi");
*(num as *const i32 as *mut i32);
println!("{}", *(num as *const _ as *const i16));
println!("{}", *(mut_num as *mut _ as *mut i16));
ffi(a.as_ptr() as *mut _);
int_ffi(num as *const _ as *mut _);
int_ffi(&3 as *const _ as *mut _);
let mut value = 3;
let value: *const i32 = &mut value;
*(value as *const i16 as *mut i16) = 42;
}
fn main() {}

View file

@ -1,60 +1,92 @@
error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
--> $DIR/reference_casting.rs:19:9
--> $DIR/reference_casting.rs:15:16
|
LL | *(a as *const _ as *mut _) = String::from("Replaced");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | let _num = &mut *(num as *const i32 as *mut i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[deny(invalid_reference_casting)]` on by default
error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
--> $DIR/reference_casting.rs:21:9
--> $DIR/reference_casting.rs:17:16
|
LL | *(a as *const _ as *mut String) += " world";
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | let _num = &mut *(num as *const i32).cast_mut();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
--> $DIR/reference_casting.rs:23:20
--> $DIR/reference_casting.rs:19:16
|
LL | let _num = &mut *(num as *const i32 as *mut i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | let _num = &mut *std::ptr::from_ref(num).cast_mut();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
--> $DIR/reference_casting.rs:25:20
--> $DIR/reference_casting.rs:21:16
|
LL | let _num = &mut *(num as *const i32).cast_mut();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | let _num = &mut *std::ptr::from_ref({ num }).cast_mut();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
--> $DIR/reference_casting.rs:27:9
--> $DIR/reference_casting.rs:23:16
|
LL | *std::ptr::from_ref(num).cast_mut() += 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | let _num = &mut *{ std::ptr::from_ref(num) }.cast_mut();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
--> $DIR/reference_casting.rs:29:9
--> $DIR/reference_casting.rs:25:16
|
LL | *std::ptr::from_ref({ num }).cast_mut() += 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | let _num = &mut *(std::ptr::from_ref({ num }) as *mut i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
--> $DIR/reference_casting.rs:31:9
--> $DIR/reference_casting.rs:29:16
|
LL | *{ std::ptr::from_ref(num) }.cast_mut() += 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | let deferred = num as *const i32 as *mut i32;
| ----------------------------- casting happend here
LL | let _num = &mut *deferred;
| ^^^^^^^^^^^^^^
error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
--> $DIR/reference_casting.rs:33:9
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
--> $DIR/reference_casting.rs:38:5
|
LL | *(std::ptr::from_ref({ num }) as *mut i32) += 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | *(a as *const _ as *mut _) = String::from("Replaced");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: casting `&T` to `&mut T` is undefined behavior, even if the reference is unused, consider instead using an `UnsafeCell`
--> $DIR/reference_casting.rs:36:9
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
--> $DIR/reference_casting.rs:40:5
|
LL | let value = num as *const i32 as *mut i32;
| ----------------------------- casting happend here
LL | *value = 1;
| ^^^^^^^^^^
LL | *(a as *const _ as *mut String) += " world";
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 9 previous errors
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
--> $DIR/reference_casting.rs:42:5
|
LL | *std::ptr::from_ref(num).cast_mut() += 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
--> $DIR/reference_casting.rs:44:5
|
LL | *std::ptr::from_ref({ num }).cast_mut() += 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
--> $DIR/reference_casting.rs:46:5
|
LL | *{ std::ptr::from_ref(num) }.cast_mut() += 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
--> $DIR/reference_casting.rs:48:5
|
LL | *(std::ptr::from_ref({ num }) as *mut i32) += 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: assigning to `&T` is undefined behavior, consider using an `UnsafeCell`
--> $DIR/reference_casting.rs:51:5
|
LL | let value = num as *const i32 as *mut i32;
| ----------------------------- casting happend here
LL | *value = 1;
| ^^^^^^^^^^
error: aborting due to 14 previous errors