rustc_attr_parsing/
safety.rs1use rustc_ast::Safety;
2use rustc_errors::MultiSpan;
3use rustc_feature::{AttributeSafety, BUILTIN_ATTRIBUTE_MAP};
4use rustc_hir::AttrPath;
5use rustc_hir::lints::AttributeLintKind;
6use rustc_session::lint::LintId;
7use rustc_session::lint::builtin::UNSAFE_ATTR_OUTSIDE_UNSAFE;
8use rustc_span::Span;
9
10use crate::context::Stage;
11use crate::{AttributeParser, ShouldEmit};
12
13impl<'sess, S: Stage> AttributeParser<'sess, S> {
14 pub fn check_attribute_safety(
15 &mut self,
16 attr_path: &AttrPath,
17 attr_span: Span,
18 attr_safety: Safety,
19 emit_lint: &mut impl FnMut(LintId, MultiSpan, AttributeLintKind),
20 ) {
21 if #[allow(non_exhaustive_omitted_patterns)] match self.stage.should_emit() {
ShouldEmit::Nothing => true,
_ => false,
}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(
86 LintId::of(UNSAFE_ATTR_OUTSIDE_UNSAFE),
87 path_span.into(),
88 AttributeLintKind::UnsafeAttrOutsideUnsafe {
89 attribute_name_span: path_span,
90 sugg_spans: not_from_proc_macro
91 .then(|| (diag_span.shrink_to_lo(), diag_span.shrink_to_hi())),
92 },
93 )
94 }
95 }
96
97 (None | Some(AttributeSafety::Normal), Safety::Unsafe(unsafe_span)) => {
100 self.stage.emit_err(
101 self.sess,
102 crate::session_diagnostics::InvalidAttrUnsafe {
103 span: unsafe_span,
104 name: attr_path.clone(),
105 },
106 );
107 }
108
109 (None | Some(AttributeSafety::Normal), Safety::Default) => {
112 }
114
115 (
116 Some(AttributeSafety::Unsafe { .. } | AttributeSafety::Normal) | None,
117 Safety::Safe(..),
118 ) => {
119 self.sess.dcx().span_delayed_bug(
120 attr_span,
121 "`check_attribute_safety` does not expect `Safety::Safe` on attributes",
122 );
123 }
124 }
125 }
126}