rustc_attr_parsing/attributes/
codegen_attrs.rs1use rustc_hir::attrs::{CoverageAttrKind, OptimizeAttr, SanitizerSet, UsedBy};
2use rustc_session::parse::feature_err;
3
4use super::prelude::*;
5use crate::session_diagnostics::{
6 NakedFunctionIncompatibleAttribute, NullOnExport, NullOnObjcClass, NullOnObjcSelector,
7 ObjcClassExpectedStringLiteral, ObjcSelectorExpectedStringLiteral,
8};
9
10pub(crate) struct OptimizeParser;
11
12impl<S: Stage> SingleAttributeParser<S> for OptimizeParser {
13 const PATH: &[Symbol] = &[sym::optimize];
14 const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
15 const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
16 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
17 Allow(Target::Fn),
18 Allow(Target::Closure),
19 Allow(Target::Method(MethodKind::Trait { body: true })),
20 Allow(Target::Method(MethodKind::TraitImpl)),
21 Allow(Target::Method(MethodKind::Inherent)),
22 ]);
23 const TEMPLATE: AttributeTemplate = template!(List: &["size", "speed", "none"]);
24
25 fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
26 let Some(list) = args.list() else {
27 cx.expected_list(cx.attr_span);
28 return None;
29 };
30
31 let Some(single) = list.single() else {
32 cx.expected_single_argument(list.span);
33 return None;
34 };
35
36 let res = match single.meta_item().and_then(|i| i.path().word().map(|i| i.name)) {
37 Some(sym::size) => OptimizeAttr::Size,
38 Some(sym::speed) => OptimizeAttr::Speed,
39 Some(sym::none) => OptimizeAttr::DoNotOptimize,
40 _ => {
41 cx.expected_specific_argument(single.span(), &[sym::size, sym::speed, sym::none]);
42 OptimizeAttr::Default
43 }
44 };
45
46 Some(AttributeKind::Optimize(res, cx.attr_span))
47 }
48}
49
50pub(crate) struct ColdParser;
51
52impl<S: Stage> NoArgsAttributeParser<S> for ColdParser {
53 const PATH: &[Symbol] = &[sym::cold];
54 const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
55 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[
56 Allow(Target::Fn),
57 Allow(Target::Method(MethodKind::Trait { body: true })),
58 Allow(Target::Method(MethodKind::TraitImpl)),
59 Allow(Target::Method(MethodKind::Trait { body: false })),
60 Allow(Target::Method(MethodKind::Inherent)),
61 Allow(Target::ForeignFn),
62 Allow(Target::Closure),
63 ]);
64 const CREATE: fn(Span) -> AttributeKind = AttributeKind::Cold;
65}
66
67pub(crate) struct CoverageParser;
68
69impl<S: Stage> SingleAttributeParser<S> for CoverageParser {
70 const PATH: &[Symbol] = &[sym::coverage];
71 const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
72 const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
73 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
74 Allow(Target::Fn),
75 Allow(Target::Closure),
76 Allow(Target::Method(MethodKind::Trait { body: true })),
77 Allow(Target::Method(MethodKind::TraitImpl)),
78 Allow(Target::Method(MethodKind::Inherent)),
79 Allow(Target::Impl { of_trait: true }),
80 Allow(Target::Impl { of_trait: false }),
81 Allow(Target::Mod),
82 Allow(Target::Crate),
83 ]);
84 const TEMPLATE: AttributeTemplate = template!(OneOf: &[sym::off, sym::on]);
85
86 fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
87 let Some(args) = args.list() else {
88 cx.expected_specific_argument_and_list(cx.attr_span, &[sym::on, sym::off]);
89 return None;
90 };
91
92 let Some(arg) = args.single() else {
93 cx.expected_single_argument(args.span);
94 return None;
95 };
96
97 let fail_incorrect_argument =
98 |span| cx.expected_specific_argument(span, &[sym::on, sym::off]);
99
100 let Some(arg) = arg.meta_item() else {
101 fail_incorrect_argument(args.span);
102 return None;
103 };
104
105 let kind = match arg.path().word_sym() {
106 Some(sym::off) => CoverageAttrKind::Off,
107 Some(sym::on) => CoverageAttrKind::On,
108 None | Some(_) => {
109 fail_incorrect_argument(arg.span());
110 return None;
111 }
112 };
113
114 Some(AttributeKind::Coverage(cx.attr_span, kind))
115 }
116}
117
118pub(crate) struct ExportNameParser;
119
120impl<S: Stage> SingleAttributeParser<S> for ExportNameParser {
121 const PATH: &[rustc_span::Symbol] = &[sym::export_name];
122 const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
123 const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
124 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
125 Allow(Target::Static),
126 Allow(Target::Fn),
127 Allow(Target::Method(MethodKind::Inherent)),
128 Allow(Target::Method(MethodKind::Trait { body: true })),
129 Allow(Target::Method(MethodKind::TraitImpl)),
130 Warn(Target::Field),
131 Warn(Target::Arm),
132 Warn(Target::MacroDef),
133 Warn(Target::MacroCall),
134 ]);
135 const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name");
136
137 fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
138 let Some(nv) = args.name_value() else {
139 cx.expected_name_value(cx.attr_span, None);
140 return None;
141 };
142 let Some(name) = nv.value_as_str() else {
143 cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
144 return None;
145 };
146 if name.as_str().contains('\0') {
147 cx.emit_err(NullOnExport { span: cx.attr_span });
150 return None;
151 }
152 Some(AttributeKind::ExportName { name, span: cx.attr_span })
153 }
154}
155
156pub(crate) struct ObjcClassParser;
157
158impl<S: Stage> SingleAttributeParser<S> for ObjcClassParser {
159 const PATH: &[rustc_span::Symbol] = &[sym::rustc_objc_class];
160 const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
161 const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
162 const ALLOWED_TARGETS: AllowedTargets =
163 AllowedTargets::AllowList(&[Allow(Target::ForeignStatic)]);
164 const TEMPLATE: AttributeTemplate = template!(NameValueStr: "ClassName");
165
166 fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
167 let Some(nv) = args.name_value() else {
168 cx.expected_name_value(cx.attr_span, None);
169 return None;
170 };
171 let Some(classname) = nv.value_as_str() else {
172 cx.emit_err(ObjcClassExpectedStringLiteral { span: nv.value_span });
176 return None;
177 };
178 if classname.as_str().contains('\0') {
179 cx.emit_err(NullOnObjcClass { span: nv.value_span });
182 return None;
183 }
184 Some(AttributeKind::ObjcClass { classname, span: cx.attr_span })
185 }
186}
187
188pub(crate) struct ObjcSelectorParser;
189
190impl<S: Stage> SingleAttributeParser<S> for ObjcSelectorParser {
191 const PATH: &[rustc_span::Symbol] = &[sym::rustc_objc_selector];
192 const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
193 const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
194 const ALLOWED_TARGETS: AllowedTargets =
195 AllowedTargets::AllowList(&[Allow(Target::ForeignStatic)]);
196 const TEMPLATE: AttributeTemplate = template!(NameValueStr: "methodName");
197
198 fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
199 let Some(nv) = args.name_value() else {
200 cx.expected_name_value(cx.attr_span, None);
201 return None;
202 };
203 let Some(methname) = nv.value_as_str() else {
204 cx.emit_err(ObjcSelectorExpectedStringLiteral { span: nv.value_span });
208 return None;
209 };
210 if methname.as_str().contains('\0') {
211 cx.emit_err(NullOnObjcSelector { span: nv.value_span });
214 return None;
215 }
216 Some(AttributeKind::ObjcSelector { methname, span: cx.attr_span })
217 }
218}
219
220#[derive(Default)]
221pub(crate) struct NakedParser {
222 span: Option<Span>,
223}
224
225impl<S: Stage> AttributeParser<S> for NakedParser {
226 const ATTRIBUTES: AcceptMapping<Self, S> =
227 &[(&[sym::naked], template!(Word), |this, cx, args| {
228 if let Err(span) = args.no_args() {
229 cx.expected_no_args(span);
230 return;
231 }
232
233 if let Some(earlier) = this.span {
234 let span = cx.attr_span;
235 cx.warn_unused_duplicate(earlier, span);
236 } else {
237 this.span = Some(cx.attr_span);
238 }
239 })];
240 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
241 Allow(Target::Fn),
242 Allow(Target::Method(MethodKind::Inherent)),
243 Allow(Target::Method(MethodKind::Trait { body: true })),
244 Allow(Target::Method(MethodKind::TraitImpl)),
245 Warn(Target::MacroCall),
246 ]);
247
248 fn finalize(self, cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
249 const ALLOW_LIST: &[rustc_span::Symbol] = &[
262 sym::cfg_trace,
264 sym::cfg_attr_trace,
265 sym::test,
267 sym::ignore,
268 sym::should_panic,
269 sym::bench,
270 sym::allow,
272 sym::warn,
273 sym::deny,
274 sym::forbid,
275 sym::deprecated,
276 sym::must_use,
277 sym::cold,
279 sym::export_name,
280 sym::link_section,
281 sym::linkage,
282 sym::no_mangle,
283 sym::instruction_set,
284 sym::repr,
285 sym::rustc_std_internal_symbol,
286 sym::rustc_align,
288 sym::rustc_align_static,
289 sym::naked,
291 sym::doc,
293 ];
294
295 let span = self.span?;
296
297 'outer: for other_attr in cx.all_attrs {
299 for allowed_attr in ALLOW_LIST {
300 if other_attr.segments().next().is_some_and(|i| cx.tools.contains(&i.name)) {
301 continue 'outer;
304 }
305 if other_attr.word_is(*allowed_attr) {
306 continue 'outer;
309 }
310
311 if other_attr.word_is(sym::target_feature) {
312 if !cx.features().naked_functions_target_feature() {
313 feature_err(
314 &cx.sess(),
315 sym::naked_functions_target_feature,
316 other_attr.span(),
317 "`#[target_feature(/* ... */)]` is currently unstable on `#[naked]` functions",
318 ).emit();
319 }
320
321 continue 'outer;
322 }
323 }
324
325 cx.emit_err(NakedFunctionIncompatibleAttribute {
326 span: other_attr.span(),
327 naked_span: span,
328 attr: other_attr.get_attribute_path().to_string(),
329 });
330 }
331
332 Some(AttributeKind::Naked(span))
333 }
334}
335
336pub(crate) struct TrackCallerParser;
337impl<S: Stage> NoArgsAttributeParser<S> for TrackCallerParser {
338 const PATH: &[Symbol] = &[sym::track_caller];
339 const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
340 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
341 Allow(Target::Fn),
342 Allow(Target::Method(MethodKind::Inherent)),
343 Allow(Target::Method(MethodKind::Trait { body: true })),
344 Allow(Target::Method(MethodKind::TraitImpl)),
345 Allow(Target::Method(MethodKind::Trait { body: false })),
346 Allow(Target::ForeignFn),
347 Allow(Target::Closure),
348 Warn(Target::MacroDef),
349 Warn(Target::Arm),
350 Warn(Target::Field),
351 Warn(Target::MacroCall),
352 ]);
353 const CREATE: fn(Span) -> AttributeKind = AttributeKind::TrackCaller;
354}
355
356pub(crate) struct NoMangleParser;
357impl<S: Stage> NoArgsAttributeParser<S> for NoMangleParser {
358 const PATH: &[Symbol] = &[sym::no_mangle];
359 const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
360 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[
361 Allow(Target::Fn),
362 Allow(Target::Static),
363 Allow(Target::Method(MethodKind::Inherent)),
364 Allow(Target::Method(MethodKind::TraitImpl)),
365 ]);
366 const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoMangle;
367}
368
369#[derive(Default)]
370pub(crate) struct UsedParser {
371 first_compiler: Option<Span>,
372 first_linker: Option<Span>,
373 first_default: Option<Span>,
374}
375
376impl<S: Stage> AttributeParser<S> for UsedParser {
381 const ATTRIBUTES: AcceptMapping<Self, S> = &[(
382 &[sym::used],
383 template!(Word, List: &["compiler", "linker"]),
384 |group: &mut Self, cx, args| {
385 let used_by = match args {
386 ArgParser::NoArgs => UsedBy::Default,
387 ArgParser::List(list) => {
388 let Some(l) = list.single() else {
389 cx.expected_single_argument(list.span);
390 return;
391 };
392
393 match l.meta_item().and_then(|i| i.path().word_sym()) {
394 Some(sym::compiler) => {
395 if !cx.features().used_with_arg() {
396 feature_err(
397 &cx.sess(),
398 sym::used_with_arg,
399 cx.attr_span,
400 "`#[used(compiler)]` is currently unstable",
401 )
402 .emit();
403 }
404 UsedBy::Compiler
405 }
406 Some(sym::linker) => {
407 if !cx.features().used_with_arg() {
408 feature_err(
409 &cx.sess(),
410 sym::used_with_arg,
411 cx.attr_span,
412 "`#[used(linker)]` is currently unstable",
413 )
414 .emit();
415 }
416 UsedBy::Linker
417 }
418 _ => {
419 cx.expected_specific_argument(l.span(), &[sym::compiler, sym::linker]);
420 return;
421 }
422 }
423 }
424 ArgParser::NameValue(_) => return,
425 };
426
427 let attr_span = cx.attr_span;
428
429 let target = match used_by {
433 UsedBy::Compiler => &mut group.first_compiler,
434 UsedBy::Linker => {
435 if let Some(prev) = group.first_default {
436 cx.warn_unused_duplicate(prev, attr_span);
437 return;
438 }
439 &mut group.first_linker
440 }
441 UsedBy::Default => {
442 if let Some(prev) = group.first_linker {
443 cx.warn_unused_duplicate(prev, attr_span);
444 return;
445 }
446 &mut group.first_default
447 }
448 };
449
450 if let Some(prev) = *target {
451 cx.warn_unused_duplicate(prev, attr_span);
452 } else {
453 *target = Some(attr_span);
454 }
455 },
456 )];
457 const ALLOWED_TARGETS: AllowedTargets =
458 AllowedTargets::AllowList(&[Allow(Target::Static), Warn(Target::MacroCall)]);
459
460 fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
461 Some(match (self.first_compiler, self.first_linker, self.first_default) {
464 (_, Some(span), _) => AttributeKind::Used { used_by: UsedBy::Linker, span },
465 (Some(span), _, _) => AttributeKind::Used { used_by: UsedBy::Compiler, span },
466 (_, _, Some(span)) => AttributeKind::Used { used_by: UsedBy::Default, span },
467 (None, None, None) => return None,
468 })
469 }
470}
471
472fn parse_tf_attribute<'c, S: Stage>(
473 cx: &'c mut AcceptContext<'_, '_, S>,
474 args: &'c ArgParser<'_>,
475) -> impl IntoIterator<Item = (Symbol, Span)> + 'c {
476 let mut features = Vec::new();
477 let ArgParser::List(list) = args else {
478 cx.expected_list(cx.attr_span);
479 return features;
480 };
481 if list.is_empty() {
482 cx.warn_empty_attribute(cx.attr_span);
483 return features;
484 }
485 for item in list.mixed() {
486 let Some(name_value) = item.meta_item() else {
487 cx.expected_name_value(item.span(), Some(sym::enable));
488 return features;
489 };
490
491 let Some(name) = name_value.path().word_sym() else {
493 cx.expected_name_value(name_value.path().span(), Some(sym::enable));
494 return features;
495 };
496 if name != sym::enable {
497 cx.expected_name_value(name_value.path().span(), Some(sym::enable));
498 return features;
499 }
500
501 let Some(name_value) = name_value.args().name_value() else {
503 cx.expected_name_value(item.span(), Some(sym::enable));
504 return features;
505 };
506 let Some(value_str) = name_value.value_as_str() else {
507 cx.expected_string_literal(name_value.value_span, Some(name_value.value_as_lit()));
508 return features;
509 };
510 for feature in value_str.as_str().split(",") {
511 features.push((Symbol::intern(feature), item.span()));
512 }
513 }
514 features
515}
516
517pub(crate) struct TargetFeatureParser;
518
519impl<S: Stage> CombineAttributeParser<S> for TargetFeatureParser {
520 type Item = (Symbol, Span);
521 const PATH: &[Symbol] = &[sym::target_feature];
522 const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {
523 features: items,
524 attr_span: span,
525 was_forced: false,
526 };
527 const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]);
528
529 fn extend<'c>(
530 cx: &'c mut AcceptContext<'_, '_, S>,
531 args: &'c ArgParser<'_>,
532 ) -> impl IntoIterator<Item = Self::Item> + 'c {
533 parse_tf_attribute(cx, args)
534 }
535
536 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
537 Allow(Target::Fn),
538 Allow(Target::Method(MethodKind::Inherent)),
539 Allow(Target::Method(MethodKind::Trait { body: true })),
540 Allow(Target::Method(MethodKind::TraitImpl)),
541 Warn(Target::Statement),
542 Warn(Target::Field),
543 Warn(Target::Arm),
544 Warn(Target::MacroDef),
545 Warn(Target::MacroCall),
546 ]);
547}
548
549pub(crate) struct ForceTargetFeatureParser;
550
551impl<S: Stage> CombineAttributeParser<S> for ForceTargetFeatureParser {
552 type Item = (Symbol, Span);
553 const PATH: &[Symbol] = &[sym::force_target_feature];
554 const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {
555 features: items,
556 attr_span: span,
557 was_forced: true,
558 };
559 const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]);
560 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
561 Allow(Target::Fn),
562 Allow(Target::Method(MethodKind::Inherent)),
563 Allow(Target::Method(MethodKind::Trait { body: true })),
564 Allow(Target::Method(MethodKind::TraitImpl)),
565 ]);
566
567 fn extend<'c>(
568 cx: &'c mut AcceptContext<'_, '_, S>,
569 args: &'c ArgParser<'_>,
570 ) -> impl IntoIterator<Item = Self::Item> + 'c {
571 parse_tf_attribute(cx, args)
572 }
573}
574
575pub(crate) struct SanitizeParser;
576
577impl<S: Stage> SingleAttributeParser<S> for SanitizeParser {
578 const PATH: &[Symbol] = &[sym::sanitize];
579
580 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
582
583 const TEMPLATE: AttributeTemplate = template!(List: &[
584 r#"address = "on|off""#,
585 r#"kernel_address = "on|off""#,
586 r#"cfi = "on|off""#,
587 r#"hwaddress = "on|off""#,
588 r#"kcfi = "on|off""#,
589 r#"memory = "on|off""#,
590 r#"memtag = "on|off""#,
591 r#"shadow_call_stack = "on|off""#,
592 r#"thread = "on|off""#
593 ]);
594
595 const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
596 const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
597
598 fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
599 let Some(list) = args.list() else {
600 cx.expected_list(cx.attr_span);
601 return None;
602 };
603
604 let mut on_set = SanitizerSet::empty();
605 let mut off_set = SanitizerSet::empty();
606
607 for item in list.mixed() {
608 let Some(item) = item.meta_item() else {
609 cx.expected_name_value(item.span(), None);
610 continue;
611 };
612
613 let path = item.path().word_sym();
614 let Some(value) = item.args().name_value() else {
615 cx.expected_name_value(item.span(), path);
616 continue;
617 };
618
619 let mut apply = |s: SanitizerSet| {
620 let is_on = match value.value_as_str() {
621 Some(sym::on) => true,
622 Some(sym::off) => false,
623 Some(_) => {
624 cx.expected_specific_argument_strings(
625 value.value_span,
626 &[sym::on, sym::off],
627 );
628 return;
629 }
630 None => {
631 cx.expected_string_literal(value.value_span, Some(value.value_as_lit()));
632 return;
633 }
634 };
635
636 if is_on {
637 on_set |= s;
638 } else {
639 off_set |= s;
640 }
641 };
642
643 match path {
644 Some(sym::address) | Some(sym::kernel_address) => {
645 apply(SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS)
646 }
647 Some(sym::cfi) => apply(SanitizerSet::CFI),
648 Some(sym::kcfi) => apply(SanitizerSet::KCFI),
649 Some(sym::memory) => apply(SanitizerSet::MEMORY),
650 Some(sym::memtag) => apply(SanitizerSet::MEMTAG),
651 Some(sym::shadow_call_stack) => apply(SanitizerSet::SHADOWCALLSTACK),
652 Some(sym::thread) => apply(SanitizerSet::THREAD),
653 Some(sym::hwaddress) => apply(SanitizerSet::HWADDRESS),
654 _ => {
655 cx.expected_specific_argument_strings(
656 item.path().span(),
657 &[
658 sym::address,
659 sym::cfi,
660 sym::kcfi,
661 sym::memory,
662 sym::memtag,
663 sym::shadow_call_stack,
664 sym::thread,
665 sym::hwaddress,
666 ],
667 );
668 continue;
669 }
670 }
671 }
672
673 Some(AttributeKind::Sanitize { on_set, off_set, span: cx.attr_span })
674 }
675}