Skip to content

Commit b4cbdb7

Browse files
committed
Fail when using safe/unsafe items inside unadorned extern blocks
1 parent 2a37712 commit b4cbdb7

9 files changed

+111
-22
lines changed

Diff for: compiler/rustc_ast_passes/messages.ftl

+3
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ ast_passes_extern_fn_qualifiers = functions in `extern` blocks cannot have quali
6767
.label = in this `extern` block
6868
.suggestion = remove this qualifier
6969
70+
ast_passes_extern_invalid_safety = items in unadorned `extern` blocks cannot have safety qualifiers
71+
.suggestion = add unsafe to this `extern` block
72+
7073
ast_passes_extern_item_ascii = items in `extern` blocks cannot use non-ascii identifiers
7174
.label = in this `extern` block
7275
.note = this limitation may be lifted in the future; see issue #83942 <https://github.com/rust-lang/rust/issues/83942> for more information

Diff for: compiler/rustc_ast_passes/src/ast_validation.rs

+47-19
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@ struct AstValidator<'a> {
8787
/// or `Foo::Bar<impl Trait>`
8888
is_impl_trait_banned: bool,
8989

90+
/// Used to ban explicit safety on foreign items when the extern block is not marked as unsafe.
91+
extern_mod_safety: Option<Safety>,
92+
9093
lint_buffer: &'a mut LintBuffer,
9194
}
9295

@@ -117,6 +120,12 @@ impl<'a> AstValidator<'a> {
117120
self.outer_trait_or_trait_impl = old;
118121
}
119122

123+
fn with_in_extern_mod(&mut self, extern_mod_safety: Safety, f: impl FnOnce(&mut Self)) {
124+
let old = mem::replace(&mut self.extern_mod_safety, Some(extern_mod_safety));
125+
f(self);
126+
self.extern_mod_safety = old;
127+
}
128+
120129
fn with_banned_impl_trait(&mut self, f: impl FnOnce(&mut Self)) {
121130
let old = mem::replace(&mut self.is_impl_trait_banned, true);
122131
f(self);
@@ -430,6 +439,20 @@ impl<'a> AstValidator<'a> {
430439
}
431440
}
432441

