From 285bb607e056786d824a984214870dfd74484713 Mon Sep 17 00:00:00 2001 From: Kivooeo Date: Sat, 20 Sep 2025 08:42:51 -0700 Subject: [PATCH] allow take union field addresses in safe rust --- crates/hir-ty/src/diagnostics/unsafe_check.rs | 16 +++++++ .../src/handlers/missing_unsafe.rs | 43 +++++++++++++++++++ .../test_data/highlight_unsafe.html | 2 +- 3 files changed, 60 insertions(+), 1 deletion(-) diff --git a/crates/hir-ty/src/diagnostics/unsafe_check.rs b/crates/hir-ty/src/diagnostics/unsafe_check.rs index 3f04b72c2fc6..3c78f5ef3870 100644 --- a/crates/hir-ty/src/diagnostics/unsafe_check.rs +++ b/crates/hir-ty/src/diagnostics/unsafe_check.rs @@ -315,6 +315,22 @@ impl<'db> UnsafeVisitor<'db> { } _ => (), } + + let mut peeled = *expr; + while let Expr::Field { expr: lhs, .. } = &self.body[peeled] { + if let Some(Either::Left(FieldId { parent: VariantId::UnionId(_), .. })) = + self.infer.field_resolution(peeled) + { + peeled = *lhs; + } else { + break; + } + } + + // Walk the peeled expression (the LHS of the union field chain) + self.walk_expr(peeled); + // Return so we don't recurse directly onto the union field access(es) + return; } Expr::MethodCall { .. } => { if let Some((func, _)) = self.infer.method_resolution(current) { diff --git a/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/crates/ide-diagnostics/src/handlers/missing_unsafe.rs index 4bb64747f5bb..029ed18a4d39 100644 --- a/crates/ide-diagnostics/src/handlers/missing_unsafe.rs +++ b/crates/ide-diagnostics/src/handlers/missing_unsafe.rs @@ -441,6 +441,49 @@ fn main() { ) } + #[test] + fn raw_deref_on_union_field() { + check_diagnostics( + r#" +fn main() { + + union U { + a: u8 + } + let x = U { a: 3 }; + + let a = &raw mut x.a; + + union U1 { + a: u8 + } + let x = U1 { a: 3 }; + + let a = x.a; + // ^^^ 💡 error: access to union field is unsafe and requires an unsafe function or block + + + let b = &raw const x.a; + + let tmp = Vec::from([1, 2, 3]); + + let c = &raw const tmp[x.a]; + // ^^^ 💡 error: access to union field is unsafe and requires an unsafe function or block + + union URef { + p: &'static mut i32, + } + + fn deref_union_field(u: URef) { + // Not an assignment but an access to the union field! + *(u.p) = 13; + // ^^^ 💡 error: access to union field is unsafe and requires an unsafe function or block + } +} +"#, + ) + } + #[test] fn unsafe_expr_as_an_argument_of_a_method_call() { check_fix( diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html index 828b8f762c58..8339daf32462 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html @@ -96,7 +96,7 @@ u.field; &u.field; - &raw const u.field; + &raw const u.field; // this should be safe! let Union { field: _ }; // but not these