From bb901a114f1abef304f2420be071da8aa3c21406 Mon Sep 17 00:00:00 2001
From: Tobias Bucher <tobiasbucher5991@gmail.com>
Date: Mon, 3 Jun 2024 13:08:39 +0200
Subject: [PATCH] Don't trigger `unsafe_op_in_unsafe_fn` for deprecated safe
 fns

Fixes #125875.
---
 .../rustc_mir_build/src/check_unsafety.rs     | 81 +++++++++++--------
 tests/ui/rust-2024/unsafe-env.e2021.stderr    | 27 ++++++-
 tests/ui/rust-2024/unsafe-env.e2024.stderr    | 49 +++++++++--
 tests/ui/rust-2024/unsafe-env.rs              | 12 ++-
 4 files changed, 124 insertions(+), 45 deletions(-)

diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs
index 659ae1724603a..9a19445e3779b 100644
--- a/compiler/rustc_mir_build/src/check_unsafety.rs
+++ b/compiler/rustc_mir_build/src/check_unsafety.rs
@@ -88,6 +88,33 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
         }
     }
 
+    fn emit_deprecated_safe_fn_call(&self, span: Span, kind: &UnsafeOpKind) -> bool {
+        match kind {
+            // Allow calls to deprecated-safe unsafe functions if the caller is
+            // from an edition before 2024.
+            &UnsafeOpKind::CallToUnsafeFunction(Some(id))
+                if !span.at_least_rust_2024()
+                    && self.tcx.has_attr(id, sym::rustc_deprecated_safe_2024) =>
+            {
+                self.tcx.emit_node_span_lint(
+                    DEPRECATED_SAFE,
+                    self.hir_context,
+                    span,
+                    CallToDeprecatedSafeFnRequiresUnsafe {
+                        span,
+                        function: with_no_trimmed_paths!(self.tcx.def_path_str(id)),
+                        sub: CallToDeprecatedSafeFnRequiresUnsafeSub {
+                            left: span.shrink_to_lo(),
+                            right: span.shrink_to_hi(),
+                        },
+                    },
+                );
+                true
+            }
+            _ => false,
+        }
+    }
+
     fn requires_unsafe(&mut self, span: Span, kind: UnsafeOpKind) {
         let unsafe_op_in_unsafe_fn_allowed = self.unsafe_op_in_unsafe_fn_allowed();
         match self.safety_context {
@@ -101,43 +128,29 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
             }
             SafetyContext::UnsafeFn if unsafe_op_in_unsafe_fn_allowed => {}
             SafetyContext::UnsafeFn => {
-                // unsafe_op_in_unsafe_fn is disallowed
-                kind.emit_unsafe_op_in_unsafe_fn_lint(
-                    self.tcx,
-                    self.hir_context,
-                    span,
-                    self.suggest_unsafe_block,
-                );
-                self.suggest_unsafe_block = false;
-            }
-            SafetyContext::Safe => match kind {
-                // Allow calls to deprecated-safe unsafe functions if the
-                // caller is from an edition before 2024.
-                UnsafeOpKind::CallToUnsafeFunction(Some(id))
-                    if !span.at_least_rust_2024()
-                        && self.tcx.has_attr(id, sym::rustc_deprecated_safe_2024) =>
-                {
-                    self.tcx.emit_node_span_lint(
-                        DEPRECATED_SAFE,
+                let deprecated_safe_fn = self.emit_deprecated_safe_fn_call(span, &kind);
+                if !deprecated_safe_fn {
+                    // unsafe_op_in_unsafe_fn is disallowed
+                    kind.emit_unsafe_op_in_unsafe_fn_lint(
+                        self.tcx,
                         self.hir_context,
                         span,
-                        CallToDeprecatedSafeFnRequiresUnsafe {
-                            span,
-                            function: with_no_trimmed_paths!(self.tcx.def_path_str(id)),
-                            sub: CallToDeprecatedSafeFnRequiresUnsafeSub {
-                                left: span.shrink_to_lo(),
-                                right: span.shrink_to_hi(),
-                            },
-                        },
-                    )
+                        self.suggest_unsafe_block,
+                    );
+                    self.suggest_unsafe_block = false;
                 }
-                _ => kind.emit_requires_unsafe_err(
-                    self.tcx,
-                    span,
-                    self.hir_context,
-                    unsafe_op_in_unsafe_fn_allowed,
-                ),
-            },
+            }
+            SafetyContext::Safe => {
+                let deprecated_safe_fn = self.emit_deprecated_safe_fn_call(span, &kind);
+                if !deprecated_safe_fn {
+                    kind.emit_requires_unsafe_err(
+                        self.tcx,
+                        span,
+                        self.hir_context,
+                        unsafe_op_in_unsafe_fn_allowed,
+                    );
+                }
+            }
         }
     }
 
