diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 95f0b651962..f482e3d7c12 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -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 diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 6d774070ec4..a6a48bf4ffa 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -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, +pub enum InvalidReferenceCastingDiag { + #[diag(lint_invalid_reference_casting_borrow_as_mut)] + BorrowAsMut { + #[label] + orig_cast: Option, + }, + #[diag(lint_invalid_reference_casting_assign_to_ref)] + AssignToRef { + #[label] + orig_cast: Option, + }, } // hidden_unicode_codepoints.rs diff --git a/compiler/rustc_lint/src/reference_casting.rs b/compiler/rustc_lint/src/reference_casting.rs index 428bf750bbd..ed3d4721049 100644 --- a/compiler/rustc_lint/src/reference_casting.rs +++ b/compiler/rustc_lint/src/reference_casting.rs @@ -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 } + }, + ); } } diff --git a/tests/ui/lint/reference_casting.rs b/tests/ui/lint/reference_casting.rs index aa946038c99..6e70626ef99 100644 --- a/tests/ui/lint/reference_casting.rs +++ b/tests/ui/lint/reference_casting.rs @@ -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() {} diff --git a/tests/ui/lint/reference_casting.stderr b/tests/ui/lint/reference_casting.stderr index 22bc66264d0..02b23600557 100644 --- a/tests/ui/lint/reference_casting.stderr +++ b/tests/ui/lint/reference_casting.stderr @@ -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