ide_diagnostics/handlers/
missing_unsafe.rs

1use hir::db::ExpandDatabase;
2use hir::{UnsafeLint, UnsafetyReason};
3use ide_db::text_edit::TextEdit;
4use ide_db::{assists::Assist, source_change::SourceChange};
5use syntax::{AstNode, match_ast};
6use syntax::{SyntaxNode, ast};
7
8use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, fix};
9
10// Diagnostic: missing-unsafe
11//
12// This diagnostic is triggered if an operation marked as `unsafe` is used outside of an `unsafe` function or block.
13pub(crate) fn missing_unsafe(ctx: &DiagnosticsContext<'_>, d: &hir::MissingUnsafe) -> Diagnostic {
14    let code = match d.lint {
15        UnsafeLint::HardError => DiagnosticCode::RustcHardError("E0133"),
16        UnsafeLint::UnsafeOpInUnsafeFn => DiagnosticCode::RustcLint("unsafe_op_in_unsafe_fn"),
17        UnsafeLint::DeprecatedSafe2024 => DiagnosticCode::RustcLint("deprecated_safe_2024"),
18    };
19    let operation = display_unsafety_reason(d.reason);
20    Diagnostic::new_with_syntax_node_ptr(
21        ctx,
22        code,
23        format!("{operation} is unsafe and requires an unsafe function or block"),
24        d.node.map(|it| it.into()),
25    )
26    .stable()
27    .with_fixes(fixes(ctx, d))
28}
29
30fn display_unsafety_reason(reason: UnsafetyReason) -> &'static str {
31    match reason {
32        UnsafetyReason::UnionField => "access to union field",
33        UnsafetyReason::UnsafeFnCall => "call to unsafe function",
34        UnsafetyReason::InlineAsm => "use of inline assembly",
35        UnsafetyReason::RawPtrDeref => "dereference of raw pointer",
36        UnsafetyReason::MutableStatic => "use of mutable static",
37        UnsafetyReason::ExternStatic => "use of extern static",
38    }
39}
40
41fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingUnsafe) -> Option<Vec<Assist>> {
42    // The fixit will not work correctly for macro expansions, so we don't offer it in that case.
43    if d.node.file_id.is_macro() {
44        return None;
45    }
46
47    let root = ctx.sema.db.parse_or_expand(d.node.file_id);
48    let node = d.node.value.to_node(&root);
49    let expr = node.syntax().ancestors().find_map(ast::Expr::cast)?;
50
51    let node_to_add_unsafe_block = pick_best_node_to_add_unsafe_block(&expr)?;
52
53    let mut replacement = format!("unsafe {{ {} }}", node_to_add_unsafe_block.text());
54    if let Some(expr) = ast::Expr::cast(node_to_add_unsafe_block.clone())
55        && needs_parentheses(&expr)
56    {
57        replacement = format!("({replacement})");
58    }
59    let edit = TextEdit::replace(node_to_add_unsafe_block.text_range(), replacement);
60    let source_change = SourceChange::from_text_edit(
61        d.node.file_id.original_file(ctx.sema.db).file_id(ctx.sema.db),
62        edit,
63    );
64    Some(vec![fix("add_unsafe", "Add unsafe block", source_change, expr.syntax().text_range())])
65}
66
67// Pick the first ancestor expression of the unsafe `expr` that is not a
68// receiver of a method call, a field access, the left-hand side of an
69// assignment, or a reference. As all of those cases would incur a forced move
70// if wrapped which might not be wanted. That is:
71// - `unsafe_expr.foo` -> `unsafe { unsafe_expr.foo }`
72// - `unsafe_expr.foo.bar` -> `unsafe { unsafe_expr.foo.bar }`
73// - `unsafe_expr.foo()` -> `unsafe { unsafe_expr.foo() }`
74// - `unsafe_expr.foo.bar()` -> `unsafe { unsafe_expr.foo.bar() }`
75// - `unsafe_expr += 1` -> `unsafe { unsafe_expr += 1 }`
76// - `&unsafe_expr` -> `unsafe { &unsafe_expr }`
77// - `&&unsafe_expr` -> `unsafe { &&unsafe_expr }`
78fn pick_best_node_to_add_unsafe_block(unsafe_expr: &ast::Expr) -> Option<SyntaxNode> {
79    // The `unsafe_expr` might be:
80    // - `ast::CallExpr`: call an unsafe function
81    // - `ast::MethodCallExpr`: call an unsafe method
82    // - `ast::PrefixExpr`: dereference a raw pointer
83    // - `ast::PathExpr`: access a static mut variable
84    for (node, parent) in
85        unsafe_expr.syntax().ancestors().zip(unsafe_expr.syntax().ancestors().skip(1))
86    {
87        match_ast! {
88            match parent {
89                // If the `parent` is a `MethodCallExpr`, that means the `node`
90                // is the receiver of the method call, because only the receiver
91                // can be a direct child of a method call. The method name
92                // itself is not an expression but a `NameRef`, and an argument
93                // is a direct child of an `ArgList`.
94                ast::MethodCallExpr(_) => continue,
95                ast::FieldExpr(_) => continue,
96                ast::RefExpr(_) => continue,
97                ast::BinExpr(it) => {
98                    // Check if the `node` is the left-hand side of an
99                    // assignment, if so, we don't want to wrap it in an unsafe
100                    // block, e.g. `unsafe_expr += 1`
101                    let is_left_hand_side_of_assignment = {
102                        if let Some(ast::BinaryOp::Assignment { .. }) = it.op_kind() {
103                            it.lhs().map(|lhs| lhs.syntax().text_range().contains_range(node.text_range())).unwrap_or(false)
104                        } else {
105                            false
106                        }
107                    };
108                    if !is_left_hand_side_of_assignment {
109                        return Some(node);
110                    }
111                },
112                _ => { return Some(node); }
113
114            }
115        }
116    }
117    None
118}
119
120fn needs_parentheses(expr: &ast::Expr) -> bool {
121    let node = expr.syntax();
122    node.ancestors()
123        .skip(1)
124        .take_while(|it| it.text_range().start() == node.text_range().start())
125        .map_while(ast::Expr::cast)
126        .last()
127        .and_then(|it| Some(it.syntax().parent()?.kind()))
128        .is_some_and(|kind| ast::ExprStmt::can_cast(kind) || ast::StmtList::can_cast(kind))
129}
130
131#[cfg(test)]
132mod tests {
133    use crate::tests::{check_diagnostics, check_fix, check_no_fix};
134
135    #[test]
136    fn missing_unsafe_diagnostic_with_raw_ptr() {
137        check_diagnostics(
138            r#"
139//- minicore: sized
140fn main() {
141    let x = &5_usize as *const usize;
142    unsafe { let _y = *x; }
143    let _z = *x;
144}          //^^💡 error: dereference of raw pointer is unsafe and requires an unsafe function or block
145"#,
146        )
147    }
148
149    #[test]
150    fn missing_unsafe_diagnostic_with_unsafe_call() {
151        check_diagnostics(
152            r#"
153//- minicore: sized
154struct HasUnsafe;
155
156impl HasUnsafe {
157    unsafe fn unsafe_fn(&self) {
158        let x = &5_usize as *const usize;
159        let _y = unsafe {*x};
160    }
161}
162
163unsafe fn unsafe_fn() {
164    let x = &5_usize as *const usize;
165    let _y = unsafe {*x};
166}
167
168fn main() {
169    unsafe_fn();
170  //^^^^^^^^^^^💡 error: call to unsafe function is unsafe and requires an unsafe function or block
171    HasUnsafe.unsafe_fn();
172  //^^^^^^^^^^^^^^^^^^^^^💡 error: call to unsafe function is unsafe and requires an unsafe function or block
173    unsafe {
174        unsafe_fn();
175        HasUnsafe.unsafe_fn();
176    }
177}
178"#,
179        );
180    }
181
182    #[test]
183    fn missing_unsafe_diagnostic_with_static_mut() {
184        check_diagnostics(
185            r#"
186//- minicore: copy
187
188struct Ty {
189    a: u8,
190}
191
192static mut STATIC_MUT: Ty = Ty { a: 0 };
193
194fn main() {
195    let _x = STATIC_MUT.a;
196           //^^^^^^^^^^💡 error: use of mutable static is unsafe and requires an unsafe function or block
197    unsafe {
198        let _x = STATIC_MUT.a;
199    }
200}
201"#,
202        );
203    }
204
205    #[test]
206    fn missing_unsafe_diagnostic_with_extern_static() {
207        check_diagnostics(
208            r#"
209//- minicore: copy
210
211extern "C" {
212    static EXTERN: i32;
213    static mut EXTERN_MUT: i32;
214}
215
216fn main() {
217    let _x = EXTERN;
218           //^^^^^^💡 error: use of extern static is unsafe and requires an unsafe function or block
219    let _x = EXTERN_MUT;
220           //^^^^^^^^^^💡 error: use of mutable static is unsafe and requires an unsafe function or block
221    unsafe {
222        let _x = EXTERN;
223        let _x = EXTERN_MUT;
224    }
225}
226"#,
227        );
228    }
229
230    #[test]
231    fn no_unsafe_diagnostic_with_addr_of_static() {
232        check_diagnostics(
233            r#"
234//- minicore: copy, addr_of
235
236use core::ptr::{addr_of, addr_of_mut};
237
238extern "C" {
239    static EXTERN: i32;
240    static mut EXTERN_MUT: i32;
241}
242static mut STATIC_MUT: i32 = 0;
243
244fn main() {
245    let _x = addr_of!(EXTERN);
246    let _x = addr_of!(EXTERN_MUT);
247    let _x = addr_of!(STATIC_MUT);
248    let _x = addr_of_mut!(EXTERN_MUT);
249    let _x = addr_of_mut!(STATIC_MUT);
250}
251"#,
252        );
253    }
254
255    #[test]
256    fn no_missing_unsafe_diagnostic_with_safe_intrinsic() {
257        check_diagnostics(
258            r#"
259#[rustc_intrinsic]
260pub fn bitreverse(x: u32) -> u32; // Safe intrinsic
261#[rustc_intrinsic]
262pub unsafe fn floorf32(x: f32) -> f32; // Unsafe intrinsic
263
264fn main() {
265    let _ = bitreverse(12);
266    let _ = floorf32(12.0);
267          //^^^^^^^^^^^^^^💡 error: call to unsafe function is unsafe and requires an unsafe function or block
268}
269"#,
270        );
271    }
272
273    #[test]
274    fn no_missing_unsafe_diagnostic_with_legacy_safe_intrinsic() {
275        check_diagnostics(
276            r#"
277extern "rust-intrinsic" {
278    #[rustc_safe_intrinsic]
279    pub fn bitreverse(x: u32) -> u32; // Safe intrinsic
280    pub fn floorf32(x: f32) -> f32; // Unsafe intrinsic
281}
282
283fn main() {
284    let _ = bitreverse(12);
285    let _ = floorf32(12.0);
286          //^^^^^^^^^^^^^^💡 error: call to unsafe function is unsafe and requires an unsafe function or block
287}
288"#,
289        );
290    }
291
292    #[test]
293    fn no_missing_unsafe_diagnostic_with_deprecated_safe_2024() {
294        check_diagnostics(
295            r#"
296#[rustc_deprecated_safe_2024]
297fn set_var() {}
298
299#[rustc_deprecated_safe_2024(audit_that = "something")]
300fn set_var2() {}
301
302fn main() {
303    set_var();
304    set_var2();
305}
306"#,
307        );
308    }
309
310    #[test]
311    fn add_unsafe_block_when_dereferencing_a_raw_pointer() {
312        check_fix(
313            r#"
314//- minicore: sized
315fn main() {
316    let x = &5_usize as *const usize;
317    let _z = *x$0;
318}
319"#,
320            r#"
321fn main() {
322    let x = &5_usize as *const usize;
323    let _z = unsafe { *x };
324}
325"#,
326        );
327    }
328
329    #[test]
330    fn add_unsafe_block_when_calling_unsafe_function() {
331        check_fix(
332            r#"
333//- minicore: sized
334unsafe fn func() {
335    let x = &5_usize as *const usize;
336    let z = *x;
337}
338fn main() {
339    func$0();
340}
341"#,
342            r#"
343unsafe fn func() {
344    let x = &5_usize as *const usize;
345    let z = *x;
346}
347fn main() {
348    unsafe { func() };
349}
350"#,
351        )
352    }
353
354    #[test]
355    fn add_unsafe_block_when_calling_unsafe_method() {
356        check_fix(
357            r#"
358//- minicore: sized
359struct S(usize);
360impl S {
361    unsafe fn func(&self) {
362        let x = &self.0 as *const usize;
363        let _z = unsafe { *x };
364    }
365}
366fn main() {
367    let s = S(5);
368    s.func$0();
369}
370"#,
371            r#"
372struct S(usize);
373impl S {
374    unsafe fn func(&self) {
375        let x = &self.0 as *const usize;
376        let _z = unsafe { *x };
377    }
378}
379fn main() {
380    let s = S(5);
381    unsafe { s.func() };
382}
383"#,
384        )
385    }
386
387    #[test]
388    fn add_unsafe_block_when_accessing_mutable_static() {
389        check_fix(
390            r#"
391//- minicore: copy
392struct Ty {
393    a: u8,
394}
395
396static mut STATIC_MUT: Ty = Ty { a: 0 };
397
398fn main() {
399    let _x = STATIC_MUT$0.a;
400}
401"#,
402            r#"
403struct Ty {
404    a: u8,
405}
406
407static mut STATIC_MUT: Ty = Ty { a: 0 };
408
409fn main() {
410    let _x = unsafe { STATIC_MUT.a };
411}
412"#,
413        )
414    }
415
416    #[test]
417    fn add_unsafe_block_when_calling_unsafe_intrinsic() {
418        check_fix(
419            r#"
420extern "rust-intrinsic" {
421    pub fn floorf32(x: f32) -> f32;
422}
423
424fn main() {
425    let _ = floorf32$0(12.0);
426}
427"#,
428            r#"
429extern "rust-intrinsic" {
430    pub fn floorf32(x: f32) -> f32;
431}
432
433fn main() {
434    let _ = unsafe { floorf32(12.0) };
435}
436"#,
437        )
438    }
439
440    #[test]
441    fn unsafe_expr_as_a_receiver_of_a_method_call() {
442        check_fix(
443            r#"
444unsafe fn foo() -> String {
445    "string".to_string()
446}
447
448fn main() {
449    foo$0().len();
450}
451"#,
452            r#"
453unsafe fn foo() -> String {
454    "string".to_string()
455}
456
457fn main() {
458    unsafe { foo().len() };
459}
460"#,
461        )
462    }
463
464    #[test]
465    fn raw_deref_on_union_field() {
466        check_diagnostics(
467            r#"
468fn main() {
469
470    union U {
471        a: u8
472    }
473    let x = U { a: 3 };
474
475    let a = &raw mut x.a;
476
477    union U1 {
478        a: u8
479    }
480    let x = U1 { a: 3 };
481
482    let a = x.a;
483         // ^^^ 💡 error: access to union field is unsafe and requires an unsafe function or block
484
485
486    let b = &raw const x.a;
487
488    let tmp = Vec::from([1, 2, 3]);
489
490    let c = &raw const tmp[x.a];
491                        // ^^^ 💡 error: access to union field is unsafe and requires an unsafe function or block
492
493    union URef {
494        p: &'static mut i32,
495    }
496
497    fn deref_union_field(u: URef) {
498        // Not an assignment but an access to the union field!
499        *(u.p) = 13;
500       // ^^^ 💡 error: access to union field is unsafe and requires an unsafe function or block
501    }
502}
503"#,
504        )
505    }
506
507    #[test]
508    fn unsafe_expr_as_an_argument_of_a_method_call() {
509        check_fix(
510            r#"
511static mut STATIC_MUT: u8 = 0;
512
513fn main() {
514    let mut v = vec![];
515    v.push(STATIC_MUT$0);
516}
517"#,
518            r#"
519static mut STATIC_MUT: u8 = 0;
520
521fn main() {
522    let mut v = vec![];
523    v.push(unsafe { STATIC_MUT });
524}
525"#,
526        )
527    }
528
529    #[test]
530    fn unsafe_expr_as_left_hand_side_of_assignment() {
531        check_fix(
532            r#"
533static mut STATIC_MUT: u8 = 0;
534
535fn main() {
536    STATIC_MUT$0 = 1;
537}
538"#,
539            r#"
540static mut STATIC_MUT: u8 = 0;
541
542fn main() {
543    unsafe { STATIC_MUT = 1 };
544}
545"#,
546        )
547    }
548
549    #[test]
550    fn unsafe_expr_as_right_hand_side_of_assignment() {
551        check_fix(
552            r#"
553//- minicore: copy
554static mut STATIC_MUT: u8 = 0;
555
556fn main() {
557    let _x;
558    _x = STATIC_MUT$0;
559}
560"#,
561            r#"
562static mut STATIC_MUT: u8 = 0;
563
564fn main() {
565    let _x;
566    _x = unsafe { STATIC_MUT };
567}
568"#,
569        )
570    }
571
572    #[test]
573    fn unsafe_expr_in_binary_plus() {
574        check_fix(
575            r#"
576//- minicore: copy
577static mut STATIC_MUT: u8 = 0;
578
579fn main() {
580    let _x = STATIC_MUT$0 + 1;
581}
582"#,
583            r#"
584static mut STATIC_MUT: u8 = 0;
585
586fn main() {
587    let _x = unsafe { STATIC_MUT } + 1;
588}
589"#,
590        )
591    }
592
593    #[test]
594    fn needs_parentheses_for_unambiguous() {
595        check_fix(
596            r#"
597//- minicore: copy
598static mut STATIC_MUT: u8 = 0;
599
600fn foo() -> u8 {
601    STATIC_MUT$0 * 2
602}
603"#,
604            r#"
605static mut STATIC_MUT: u8 = 0;
606
607fn foo() -> u8 {
608    (unsafe { STATIC_MUT }) * 2
609}
610"#,
611        )
612    }
613
614    #[test]
615    fn ref_to_unsafe_expr() {
616        check_fix(
617            r#"
618static mut STATIC_MUT: u8 = 0;
619
620fn main() {
621    let _x = &STATIC_MUT$0;
622}
623"#,
624            r#"
625static mut STATIC_MUT: u8 = 0;
626
627fn main() {
628    let _x = unsafe { &STATIC_MUT };
629}
630"#,
631        )
632    }
633
634    #[test]
635    fn ref_ref_to_unsafe_expr() {
636        check_fix(
637            r#"
638static mut STATIC_MUT: u8 = 0;
639
640fn main() {
641    let _x = &&STATIC_MUT$0;
642}
643"#,
644            r#"
645static mut STATIC_MUT: u8 = 0;
646
647fn main() {
648    let _x = unsafe { &&STATIC_MUT };
649}
650"#,
651        )
652    }
653
654    #[test]
655    fn unsafe_expr_in_macro_call() {
656        check_no_fix(
657            r#"
658unsafe fn foo() -> u8 {
659    0
660}
661
662fn main() {
663    let x = format!("foo: {}", foo$0());
664}
665            "#,
666        )
667    }
668
669    #[test]
670    fn rustc_deprecated_safe_2024() {
671        check_diagnostics(
672            r#"
673//- /ed2021.rs crate:ed2021 edition:2021
674#[rustc_deprecated_safe_2024]
675unsafe fn deprecated_safe() -> u8 {
676    0
677}
678
679//- /ed2024.rs crate:ed2024 edition:2024
680#[rustc_deprecated_safe_2024]
681unsafe fn deprecated_safe() -> u8 {
682    0
683}
684
685//- /dep1.rs crate:dep1 deps:ed2021,ed2024 edition:2021
686fn main() {
687    ed2021::deprecated_safe();
688    ed2024::deprecated_safe();
689}
690
691//- /dep2.rs crate:dep2 deps:ed2021,ed2024 edition:2024
692fn main() {
693    ed2021::deprecated_safe();
694 // ^^^^^^^^^^^^^^^^^^^^^^^^^💡 error: call to unsafe function is unsafe and requires an unsafe function or block
695    ed2024::deprecated_safe();
696 // ^^^^^^^^^^^^^^^^^^^^^^^^^💡 error: call to unsafe function is unsafe and requires an unsafe function or block
697}
698
699//- /dep3.rs crate:dep3 deps:ed2021,ed2024 edition:2021
700#![warn(deprecated_safe)]
701
702fn main() {
703    ed2021::deprecated_safe();
704 // ^^^^^^^^^^^^^^^^^^^^^^^^^💡 warn: call to unsafe function is unsafe and requires an unsafe function or block
705    ed2024::deprecated_safe();
706 // ^^^^^^^^^^^^^^^^^^^^^^^^^💡 warn: call to unsafe function is unsafe and requires an unsafe function or block
707}
708            "#,
709        )
710    }
711
712    #[test]
713    fn orphan_unsafe_format_args() {
714        // Checks that we don't place orphan arguments for formatting under an unsafe block.
715        check_diagnostics(
716            r#"
717//- minicore: fmt_before_1_89_0
718fn foo() {
719    let p = 0xDEADBEEF as *const i32;
720    format_args!("", *p);
721                  // ^^ error: dereference of raw pointer is unsafe and requires an unsafe function or block
722}
723        "#,
724        );
725
726        check_diagnostics(
727            r#"
728//- minicore: fmt
729fn foo() {
730    let p = 0xDEADBEEF as *const i32;
731    format_args!("", *p);
732                  // ^^ error: dereference of raw pointer is unsafe and requires an unsafe function or block
733}
734        "#,
735        );
736    }
737
738    #[test]
739    fn unsafe_op_in_unsafe_fn_allowed_by_default_in_edition_2021() {
740        check_diagnostics(
741            r#"
742//- /lib.rs crate:foo edition:2021
743unsafe fn foo(p: *mut i32) {
744    *p = 123;
745}
746            "#,
747        );
748        check_diagnostics(
749            r#"
750//- /lib.rs crate:foo edition:2021
751#![deny(warnings)]
752unsafe fn foo(p: *mut i32) {
753    *p = 123;
754}
755            "#,
756        );
757    }
758
759    #[test]
760    fn unsafe_op_in_unsafe_fn_warn_by_default_in_edition_2024() {
761        check_diagnostics(
762            r#"
763//- /lib.rs crate:foo edition:2024
764unsafe fn foo(p: *mut i32) {
765    *p = 123;
766  //^^💡 warn: dereference of raw pointer is unsafe and requires an unsafe function or block
767}
768            "#,
769        );
770        check_diagnostics(
771            r#"
772//- /lib.rs crate:foo edition:2024
773#![deny(warnings)]
774unsafe fn foo(p: *mut i32) {
775    *p = 123;
776  //^^💡 error: dereference of raw pointer is unsafe and requires an unsafe function or block
777}
778            "#,
779        );
780    }
781
782    #[test]
783    fn unsafe_op_in_unsafe_fn() {
784        check_diagnostics(
785            r#"
786#![warn(unsafe_op_in_unsafe_fn)]
787unsafe fn foo(p: *mut i32) {
788    *p = 123;
789  //^^💡 warn: dereference of raw pointer is unsafe and requires an unsafe function or block
790}
791            "#,
792        )
793    }
794
795    #[test]
796    fn no_unsafe_diagnostic_with_safe_kw() {
797        check_diagnostics(
798            r#"
799unsafe extern {
800    pub safe fn f();
801
802    pub unsafe fn g();
803
804    pub fn h();
805
806    pub safe static S1: i32;
807
808    pub unsafe static S2: i32;
809
810    pub static S3: i32;
811}
812
813fn main() {
814    f();
815    g();
816  //^^^💡 error: call to unsafe function is unsafe and requires an unsafe function or block
817    h();
818  //^^^💡 error: call to unsafe function is unsafe and requires an unsafe function or block
819
820    let _ = S1;
821    let _ = S2;
822          //^^💡 error: use of extern static is unsafe and requires an unsafe function or block
823    let _ = S3;
824          //^^💡 error: use of extern static is unsafe and requires an unsafe function or block
825}
826"#,
827        );
828    }
829
830    #[test]
831    fn no_unsafe_diagnostic_when_destructuring_union_with_wildcard() {
832        check_diagnostics(
833            r#"
834union Union { field: i32 }
835fn foo(v: &Union) {
836    let Union { field: _ } = v;
837    let Union { field: _ | _ } = v;
838    Union { field: _ } = *v;
839}
840"#,
841        );
842    }
843
844    #[test]
845    fn union_destructuring() {
846        check_diagnostics(
847            r#"
848union Union { field: u8 }
849fn foo(v @ Union { field: _field }: &Union) {
850                       // ^^^^^^ error: access to union field is unsafe and requires an unsafe function or block
851    let Union { mut field } = v;
852             // ^^^^^^^^^💡 error: access to union field is unsafe and requires an unsafe function or block
853    let Union { field: 0..=255 } = v;
854                    // ^^^^^^^💡 error: access to union field is unsafe and requires an unsafe function or block
855    let Union { field: 0
856                    // ^💡 error: access to union field is unsafe and requires an unsafe function or block
857        | 1..=255 } = v;
858       // ^^^^^^^💡 error: access to union field is unsafe and requires an unsafe function or block
859    Union { field } = *v;
860         // ^^^^^💡 error: access to union field is unsafe and requires an unsafe function or block
861    match v {
862        Union { field: _field } => {}
863                    // ^^^^^^💡 error: access to union field is unsafe and requires an unsafe function or block
864    }
865    if let Union { field: _field } = v {}
866                       // ^^^^^^💡 error: access to union field is unsafe and requires an unsafe function or block
867    (|&Union { field }| { _ = field; })(v);
868            // ^^^^^💡 error: access to union field is unsafe and requires an unsafe function or block
869}
870"#,
871        );
872    }
873
874    #[test]
875    fn union_field_access() {
876        check_diagnostics(
877            r#"
878union Union { field: u8 }
879fn foo(v: &Union) {
880    v.field;
881 // ^^^^^^^💡 error: access to union field is unsafe and requires an unsafe function or block
882}
883"#,
884        );
885    }
886
887    #[test]
888    fn inline_asm() {
889        check_diagnostics(
890            r#"
891//- minicore: asm
892fn foo() {
893    core::arch::asm!("");
894                 // ^^^^ error: use of inline assembly is unsafe and requires an unsafe function or block
895}
896"#,
897        );
898    }
899
900    #[test]
901    fn unsafe_op_in_unsafe_fn_dismissed_in_signature() {
902        check_diagnostics(
903            r#"
904#![warn(unsafe_op_in_unsafe_fn)]
905union Union { field: u32 }
906unsafe fn foo(Union { field: _field }: Union) {}
907            "#,
908        )
909    }
910
911    #[test]
912    fn union_assignment_allowed() {
913        check_diagnostics(
914            r#"
915union Union { field: u32 }
916fn foo(mut v: Union) {
917    v.field = 123;
918    (v.field,) = (123,);
919    *&mut v.field = 123;
920       // ^^^^^^^💡 error: access to union field is unsafe and requires an unsafe function or block
921}
922struct Struct { field: u32 }
923union Union2 { field: Struct }
924fn bar(mut v: Union2) {
925    v.field.field = 123;
926}
927
928            "#,
929        )
930    }
931
932    #[test]
933    fn raw_ref_reborrow_is_safe() {
934        check_diagnostics(
935            r#"
936fn main() {
937    let ptr: *mut i32;
938    let _addr = &raw const *ptr;
939
940    let local = 1;
941    let ptr = &local as *const i32;
942    let _addr = &raw const *ptr;
943}
944"#,
945        )
946    }
947
948    #[test]
949    fn target_feature() {
950        check_diagnostics(
951            r#"
952#[target_feature(enable = "avx")]
953fn foo() {}
954
955#[target_feature(enable = "avx2")]
956fn bar() {
957    foo();
958}
959
960fn baz() {
961    foo();
962 // ^^^^^ 💡 error: call to unsafe function is unsafe and requires an unsafe function or block
963}
964        "#,
965        );
966    }
967
968    #[test]
969    fn unsafe_fn_ptr_call() {
970        check_diagnostics(
971            r#"
972fn f(it: unsafe fn()){
973    it();
974 // ^^^^ 💡 error: call to unsafe function is unsafe and requires an unsafe function or block
975}
976        "#,
977        );
978    }
979
980    #[test]
981    fn unsafe_call_in_const_expr() {
982        check_diagnostics(
983            r#"
984unsafe fn f() {}
985fn main() {
986    const { f(); };
987         // ^^^ 💡 error: call to unsafe function is unsafe and requires an unsafe function or block
988}
989        "#,
990        );
991    }
992
993    #[test]
994    fn asm_label() {
995        check_diagnostics(
996            r#"
997//- minicore: asm
998fn foo() {
999    unsafe {
1000        core::arch::asm!(
1001            "jmp {}",
1002            label {
1003                let p = 0xDEADBEAF as *mut u8;
1004                *p = 3;
1005             // ^^ error: dereference of raw pointer is unsafe and requires an unsafe function or block
1006            },
1007        );
1008    }
1009}
1010            "#,
1011        );
1012    }
1013
1014    #[test]
1015    fn regression_19823() {
1016        check_diagnostics(
1017            r#"
1018pub trait FooTrait {
1019    unsafe fn method1();
1020    unsafe fn method2();
1021}
1022
1023unsafe fn some_unsafe_fn() {}
1024
1025macro_rules! impl_foo {
1026    () => {
1027        unsafe fn method1() {
1028            some_unsafe_fn();
1029        }
1030        unsafe fn method2() {
1031            some_unsafe_fn();
1032        }
1033    };
1034}
1035
1036pub struct S1;
1037#[allow(unsafe_op_in_unsafe_fn)]
1038impl FooTrait for S1 {
1039    unsafe fn method1() {
1040        some_unsafe_fn();
1041    }
1042
1043    unsafe fn method2() {
1044        some_unsafe_fn();
1045    }
1046}
1047
1048pub struct S2;
1049#[allow(unsafe_op_in_unsafe_fn)]
1050impl FooTrait for S2 {
1051    impl_foo!();
1052}
1053        "#,
1054        );
1055    }
1056
1057    #[test]
1058    fn no_false_positive_on_format_args_since_1_89_0() {
1059        check_diagnostics(
1060            r#"
1061//- minicore: fmt
1062fn test() {
1063    let foo = 10;
1064    let bar = true;
1065    let _x = format_args!("{} {0} {} {last}", foo, bar, last = "!");
1066}
1067            "#,
1068        );
1069    }
1070
1071    #[test]
1072    fn naked_asm_is_safe() {
1073        check_diagnostics(
1074            r#"
1075#[rustc_builtin_macro]
1076macro_rules! naked_asm { () => {} }
1077
1078#[unsafe(naked)]
1079extern "C" fn naked() {
1080    naked_asm!("");
1081}
1082        "#,
1083        );
1084    }
1085
1086    #[test]
1087    fn target_feature_safe_on_wasm() {
1088        check_diagnostics(
1089            r#"
1090//- target_arch: wasm32
1091
1092#[target_feature(enable = "simd128")]
1093fn requires_target_feature() {}
1094
1095fn main() {
1096    requires_target_feature();
1097}
1098            "#,
1099        );
1100    }
1101
1102    #[test]
1103    fn multiple_target_feature_enable() {
1104        check_diagnostics(
1105            r#"
1106#[target_feature(enable = "avx2,fma")]
1107fn foo() {}
1108
1109#[target_feature(enable = "avx2", enable = "fma")]
1110fn bar() {
1111    foo();
1112}
1113        "#,
1114        );
1115    }
1116}