forked from rust-lang/rust
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsafety.rs
More file actions
132 lines (119 loc) · 5.38 KB
/
safety.rs
File metadata and controls
132 lines (119 loc) · 5.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
use rustc_ast::Safety;
use rustc_feature::{AttributeSafety, BUILTIN_ATTRIBUTE_MAP};
use rustc_hir::AttrPath;
use rustc_hir::lints::{AttributeLint, AttributeLintKind};
use rustc_session::lint::LintId;
use rustc_session::lint::builtin::UNSAFE_ATTR_OUTSIDE_UNSAFE;
use rustc_span::{Span, sym};
use crate::context::Stage;
use crate::{AttributeParser, ShouldEmit};
impl<'sess, S: Stage> AttributeParser<'sess, S> {
pub fn check_attribute_safety(
&mut self,
attr_path: &AttrPath,
attr_span: Span,
attr_safety: Safety,
emit_lint: &mut impl FnMut(AttributeLint<S::Id>),
target_id: S::Id,
) {
if matches!(self.stage.should_emit(), ShouldEmit::Nothing) {
return;
}
let name = (attr_path.segments.len() == 1).then_some(attr_path.segments[0].name);
if let Some(name) = name
&& [sym::cfg_trace, sym::cfg_attr_trace].contains(&name)
{
return;
}
// FIXME: We should retrieve this information from the attribute parsers instead of from `BUILTIN_ATTRIBUTE_MAP`
let builtin_attr_info = name.and_then(|name| BUILTIN_ATTRIBUTE_MAP.get(&name));
let builtin_attr_safety = builtin_attr_info.map(|x| x.safety);
match (builtin_attr_safety, attr_safety) {
// - Unsafe builtin attribute
// - User wrote `#[unsafe(..)]`, which is permitted on any edition
(Some(AttributeSafety::Unsafe { .. }), Safety::Unsafe(..)) => {
// OK
}
// - Unsafe builtin attribute
// - User did not write `#[unsafe(..)]`
(Some(AttributeSafety::Unsafe { unsafe_since }), Safety::Default) => {
let path_span = attr_path.span;
// If the `attr_item`'s span is not from a macro, then just suggest
// wrapping it in `unsafe(...)`. Otherwise, we suggest putting the
// `unsafe(`, `)` right after and right before the opening and closing
// square bracket respectively.
let diag_span = attr_span;
// Attributes can be safe in earlier editions, and become unsafe in later ones.
//
// Use the span of the attribute's name to determine the edition: the span of the
// attribute as a whole may be inaccurate if it was emitted by a macro.
//
// See https://github.com/rust-lang/rust/issues/142182.
let emit_error = match unsafe_since {
None => true,
Some(unsafe_since) => path_span.edition() >= unsafe_since,
};
let mut not_from_proc_macro = true;
if diag_span.from_expansion()
&& let Ok(mut snippet) = self.sess.source_map().span_to_snippet(diag_span)
{
snippet.retain(|c| !c.is_whitespace());
if snippet.contains("!(") || snippet.starts_with("#[") && snippet.ends_with("]")
{
not_from_proc_macro = false;
}
}
if emit_error {
self.stage.emit_err(
self.sess,
crate::session_diagnostics::UnsafeAttrOutsideUnsafe {
span: path_span,
suggestion: not_from_proc_macro.then(|| {
crate::session_diagnostics::UnsafeAttrOutsideUnsafeSuggestion {
left: diag_span.shrink_to_lo(),
right: diag_span.shrink_to_hi(),
}
}),
},
);
} else {
emit_lint(AttributeLint {
lint_id: LintId::of(UNSAFE_ATTR_OUTSIDE_UNSAFE),
id: target_id,
span: path_span,
kind: AttributeLintKind::UnsafeAttrOutsideUnsafe {
attribute_name_span: path_span,
sugg_spans: not_from_proc_macro
.then(|| (diag_span.shrink_to_lo(), diag_span.shrink_to_hi())),
},
})
}
}
// - Normal builtin attribute
// - Writing `#[unsafe(..)]` is not permitted on normal builtin attributes
(None | Some(AttributeSafety::Normal), Safety::Unsafe(unsafe_span)) => {
self.stage.emit_err(
self.sess,
crate::session_diagnostics::InvalidAttrUnsafe {
span: unsafe_span,
name: attr_path.clone(),
},
);
}
// - Normal builtin attribute
// - No explicit `#[unsafe(..)]` written.
(None | Some(AttributeSafety::Normal), Safety::Default) => {
// OK
}
(
Some(AttributeSafety::Unsafe { .. } | AttributeSafety::Normal) | None,
Safety::Safe(..),
) => {
self.sess.dcx().span_delayed_bug(
attr_span,
"`check_attribute_safety` does not expect `Safety::Safe` on attributes",
);
}
}
}
}