Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide-diagnostics/src/handlers/missing_unsafe.rs')
-rw-r--r--crates/ide-diagnostics/src/handlers/missing_unsafe.rs98
1 files changed, 97 insertions, 1 deletions
diff --git a/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
index 17caf63018..df1cd76cf7 100644
--- a/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
+++ b/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
@@ -50,7 +50,12 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingUnsafe) -> Option<Vec<Ass
let node_to_add_unsafe_block = pick_best_node_to_add_unsafe_block(&expr)?;
- let replacement = format!("unsafe {{ {} }}", node_to_add_unsafe_block.text());
+ let mut replacement = format!("unsafe {{ {} }}", node_to_add_unsafe_block.text());
+ if let Some(expr) = ast::Expr::cast(node_to_add_unsafe_block.clone())
+ && needs_parentheses(&expr)
+ {
+ replacement = format!("({replacement})");
+ }
let edit = TextEdit::replace(node_to_add_unsafe_block.text_range(), replacement);
let source_change = SourceChange::from_text_edit(
d.node.file_id.original_file(ctx.sema.db).file_id(ctx.sema.db),
@@ -112,6 +117,17 @@ fn pick_best_node_to_add_unsafe_block(unsafe_expr: &ast::Expr) -> Option<SyntaxN
None
}
+fn needs_parentheses(expr: &ast::Expr) -> bool {
+ let node = expr.syntax();
+ node.ancestors()
+ .skip(1)
+ .take_while(|it| it.text_range().start() == node.text_range().start())
+ .map_while(ast::Expr::cast)
+ .last()
+ .and_then(|it| Some(it.syntax().parent()?.kind()))
+ .is_some_and(|kind| ast::ExprStmt::can_cast(kind) || ast::StmtList::can_cast(kind))
+}
+
#[cfg(test)]
mod tests {
use crate::tests::{check_diagnostics, check_fix, check_no_fix};
@@ -442,6 +458,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(
r#"
@@ -528,6 +587,27 @@ fn main() {
}
#[test]
+ fn needs_parentheses_for_unambiguous() {
+ check_fix(
+ r#"
+//- minicore: copy
+static mut STATIC_MUT: u8 = 0;
+
+fn foo() -> u8 {
+ STATIC_MUT$0 * 2
+}
+"#,
+ r#"
+static mut STATIC_MUT: u8 = 0;
+
+fn foo() -> u8 {
+ (unsafe { STATIC_MUT }) * 2
+}
+"#,
+ )
+ }
+
+ #[test]
fn ref_to_unsafe_expr() {
check_fix(
r#"
@@ -998,4 +1078,20 @@ extern "C" fn naked() {
"#,
);
}
+
+ #[test]
+ fn target_feature_safe_on_wasm() {
+ check_diagnostics(
+ r#"
+//- target_arch: wasm32
+
+#[target_feature(enable = "simd128")]
+fn requires_target_feature() {}
+
+fn main() {
+ requires_target_feature();
+}
+ "#,
+ );
+ }
}