diff --git a/tests/ui/rust-2024/unsafe-env.e2021.stderr b/tests/ui/rust-2024/unsafe-env.e2021.stderr
index cc40ec2e466be..90c1df192aa05 100644
--- a/tests/ui/rust-2024/unsafe-env.e2021.stderr
+++ b/tests/ui/rust-2024/unsafe-env.e2021.stderr
@@ -1,5 +1,24 @@
+error[E0133]: call to unsafe function `unsafe_fn` is unsafe and requires unsafe block
+  --> $DIR/unsafe-env.rs:15:9
+   |
+LL |         unsafe_fn();
+   |         ^^^^^^^^^^^ call to unsafe function
+   |
+   = note: for more information, see issue #71668 <https://github.com/rust-lang/rust/issues/71668>
+   = note: consult the function's documentation for information on how to avoid undefined behavior
+note: an unsafe function restricts its caller, but its body is safe by default
+  --> $DIR/unsafe-env.rs:9:1
+   |
+LL | unsafe fn unsafe_fn() {
+   | ^^^^^^^^^^^^^^^^^^^^^
+note: the lint level is defined here
+  --> $DIR/unsafe-env.rs:8:8
+   |
+LL | #[deny(unsafe_op_in_unsafe_fn)]
+   |        ^^^^^^^^^^^^^^^^^^^^^^
+
 error[E0133]: call to unsafe function `unsafe_fn` is unsafe and requires unsafe function or block
-  --> $DIR/unsafe-env.rs:23:5
+  --> $DIR/unsafe-env.rs:33:5
    |
 LL |     unsafe_fn();
    |     ^^^^^^^^^^^ call to unsafe function
@@ -7,17 +26,17 @@ LL |     unsafe_fn();
    = note: consult the function's documentation for information on how to avoid undefined behavior
 
 error: unnecessary `unsafe` block
-  --> $DIR/unsafe-env.rs:26:5
+  --> $DIR/unsafe-env.rs:36:5
    |
 LL |     unsafe {
    |     ^^^^^^ unnecessary `unsafe` block
    |
 note: the lint level is defined here
-  --> $DIR/unsafe-env.rs:11:8
+  --> $DIR/unsafe-env.rs:21:8
    |
 LL | #[deny(unused_unsafe)]
    |        ^^^^^^^^^^^^^
 
-error: aborting due to 2 previous errors
+error: aborting due to 3 previous errors
 
 For more information about this error, try `rustc --explain E0133`.
diff --git a/tests/ui/rust-2024/unsafe-env.e2024.stderr b/tests/ui/rust-2024/unsafe-env.e2024.stderr
index b43f817cf72ad..5ecdf3cd7a74c 100644
--- a/tests/ui/rust-2024/unsafe-env.e2024.stderr
+++ b/tests/ui/rust-2024/unsafe-env.e2024.stderr
@@ -1,5 +1,42 @@
+error[E0133]: call to unsafe function `std::env::set_var` is unsafe and requires unsafe block
+  --> $DIR/unsafe-env.rs:10:5
+   |
+LL |     env::set_var("FOO", "BAR");
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
+   |
+   = note: for more information, see issue #71668 <https://github.com/rust-lang/rust/issues/71668>
+   = note: consult the function's documentation for information on how to avoid undefined behavior
+note: an unsafe function restricts its caller, but its body is safe by default
+  --> $DIR/unsafe-env.rs:9:1
+   |
+LL | unsafe fn unsafe_fn() {
+   | ^^^^^^^^^^^^^^^^^^^^^
+note: the lint level is defined here
+  --> $DIR/unsafe-env.rs:8:8
+   |
+LL | #[deny(unsafe_op_in_unsafe_fn)]
+   |        ^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0133]: call to unsafe function `std::env::remove_var` is unsafe and requires unsafe block
+  --> $DIR/unsafe-env.rs:12:5
+   |
+LL |     env::remove_var("FOO");
+   |     ^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
+   |
+   = note: for more information, see issue #71668 <https://github.com/rust-lang/rust/issues/71668>
+   = note: consult the function's documentation for information on how to avoid undefined behavior
+
+error[E0133]: call to unsafe function `unsafe_fn` is unsafe and requires unsafe block
+  --> $DIR/unsafe-env.rs:15:9
+   |
+LL |         unsafe_fn();
+   |         ^^^^^^^^^^^ call to unsafe function
+   |
+   = note: for more information, see issue #71668 <https://github.com/rust-lang/rust/issues/71668>
+   = note: consult the function's documentation for information on how to avoid undefined behavior
+
 error[E0133]: call to unsafe function `set_var` is unsafe and requires unsafe block
-  --> $DIR/unsafe-env.rs:13:5
+  --> $DIR/unsafe-env.rs:23:5
    |
 LL |     env::set_var("FOO", "BAR");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
@@ -7,7 +44,7 @@ LL |     env::set_var("FOO", "BAR");
    = note: consult the function's documentation for information on how to avoid undefined behavior
 
 error[E0133]: call to unsafe function `remove_var` is unsafe and requires unsafe block
-  --> $DIR/unsafe-env.rs:15:5
+  --> $DIR/unsafe-env.rs:25:5
    |
 LL |     env::remove_var("FOO");
    |     ^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
@@ -15,7 +52,7 @@ LL |     env::remove_var("FOO");
    = note: consult the function's documentation for information on how to avoid undefined behavior
 
 error[E0133]: call to unsafe function `unsafe_fn` is unsafe and requires unsafe block
-  --> $DIR/unsafe-env.rs:23:5
+  --> $DIR/unsafe-env.rs:33:5
    |
 LL |     unsafe_fn();
    |     ^^^^^^^^^^^ call to unsafe function
@@ -23,17 +60,17 @@ LL |     unsafe_fn();
    = note: consult the function's documentation for information on how to avoid undefined behavior
 
 error: unnecessary `unsafe` block
-  --> $DIR/unsafe-env.rs:26:5
+  --> $DIR/unsafe-env.rs:36:5
    |
 LL |     unsafe {
    |     ^^^^^^ unnecessary `unsafe` block
    |
 note: the lint level is defined here
-  --> $DIR/unsafe-env.rs:11:8
+  --> $DIR/unsafe-env.rs:21:8
    |
 LL | #[deny(unused_unsafe)]
    |        ^^^^^^^^^^^^^
 
-error: aborting due to 4 previous errors
+error: aborting due to 7 previous errors
 
 For more information about this error, try `rustc --explain E0133`.
diff --git a/tests/ui/rust-2024/unsafe-env.rs b/tests/ui/rust-2024/unsafe-env.rs
index a882f077b9bb7..601f44e1d3ec5 100644
--- a/tests/ui/rust-2024/unsafe-env.rs
+++ b/tests/ui/rust-2024/unsafe-env.rs
@@ -5,7 +5,17 @@
 
 use std::env;
 
-unsafe fn unsafe_fn() {}
+#[deny(unsafe_op_in_unsafe_fn)]
+unsafe fn unsafe_fn() {
+    env::set_var("FOO", "BAR");
+    //[e2024]~^ ERROR call to unsafe function `std::env::set_var` is unsafe
+    env::remove_var("FOO");
+    //[e2024]~^ ERROR call to unsafe function `std::env::remove_var` is unsafe
+    if false {
+        unsafe_fn();
+        //~^ ERROR call to unsafe function `unsafe_fn` is unsafe
+    }
+}
 fn safe_fn() {}
 
 #[deny(unused_unsafe)]