rustc_attr_parsing/attributes/
codegen_attrs.rs1use rustc_attr_data_structures::{AttributeKind, OptimizeAttr, UsedBy};
2use rustc_feature::{AttributeTemplate, template};
3use rustc_session::parse::feature_err;
4use rustc_span::{Span, Symbol, sym};
5
6use super::{
7 AcceptMapping, AttributeOrder, AttributeParser, CombineAttributeParser, ConvertFn,
8 NoArgsAttributeParser, OnDuplicate, SingleAttributeParser,
9};
10use crate::context::{AcceptContext, FinalizeContext, Stage};
11use crate::parser::ArgParser;
12use crate::session_diagnostics::{NakedFunctionIncompatibleAttribute, NullOnExport};
13
14pub(crate) struct OptimizeParser;
15
16impl<S: Stage> SingleAttributeParser<S> for OptimizeParser {
17 const PATH: &[Symbol] = &[sym::optimize];
18 const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast;
19 const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
20 const TEMPLATE: AttributeTemplate = template!(List: "size|speed|none");
21
22 fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
23 let Some(list) = args.list() else {
24 cx.expected_list(cx.attr_span);
25 return None;
26 };
27
28 let Some(single) = list.single() else {
29 cx.expected_single_argument(list.span);
30 return None;
31 };
32
33 let res = match single.meta_item().and_then(|i| i.path().word().map(|i| i.name)) {
34 Some(sym::size) => OptimizeAttr::Size,
35 Some(sym::speed) => OptimizeAttr::Speed,
36 Some(sym::none) => OptimizeAttr::DoNotOptimize,
37 _ => {
38 cx.expected_specific_argument(single.span(), vec!["size", "speed", "none"]);
39 OptimizeAttr::Default
40 }
41 };
42
43 Some(AttributeKind::Optimize(res, cx.attr_span))
44 }
45}
46
47pub(crate) struct ColdParser;
48
49impl<S: Stage> NoArgsAttributeParser<S> for ColdParser {
50 const PATH: &[Symbol] = &[sym::cold];
51 const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
52 const CREATE: fn(Span) -> AttributeKind = AttributeKind::Cold;
53}
54
55pub(crate) struct ExportNameParser;
56
57impl<S: Stage> SingleAttributeParser<S> for ExportNameParser {
58 const PATH: &[rustc_span::Symbol] = &[sym::export_name];
59 const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst;
60 const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
61 const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name");
62
63 fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
64 let Some(nv) = args.name_value() else {
65 cx.expected_name_value(cx.attr_span, None);
66 return None;
67 };
68 let Some(name) = nv.value_as_str() else {
69 cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
70 return None;
71 };
72 if name.as_str().contains('\0') {
73 cx.emit_err(NullOnExport { span: cx.attr_span });
76 return None;
77 }
78 Some(AttributeKind::ExportName { name, span: cx.attr_span })
79 }
80}
81
82#[derive(Default)]
83pub(crate) struct NakedParser {
84 span: Option<Span>,
85}
86
87impl<S: Stage> AttributeParser<S> for NakedParser {
88 const ATTRIBUTES: AcceptMapping<Self, S> =
89 &[(&[sym::naked], template!(Word), |this, cx, args| {
90 if let Err(span) = args.no_args() {
91 cx.expected_no_args(span);
92 return;
93 }
94
95 if let Some(earlier) = this.span {
96 let span = cx.attr_span;
97 cx.warn_unused_duplicate(earlier, span);
98 } else {
99 this.span = Some(cx.attr_span);
100 }
101 })];
102
103 fn finalize(self, cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
104 const ALLOW_LIST: &[rustc_span::Symbol] = &[
117 sym::cfg_trace,
119 sym::cfg_attr_trace,
120 sym::test,
122 sym::ignore,
123 sym::should_panic,
124 sym::bench,
125 sym::allow,
127 sym::warn,
128 sym::deny,
129 sym::forbid,
130 sym::deprecated,
131 sym::must_use,
132 sym::cold,
134 sym::export_name,
135 sym::link_section,
136 sym::linkage,
137 sym::no_mangle,
138 sym::instruction_set,
139 sym::repr,
140 sym::rustc_std_internal_symbol,
141 sym::align,
142 sym::naked,
144 sym::doc,
146 ];
147
148 let span = self.span?;
149
150 'outer: for other_attr in cx.all_attrs {
152 for allowed_attr in ALLOW_LIST {
153 if other_attr.segments().next().is_some_and(|i| cx.tools.contains(&i.name)) {
154 continue 'outer;
157 }
158 if other_attr.word_is(*allowed_attr) {
159 continue 'outer;
162 }
163
164 if other_attr.word_is(sym::target_feature) {
165 if !cx.features().naked_functions_target_feature() {
166 feature_err(
167 &cx.sess(),
168 sym::naked_functions_target_feature,
169 other_attr.span(),
170 "`#[target_feature(/* ... */)]` is currently unstable on `#[naked]` functions",
171 ).emit();
172 }
173
174 continue 'outer;
175 }
176 }
177
178 cx.emit_err(NakedFunctionIncompatibleAttribute {
179 span: other_attr.span(),
180 naked_span: span,
181 attr: other_attr.get_attribute_path().to_string(),
182 });
183 }
184
185 Some(AttributeKind::Naked(span))
186 }
187}
188
189pub(crate) struct TrackCallerParser;
190impl<S: Stage> NoArgsAttributeParser<S> for TrackCallerParser {
191 const PATH: &[Symbol] = &[sym::track_caller];
192 const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
193 const CREATE: fn(Span) -> AttributeKind = AttributeKind::TrackCaller;
194}
195
196pub(crate) struct NoMangleParser;
197impl<S: Stage> NoArgsAttributeParser<S> for NoMangleParser {
198 const PATH: &[Symbol] = &[sym::no_mangle];
199 const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
200 const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoMangle;
201}
202
203#[derive(Default)]
204pub(crate) struct UsedParser {
205 first_compiler: Option<Span>,
206 first_linker: Option<Span>,
207}
208
209impl<S: Stage> AttributeParser<S> for UsedParser {
214 const ATTRIBUTES: AcceptMapping<Self, S> = &[(
215 &[sym::used],
216 template!(Word, List: "compiler|linker"),
217 |group: &mut Self, cx, args| {
218 let used_by = match args {
219 ArgParser::NoArgs => UsedBy::Linker,
220 ArgParser::List(list) => {
221 let Some(l) = list.single() else {
222 cx.expected_single_argument(list.span);
223 return;
224 };
225
226 match l.meta_item().and_then(|i| i.path().word_sym()) {
227 Some(sym::compiler) => {
228 if !cx.features().used_with_arg() {
229 feature_err(
230 &cx.sess(),
231 sym::used_with_arg,
232 cx.attr_span,
233 "`#[used(compiler)]` is currently unstable",
234 )
235 .emit();
236 }
237 UsedBy::Compiler
238 }
239 Some(sym::linker) => {
240 if !cx.features().used_with_arg() {
241 feature_err(
242 &cx.sess(),
243 sym::used_with_arg,
244 cx.attr_span,
245 "`#[used(linker)]` is currently unstable",
246 )
247 .emit();
248 }
249 UsedBy::Linker
250 }
251 _ => {
252 cx.expected_specific_argument(l.span(), vec!["compiler", "linker"]);
253 return;
254 }
255 }
256 }
257 ArgParser::NameValue(_) => return,
258 };
259
260 let target = match used_by {
261 UsedBy::Compiler => &mut group.first_compiler,
262 UsedBy::Linker => &mut group.first_linker,
263 };
264
265 let attr_span = cx.attr_span;
266 if let Some(prev) = *target {
267 cx.warn_unused_duplicate(prev, attr_span);
268 } else {
269 *target = Some(attr_span);
270 }
271 },
272 )];
273
274 fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
275 Some(match (self.first_compiler, self.first_linker) {
277 (_, Some(span)) => AttributeKind::Used { used_by: UsedBy::Linker, span },
278 (Some(span), _) => AttributeKind::Used { used_by: UsedBy::Compiler, span },
279 (None, None) => return None,
280 })
281 }
282}
283
284pub(crate) struct TargetFeatureParser;
285
286impl<S: Stage> CombineAttributeParser<S> for TargetFeatureParser {
287 type Item = (Symbol, Span);
288 const PATH: &[Symbol] = &[sym::target_feature];
289 const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature(items, span);
290 const TEMPLATE: AttributeTemplate = template!(List: "enable = \"feat1, feat2\"");
291
292 fn extend<'c>(
293 cx: &'c mut AcceptContext<'_, '_, S>,
294 args: &'c ArgParser<'_>,
295 ) -> impl IntoIterator<Item = Self::Item> + 'c {
296 let mut features = Vec::new();
297 let ArgParser::List(list) = args else {
298 cx.expected_list(cx.attr_span);
299 return features;
300 };
301 if list.is_empty() {
302 cx.warn_empty_attribute(cx.attr_span);
303 return features;
304 }
305 for item in list.mixed() {
306 let Some(name_value) = item.meta_item() else {
307 cx.expected_name_value(item.span(), Some(sym::enable));
308 return features;
309 };
310
311 let Some(name) = name_value.path().word_sym() else {
313 cx.expected_name_value(name_value.path().span(), Some(sym::enable));
314 return features;
315 };
316 if name != sym::enable {
317 cx.expected_name_value(name_value.path().span(), Some(sym::enable));
318 return features;
319 }
320
321 let Some(name_value) = name_value.args().name_value() else {
323 cx.expected_name_value(item.span(), Some(sym::enable));
324 return features;
325 };
326 let Some(value_str) = name_value.value_as_str() else {
327 cx.expected_string_literal(name_value.value_span, Some(name_value.value_as_lit()));
328 return features;
329 };
330 for feature in value_str.as_str().split(",") {
331 features.push((Symbol::intern(feature), item.span()));
332 }
333 }
334 features
335 }
336}