Skip to main content

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