@@ -15,7 +15,8 @@ use rustc_data_structures::fx::FxIndexMap;
1515use rustc_feature:: Features ;
1616use rustc_parse:: validate_attr;
1717use 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 ,
1920} ;
2021use rustc_session:: lint:: { BuiltinLintDiag , LintBuffer } ;
2122use rustc_session:: Session ;
@@ -86,6 +87,9 @@ struct AstValidator<'a> {
8687 /// or `Foo::Bar<impl Trait>`
8788 is_impl_trait_banned : bool ,
8889
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+
8993 lint_buffer : & ' a mut LintBuffer ,
9094}
9195
@@ -116,6 +120,12 @@ impl<'a> AstValidator<'a> {
116120 self . outer_trait_or_trait_impl = old;
117121 }
118122
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+
119129 fn with_banned_impl_trait ( & mut self , f : impl FnOnce ( & mut Self ) ) {
120130 let old = mem:: replace ( & mut self . is_impl_trait_banned , true ) ;
121131 f ( self ) ;
@@ -429,6 +439,18 @@ impl<'a> AstValidator<'a> {
429439 }
430440 }
431441
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+
432454 fn check_defaultness ( & self , span : Span , defaultness : Defaultness ) {
433455 if let Defaultness :: Default ( def_span) = defaultness {
434456 let span = self . session . source_map ( ) . guess_head_span ( span) ;
@@ -518,18 +540,14 @@ impl<'a> AstValidator<'a> {
518540 fn check_foreign_fn_headerless (
519541 & self ,
520542 // Deconstruct to ensure exhaustiveness
521- FnHeader { safety, coroutine_kind, constness, ext } : FnHeader ,
543+ FnHeader { safety : _ , coroutine_kind, constness, ext } : FnHeader ,
522544 ) {
523545 let report_err = |span| {
524546 self . dcx ( ) . emit_err ( errors:: FnQualifierInExtern {
525547 span : span,
526548 block : self . current_extern_span ( ) ,
527549 } ) ;
528550 } ;
529- match safety {
530- Safety :: Unsafe ( span) => report_err ( span) ,
531- Safety :: Default => ( ) ,
532- }
533551 match coroutine_kind {
534552 Some ( knd) => report_err ( knd. span ( ) ) ,
535553 None => ( ) ,
@@ -1017,19 +1035,39 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
10171035 return ; // Avoid visiting again.
10181036 }
10191037 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+ } ) ;
10331071 return ; // Avoid visiting again.
10341072 }
10351073 ItemKind :: Enum ( def, _) => {
@@ -1161,6 +1199,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
11611199 fn visit_foreign_item ( & mut self , fi : & ' a ForeignItem ) {
11621200 match & fi. kind {
11631201 ForeignItemKind :: Fn ( box Fn { defaultness, sig, body, .. } ) => {
1202+ self . check_foreign_item_safety ( fi. span , sig. header . safety ) ;
11641203 self . check_defaultness ( fi. span , * defaultness) ;
11651204 self . check_foreign_fn_bodyless ( fi. ident , body. as_deref ( ) ) ;
11661205 self . check_foreign_fn_headerless ( sig. header ) ;
@@ -1180,7 +1219,8 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
11801219 self . check_foreign_ty_genericless ( generics, where_clauses) ;
11811220 self . check_foreign_item_ascii_only ( fi. ident ) ;
11821221 }
1183- ForeignItemKind :: Static ( box StaticForeignItem { ty : _, mutability : _, expr } ) => {
1222+ ForeignItemKind :: Static ( box StaticForeignItem { expr, safety, .. } ) => {
1223+ self . check_foreign_item_safety ( fi. span , * safety) ;
11841224 self . check_foreign_kind_bodyless ( fi. ident , "static" , expr. as_ref ( ) . map ( |b| b. span ) ) ;
11851225 self . check_foreign_item_ascii_only ( fi. ident ) ;
11861226 }
@@ -1736,6 +1776,7 @@ pub fn check_crate(
17361776 outer_impl_trait : None ,
17371777 disallow_tilde_const : Some ( DisallowTildeConstContext :: Item ) ,
17381778 is_impl_trait_banned : false ,
1779+ extern_mod_safety : None ,
17391780 lint_buffer : lints,
17401781 } ;
17411782 visit:: walk_crate ( & mut validator, krate) ;
0 commit comments