442+
fn check_foreign_item_safety(&self, item_span: Span, safety: Safety) {
443+
match safety {
444+
Safety::Unsafe(_) | Safety::Safe(_)
445+
if self.extern_mod_safety == Some(Safety::Default) =>
446+
{
447+
self.dcx().emit_err(errors::InvalidSafetyOnExtern {
448+
item_span,
449+
block: self.current_extern_span(),
450+
});
451+
}
452+
_ => {}
453+
}
454+
}
455+
433456
fn check_defaultness(&self, span: Span, defaultness: Defaultness) {
434457
if let Defaultness::Default(def_span) = defaultness {
435458
let span = self.session.source_map().guess_head_span(span);
@@ -1014,26 +1037,28 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
10141037
return; // Avoid visiting again.
10151038
}
10161039
ItemKind::ForeignMod(ForeignMod { abi, safety, .. }) => {
1017-
let old_item = mem::replace(&mut self.extern_mod, Some(item));
1018-
self.visibility_not_permitted(
1019-
&item.vis,
1020-
errors::VisibilityNotPermittedNote::IndividualForeignItems,
1021-
);
1022-
1023-
if &Safety::Default == safety {
1024-
self.lint_buffer.buffer_lint(
1025-
MISSING_UNSAFE_ON_EXTERN,
1026-
item.id,
1027-
item.span,
1028-
BuiltinLintDiag::MissingUnsafeOnExtern,
1040+
self.with_in_extern_mod(*safety, |this| {
1041+
let old_item = mem::replace(&mut this.extern_mod, Some(item));
1042+
this.visibility_not_permitted(
1043+
&item.vis,
1044+
errors::VisibilityNotPermittedNote::IndividualForeignItems,
10291045
);
1030-
}
10311046

1032-
if abi.is_none() {
1033-
self.maybe_lint_missing_abi(item.span, item.id);
1034-
}
1035-
visit::walk_item(self, item);
1036-
self.extern_mod = old_item;
1047+
if &Safety::Default == safety {
1048+
this.lint_buffer.buffer_lint(
1049+
MISSING_UNSAFE_ON_EXTERN,
1050+
item.id,
1051+
item.span,
1052+
BuiltinLintDiag::MissingUnsafeOnExtern,
1053+
);
1054+
}
1055+
1056+
if abi.is_none() {
1057+
this.maybe_lint_missing_abi(item.span, item.id);
1058+
}
1059+
visit::walk_item(this, item);
1060+
this.extern_mod = old_item;
1061+
});
10371062
return; // Avoid visiting again.
10381063
}
10391064
ItemKind::Enum(def, _) => {
@@ -1165,6 +1190,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
11651190
fn visit_foreign_item(&mut self, fi: &'a ForeignItem) {
11661191
match &fi.kind {
11671192
ForeignItemKind::Fn(box Fn { defaultness, sig, body, .. }) => {
1193+
self.check_foreign_item_safety(fi.span, sig.header.safety);
11681194
self.check_defaultness(fi.span, *defaultness);
11691195
self.check_foreign_fn_bodyless(fi.ident, body.as_deref());
11701196
self.check_foreign_fn_headerless(sig.header);
@@ -1184,7 +1210,8 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
11841210
self.check_foreign_ty_genericless(generics, where_clauses);
11851211
self.check_foreign_item_ascii_only(fi.ident);
11861212
}
1187-
ForeignItemKind::Static(box StaticForeignItem { expr, .. }) => {
1213+
ForeignItemKind::Static(box StaticForeignItem { expr, safety, .. }) => {
1214+
self.check_foreign_item_safety(fi.span, *safety);
11881215
self.check_foreign_kind_bodyless(fi.ident, "static", expr.as_ref().map(|b| b.span));
11891216
self.check_foreign_item_ascii_only(fi.ident);
11901217
}
@@ -1740,6 +1767,7 @@ pub fn check_crate(
17401767
outer_impl_trait: None,
17411768
disallow_tilde_const: Some(DisallowTildeConstContext::Item),
17421769
is_impl_trait_banned: false,
1770+
extern_mod_safety: None,
17431771
lint_buffer: lints,
17441772
};
17451773
visit::walk_crate(&mut validator, krate);

Diff for: compiler/rustc_ast_passes/src/errors.rs

+9
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,15 @@ pub enum ExternBlockSuggestion {
216216
},
217217
}
218218

219+
#[derive(Diagnostic)]
220+
#[diag(ast_passes_extern_invalid_safety)]
221+
pub struct InvalidSafetyOnExtern {
222+
#[primary_span]
223+
pub item_span: Span,
224+
#[suggestion(code = "", applicability = "maybe-incorrect")]
225+
pub block: Span,
226+
}
227+
219228
#[derive(Diagnostic)]
220229
#[diag(ast_passes_bound_in_context)]
221230
pub struct BoundInContext<'a> {

Diff for: tests/ui/parser/fn-header-semantic-fail.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,14 @@ fn main() {
4444

4545
extern "C" {
4646
async fn fe1(); //~ ERROR functions in `extern` blocks cannot have qualifiers
47-
unsafe fn fe2();
47+
unsafe fn fe2(); //~ ERROR items in unadorned `extern` blocks cannot have safety qualifiers
4848
const fn fe3(); //~ ERROR functions in `extern` blocks cannot have qualifiers
4949
extern "C" fn fe4(); //~ ERROR functions in `extern` blocks cannot have qualifiers
5050
const async unsafe extern "C" fn fe5();
5151
//~^ ERROR functions in `extern` blocks
5252
//~| ERROR functions in `extern` blocks
5353
//~| ERROR functions in `extern` blocks
5454
//~| ERROR functions cannot be both `const` and `async`
55+
//~| ERROR items in unadorned `extern` blocks cannot have safety qualifiers
5556
}
5657
}

Diff for: tests/ui/parser/fn-header-semantic-fail.stderr

+19-1
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,15 @@ LL | extern "C" {
7878
LL | async fn fe1();
7979
| ^^^^^ help: remove this qualifier
8080

81+
error: items in unadorned `extern` blocks cannot have safety qualifiers
82+
--> $DIR/fn-header-semantic-fail.rs:47:9
83+
|
84+
LL | extern "C" {
85+
| ---------- help: add unsafe to this `extern` block
86+
LL | async fn fe1();
87+
LL | unsafe fn fe2();
88+
| ^^^^^^^^^^^^^^^^
89+
8190
error: functions in `extern` blocks cannot have qualifiers
8291
--> $DIR/fn-header-semantic-fail.rs:48:9
8392
|
@@ -96,6 +105,15 @@ LL | extern "C" {
96105
LL | extern "C" fn fe4();
97106
| ^^^^^^^^^^ help: remove this qualifier
98107

108+
error: items in unadorned `extern` blocks cannot have safety qualifiers
109+
--> $DIR/fn-header-semantic-fail.rs:50:9
110+
|
111+
LL | extern "C" {
112+
| ---------- help: add unsafe to this `extern` block
113+
...
114+
LL | const async unsafe extern "C" fn fe5();
115+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
116+
99117
error: functions in `extern` blocks cannot have qualifiers
100118
--> $DIR/fn-header-semantic-fail.rs:50:15
101119
|
@@ -132,6 +150,6 @@ LL | const async unsafe extern "C" fn fe5();
132150
| | `async` because of this
133151
| `const` because of this
134152

135-
error: aborting due to 15 previous errors
153+
error: aborting due to 17 previous errors
136154

137155
For more information about this error, try `rustc --explain E0379`.

Diff for: tests/ui/parser/no-const-fn-in-extern-block.rs

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ extern "C" {
33
//~^ ERROR functions in `extern` blocks cannot have qualifiers
44
const unsafe fn bar();
55
//~^ ERROR functions in `extern` blocks cannot have qualifiers
6+
//~| ERROR items in unadorned `extern` blocks cannot have safety qualifiers
67
}
78

89
fn main() {}

Diff for: tests/ui/parser/no-const-fn-in-extern-block.stderr

+10-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,15 @@ LL | extern "C" {
66
LL | const fn foo();
77
| ^^^^^ help: remove this qualifier
88

9+
error: items in unadorned `extern` blocks cannot have safety qualifiers
10+
--> $DIR/no-const-fn-in-extern-block.rs:4:5
11+
|
12+
LL | extern "C" {
13+
| ---------- help: add unsafe to this `extern` block
14+
...
15+
LL | const unsafe fn bar();
16+
| ^^^^^^^^^^^^^^^^^^^^^^
17+
918
error: functions in `extern` blocks cannot have qualifiers
1019
--> $DIR/no-const-fn-in-extern-block.rs:4:5
1120
|
@@ -15,5 +24,5 @@ LL | extern "C" {
1524
LL | const unsafe fn bar();
1625
| ^^^^^ help: remove this qualifier
1726

18-
error: aborting due to 2 previous errors
27+
error: aborting due to 3 previous errors
1928

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
extern "C" {
2+
safe fn test1(i: i32);
3+
//~^ ERROR items in unadorned `extern` blocks cannot have safety qualifiers
4+
}
5+
6+
fn test2(i: i32) {
7+
test1(i);
8+
}
9+
10+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
error: items in unadorned `extern` blocks cannot have safety qualifiers
2+
--> $DIR/safe-unsafe-on-unadorned-extern-block.rs:2:5
3+
|
4+
LL | extern "C" {
5+
| ---------- help: add unsafe to this `extern` block
6+
LL | safe fn test1(i: i32);
7+
| ^^^^^^^^^^^^^^^^^^^^^^
8+
9+
error: aborting due to 1 previous error
10+

0 commit comments

Comments
 (0)