about summary refs log tree commit diff
diff options
context:
space:
mode:
authorTobias Bucher <tobiasbucher5991@gmail.com>2024-05-28 15:16:25 +0200
committerTobias Bucher <tobiasbucher5991@gmail.com>2024-05-30 00:20:48 +0200
commit44f9f8bc33c0b5537c52c9e18697b83f12f19604 (patch)
tree6fc8081abbf8964feed9269addbf0172904b013e
parentd7680e355617f409db3cda62afe35fffbb52e70c (diff)
downloadrust-44f9f8bc33c0b5537c52c9e18697b83f12f19604.tar.gz
rust-44f9f8bc33c0b5537c52c9e18697b83f12f19604.zip
Add `deprecated_safe` lint
It warns about usages of `std::env::{set_var, remove_var}` with an
automatic fix wrapping the call in an `unsafe` block.
-rw-r--r--compiler/rustc_lint_defs/src/builtin.rs49
-rw-r--r--compiler/rustc_mir_build/messages.ftl6
-rw-r--r--compiler/rustc_mir_build/src/check_unsafety.rs19
-rw-r--r--compiler/rustc_mir_build/src/errors.rs19
-rw-r--r--tests/ui/rust-2024/unsafe-env-suggestion.fixed20
-rw-r--r--tests/ui/rust-2024/unsafe-env-suggestion.rs20
-rw-r--r--tests/ui/rust-2024/unsafe-env-suggestion.stderr33
-rw-r--r--tests/ui/rust-2024/unsafe-env.e2021.stderr6
-rw-r--r--tests/ui/rust-2024/unsafe-env.e2024.stderr10
-rw-r--r--tests/ui/rust-2024/unsafe-env.rs1
10 files changed, 172 insertions, 11 deletions
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index 13867319e5c..93995fe60a3 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -37,6 +37,7 @@ declare_lint_pass! {
         DEPRECATED,
         DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME,
         DEPRECATED_IN_FUTURE,
+        DEPRECATED_SAFE,
         DEPRECATED_WHERE_CLAUSE_LOCATION,
         DUPLICATE_MACRO_ATTRIBUTES,
         ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT,
@@ -4844,3 +4845,51 @@ declare_lint! {
         reference: "issue #124559 <https://github.com/rust-lang/rust/issues/124559>",
     };
 }
+
+declare_lint! {
+    /// The `deprecated_safe` lint detects unsafe functions being used as safe
+    /// functions.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,edition2021,compile_fail
+    /// #![deny(deprecated_safe)]
+    /// // edition 2021
+    /// use std::env;
+    /// fn enable_backtrace() {
+    ///     env::set_var("RUST_BACKTRACE", "1");
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Rust [editions] allow the language to evolve without breaking backward
+    /// compatibility. This lint catches code that uses `unsafe` functions that
+    /// were declared as safe (non-`unsafe`) in earlier editions. If you switch
+    /// the compiler to a new edition without updating the code, then it
+    /// will fail to compile if you are using a function previously marked as
+    /// safe.
+    ///
+    /// You can audit the code to see if it suffices the preconditions of the
+    /// `unsafe` code, and if it does, you can wrap it in an `unsafe` block. If
+    /// you can't fulfill the preconditions, you probably need to switch to a
+    /// different way of doing what you want to achieve.
+    ///
+    /// This lint can automatically wrap the calls in `unsafe` blocks, but this
+    /// obviously cannot verify that the preconditions of the `unsafe`
+    /// functions are fulfilled, so that is still up to the user.
+    ///
+    /// The lint is currently "allow" by default, but that might change in the
+    /// future.
+    ///
+    /// [editions]: https://doc.rust-lang.org/edition-guide/
+    pub DEPRECATED_SAFE,
+    Allow,
+    "detects unsafe functions being used as safe functions",
+    @future_incompatible = FutureIncompatibleInfo {
+        reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024),
+        reference: "issue #27970 <https://github.com/rust-lang/rust/issues/27970>",
+    };
+}
diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl
index 4ba61226a3f..40a84a599af 100644
--- a/compiler/rustc_mir_build/messages.ftl
+++ b/compiler/rustc_mir_build/messages.ftl
@@ -28,6 +28,12 @@ mir_build_borrow_of_moved_value = borrow of moved value
     .value_borrowed_label = value borrowed here after move
     .suggestion = borrow this binding in the pattern to avoid moving the value
 
