Skip to content

Commit 6dc5b32

Browse files
committed
Auto merge of #18350 - roife:safe-kw-1, r=lnicola
feat: initial support for safe_kw in extern blocks This PR adds initial support for `safe` keywords in external blocks. ## Changes 1. Parsing static declarations with `safe` kw and `unsafe` kw, as well as functions with `safe` kw in extern_blocks 2. Add `HAS_SAFE_KW ` to `FnFlags` 3. Handle `safe` kw in `is_fn_unsafe_to_call` query 4. Handle safe_kw in unsafe diagnostics
2 parents 687b72c + 002f6ad commit 6dc5b32

File tree

13 files changed

+326
-12
lines changed

13 files changed

+326
-12
lines changed

crates/hir-def/src/data.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,10 @@ impl FunctionData {
148148
self.flags.contains(FnFlags::HAS_UNSAFE_KW)
149149
}
150150

151+
pub fn is_safe(&self) -> bool {
152+
self.flags.contains(FnFlags::HAS_SAFE_KW)
153+
}
154+
151155
pub fn is_varargs(&self) -> bool {
152156
self.flags.contains(FnFlags::IS_VARARGS)
153157
}
@@ -567,6 +571,8 @@ pub struct StaticData {
567571
pub visibility: RawVisibility,
568572
pub mutable: bool,
569573
pub is_extern: bool,
574+
pub has_safe_kw: bool,
575+
pub has_unsafe_kw: bool,
570576
}
571577

572578
impl StaticData {
@@ -581,6 +587,8 @@ impl StaticData {
581587
visibility: item_tree[statik.visibility].clone(),
582588
mutable: statik.mutable,
583589
is_extern: matches!(loc.container, ItemContainerId::ExternBlockId(_)),
590+
has_safe_kw: statik.has_safe_kw,
591+
has_unsafe_kw: statik.has_unsafe_kw,
584592
})
585593
}
586594
}

crates/hir-def/src/item_tree.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -754,6 +754,7 @@ bitflags::bitflags! {
754754
const HAS_ASYNC_KW = 1 << 4;
755755
const HAS_UNSAFE_KW = 1 << 5;
756756
const IS_VARARGS = 1 << 6;
757+
const HAS_SAFE_KW = 1 << 7;
757758
}
758759
}
759760

@@ -822,7 +823,10 @@ pub struct Const {
822823
pub struct Static {
823824
pub name: Name,
824825
pub visibility: RawVisibilityId,
826+
// TODO: use bitflags when we have more flags
825827
pub mutable: bool,
828+
pub has_safe_kw: bool,
829+
pub has_unsafe_kw: bool,
826830
pub type_ref: Interned<TypeRef>,
827831
pub ast_id: FileAstId<ast::Static>,
828832
}

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,9 @@ impl<'a> Ctx<'a> {
440440
if func.unsafe_token().is_some() {
441441
flags |= FnFlags::HAS_UNSAFE_KW;
442442
}
443+
if func.safe_token().is_some() {
444+
flags |= FnFlags::HAS_SAFE_KW;
445+
}
443446
if has_var_args {
444447
flags |= FnFlags::IS_VARARGS;
445448
}
@@ -484,8 +487,11 @@ impl<'a> Ctx<'a> {
484487
let type_ref = self.lower_type_ref_opt(static_.ty());
485488
let visibility = self.lower_visibility(static_);
486489
let mutable = static_.mut_token().is_some();
490+
let has_safe_kw = static_.safe_token().is_some();
491+
let has_unsafe_kw = static_.unsafe_token().is_some();
487492
let ast_id = self.source_ast_id_map.ast_id(static_);
488-
let res = Static { name, visibility, mutable, type_ref, ast_id };
493+
let res =
494+
Static { name, visibility, mutable, type_ref, ast_id, has_safe_kw, has_unsafe_kw };
489495
Some(id(self.data().statics.alloc(res)))
490496
}
491497

crates/hir-def/src/item_tree/pretty.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,9 @@ impl Printer<'_> {
278278
if flags.contains(FnFlags::HAS_UNSAFE_KW) {
279279
w!(self, "unsafe ");
280280
}
281+
if flags.contains(FnFlags::HAS_SAFE_KW) {
282+
w!(self, "safe ");
283+
}
281284
if let Some(abi) = abi {
282285
w!(self, "extern \"{}\" ", abi);
283286
}
@@ -379,9 +382,23 @@ impl Printer<'_> {
379382
wln!(self, " = _;");
380383
}
381384
ModItem::Static(it) => {
382-
let Static { name, visibility, mutable, type_ref, ast_id } = &self.tree[it];
385+
let Static {
386+
name,
387+
visibility,
388+
mutable,
389+
type_ref,
390+
ast_id,
391+
has_safe_kw,
392+
has_unsafe_kw,
393+
} = &self.tree[it];
383394
self.print_ast_id(ast_id.erase());
384395
self.print_visibility(*visibility);
396+
if *has_safe_kw {
397+
w!(self, "safe ");
398+
}
399+
if *has_unsafe_kw {
400+
w!(self, "unsafe ");
401+
}
385402
w!(self, "static ");
386403
if *mutable {
387404
w!(self, "mut ");

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ fn walk_unsafe(
8989
let value_or_partial = resolver.resolve_path_in_value_ns(db.upcast(), path);
9090
if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id), _)) = value_or_partial {
9191
let static_data = db.static_data(id);
92-
if static_data.mutable || static_data.is_extern {
92+
if static_data.mutable || (static_data.is_extern && !static_data.has_safe_kw) {
9393
unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block });
9494
}
9595
}

