rustc_attr_parsing/attributes/
codegen_attrs.rs1use rustc_hir::attrs::{CoverageAttrKind, OptimizeAttr, UsedBy};
2use rustc_session::parse::feature_err;
3
4use super::prelude::*;
5use crate::session_diagnostics::{NakedFunctionIncompatibleAttribute, NullOnExport};
6
7pub(crate) struct OptimizeParser;
8
9impl<S: Stage> SingleAttributeParser<S> for OptimizeParser {
10 const PATH: &[Symbol] = &[sym::optimize];
11 const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
12 const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
13 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
14 Allow(Target::Fn),
15 Allow(Target::Closure),
16 Allow(Target::Method(MethodKind::Trait { body: true })),
17 Allow(Target::Method(MethodKind::TraitImpl)),
18 Allow(Target::Method(MethodKind::Inherent)),
19 ]);
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(), &[sym::size, sym::speed, sym::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 ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[
53 Allow(Target::Fn),
54 Allow(Target::Method(MethodKind::Trait { body: true })),
55 Allow(Target::Method(MethodKind::TraitImpl)),
56 Allow(Target::Method(MethodKind::Trait { body: false })),
57 Allow(Target::Method(MethodKind::Inherent)),
58 Allow(Target::ForeignFn),
59 Allow(Target::Closure),
60 ]);
61 const CREATE: fn(Span) -> AttributeKind = AttributeKind::Cold;
62}
63
64pub(crate) struct CoverageParser;
65
66impl<S: Stage> SingleAttributeParser<S> for CoverageParser {
67 const PATH: &[Symbol] = &[sym::coverage];
68 const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
69 const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
70 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
71 Allow(Target::Fn),
72 Allow(Target::Closure),
73 Allow(Target::Method(MethodKind::Trait { body: true })),
74 Allow(Target::Method(MethodKind::TraitImpl)),
75 Allow(Target::Method(MethodKind::Inherent)),
76 Allow(Target::Impl { of_trait: true }),
77 Allow(Target::Impl { of_trait: false }),
78 Allow(Target::Mod),
79 Allow(Target::Crate),
80 ]);
81 const TEMPLATE: AttributeTemplate = template!(OneOf: &[sym::off, sym::on]);
82
83 fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
84 let Some(args) = args.list() else {
85 cx.expected_specific_argument_and_list(cx.attr_span, &[sym::on, sym::off]);
86 return None;
87 };
88
89 let Some(arg) = args.single() else {
90 cx.expected_single_argument(args.span);
91 return None;
92 };
93
94 let fail_incorrect_argument =
95 |span| cx.expected_specific_argument(span, &[sym::on, sym::off]);
96
97 let Some(arg) = arg.meta_item() else {
98 fail_incorrect_argument(args.span);
99 return None;
100 };
101
102 let kind = match arg.path().word_sym() {
103 Some(sym::off) => CoverageAttrKind::Off,
104 Some(sym::on) => CoverageAttrKind::On,
105 None | Some(_) => {
106 fail_incorrect_argument(arg.span());
107 return None;
108 }
109 };
110
111 Some(AttributeKind::Coverage(cx.attr_span, kind))
112 }
113}
114
115pub(crate) struct ExportNameParser;
116
117impl<S: Stage> SingleAttributeParser<S> for ExportNameParser {
118 const PATH: &[rustc_span::Symbol] = &[sym::export_name];
119 const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
120 const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
121 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
122 Allow(Target::Static),
123 Allow(Target::Fn),
124 Allow(Target::Method(MethodKind::Inherent)),
125 Allow(Target::Method(MethodKind::Trait { body: true })),
126 Allow(Target::Method(MethodKind::TraitImpl)),
127 Warn(Target::Field),
128 Warn(Target::Arm),
129 Warn(Target::MacroDef),
130 ]);
131 const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name");
132
133 fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
134 let Some(nv) = args.name_value() else {
135 cx.expected_name_value(cx.attr_span, None);
136 return None;
137 };
138 let Some(name) = nv.value_as_str() else {
139 cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
140 return None;
141 };
142 if name.as_str().contains('\0') {
143 cx.emit_err(NullOnExport { span: cx.attr_span });
146 return None;
147 }
148 Some(AttributeKind::ExportName { name, span: cx.attr_span })
149 }
150}
151
152#[derive(Default)]
153pub(crate) struct NakedParser {
154 span: Option<Span>,
155}
156
157impl<S: Stage> AttributeParser<S> for NakedParser {
158 const ATTRIBUTES: AcceptMapping<Self, S> =
159 &[(&[sym::naked], template!(Word), |this, cx, args| {
160 if let Err(span) = args.no_args() {
161 cx.expected_no_args(span);
162 return;
163 }
164
165 if let Some(earlier) = this.span {
166 let span = cx.attr_span;
167 cx.warn_unused_duplicate(earlier, span);
168 } else {
169 this.span = Some(cx.attr_span);
170 }
171 })];
172 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
173 Allow(Target::Fn),
174 Allow(Target::Method(MethodKind::Inherent)),
175 Allow(Target::Method(MethodKind::Trait { body: true })),
176 Allow(Target::Method(MethodKind::TraitImpl)),
177 ]);
178
179 fn finalize(self, cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
180 const ALLOW_LIST: &[rustc_span::Symbol] = &[
193 sym::cfg_trace,
195 sym::cfg_attr_trace,
196 sym::test,
198 sym::ignore,
199 sym::should_panic,
200 sym::bench,
201 sym::allow,
203 sym::warn,
204 sym::deny,
205 sym::forbid,
206 sym::deprecated,
207 sym::must_use,
208 sym::cold,
210 sym::export_name,
211 sym::link_section,
212 sym::linkage,
213 sym::no_mangle,
214 sym::instruction_set,
215 sym::repr,
216 sym::rustc_std_internal_symbol,
217 sym::rustc_align,
219 sym::naked,
221 sym::doc,
223 ];
224
225 let span = self.span?;
226
227 'outer: for other_attr in cx.all_attrs {
229 for allowed_attr in ALLOW_LIST {
230 if other_attr.segments().next().is_some_and(|i| cx.tools.contains(&i.name)) {
231 continue 'outer;
234 }
235 if other_attr.word_is(*allowed_attr) {
236 continue 'outer;
239 }
240
241 if other_attr.word_is(sym::target_feature) {
242 if !cx.features().naked_functions_target_feature() {
243 feature_err(
244 &cx.sess(),
245 sym::naked_functions_target_feature,
246 other_attr.span(),
247 "`#[target_feature(/* ... */)]` is currently unstable on `#[naked]` functions",
248 ).emit();
249 }
250
251 continue 'outer;
252 }
253 }
254
255 cx.emit_err(NakedFunctionIncompatibleAttribute {
256 span: other_attr.span(),
257 naked_span: span,
258 attr: other_attr.get_attribute_path().to_string(),
259 });
260 }
261
262 Some(AttributeKind::Naked(span))
263 }
264}
265
266pub(crate) struct TrackCallerParser;
267impl<S: Stage> NoArgsAttributeParser<S> for TrackCallerParser {
268 const PATH: &[Symbol] = &[sym::track_caller];
269 const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
270 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
271 Allow(Target::Fn),
272 Allow(Target::Method(MethodKind::Inherent)),
273 Allow(Target::Method(MethodKind::Trait { body: true })),
274 Allow(Target::Method(MethodKind::TraitImpl)),
275 Allow(Target::Method(MethodKind::Trait { body: false })),
276 Allow(Target::ForeignFn),
277 Allow(Target::Closure),
278 Warn(Target::MacroDef),
279 Warn(Target::Arm),
280 Warn(Target::Field),
281 ]);
282 const CREATE: fn(Span) -> AttributeKind = AttributeKind::TrackCaller;
283}
284
285pub(crate) struct NoMangleParser;
286impl<S: Stage> NoArgsAttributeParser<S> for NoMangleParser {
287 const PATH: &[Symbol] = &[sym::no_mangle];
288 const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
289 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[
290 Allow(Target::Fn),
291 Allow(Target::Static),
292 Allow(Target::Method(MethodKind::Inherent)),
293 Allow(Target::Method(MethodKind::TraitImpl)),
294 ]);
295 const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoMangle;
296}
297
298#[derive(Default)]
299pub(crate) struct UsedParser {
300 first_compiler: Option<Span>,
301 first_linker: Option<Span>,
302}
303
304impl<S: Stage> AttributeParser<S> for UsedParser {
309 const ATTRIBUTES: AcceptMapping<Self, S> = &[(
310 &[sym::used],
311 template!(Word, List: &["compiler", "linker"]),
312 |group: &mut Self, cx, args| {
313 let used_by = match args {
314 ArgParser::NoArgs => UsedBy::Linker,
315 ArgParser::List(list) => {
316 let Some(l) = list.single() else {
317 cx.expected_single_argument(list.span);
318 return;
319 };
320
321 match l.meta_item().and_then(|i| i.path().word_sym()) {
322 Some(sym::compiler) => {
323 if !cx.features().used_with_arg() {
324 feature_err(
325 &cx.sess(),
326 sym::used_with_arg,
327 cx.attr_span,
328 "`#[used(compiler)]` is currently unstable",
329 )
330 .emit();
331 }
332 UsedBy::Compiler
333 }
334 Some(sym::linker) => {
335 if !cx.features().used_with_arg() {
336 feature_err(
337 &cx.sess(),
338 sym::used_with_arg,
339 cx.attr_span,
340 "`#[used(linker)]` is currently unstable",
341 )
342 .emit();
343 }
344 UsedBy::Linker
345 }
346 _ => {
347 cx.expected_specific_argument(l.span(), &[sym::compiler, sym::linker]);
348 return;
349 }
350 }
351 }
352 ArgParser::NameValue(_) => return,
353 };
354
355 let target = match used_by {
356 UsedBy::Compiler => &mut group.first_compiler,
357 UsedBy::Linker => &mut group.first_linker,
358 };
359
360 let attr_span = cx.attr_span;
361 if let Some(prev) = *target {
362 cx.warn_unused_duplicate(prev, attr_span);
363 } else {
364 *target = Some(attr_span);
365 }
366 },
367 )];
368 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Static)]);
369
370 fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
371 Some(match (self.first_compiler, self.first_linker) {
373 (_, Some(span)) => AttributeKind::Used { used_by: UsedBy::Linker, span },
374 (Some(span), _) => AttributeKind::Used { used_by: UsedBy::Compiler, span },
375 (None, None) => return None,
376 })
377 }
378}
379
380fn parse_tf_attribute<'c, S: Stage>(
381 cx: &'c mut AcceptContext<'_, '_, S>,
382 args: &'c ArgParser<'_>,
383) -> impl IntoIterator<Item = (Symbol, Span)> + 'c {
384 let mut features = Vec::new();
385 let ArgParser::List(list) = args else {
386 cx.expected_list(cx.attr_span);
387 return features;
388 };
389 if list.is_empty() {
390 cx.warn_empty_attribute(cx.attr_span);
391 return features;
392 }
393 for item in list.mixed() {
394 let Some(name_value) = item.meta_item() else {
395 cx.expected_name_value(item.span(), Some(sym::enable));
396 return features;
397 };
398
399 let Some(name) = name_value.path().word_sym() else {
401 cx.expected_name_value(name_value.path().span(), Some(sym::enable));
402 return features;
403 };
404 if name != sym::enable {
405 cx.expected_name_value(name_value.path().span(), Some(sym::enable));
406 return features;
407 }
408
409 let Some(name_value) = name_value.args().name_value() else {
411 cx.expected_name_value(item.span(), Some(sym::enable));
412 return features;
413 };
414 let Some(value_str) = name_value.value_as_str() else {
415 cx.expected_string_literal(name_value.value_span, Some(name_value.value_as_lit()));
416 return features;
417 };
418 for feature in value_str.as_str().split(",") {
419 features.push((Symbol::intern(feature), item.span()));
420 }
421 }
422 features
423}
424
425pub(crate) struct TargetFeatureParser;
426
427impl<S: Stage> CombineAttributeParser<S> for TargetFeatureParser {
428 type Item = (Symbol, Span);
429 const PATH: &[Symbol] = &[sym::target_feature];
430 const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {
431 features: items,
432 attr_span: span,
433 was_forced: false,
434 };
435 const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]);
436
437 fn extend<'c>(
438 cx: &'c mut AcceptContext<'_, '_, S>,
439 args: &'c ArgParser<'_>,
440 ) -> impl IntoIterator<Item = Self::Item> + 'c {
441 parse_tf_attribute(cx, args)
442 }
443
444 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
445 Allow(Target::Fn),
446 Allow(Target::Method(MethodKind::Inherent)),
447 Allow(Target::Method(MethodKind::Trait { body: true })),
448 Allow(Target::Method(MethodKind::TraitImpl)),
449 Warn(Target::Statement),
450 Warn(Target::Field),
451 Warn(Target::Arm),
452 Warn(Target::MacroDef),
453 ]);
454}
455
456pub(crate) struct ForceTargetFeatureParser;
457
458impl<S: Stage> CombineAttributeParser<S> for ForceTargetFeatureParser {
459 type Item = (Symbol, Span);
460 const PATH: &[Symbol] = &[sym::force_target_feature];
461 const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {
462 features: items,
463 attr_span: span,
464 was_forced: true,
465 };
466 const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]);
467
468 fn extend<'c>(
469 cx: &'c mut AcceptContext<'_, '_, S>,
470 args: &'c ArgParser<'_>,
471 ) -> impl IntoIterator<Item = Self::Item> + 'c {
472 parse_tf_attribute(cx, args)
473 }
474
475 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
476 Allow(Target::Fn),
477 Allow(Target::Method(MethodKind::Inherent)),
478 Allow(Target::Method(MethodKind::Trait { body: true })),
479 Allow(Target::Method(MethodKind::TraitImpl)),
480 ]);
481}