@@ -5,21 +5,68 @@ use crate::{errors, parse_in};
55use rustc_ast:: token:: Delimiter ;
66use rustc_ast:: tokenstream:: DelimSpan ;
77use rustc_ast:: MetaItemKind ;
8- use rustc_ast:: { self as ast, AttrArgs , AttrArgsEq , Attribute , DelimArgs , MetaItem } ;
8+ use rustc_ast:: { self as ast, AttrArgs , AttrArgsEq , Attribute , DelimArgs , MetaItem , Safety } ;
99use rustc_errors:: { Applicability , FatalError , PResult } ;
10- use rustc_feature:: { AttributeTemplate , BuiltinAttribute , BUILTIN_ATTRIBUTE_MAP } ;
10+ use rustc_feature:: { AttributeSafety , AttributeTemplate , BuiltinAttribute , BUILTIN_ATTRIBUTE_MAP } ;
1111use rustc_session:: errors:: report_lit_error;
12- use rustc_session:: lint:: builtin:: ILL_FORMED_ATTRIBUTE_INPUT ;
12+ use rustc_session:: lint:: builtin:: { ILL_FORMED_ATTRIBUTE_INPUT , UNSAFE_ATTR_OUTSIDE_UNSAFE } ;
1313use rustc_session:: lint:: BuiltinLintDiag ;
1414use rustc_session:: parse:: ParseSess ;
15- use rustc_span:: { sym, Span , Symbol } ;
15+ use rustc_span:: { sym, BytePos , Span , Symbol } ;
1616
1717pub fn check_attr ( psess : & ParseSess , attr : & Attribute ) {
1818 if attr. is_doc_comment ( ) {
1919 return ;
2020 }
2121
2222 let attr_info = attr. ident ( ) . and_then ( |ident| BUILTIN_ATTRIBUTE_MAP . get ( & ident. name ) ) ;
23+ let attr_item = attr. get_normal_item ( ) ;
24+
25+ let is_unsafe_attr =
26+ attr_info. map ( |attr| attr. safety == AttributeSafety :: Unsafe ) . unwrap_or ( false ) ;
27+
28+ if is_unsafe_attr {
29+ if let ast:: Safety :: Default = attr_item. unsafety {
30+ let path_span = attr_item. path . span ;
31+
32+ // If the `attr_item`'s span is not from a macro, then just suggest
33+ // wrapping it in `unsafe(...)`. Otherwise, we suggest putting the
34+ // `unsafe(`, `)` right after and right before the opening and closing
35+ // square bracket respectively.
36+ let diag_span = if attr_item. span ( ) . can_be_used_for_suggestions ( ) {
37+ attr_item. span ( )
38+ } else {
39+ attr. span . with_lo ( attr. span . lo ( ) + BytePos ( 2 ) ) . with_hi ( attr. span . hi ( ) - BytePos ( 1 ) )
40+ } ;
41+
42+ if attr. span . at_least_rust_2024 ( ) {
43+ psess. dcx . emit_err ( errors:: UnsafeAttrOutsideUnsafe {
44+ span : path_span,
45+ suggestion : errors:: UnsafeAttrOutsideUnsafeSuggestion {
46+ left : diag_span. shrink_to_lo ( ) ,
47+ right : diag_span. shrink_to_hi ( ) ,
48+ } ,
49+ } ) ;
50+ } else {
51+ psess. buffer_lint (
52+ UNSAFE_ATTR_OUTSIDE_UNSAFE ,
53+ path_span,
54+ ast:: CRATE_NODE_ID ,
55+ BuiltinLintDiag :: UnsafeAttrOutsideUnsafe {
56+ attribute_name_span : path_span,
57+ sugg_spans : ( diag_span. shrink_to_lo ( ) , diag_span. shrink_to_hi ( ) ) ,
58+ } ,
59+ ) ;
60+ }
61+ }
62+ } else {
63+ if let Safety :: Unsafe ( unsafe_span) = attr_item. unsafety {
64+ psess. dcx . emit_err ( errors:: InvalidAttrUnsafe {
65+ span : unsafe_span,
66+ name : attr_item. path . clone ( ) ,
67+ } ) ;
68+ }
69+ }
2370
2471 // Check input tokens for built-in and key-value attributes.
2572 match attr_info {
@@ -32,7 +79,7 @@ pub fn check_attr(psess: &ParseSess, attr: &Attribute) {
3279 }
3380 }
3481 }
35- _ if let AttrArgs :: Eq ( ..) = attr . get_normal_item ( ) . args => {
82+ _ if let AttrArgs :: Eq ( ..) = attr_item . args => {
3683 // All key-value attributes are restricted to meta-item syntax.
3784 match parse_meta ( psess, attr) {
3885 Ok ( _) => { }
0 commit comments