@@ -15,7 +15,8 @@ use rustc_data_structures::fx::FxIndexMap;
15
15
use rustc_feature:: Features ;
16
16
use rustc_parse:: validate_attr;
17
17
use rustc_session:: lint:: builtin:: {
18
- DEPRECATED_WHERE_CLAUSE_LOCATION , MISSING_ABI , PATTERNS_IN_FNS_WITHOUT_BODY ,
18
+ DEPRECATED_WHERE_CLAUSE_LOCATION , MISSING_ABI , MISSING_UNSAFE_ON_EXTERN ,
19
+ PATTERNS_IN_FNS_WITHOUT_BODY ,
19
20
} ;
20
21
use rustc_session:: lint:: { BuiltinLintDiag , LintBuffer } ;
21
22
use rustc_session:: Session ;
@@ -86,6 +87,9 @@ struct AstValidator<'a> {
86
87
/// or `Foo::Bar<impl Trait>`
87
88
is_impl_trait_banned : bool ,
88
89
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
+
89
93
lint_buffer : & ' a mut LintBuffer ,
90
94
}
91
95
@@ -116,6 +120,12 @@ impl<'a> AstValidator<'a> {
116
120
self . outer_trait_or_trait_impl = old;
117
121
}
118
122
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
+
119
129
fn with_banned_impl_trait ( & mut self , f : impl FnOnce ( & mut Self ) ) {
120
130
let old = mem:: replace ( & mut self . is_impl_trait_banned , true ) ;
121
131
f ( self ) ;
@@ -429,6 +439,18 @@ impl<'a> AstValidator<'a> {
429
439
}
430
440
}
431
441
442
+ fn check_foreign_item_safety ( & self , item_span : Span , safety : Safety ) {
443
+ if matches ! ( safety, Safety :: Unsafe ( _) | Safety :: Safe ( _) )
444
+ && ( self . extern_mod_safety == Some ( Safety :: Default )
445
+ || !self . features . unsafe_extern_blocks )
446
+ {
447
+ self . dcx ( ) . emit_err ( errors:: InvalidSafetyOnExtern {
448
+ item_span,
449
+ block : self . current_extern_span ( ) ,
450
+ } ) ;
451
+ }
452
+ }
453
+
432
454
fn check_defaultness ( & self , span : Span , defaultness : Defaultness ) {
433
455
if let Defaultness :: Default ( def_span) = defaultness {
434
456
let span = self . session . source_map ( ) . guess_head_span ( span) ;
@@ -518,18 +540,14 @@ impl<'a> AstValidator<'a> {
518
540
fn check_foreign_fn_headerless (
519
541
& self ,
520
542
// Deconstruct to ensure exhaustiveness
521
- FnHeader { safety, coroutine_kind, constness, ext } : FnHeader ,
543
+ FnHeader { safety : _ , coroutine_kind, constness, ext } : FnHeader ,
522
544
) {
523
545
let report_err = |span| {
524
546
self . dcx ( ) . emit_err ( errors:: FnQualifierInExtern {
525
547
span : span,
526
548
block : self . current_extern_span ( ) ,
527
549
} ) ;
528
550
} ;
529
- match safety {
530
- Safety :: Unsafe ( span) => report_err ( span) ,
531
- Safety :: Default => ( ) ,
532
- }
533
551
match coroutine_kind {
534
552
Some ( knd) => report_err ( knd. span ( ) ) ,
535
553
None => ( ) ,
@@ -1017,19 +1035,39 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
1017
1035
return ; // Avoid visiting again.
1018
1036
}
1019
1037
ItemKind :: ForeignMod ( ForeignMod { abi, safety, .. } ) => {
1020
- let old_item = mem:: replace ( & mut self . extern_mod , Some ( item) ) ;
1021
- self . visibility_not_permitted (
1022
- & item. vis ,
1023
- errors:: VisibilityNotPermittedNote :: IndividualForeignItems ,
1024
- ) ;
1025
- if let & Safety :: Unsafe ( span) = safety {
1026
- self . dcx ( ) . emit_err ( errors:: UnsafeItem { span, kind : "extern block" } ) ;
1027
- }
1028
- if abi. is_none ( ) {
1029
- self . maybe_lint_missing_abi ( item. span , item. id ) ;
1030
- }
1031
- visit:: walk_item ( self , item) ;
1032
- self . extern_mod = old_item;
1038
+ self . with_in_extern_mod ( * safety, |this| {
1039
+ let old_item = mem:: replace ( & mut this. extern_mod , Some ( item) ) ;
1040
+ this. visibility_not_permitted (
1041
+ & item. vis ,
1042
+ errors:: VisibilityNotPermittedNote :: IndividualForeignItems ,
1043
+ ) ;
1044
+
1045
+ if this. features . unsafe_extern_blocks {
1046
+ if & Safety :: Default == safety {
1047
+ if item. span . at_least_rust_2024 ( ) {
1048
+ this. dcx ( )
1049
+ . emit_err ( errors:: MissingUnsafeOnExtern { span : item. span } ) ;
1050
+ } else {
1051
+ this. lint_buffer . buffer_lint (
1052
+ MISSING_UNSAFE_ON_EXTERN ,
1053
+ item. id ,
1054
+ item. span ,
1055
+ BuiltinLintDiag :: MissingUnsafeOnExtern {
1056
+ suggestion : item. span . shrink_to_lo ( ) ,
1057
+ } ,
1058
+ ) ;
1059
+ }
1060
+ }
1061
+ } else if let & Safety :: Unsafe ( span) = safety {
1062
+ this. dcx ( ) . emit_err ( errors:: UnsafeItem { span, kind : "extern block" } ) ;
1063
+ }
1064
+
1065
+ if abi. is_none ( ) {
1066
+ this. maybe_lint_missing_abi ( item. span , item. id ) ;
1067
+ }
1068
+ visit:: walk_item ( this, item) ;
1069
+ this. extern_mod = old_item;
1070
+ } ) ;
1033
1071
return ; // Avoid visiting again.
1034
1072
}
1035
1073
ItemKind :: Enum ( def, _) => {
@@ -1161,6 +1199,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
1161
1199
fn visit_foreign_item ( & mut self , fi : & ' a ForeignItem ) {
1162
1200
match & fi. kind {
1163
1201
ForeignItemKind :: Fn ( box Fn { defaultness, sig, body, .. } ) => {
1202
+ self . check_foreign_item_safety ( fi. span , sig. header . safety ) ;
1164
1203
self . check_defaultness ( fi. span , * defaultness) ;
1165
1204
self . check_foreign_fn_bodyless ( fi. ident , body. as_deref ( ) ) ;
1166
1205
self . check_foreign_fn_headerless ( sig. header ) ;
@@ -1180,7 +1219,8 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
1180
1219
self . check_foreign_ty_genericless ( generics, where_clauses) ;
1181
1220
self . check_foreign_item_ascii_only ( fi. ident ) ;
1182
1221
}
1183
- ForeignItemKind :: Static ( box StaticForeignItem { ty : _, mutability : _, expr } ) => {
1222
+ ForeignItemKind :: Static ( box StaticForeignItem { expr, safety, .. } ) => {
1223
+ self . check_foreign_item_safety ( fi. span , * safety) ;
1184
1224
self . check_foreign_kind_bodyless ( fi. ident , "static" , expr. as_ref ( ) . map ( |b| b. span ) ) ;
1185
1225
self . check_foreign_item_ascii_only ( fi. ident ) ;
1186
1226
}
@@ -1736,6 +1776,7 @@ pub fn check_crate(
1736
1776
outer_impl_trait : None ,
1737
1777
disallow_tilde_const : Some ( DisallowTildeConstContext :: Item ) ,
1738
1778
is_impl_trait_banned : false ,
1779
+ extern_mod_safety : None ,
1739
1780
lint_buffer : lints,
1740
1781
} ;
1741
1782
visit:: walk_crate ( & mut validator, krate) ;
0 commit comments