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, sym};
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].name);
26 if let Some(name) = name
27 && [sym::cfg_trace, sym::cfg_attr_trace].contains(&name)
28 {
29 return;
30 }
31
32 let builtin_attr_info = name.and_then(|name| BUILTIN_ATTRIBUTE_MAP.get(&name));
34 let builtin_attr_safety = builtin_attr_info.map(|x| x.safety);
35
36 match (builtin_attr_safety, attr_safety) {
37 (Some(AttributeSafety::Unsafe { .. }), Safety::Unsafe(..)) => {
40 }
42
43 (Some(AttributeSafety::Unsafe { unsafe_since }), Safety::Default) => {
46 let path_span = attr_path.span;
47
48 let diag_span = attr_span;
53
54 let emit_error = match unsafe_since {
61 None => true,
62 Some(unsafe_since) => path_span.edition() >= unsafe_since,
63 };
64
65 let mut not_from_proc_macro = true;
66 if diag_span.from_expansion()
67 && let Ok(mut snippet) = self.sess.source_map().span_to_snippet(diag_span)
68 {
69 snippet.retain(|c| !c.is_whitespace());
70 if snippet.contains("!(") || snippet.starts_with("#[") && snippet.ends_with("]")
71 {
72 not_from_proc_macro = false;
73 }
74 }
75
76 if emit_error {
77 self.stage.emit_err(
78 self.sess,
79 crate::session_diagnostics::UnsafeAttrOutsideUnsafe {
80 span: path_span,
81 suggestion: not_from_proc_macro.then(|| {
82 crate::session_diagnostics::UnsafeAttrOutsideUnsafeSuggestion {
83 left: diag_span.shrink_to_lo(),
84 right: diag_span.shrink_to_hi(),
85 }
86 }),
87 },
88 );
89 } else {
90 emit_lint(AttributeLint {
91 lint_id: LintId::of(UNSAFE_ATTR_OUTSIDE_UNSAFE),
92 id: target_id,
93 span: path_span,
94 kind: AttributeLintKind::UnsafeAttrOutsideUnsafe {
95 attribute_name_span: path_span,
96 sugg_spans: not_from_proc_macro
97 .then(|| (diag_span.shrink_to_lo(), diag_span.shrink_to_hi())),
98 },
99 })
100 }
101 }
102
103 (None | Some(AttributeSafety::Normal), Safety::Unsafe(unsafe_span)) => {
106 self.stage.emit_err(
107 self.sess,
108 crate::session_diagnostics::InvalidAttrUnsafe {
109 span: unsafe_span,
110 name: attr_path.clone(),
111 },
112 );
113 }
114
115 (None | Some(AttributeSafety::Normal), Safety::Default) => {
118 }
120
121 (
122 Some(AttributeSafety::Unsafe { .. } | AttributeSafety::Normal) | None,
123 Safety::Safe(..),
124 ) => {
125 self.sess.dcx().span_delayed_bug(
126 attr_span,
127 "`check_attribute_safety` does not expect `Safety::Safe` on attributes",
128 );
129 }
130 }
131 }
132}