Skip to main content

rustc_attr_parsing/
safety.rs

1use rustc_ast::Safety;
2use rustc_errors::{Diagnostic, MultiSpan};
3use rustc_hir::AttrPath;
4use rustc_session::lint::LintId;
5use rustc_session::lint::builtin::UNSAFE_ATTR_OUTSIDE_UNSAFE;
6use rustc_span::Span;
7
8use crate::attributes::AttributeSafety;
9use crate::context::Stage;
10use crate::{AttributeParser, EmitAttribute, ShouldEmit, errors};
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        expected_safety: AttributeSafety,
19        emit_lint: &mut impl FnMut(LintId, MultiSpan, EmitAttribute),
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        match (expected_safety, attr_safety) {
26            // - Unsafe builtin attribute
27            // - User wrote `#[unsafe(..)]`, which is permitted on any edition
28            (AttributeSafety::Unsafe { .. }, Safety::Unsafe(..)) => {
29                // OK
30            }
31
32            // - Unsafe builtin attribute
33            // - User did not write `#[unsafe(..)]`
34            (AttributeSafety::Unsafe { unsafe_since }, Safety::Default) => {
35                let path_span = attr_path.span;
36
37                // If the `attr_item`'s span is not from a macro, then just suggest
38                // wrapping it in `unsafe(...)`. Otherwise, we suggest putting the
39                // `unsafe(`, `)` right after and right before the opening and closing
40                // square bracket respectively.
41                let diag_span = attr_span;
42
43                // Attributes can be safe in earlier editions, and become unsafe in later ones.
44                //
45                // Use the span of the attribute's name to determine the edition: the span of the
46                // attribute as a whole may be inaccurate if it was emitted by a macro.
47                //
48                // See https://github.com/rust-lang/rust/issues/142182.
49                let emit_error = match unsafe_since {
50                    None => true,
51                    Some(unsafe_since) => path_span.edition() >= unsafe_since,
52                };
53
54                let mut not_from_proc_macro = true;
55                if diag_span.from_expansion()
56                    && let Ok(mut snippet) = self.sess.source_map().span_to_snippet(diag_span)
57                {
58                    snippet.retain(|c| !c.is_whitespace());
59                    if snippet.contains("!(") || snippet.starts_with("#[") && snippet.ends_with("]")
60                    {
61                        not_from_proc_macro = false;
62                    }
63                }
64
65                if emit_error {
66                    self.stage.emit_err(
67                        self.sess,
68                        crate::session_diagnostics::UnsafeAttrOutsideUnsafe {
69                            span: path_span,
70                            suggestion: not_from_proc_macro.then(|| {
71                                crate::session_diagnostics::UnsafeAttrOutsideUnsafeSuggestion {
72                                    left: diag_span.shrink_to_lo(),
73                                    right: diag_span.shrink_to_hi(),
74                                }
75                            }),
76                        },
77                    );
78                } else {
79                    emit_lint(
80                        LintId::of(UNSAFE_ATTR_OUTSIDE_UNSAFE),
81                        path_span.into(),
82                        EmitAttribute(Box::new(move |dcx, level, _| {
83                            errors::UnsafeAttrOutsideUnsafeLint {
84                                span: path_span,
85                                suggestion: not_from_proc_macro
86                                    .then(|| (diag_span.shrink_to_lo(), diag_span.shrink_to_hi()))
87                                    .map(|(left, right)| {
88                                        crate::session_diagnostics::UnsafeAttrOutsideUnsafeSuggestion { left, right }
89                                    }),
90                            }
91                            .into_diag(dcx, level)
92                        })),
93                    )
94                }
95            }
96
97            // - Normal builtin attribute
98            // - Writing `#[unsafe(..)]` is not permitted on normal builtin attributes
99            (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            // - Normal builtin attribute
110            // - No explicit `#[unsafe(..)]` written.
111            (AttributeSafety::Normal, Safety::Default) => {
112                // OK
113            }
114
115            (_, Safety::Safe(..)) => {
116                self.sess.dcx().span_delayed_bug(
117                    attr_span,
118                    "`check_attribute_safety` does not expect `Safety::Safe` on attributes",
119                );
120            }
121        }
122    }
123}