Skip to content

Commit 2f672dd

Browse files
authored
Merge pull request #19044 from ChayimFriedman2/deprecated-safe
fix: Fix #[rustc_deprecated_safe_2024]
2 parents ac6015f + 55c63ab commit 2f672dd

File tree

16 files changed

+274
-100
lines changed

16 files changed

+274
-100
lines changed

crates/hir-def/src/data.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -95,10 +95,10 @@ impl FunctionData {
9595
.map(Box::new);
9696
let rustc_allow_incoherent_impl = attrs.by_key(&sym::rustc_allow_incoherent_impl).exists();
9797
if flags.contains(FnFlags::HAS_UNSAFE_KW)
98-
&& !crate_graph[krate].edition.at_least_2024()
9998
&& attrs.by_key(&sym::rustc_deprecated_safe_2024).exists()
10099
{
101100
flags.remove(FnFlags::HAS_UNSAFE_KW);
101+
flags.insert(FnFlags::DEPRECATED_SAFE_2024);
102102
}
103103

104104
if attrs.by_key(&sym::target_feature).exists() {
@@ -152,6 +152,10 @@ impl FunctionData {
152152
self.flags.contains(FnFlags::HAS_UNSAFE_KW)
153153
}
154154

155+
pub fn is_deprecated_safe_2024(&self) -> bool {
156+
self.flags.contains(FnFlags::DEPRECATED_SAFE_2024)
157+
}
158+
155159
pub fn is_safe(&self) -> bool {
156160
self.flags.contains(FnFlags::HAS_SAFE_KW)
157161
}

crates/hir-def/src/expr_store/lower.rs

+18-8
Original file line numberDiff line numberDiff line change
@@ -2238,17 +2238,27 @@ impl ExprCollector<'_> {
22382238
let unsafe_arg_new = self.alloc_expr_desugared(Expr::Path(unsafe_arg_new));
22392239
let unsafe_arg_new =
22402240
self.alloc_expr_desugared(Expr::Call { callee: unsafe_arg_new, args: Box::default() });
2241-
let unsafe_arg_new = self.alloc_expr_desugared(Expr::Unsafe {
2241+
let mut unsafe_arg_new = self.alloc_expr_desugared(Expr::Unsafe {
22422242
id: None,
2243-
// We collect the unused expressions here so that we still infer them instead of
2244-
// dropping them out of the expression tree
2245-
statements: fmt
2246-
.orphans
2247-
.into_iter()
2248-
.map(|expr| Statement::Expr { expr, has_semi: true })
2249-
.collect(),
2243+
statements: Box::new([]),
22502244
tail: Some(unsafe_arg_new),
22512245
});
2246+
if !fmt.orphans.is_empty() {
2247+
unsafe_arg_new = self.alloc_expr_desugared(Expr::Block {
2248+
id: None,
2249+
// We collect the unused expressions here so that we still infer them instead of
2250+
// dropping them out of the expression tree. We cannot store them in the `Unsafe`
2251+
// block because then unsafe blocks within them will get a false "unused unsafe"
2252+
// diagnostic (rustc has a notion of builtin unsafe blocks, but we don't).
2253+
statements: fmt
2254+
.orphans
2255+
.into_iter()
2256+
.map(|expr| Statement::Expr { expr, has_semi: true })
2257+
.collect(),
2258+
tail: Some(unsafe_arg_new),
2259+
label: None,
2260+
});
2261+
}
22522262

22532263
let idx = self.alloc_expr(
22542264
Expr::Call {

crates/hir-def/src/item_tree.rs

+1
Original file line numberDiff line numberDiff line change
@@ -951,6 +951,7 @@ bitflags::bitflags! {
951951
/// only very few functions with it. So we only encode its existence here, and lookup
952952
/// it if needed.
953953
const HAS_TARGET_FEATURE = 1 << 8;
954+
const DEPRECATED_SAFE_2024 = 1 << 9;
954955
}
955956
}
956957

crates/hir-ty/src/diagnostics/unsafe_check.rs

+81-36
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,26 @@ use hir_def::{
1010
path::Path,
1111
resolver::{HasResolver, ResolveValueResult, Resolver, ValueNs},
1212
type_ref::Rawness,
13-
AdtId, DefWithBodyId, FieldId, VariantId,
13+
AdtId, DefWithBodyId, FieldId, FunctionId, VariantId,
1414
};
15+
use span::Edition;
1516

1617
use crate::{
1718
db::HirDatabase, utils::is_fn_unsafe_to_call, InferenceResult, Interner, TargetFeatures, TyExt,
1819
TyKind,
1920
};
2021

21-
/// Returns `(unsafe_exprs, fn_is_unsafe)`.
22-
///
23-
/// If `fn_is_unsafe` is false, `unsafe_exprs` are hard errors. If true, they're `unsafe_op_in_unsafe_fn`.
24-
pub fn missing_unsafe(
25-
db: &dyn HirDatabase,
26-
def: DefWithBodyId,
27-
) -> (Vec<(ExprOrPatId, UnsafetyReason)>, bool) {
22+
#[derive(Debug, Default)]
23+
pub struct MissingUnsafeResult {
24+
pub unsafe_exprs: Vec<(ExprOrPatId, UnsafetyReason)>,
25+
/// If `fn_is_unsafe` is false, `unsafe_exprs` are hard errors. If true, they're `unsafe_op_in_unsafe_fn`.
26+
pub fn_is_unsafe: bool,
27+
pub deprecated_safe_calls: Vec<ExprId>,
28+
}
29+
30+
pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> MissingUnsafeResult {
2831
let _p = tracing::info_span!("missing_unsafe").entered();
2932

30-
let mut res = Vec::new();
3133
let is_unsafe = match def {
3234
DefWithBodyId::FunctionId(it) => db.function_data(it).is_unsafe(),
3335
DefWithBodyId::StaticId(_)
@@ -37,11 +39,19 @@ pub fn missing_unsafe(
3739
| DefWithBodyId::FieldId(_) => false,
3840
};
3941

42+
let mut res = MissingUnsafeResult { fn_is_unsafe: is_unsafe, ..MissingUnsafeResult::default() };
4043
let body = db.body(def);
4144
let infer = db.infer(def);
42-
let mut callback = |node, inside_unsafe_block, reason| {
43-
if inside_unsafe_block == InsideUnsafeBlock::No {
44-
res.push((node, reason));
45+
let mut callback = |diag| match diag {
46+
UnsafeDiagnostic::UnsafeOperation { node, inside_unsafe_block, reason } => {
47+
if inside_unsafe_block == InsideUnsafeBlock::No {
48+
res.unsafe_exprs.push((node, reason));
49+
}
50+
}
51+
UnsafeDiagnostic::DeprecatedSafe2024 { node, inside_unsafe_block } => {
52+
if inside_unsafe_block == InsideUnsafeBlock::No {
53+
res.deprecated_safe_calls.push(node)
54+
}
4555
}
4656
};
4757
let mut visitor = UnsafeVisitor::new(db, &infer, &body, def, &mut callback);
@@ -56,7 +66,7 @@ pub fn missing_unsafe(
5666
}
5767
}
5868

59-
(res, is_unsafe)
69+
res
6070
}
6171

6272
#[derive(Debug, Clone, Copy)]
@@ -75,15 +85,31 @@ pub enum InsideUnsafeBlock {
7585
Yes,
7686
}
7787

88+
#[derive(Debug)]
89+
enum UnsafeDiagnostic {
90+
UnsafeOperation {
91+
node: ExprOrPatId,
92+
inside_unsafe_block: InsideUnsafeBlock,
93+
reason: UnsafetyReason,
94+
},
95+
/// A lint.
96+
DeprecatedSafe2024 { node: ExprId, inside_unsafe_block: InsideUnsafeBlock },
97+
}
98+
7899
pub fn unsafe_expressions(
79100
db: &dyn HirDatabase,
80101
infer: &InferenceResult,
81102
def: DefWithBodyId,
82103
body: &Body,
83104
current: ExprId,
84-
unsafe_expr_cb: &mut dyn FnMut(ExprOrPatId, InsideUnsafeBlock, UnsafetyReason),
105+
callback: &mut dyn FnMut(InsideUnsafeBlock),
85106
) {
86-
let mut visitor = UnsafeVisitor::new(db, infer, body, def, unsafe_expr_cb);
107+
let mut visitor_callback = |diag| {
108+
if let UnsafeDiagnostic::UnsafeOperation { inside_unsafe_block, .. } = diag {
109+
callback(inside_unsafe_block);
110+
}
111+
};
112+
let mut visitor = UnsafeVisitor::new(db, infer, body, def, &mut visitor_callback);
87113
_ = visitor.resolver.update_to_inner_scope(db.upcast(), def, current);
88114
visitor.walk_expr(current);
89115
}
@@ -97,8 +123,10 @@ struct UnsafeVisitor<'a> {
97123
inside_unsafe_block: InsideUnsafeBlock,
98124
inside_assignment: bool,
99125
inside_union_destructure: bool,
100-
unsafe_expr_cb: &'a mut dyn FnMut(ExprOrPatId, InsideUnsafeBlock, UnsafetyReason),
126+
callback: &'a mut dyn FnMut(UnsafeDiagnostic),
101127
def_target_features: TargetFeatures,
128+
// FIXME: This needs to be the edition of the span of each call.
129+
edition: Edition,
102130
}
103131

104132
impl<'a> UnsafeVisitor<'a> {
@@ -107,13 +135,14 @@ impl<'a> UnsafeVisitor<'a> {
107135
infer: &'a InferenceResult,
108136
body: &'a Body,
109137
def: DefWithBodyId,
110-
unsafe_expr_cb: &'a mut dyn FnMut(ExprOrPatId, InsideUnsafeBlock, UnsafetyReason),
138+
unsafe_expr_cb: &'a mut dyn FnMut(UnsafeDiagnostic),
111139
) -> Self {
112140
let resolver = def.resolver(db.upcast());
113141
let def_target_features = match def {
114142
DefWithBodyId::FunctionId(func) => TargetFeatures::from_attrs(&db.attrs(func.into())),
115143
_ => TargetFeatures::default(),
116144
};
145+
let edition = db.crate_graph()[resolver.module().krate()].edition;
117146
Self {
118147
db,
119148
infer,
@@ -123,13 +152,34 @@ impl<'a> UnsafeVisitor<'a> {
123152
inside_unsafe_block: InsideUnsafeBlock::No,
124153
inside_assignment: false,
125154
inside_union_destructure: false,
126-
unsafe_expr_cb,
155+
callback: unsafe_expr_cb,
127156
def_target_features,
157+
edition,
128158
}
129159
}
130160

131-
fn call_cb(&mut self, node: ExprOrPatId, reason: UnsafetyReason) {
132-
(self.unsafe_expr_cb)(node, self.inside_unsafe_block, reason);
161+
fn on_unsafe_op(&mut self, node: ExprOrPatId, reason: UnsafetyReason) {
162+
(self.callback)(UnsafeDiagnostic::UnsafeOperation {
163+
node,
164+
inside_unsafe_block: self.inside_unsafe_block,
165+
reason,
166+
});
167+
}
168+
169+
fn check_call(&mut self, node: ExprId, func: FunctionId) {
170+
let unsafety = is_fn_unsafe_to_call(self.db, func, &self.def_target_features, self.edition);
171+
match unsafety {
172+
crate::utils::Unsafety::Safe => {}
173+
crate::utils::Unsafety::Unsafe => {
174+
self.on_unsafe_op(node.into(), UnsafetyReason::UnsafeFnCall)
175+
}
176+
crate::utils::Unsafety::DeprecatedSafe2024 => {
177+
(self.callback)(UnsafeDiagnostic::DeprecatedSafe2024 {
178+
node,
179+
inside_unsafe_block: self.inside_unsafe_block,
180+
})
181+
}
182+
}
133183
}
134184

135185
fn walk_pats_top(&mut self, pats: impl Iterator<Item = PatId>, parent_expr: ExprId) {
@@ -154,7 +204,9 @@ impl<'a> UnsafeVisitor<'a> {
154204
| Pat::Ref { .. }
155205
| Pat::Box { .. }
156206
| Pat::Expr(..)
157-
| Pat::ConstBlock(..) => self.call_cb(current.into(), UnsafetyReason::UnionField),
207+
| Pat::ConstBlock(..) => {
208+
self.on_unsafe_op(current.into(), UnsafetyReason::UnionField)
209+
}
158210
// `Or` only wraps other patterns, and `Missing`/`Wild` do not constitute a read.
159211
Pat::Missing | Pat::Wild | Pat::Or(_) => {}
160212
}
@@ -189,9 +241,7 @@ impl<'a> UnsafeVisitor<'a> {
189241
match expr {
190242
&Expr::Call { callee, .. } => {
191243
if let Some(func) = self.infer[callee].as_fn_def(self.db) {
192-
if is_fn_unsafe_to_call(self.db, func, &self.def_target_features) {
193-
self.call_cb(current.into(), UnsafetyReason::UnsafeFnCall);
194-
}
244+
self.check_call(current, func);
195245
}
196246
}
197247
Expr::Path(path) => {
@@ -217,18 +267,13 @@ impl<'a> UnsafeVisitor<'a> {
217267
}
218268
}
219269
Expr::MethodCall { .. } => {
220-
if self
221-
.infer
222-
.method_resolution(current)
223-
.map(|(func, _)| is_fn_unsafe_to_call(self.db, func, &self.def_target_features))
224-
.unwrap_or(false)
225-
{
226-
self.call_cb(current.into(), UnsafetyReason::UnsafeFnCall);
270+
if let Some((func, _)) = self.infer.method_resolution(current) {
271+
self.check_call(current, func);
227272
}
228273
}
229274
Expr::UnaryOp { expr, op: UnaryOp::Deref } => {
230275
if let TyKind::Raw(..) = &self.infer[*expr].kind(Interner) {
231-
self.call_cb(current.into(), UnsafetyReason::RawPtrDeref);
276+
self.on_unsafe_op(current.into(), UnsafetyReason::RawPtrDeref);
232277
}
233278
}
234279
Expr::Unsafe { .. } => {
@@ -243,7 +288,7 @@ impl<'a> UnsafeVisitor<'a> {
243288
self.walk_pats_top(std::iter::once(target), current);
244289
self.inside_assignment = old_inside_assignment;
245290
}
246-
Expr::InlineAsm(_) => self.call_cb(current.into(), UnsafetyReason::InlineAsm),
291+
Expr::InlineAsm(_) => self.on_unsafe_op(current.into(), UnsafetyReason::InlineAsm),
247292
// rustc allows union assignment to propagate through field accesses and casts.
248293
Expr::Cast { .. } => self.inside_assignment = inside_assignment,
249294
Expr::Field { .. } => {
@@ -252,7 +297,7 @@ impl<'a> UnsafeVisitor<'a> {
252297
if let Some(Either::Left(FieldId { parent: VariantId::UnionId(_), .. })) =
253298
self.infer.field_resolution(current)
254299
{
255-
self.call_cb(current.into(), UnsafetyReason::UnionField);
300+
self.on_unsafe_op(current.into(), UnsafetyReason::UnionField);
256301
}
257302
}
258303
}
@@ -287,9 +332,9 @@ impl<'a> UnsafeVisitor<'a> {
287332
if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id), _)) = value_or_partial {
288333
let static_data = self.db.static_data(id);
289334
if static_data.mutable {
290-
self.call_cb(node, UnsafetyReason::MutableStatic);
335+
self.on_unsafe_op(node, UnsafetyReason::MutableStatic);
291336
} else if static_data.is_extern && !static_data.has_safe_kw {
292-
self.call_cb(node, UnsafetyReason::ExternStatic);
337+
self.on_unsafe_op(node, UnsafetyReason::ExternStatic);
293338
}
294339
}
295340
}

crates/hir-ty/src/lib.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,9 @@ pub use mapping::{
100100
};
101101
pub use method_resolution::check_orphan_rules;
102102
pub use traits::TraitEnvironment;
103-
pub use utils::{all_super_traits, direct_super_traits, is_fn_unsafe_to_call, TargetFeatures};
103+
pub use utils::{
104+
all_super_traits, direct_super_traits, is_fn_unsafe_to_call, TargetFeatures, Unsafety,
105+
};
104106
pub use variance::Variance;
105107

106108
pub use chalk_ir::{

crates/hir-ty/src/utils.rs

+32-6
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use intern::{sym, Symbol};
2424
use rustc_abi::TargetDataLayout;
2525
use rustc_hash::FxHashSet;
2626
use smallvec::{smallvec, SmallVec};
27+
use span::Edition;
2728
use stdx::never;
2829

2930
use crate::{
@@ -292,21 +293,38 @@ impl TargetFeatures {
292293
}
293294
}
294295

296+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
297+
pub enum Unsafety {
298+
Safe,
299+
Unsafe,
300+
/// A lint.
301+
DeprecatedSafe2024,
302+
}
303+
295304
pub fn is_fn_unsafe_to_call(
296305
db: &dyn HirDatabase,
297306
func: FunctionId,
298307
caller_target_features: &TargetFeatures,
299-
) -> bool {
308+
call_edition: Edition,
309+
) -> Unsafety {
300310
let data = db.function_data(func);
301311
if data.is_unsafe() {
302-
return true;
312+
return Unsafety::Unsafe;
303313
}
304314

305315
if data.has_target_feature() {
306316
// RFC 2396 <https://rust-lang.github.io/rfcs/2396-target-feature-1.1.html>.
307317
let callee_target_features = TargetFeatures::from_attrs(&db.attrs(func.into()));
308318
if !caller_target_features.enabled.is_superset(&callee_target_features.enabled) {
309-
return true;
319+
return Unsafety::Unsafe;
320+
}
321+
}
322+
323+
if data.is_deprecated_safe_2024() {
324+
if call_edition.at_least_2024() {
325+
return Unsafety::Unsafe;
326+
} else {
327+
return Unsafety::DeprecatedSafe2024;
310328
}
311329
}
312330

@@ -319,14 +337,22 @@ pub fn is_fn_unsafe_to_call(
319337
if is_intrinsic_block {
320338
// legacy intrinsics
321339
// extern "rust-intrinsic" intrinsics are unsafe unless they have the rustc_safe_intrinsic attribute
322-
!db.attrs(func.into()).by_key(&sym::rustc_safe_intrinsic).exists()
340+
if db.attrs(func.into()).by_key(&sym::rustc_safe_intrinsic).exists() {
341+
Unsafety::Safe
342+
} else {
343+
Unsafety::Unsafe
344+
}
323345
} else {
324346
// Function in an `extern` block are always unsafe to call, except when
325347
// it is marked as `safe`.
326-
!data.is_safe()
348+
if data.is_safe() {
349+
Unsafety::Safe
350+
} else {
351+
Unsafety::Unsafe
352+
}
327353
}
328354
}
329-
_ => false,
355+
_ => Unsafety::Safe,
330356
}
331357
}
332358

0 commit comments

Comments
 (0)