Skip to content

Commit 44f9f8b

Browse files
committed
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.
1 parent d7680e3 commit 44f9f8b

10 files changed

+172
-11
lines changed

compiler/rustc_lint_defs/src/builtin.rs

+49
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ declare_lint_pass! {
3737
DEPRECATED,
3838
DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME,
3939
DEPRECATED_IN_FUTURE,
40+
DEPRECATED_SAFE,
4041
DEPRECATED_WHERE_CLAUSE_LOCATION,
4142
DUPLICATE_MACRO_ATTRIBUTES,
4243
ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT,
@@ -4844,3 +4845,51 @@ declare_lint! {
48444845
reference: "issue #124559 <https://github.com/rust-lang/rust/issues/124559>",
48454846
};
48464847
}
4848+
4849+
declare_lint! {
4850+
/// The `deprecated_safe` lint detects unsafe functions being used as safe
4851+
/// functions.
4852+
///
4853+
/// ### Example
4854+
///
4855+
/// ```rust,edition2021,compile_fail
4856+
/// #![deny(deprecated_safe)]
4857+
/// // edition 2021
4858+
/// use std::env;
4859+
/// fn enable_backtrace() {
4860+
/// env::set_var("RUST_BACKTRACE", "1");
4861+
/// }
4862+
/// ```
4863+
///
4864+
/// {{produces}}
4865+
///
4866+
/// ### Explanation
4867+
///
4868+
/// Rust [editions] allow the language to evolve without breaking backward
4869+
/// compatibility. This lint catches code that uses `unsafe` functions that
4870+
/// were declared as safe (non-`unsafe`) in earlier editions. If you switch
4871+
/// the compiler to a new edition without updating the code, then it
4872+
/// will fail to compile if you are using a function previously marked as
4873+
/// safe.
4874+
///
4875+
/// You can audit the code to see if it suffices the preconditions of the
4876+
/// `unsafe` code, and if it does, you can wrap it in an `unsafe` block. If
4877+
/// you can't fulfill the preconditions, you probably need to switch to a
4878+
/// different way of doing what you want to achieve.
4879+
///
4880+
/// This lint can automatically wrap the calls in `unsafe` blocks, but this
4881+
/// obviously cannot verify that the preconditions of the `unsafe`
4882+
/// functions are fulfilled, so that is still up to the user.
4883+
///
4884+
/// The lint is currently "allow" by default, but that might change in the
4885+
/// future.
4886+
///
4887+
/// [editions]: https://doc.rust-lang.org/edition-guide/
4888+
pub DEPRECATED_SAFE,
4889+
Allow,
4890+
"detects unsafe functions being used as safe functions",
4891+
@future_incompatible = FutureIncompatibleInfo {
4892+
reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024),
4893+
reference: "issue #27970 <https://github.com/rust-lang/rust/issues/27970>",
4894+
};
4895+
}

compiler/rustc_mir_build/messages.ftl

