rustc_attr_parsing/
safety.rs1use rustc_ast::Safety;
2use rustc_feature::{AttributeSafety, BUILTIN_ATTRIBUTE_MAP};
3use rustc_hir::AttrPath;
4use rustc_hir::lints::{AttributeLint, AttributeLintKind};
5use rustc_session::lint::LintId;
6use rustc_session::lint::builtin::UNSAFE_ATTR_OUTSIDE_UNSAFE;
7use rustc_span::Span;
8
9use crate::context::Stage;
10use crate::{AttributeParser, ShouldEmit};
11
12impl<'sess, S: Stage> AttributeParser<'sess, S> {
13 pub fn check_attribute_safety(
14 &mut self,
15 attr_path: &AttrPath,
16 attr_span: Span,
17 attr_safety: Safety,
18 emit_lint: &mut impl FnMut(AttributeLint<S::Id>),
19 target_id: S::Id,
20 ) {
21 if matches!(self.stage.should_emit(), ShouldEmit::Nothing) {
22 return;
23 }
24
25 let name = (attr_path.segments.len() == 1).then_some(attr_path.segments[0]);
26
27 let builtin_attr_info = name.and_then(|name| BUILTIN_ATTRIBUTE_MAP.get(&name));
29 let builtin_attr_safety = builtin_attr_info.map(|x| x.safety);
30
31 match (builtin_attr_safety, attr_safety) {
32 (Some(AttributeSafety::Unsafe { .. }), Safety::Unsafe(..)) => {
35 }
37
38 (Some(AttributeSafety::Unsafe { unsafe_since }), Safety::Default) => {
41 let path_span = attr_path.span;
42
43 let diag_span = attr_span;
48
49 let emit_error = match unsafe_since {
56 None => true,
57 Some(unsafe_since) => path_span.edition() >= unsafe_since,
58 };
59
60 let mut not_from_proc_macro = true;
61 if diag_span.from_expansion()
62 && let Ok(mut snippet) = self.sess.source_map().span_to_snippet(diag_span)
63 {
64 snippet.retain(|c| !c.is_whitespace());
65 if snippet.contains("!(") || snippet.starts_with("#[") && snippet.ends_with("]")
66 {
67 not_from_proc_macro = false;
68 }
69 }
70
71 if emit_error {
72 self.stage.emit_err(
73 self.sess,
74 crate::session_diagnostics::UnsafeAttrOutsideUnsafe {
75 span: path_span,
76 suggestion: not_from_proc_macro.then(|| {
77 crate::session_diagnostics::UnsafeAttrOutsideUnsafeSuggestion {
78 left: diag_span.shrink_to_lo(),
79 right: diag_span.shrink_to_hi(),
80 }
81 }),
82 },
83 );
84 } else {
85 emit_lint(AttributeLint {
86 lint_id: LintId::of(UNSAFE_ATTR_OUTSIDE_UNSAFE),
87 id: target_id,
88 span: path_span,
89 kind: AttributeLintKind::UnsafeAttrOutsideUnsafe {
90 attribute_name_span: path_span,
91 sugg_spans: not_from_proc_macro
92 .then(|| (diag_span.shrink_to_lo(), diag_span.shrink_to_hi())),
93 },
94 })
95 }
96 }
97
98 (None | Some(AttributeSafety::Normal), Safety::Unsafe(unsafe_span)) => {
101 self.stage.emit_err(
102 self.sess,
103 crate::session_diagnostics::InvalidAttrUnsafe {
104 span: unsafe_span,
105 name: attr_path.clone(),
106 },
107 );
108 }
109
110 (None | Some(AttributeSafety::Normal), Safety::Default) => {
113 }
115
116 (
117 Some(AttributeSafety::Unsafe { .. } | AttributeSafety::Normal) | None,
118 Safety::Safe(..),
119 ) => {
120 self.sess.dcx().span_delayed_bug(
121 attr_span,
122 "`check_attribute_safety` does not expect `Safety::Safe` on attributes",
123 );
124 }
125 }
126 }
127}