+mir_build_call_to_deprecated_safe_fn_requires_unsafe =
+    call to deprecated safe function `{$function}` is unsafe and requires unsafe block
+    .note = consult the function's documentation for information on how to avoid undefined behavior
+    .label = call to unsafe function
+    .suggestion = you can wrap the call in an `unsafe` block if you can guarantee the code is only ever called from single-threaded code
+
 mir_build_call_to_fn_with_requires_unsafe =
     call to function `{$function}` with `#[target_feature]` is unsafe and requires unsafe block
     .help = in order for the call to be safe, the context requires the following additional target {$missing_target_features_count ->
diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs
index 403b7b31f1e..24098282d93 100644
--- a/compiler/rustc_mir_build/src/check_unsafety.rs
+++ b/compiler/rustc_mir_build/src/check_unsafety.rs
@@ -9,7 +9,7 @@ use rustc_middle::thir::visit::Visitor;
 use rustc_middle::thir::*;
 use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt};
-use rustc_session::lint::builtin::{UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE};
+use rustc_session::lint::builtin::{DEPRECATED_SAFE, UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE};
 use rustc_session::lint::Level;
 use rustc_span::def_id::{DefId, LocalDefId};
 use rustc_span::symbol::Symbol;
@@ -115,7 +115,22 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
                 // 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.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(),