+6
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ mir_build_borrow_of_moved_value = borrow of moved value
2828
.value_borrowed_label = value borrowed here after move
2929
.suggestion = borrow this binding in the pattern to avoid moving the value
3030
31+
mir_build_call_to_deprecated_safe_fn_requires_unsafe =
32+
call to deprecated safe function `{$function}` is unsafe and requires unsafe block
33+
.note = consult the function's documentation for information on how to avoid undefined behavior
34+
.label = call to unsafe function
35+
.suggestion = you can wrap the call in an `unsafe` block if you can guarantee the code is only ever called from single-threaded code
36+
3137
mir_build_call_to_fn_with_requires_unsafe =
3238
call to function `{$function}` with `#[target_feature]` is unsafe and requires unsafe block
3339
.help = in order for the call to be safe, the context requires the following additional target {$missing_target_features_count ->

compiler/rustc_mir_build/src/check_unsafety.rs

+17-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use rustc_middle::thir::visit::Visitor;
99
use rustc_middle::thir::*;
1010
use rustc_middle::ty::print::with_no_trimmed_paths;
1111
use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt};
12-
use rustc_session::lint::builtin::{UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE};
12+
use rustc_session::lint::builtin::{DEPRECATED_SAFE, UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE};
1313
use rustc_session::lint::Level;
1414
use rustc_span::def_id::{DefId, LocalDefId};
1515
use rustc_span::symbol::Symbol;
@@ -115,7 +115,22 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
115115
// caller is from an edition before 2024.
116116
UnsafeOpKind::CallToUnsafeFunction(Some(id))
117117
if !span.at_least_rust_2024()
118-
&& self.tcx.has_attr(id, sym::rustc_deprecated_safe_2024) => {}
118+
&& self.tcx.has_attr(id, sym::rustc_deprecated_safe_2024) =>
119+
{
120+
self.tcx.emit_node_span_lint(
121+
DEPRECATED_SAFE,
122+
self.hir_context,
123+
span,
124+
CallToDeprecatedSafeFnRequiresUnsafe {
125+
span,
126+
function: with_no_trimmed_paths!(self.tcx.def_path_str(id)),
127+
sub: CallToDeprecatedSafeFnRequiresUnsafeSub {
128+
left: span.shrink_to_lo(),
129+
right: span.shrink_to_hi(),
130+
},
131+
},
132+
)
133+
}
119134
_ => kind.emit_requires_unsafe_err(
120135
self.tcx,
121136
span,

compiler/rustc_mir_build/src/errors.rs

+19
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,25 @@ pub struct UnconditionalRecursion {
2020
pub call_sites: Vec<Span>,
2121
}
2222

23+
#[derive(LintDiagnostic)]
24+
#[diag(mir_build_call_to_deprecated_safe_fn_requires_unsafe)]
25+
pub struct CallToDeprecatedSafeFnRequiresUnsafe {
26+
#[label]
27+
pub span: Span,
28+
pub function: String,
29+
#[subdiagnostic]
30+
pub sub: CallToDeprecatedSafeFnRequiresUnsafeSub,
31+
}
32+
33+
#[derive(Subdiagnostic)]
34+
#[multipart_suggestion(mir_build_suggestion, applicability = "machine-applicable")]
35+
pub struct CallToDeprecatedSafeFnRequiresUnsafeSub {
36+
#[suggestion_part(code = "unsafe {{ ")]
37+
pub left: Span,
38+
#[suggestion_part(code = " }}")]
39+
pub right: Span,
40+
}
41+
2342
#[derive(LintDiagnostic)]
2443
#[diag(mir_build_unsafe_op_in_unsafe_fn_call_to_unsafe_fn_requires_unsafe, code = E0133)]
2544
#[note]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//@ run-rustfix
2+
3+
#![deny(deprecated_safe)]
4+
5+
use std::env;
6+
7+
#[deny(unused_unsafe)]
8+
fn main() {
9+
unsafe { env::set_var("FOO", "BAR") };
10+
//~^ ERROR call to deprecated safe function
11+
//~| WARN this is accepted in the current edition
12+
unsafe { env::remove_var("FOO") };
13+
//~^ ERROR call to deprecated safe function
14+
//~| WARN this is accepted in the current edition
15+
16+
unsafe {
17+
env::set_var("FOO", "BAR");
18+
env::remove_var("FOO");
19+
}
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//@ run-rustfix
2+
3+
#![deny(deprecated_safe)]
4+
5+
use std::env;
6+
7+
#[deny(unused_unsafe)]
8+
fn main() {
9+
env::set_var("FOO", "BAR");
10+
//~^ ERROR call to deprecated safe function
11+
//~| WARN this is accepted in the current edition
12+
env::remove_var("FOO");
13+
//~^ ERROR call to deprecated safe function
14+
//~| WARN this is accepted in the current edition
15+
16+
unsafe {
17+
env::set_var("FOO", "BAR");
18+
env::remove_var("FOO");
19+
}
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
error: call to deprecated safe function `std::env::set_var` is unsafe and requires unsafe block
2+
--> $DIR/unsafe-env-suggestion.rs:9:5
3+
|
4+
LL | env::set_var("FOO", "BAR");
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
6+
|
7+
= warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2024!
8+
= note: for more information, see issue #27970 <https://github.com/rust-lang/rust/issues/27970>
9+
note: the lint level is defined here
10+
--> $DIR/unsafe-env-suggestion.rs:3:9
11+
|
12+
LL | #![deny(deprecated_safe)]
13+
| ^^^^^^^^^^^^^^^
14+
help: you can wrap the call in an `unsafe` block if you can guarantee the code is only ever called from single-threaded code
15+
|
16+
LL | unsafe { env::set_var("FOO", "BAR") };
17+
| ++++++++ +
18+
19+
error: call to deprecated safe function `std::env::remove_var` is unsafe and requires unsafe block
20+
--> $DIR/unsafe-env-suggestion.rs:12:5
21+
|
22+
LL | env::remove_var("FOO");
23+
| ^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
24+
|
25+
= warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2024!
26+
= note: for more information, see issue #27970 <https://github.com/rust-lang/rust/issues/27970>
27+
help: you can wrap the call in an `unsafe` block if you can guarantee the code is only ever called from single-threaded code
28+
|
29+
LL | unsafe { env::remove_var("FOO") };
30+
| ++++++++ +
31+
32+
error: aborting due to 2 previous errors
33+

tests/ui/rust-2024/unsafe-env.e2021.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
error[E0133]: call to unsafe function `unsafe_fn` is unsafe and requires unsafe function or block
2-
--> $DIR/unsafe-env.rs:24:5
2+
--> $DIR/unsafe-env.rs:23:5
33
|
44
LL | unsafe_fn();
55
| ^^^^^^^^^^^ call to unsafe function
66
|
77
= note: consult the function's documentation for information on how to avoid undefined behavior
88

99
error: unnecessary `unsafe` block
10-
--> $DIR/unsafe-env.rs:27:5
10+
--> $DIR/unsafe-env.rs:26:5
1111
|
1212
LL | unsafe {
1313
| ^^^^^^ unnecessary `unsafe` block
1414
|
1515
note: the lint level is defined here
16-
--> $DIR/unsafe-env.rs:12:8
16+
--> $DIR/unsafe-env.rs:11:8
1717
|
1818
LL | #[deny(unused_unsafe)]
1919
| ^^^^^^^^^^^^^

tests/ui/rust-2024/unsafe-env.e2024.stderr

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,35 @@
11
error[E0133]: call to unsafe function `set_var` is unsafe and requires unsafe block
2-
--> $DIR/unsafe-env.rs:14:5
2+
--> $DIR/unsafe-env.rs:13:5
33
|
44
LL | env::set_var("FOO", "BAR");
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
66
|
77
= note: consult the function's documentation for information on how to avoid undefined behavior
88

99
error[E0133]: call to unsafe function `remove_var` is unsafe and requires unsafe block
10-
--> $DIR/unsafe-env.rs:16:5
10+
--> $DIR/unsafe-env.rs:15:5
1111
|
1212
LL | env::remove_var("FOO");
1313
| ^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
1414
|
1515
= note: consult the function's documentation for information on how to avoid undefined behavior
1616

1717
error[E0133]: call to unsafe function `unsafe_fn` is unsafe and requires unsafe block
18-
--> $DIR/unsafe-env.rs:24:5
18+
--> $DIR/unsafe-env.rs:23:5
1919
|
2020
LL | unsafe_fn();
2121
| ^^^^^^^^^^^ call to unsafe function
2222
|
2323
= note: consult the function's documentation for information on how to avoid undefined behavior
2424

2525
error: unnecessary `unsafe` block
26-
--> $DIR/unsafe-env.rs:27:5
26+
--> $DIR/unsafe-env.rs:26:5
2727
|
2828
LL | unsafe {
2929
| ^^^^^^ unnecessary `unsafe` block
3030
|
3131
note: the lint level is defined here
32-
--> $DIR/unsafe-env.rs:12:8
32+
--> $DIR/unsafe-env.rs:11:8
3333
|
3434
LL | #[deny(unused_unsafe)]
3535
| ^^^^^^^^^^^^^

tests/ui/rust-2024/unsafe-env.rs

-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
//@[e2024] compile-flags: -Zunstable-options
55

66
use std::env;
7-
use std::mem;
87

98
unsafe fn unsafe_fn() {}
109
fn safe_fn() {}

0 commit comments

Comments
 (0)