crates/hir-ty/src/utils.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -257,10 +257,12 @@ pub fn is_fn_unsafe_to_call(db: &dyn HirDatabase, func: FunctionId) -> bool {
257257
return true;
258258
}
259259

260-
match func.lookup(db.upcast()).container {
260+
let loc = func.lookup(db.upcast());
261+
match loc.container {
261262
hir_def::ItemContainerId::ExternBlockId(block) => {
262-
// Function in an `extern` block are always unsafe to call, except when it has
263-
// `"rust-intrinsic"` ABI there are a few exceptions.
263+
// Function in an `extern` block are always unsafe to call, except when
264+
// it is marked as `safe` or it has `"rust-intrinsic"` ABI there are a
265+
// few exceptions.
264266
let id = block.lookup(db.upcast()).id;
265267

266268
let is_intrinsic =
@@ -270,8 +272,8 @@ pub fn is_fn_unsafe_to_call(db: &dyn HirDatabase, func: FunctionId) -> bool {
270272
// Intrinsics are unsafe unless they have the rustc_safe_intrinsic attribute
271273
!data.attrs.by_key(&sym::rustc_safe_intrinsic).exists()
272274
} else {
273-
// Extern items are always unsafe
274-
true
275+
// Extern items without `safe` modifier are always unsafe
276+
!db.function_data(func).is_safe()
275277
}
276278
}
277279
_ => false,

crates/ide-diagnostics/src/handlers/missing_unsafe.rs

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -554,7 +554,7 @@ fn main() {
554554
r#"
555555
//- /ed2021.rs crate:ed2021 edition:2021
556556
#[rustc_deprecated_safe_2024]
557-
unsafe fn safe() -> u8 {
557+
unsafe fn safe_fn() -> u8 {
558558
0
559559
}
560560
//- /ed2024.rs crate:ed2024 edition:2024
@@ -564,7 +564,7 @@ unsafe fn not_safe() -> u8 {
564564
}
565565
//- /main.rs crate:main deps:ed2021,ed2024
566566
fn main() {
567-
ed2021::safe();
567+
ed2021::safe_fn();
568568
ed2024::not_safe();
569569
//^^^^^^^^^^^^^^^^^^💡 error: this operation is unsafe and requires an unsafe function or block
570570
}
@@ -595,4 +595,39 @@ unsafe fn foo(p: *mut i32) {
595595
"#,
596596
)
597597
}
598+
599+
#[test]
600+
fn no_unsafe_diagnostic_with_safe_kw() {
601+
check_diagnostics(
602+
r#"
603+
unsafe extern {
604+
pub safe fn f();
605+
606+
pub unsafe fn g();
607+
608+
pub fn h();
609+
610+
pub safe static S1: i32;
611+
612+
pub unsafe static S2: i32;
613+
614+
pub static S3: i32;
615+
}
616+
617+
fn main() {
618+
f();
619+
g();
620+
//^^^💡 error: this operation is unsafe and requires an unsafe function or block
621+
h();
622+
//^^^💡 error: this operation is unsafe and requires an unsafe function or block
623+
624+
let _ = S1;
625+
let _ = S2;
626+
//^^💡 error: this operation is unsafe and requires an unsafe function or block
627+
let _ = S3;
628+
//^^💡 error: this operation is unsafe and requires an unsafe function or block
629+
}
630+
"#,
631+
);
632+
}
598633
}

crates/parser/src/grammar/items.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,11 @@ pub(super) fn opt_item(p: &mut Parser<'_>, m: Marker) -> Result<(), Marker> {
135135
has_mods = true;
136136
}
137137

138+
if p.at(T![safe]) {
139+
p.eat(T![safe]);
140+
has_mods = true;
141+
}
142+
138143
if p.at(T![extern]) {
139144
has_extern = true;
140145
has_mods = true;
@@ -189,6 +194,7 @@ pub(super) fn opt_item(p: &mut Parser<'_>, m: Marker) -> Result<(), Marker> {
189194
T![fn] => fn_(p, m),
190195

191196
T![const] if p.nth(1) != T!['{'] => consts::konst(p, m),
197+
T![static] if matches!(p.nth(1), IDENT | T![_] | T![mut]) => consts::static_(p, m),
192198

193199
T![trait] => traits::trait_(p, m),
194200
T![impl] => traits::impl_(p, m),

0 commit comments

Comments
 (0)