+                            },
+                        },
+                    )
+                }
                 _ => kind.emit_requires_unsafe_err(
                     self.tcx,
                     span,
diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs
index ae6e126a4e2..b29cc24cff8 100644
--- a/compiler/rustc_mir_build/src/errors.rs
+++ b/compiler/rustc_mir_build/src/errors.rs
@@ -21,6 +21,25 @@ pub struct UnconditionalRecursion {
 }
 
 #[derive(LintDiagnostic)]
+#[diag(mir_build_call_to_deprecated_safe_fn_requires_unsafe)]
+pub struct CallToDeprecatedSafeFnRequiresUnsafe {
+    #[label]
+    pub span: Span,
+    pub function: String,
+    #[subdiagnostic]
+    pub sub: CallToDeprecatedSafeFnRequiresUnsafeSub,
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(mir_build_suggestion, applicability = "machine-applicable")]
+pub struct CallToDeprecatedSafeFnRequiresUnsafeSub {
+    #[suggestion_part(code = "unsafe {{ ")]
+    pub left: Span,
+    #[suggestion_part(code = " }}")]
+    pub right: Span,
+}
+
+#[derive(LintDiagnostic)]
 #[diag(mir_build_unsafe_op_in_unsafe_fn_call_to_unsafe_fn_requires_unsafe, code = E0133)]
 #[note]
 pub struct UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafe {
diff --git a/tests/ui/rust-2024/unsafe-env-suggestion.fixed b/tests/ui/rust-2024/unsafe-env-suggestion.fixed
new file mode 100644
index 00000000000..d9c738edfac
--- /dev/null
+++ b/tests/ui/rust-2024/unsafe-env-suggestion.fixed
@@ -0,0 +1,20 @@
+//@ run-rustfix
+
+#![deny(deprecated_safe)]
+
+use std::env;
+
+#[deny(unused_unsafe)]
+fn main() {
+    unsafe { env::set_var("FOO", "BAR") };
+    //~^ ERROR call to deprecated safe function
+    //~| WARN this is accepted in the current edition
+    unsafe { env::remove_var("FOO") };
+    //~^ ERROR call to deprecated safe function
+    //~| WARN this is accepted in the current edition
+
+    unsafe {
+        env::set_var("FOO", "BAR");
+        env::remove_var("FOO");
+    }
+}
diff --git a/tests/ui/rust-2024/unsafe-env-suggestion.rs b/tests/ui/rust-2024/unsafe-env-suggestion.rs
new file mode 100644
index 00000000000..3bd169973e3
--- /dev/null
+++ b/tests/ui/rust-2024/unsafe-env-suggestion.rs
@@ -0,0 +1,20 @@
+//@ run-rustfix
+
+#![deny(deprecated_safe)]
+
+use std::env;
+
+#[deny(unused_unsafe)]
+fn main() {
+    env::set_var("FOO", "BAR");
+    //~^ ERROR call to deprecated safe function
+    //~| WARN this is accepted in the current edition
+    env::remove_var("FOO");
+    //~^ ERROR call to deprecated safe function
+    //~| WARN this is accepted in the current edition
+
+    unsafe {
+        env::set_var("FOO", "BAR");
+        env::remove_var("FOO");
+    }
+}
diff --git a/tests/ui/rust-2024/unsafe-env-suggestion.stderr b/tests/ui/rust-2024/unsafe-env-suggestion.stderr
new file mode 100644
index 00000000000..90c91c2a474
--- /dev/null
+++ b/tests/ui/rust-2024/unsafe-env-suggestion.stderr
@@ -0,0 +1,33 @@
+error: call to deprecated safe function `std::env::set_var` is unsafe and requires unsafe block
+  --> $DIR/unsafe-env-suggestion.rs:9:5
+   |
+LL |     env::set_var("FOO", "BAR");
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
+   |
+   = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2024!
+   = note: for more information, see issue #27970 <https://github.com/rust-lang/rust/issues/27970>
+note: the lint level is defined here
+  --> $DIR/unsafe-env-suggestion.rs:3:9
+   |
+LL | #![deny(deprecated_safe)]
+   |         ^^^^^^^^^^^^^^^
+help: you can wrap the call in an `unsafe` block if you can guarantee the code is only ever called from single-threaded code
+   |
+LL |     unsafe { env::set_var("FOO", "BAR") };
+   |     ++++++++                            +
+
+error: call to deprecated safe function `std::env::remove_var` is unsafe and requires unsafe block
+  --> $DIR/unsafe-env-suggestion.rs:12:5
+   |
+LL |     env::remove_var("FOO");
+   |     ^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
+   |
+   = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2024!
+   = note: for more information, see issue #27970 <https://github.com/rust-lang/rust/issues/27970>
+help: you can wrap the call in an `unsafe` block if you can guarantee the code is only ever called from single-threaded code
+   |
+LL |     unsafe { env::remove_var("FOO") };
+   |     ++++++++                        +
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/rust-2024/unsafe-env.e2021.stderr b/tests/ui/rust-2024/unsafe-env.e2021.stderr
index ebca04d348a..cc40ec2e466 100644
--- a/tests/ui/rust-2024/unsafe-env.e2021.stderr
+++ b/tests/ui/rust-2024/unsafe-env.e2021.stderr
@@ -1,5 +1,5 @@
 error[E0133]: call to unsafe function `unsafe_fn` is unsafe and requires unsafe function or block
-  --> $DIR/unsafe-env.rs:24:5
+  --> $DIR/unsafe-env.rs:23:5
    |
 LL |     unsafe_fn();
    |     ^^^^^^^^^^^ call to unsafe function
@@ -7,13 +7,13 @@ 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:27:5
+  --> $DIR/unsafe-env.rs:26:5
    |
 LL |     unsafe {
    |     ^^^^^^ unnecessary `unsafe` block
    |
 note: the lint level is defined here
-  --> $DIR/unsafe-env.rs:12:8
+  --> $DIR/unsafe-env.rs:11:8
    |
 LL | #[deny(unused_unsafe)]
    |        ^^^^^^^^^^^^^
diff --git a/tests/ui/rust-2024/unsafe-env.e2024.stderr b/tests/ui/rust-2024/unsafe-env.e2024.stderr
index 212ff1fca00..b43f817cf72 100644
--- a/tests/ui/rust-2024/unsafe-env.e2024.stderr
+++ b/tests/ui/rust-2024/unsafe-env.e2024.stderr
@@ -1,5 +1,5 @@
 error[E0133]: call to unsafe function `set_var` is unsafe and requires unsafe block
-  --> $DIR/unsafe-env.rs:14:5
+  --> $DIR/unsafe-env.rs:13:5
    |
 LL |     env::set_var("FOO", "BAR");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
@@ -7,7 +7,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:16:5
+  --> $DIR/unsafe-env.rs:15:5
    |
 LL |     env::remove_var("FOO");
    |     ^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
@@ -15,7 +15,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:24:5
+  --> $DIR/unsafe-env.rs:23:5
    |
 LL |     unsafe_fn();
    |     ^^^^^^^^^^^ call to unsafe function
@@ -23,13 +23,13 @@ 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:27:5
+  --> $DIR/unsafe-env.rs:26:5
    |
 LL |     unsafe {
    |     ^^^^^^ unnecessary `unsafe` block
    |
 note: the lint level is defined here
-  --> $DIR/unsafe-env.rs:12:8
+  --> $DIR/unsafe-env.rs:11:8
    |
 LL | #[deny(unused_unsafe)]
    |        ^^^^^^^^^^^^^
diff --git a/tests/ui/rust-2024/unsafe-env.rs b/tests/ui/rust-2024/unsafe-env.rs
index e5c1d5778fd..a882f077b9b 100644
--- a/tests/ui/rust-2024/unsafe-env.rs
+++ b/tests/ui/rust-2024/unsafe-env.rs
@@ -4,7 +4,6 @@
 //@[e2024] compile-flags: -Zunstable-options
 
 use std::env;
-use std::mem;
 
 unsafe fn unsafe_fn() {}
 fn safe_fn() {}