1use rustc_hir::attrs::{CoverageAttrKind, OptimizeAttr, RtsanSetting, 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};
9use crate::target_checking::Policy::AllowSilent;
10
11pub(crate) struct OptimizeParser;
12
13impl<S: Stage> SingleAttributeParser<S> for OptimizeParser {
14 const PATH: &[Symbol] = &[sym::optimize];
15 const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
16 const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
17 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
18 Allow(Target::Fn),
19 Allow(Target::Closure),
20 Allow(Target::Method(MethodKind::Trait { body: true })),
21 Allow(Target::Method(MethodKind::TraitImpl)),
22 Allow(Target::Method(MethodKind::Inherent)),
23 ]);
24 const TEMPLATE: AttributeTemplate = template!(List: &["size", "speed", "none"]);
25
26 fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
27 let Some(list) = args.list() else {
28 cx.expected_list(cx.attr_span, args);
29 return None;
30 };
31
32 let Some(single) = list.single() else {
33 cx.expected_single_argument(list.span);
34 return None;
35 };
36
37 let res = match single.meta_item().and_then(|i| i.path().word().map(|i| i.name)) {
38 Some(sym::size) => OptimizeAttr::Size,
39 Some(sym::speed) => OptimizeAttr::Speed,
40 Some(sym::none) => OptimizeAttr::DoNotOptimize,
41 _ => {
42 cx.expected_specific_argument(single.span(), &[sym::size, sym::speed, sym::none]);
43 OptimizeAttr::Default
44 }
45 };
46
47 Some(AttributeKind::Optimize(res, cx.attr_span))
48 }
49}
50
51pub(crate) struct ColdParser;
52
53impl<S: Stage> NoArgsAttributeParser<S> for ColdParser {
54 const PATH: &[Symbol] = &[sym::cold];
55 const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
56 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[
57 Allow(Target::Fn),
58 Allow(Target::Method(MethodKind::Trait { body: true })),
59 Allow(Target::Method(MethodKind::TraitImpl)),
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 })), 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 AllowSilent(Target::Const), Error(Target::Closure),
367 ]);
368 const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoMangle;
369}
370
371#[derive(Default)]
372pub(crate) struct UsedParser {
373 first_compiler: Option<Span>,
374 first_linker: Option<Span>,
375 first_default: Option<Span>,
376}
377
378impl<S: Stage> AttributeParser<S> for UsedParser {
383 const ATTRIBUTES: AcceptMapping<Self, S> = &[(
384 &[sym::used],
385 template!(Word, List: &["compiler", "linker"]),
386 |group: &mut Self, cx, args| {
387 let used_by = match args {
388 ArgParser::NoArgs => UsedBy::Default,
389 ArgParser::List(list) => {
390 let Some(l) = list.single() else {
391 cx.expected_single_argument(list.span);
392 return;
393 };
394
395 match l.meta_item().and_then(|i| i.path().word_sym()) {
396 Some(sym::compiler) => {
397 if !cx.features().used_with_arg() {
398 feature_err(
399 &cx.sess(),
400 sym::used_with_arg,
401 cx.attr_span,
402 "`#[used(compiler)]` is currently unstable",
403 )
404 .emit();
405 }
406 UsedBy::Compiler
407 }
408 Some(sym::linker) => {
409 if !cx.features().used_with_arg() {
410 feature_err(
411 &cx.sess(),
412 sym::used_with_arg,
413 cx.attr_span,
414 "`#[used(linker)]` is currently unstable",
415 )
416 .emit();
417 }
418 UsedBy::Linker
419 }
420 _ => {
421 cx.expected_specific_argument(l.span(), &[sym::compiler, sym::linker]);
422 return;
423 }
424 }
425 }
426 ArgParser::NameValue(_) => return,
427 };
428
429 let attr_span = cx.attr_span;
430
431 let target = match used_by {
435 UsedBy::Compiler => &mut group.first_compiler,
436 UsedBy::Linker => {
437 if let Some(prev) = group.first_default {
438 cx.warn_unused_duplicate(prev, attr_span);
439 return;
440 }
441 &mut group.first_linker
442 }
443 UsedBy::Default => {
444 if let Some(prev) = group.first_linker {
445 cx.warn_unused_duplicate(prev, attr_span);
446 return;
447 }
448 &mut group.first_default
449 }
450 };
451
452 if let Some(prev) = *target {
453 cx.warn_unused_duplicate(prev, attr_span);
454 } else {
455 *target = Some(attr_span);
456 }
457 },
458 )];
459 const ALLOWED_TARGETS: AllowedTargets =
460 AllowedTargets::AllowList(&[Allow(Target::Static), Warn(Target::MacroCall)]);
461
462 fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
463 Some(match (self.first_compiler, self.first_linker, self.first_default) {
466 (_, Some(span), _) => AttributeKind::Used { used_by: UsedBy::Linker, span },
467 (Some(span), _, _) => AttributeKind::Used { used_by: UsedBy::Compiler, span },
468 (_, _, Some(span)) => AttributeKind::Used { used_by: UsedBy::Default, span },
469 (None, None, None) => return None,
470 })
471 }
472}
473
474fn parse_tf_attribute<S: Stage>(
475 cx: &mut AcceptContext<'_, '_, S>,
476 args: &ArgParser,
477) -> impl IntoIterator<Item = (Symbol, Span)> {
478 let mut features = Vec::new();
479 let ArgParser::List(list) = args else {
480 cx.expected_list(cx.attr_span, args);
481 return features;
482 };
483 if list.is_empty() {
484 cx.warn_empty_attribute(cx.attr_span);
485 return features;
486 }
487 for item in list.mixed() {
488 let Some(name_value) = item.meta_item() else {
489 cx.expected_name_value(item.span(), Some(sym::enable));
490 return features;
491 };
492
493 let Some(name) = name_value.path().word_sym() else {
495 cx.expected_name_value(name_value.path().span(), Some(sym::enable));
496 return features;
497 };
498 if name != sym::enable {
499 cx.expected_name_value(name_value.path().span(), Some(sym::enable));
500 return features;
501 }
502
503 let Some(name_value) = name_value.args().name_value() else {
505 cx.expected_name_value(item.span(), Some(sym::enable));
506 return features;
507 };
508 let Some(value_str) = name_value.value_as_str() else {
509 cx.expected_string_literal(name_value.value_span, Some(name_value.value_as_lit()));
510 return features;
511 };
512 for feature in value_str.as_str().split(",") {
513 features.push((Symbol::intern(feature), item.span()));
514 }
515 }
516 features
517}
518
519pub(crate) struct TargetFeatureParser;
520
521impl<S: Stage> CombineAttributeParser<S> for TargetFeatureParser {
522 type Item = (Symbol, Span);
523 const PATH: &[Symbol] = &[sym::target_feature];
524 const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {
525 features: items,
526 attr_span: span,
527 was_forced: false,
528 };
529 const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]);
530
531 fn extend(
532 cx: &mut AcceptContext<'_, '_, S>,
533 args: &ArgParser,
534 ) -> impl IntoIterator<Item = Self::Item> {
535 parse_tf_attribute(cx, args)
536 }
537
538 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
539 Allow(Target::Fn),
540 Allow(Target::Method(MethodKind::Inherent)),
541 Allow(Target::Method(MethodKind::Trait { body: true })),
542 Allow(Target::Method(MethodKind::TraitImpl)),
543 Warn(Target::Statement),
544 Warn(Target::Field),
545 Warn(Target::Arm),
546 Warn(Target::MacroDef),
547 Warn(Target::MacroCall),
548 ]);
549}
550
551pub(crate) struct ForceTargetFeatureParser;
552
553impl<S: Stage> CombineAttributeParser<S> for ForceTargetFeatureParser {
554 type Item = (Symbol, Span);
555 const PATH: &[Symbol] = &[sym::force_target_feature];
556 const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {
557 features: items,
558 attr_span: span,
559 was_forced: true,
560 };
561 const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]);
562 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
563 Allow(Target::Fn),
564 Allow(Target::Method(MethodKind::Inherent)),
565 Allow(Target::Method(MethodKind::Trait { body: true })),
566 Allow(Target::Method(MethodKind::TraitImpl)),
567 ]);
568
569 fn extend(
570 cx: &mut AcceptContext<'_, '_, S>,
571 args: &ArgParser,
572 ) -> impl IntoIterator<Item = Self::Item> {
573 parse_tf_attribute(cx, args)
574 }
575}
576
577pub(crate) struct SanitizeParser;
578
579impl<S: Stage> SingleAttributeParser<S> for SanitizeParser {
580 const PATH: &[Symbol] = &[sym::sanitize];
581
582 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
584
585 const TEMPLATE: AttributeTemplate = template!(List: &[
586 r#"address = "on|off""#,
587 r#"kernel_address = "on|off""#,
588 r#"cfi = "on|off""#,
589 r#"hwaddress = "on|off""#,
590 r#"kcfi = "on|off""#,
591 r#"memory = "on|off""#,
592 r#"memtag = "on|off""#,
593 r#"shadow_call_stack = "on|off""#,
594 r#"thread = "on|off""#,
595 r#"realtime = "nonblocking|blocking|caller""#,
596 ]);
597
598 const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
599 const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
600
601 fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
602 let Some(list) = args.list() else {
603 cx.expected_list(cx.attr_span, args);
604 return None;
605 };
606
607 let mut on_set = SanitizerSet::empty();
608 let mut off_set = SanitizerSet::empty();
609 let mut rtsan = None;
610
611 for item in list.mixed() {
612 let Some(item) = item.meta_item() else {
613 cx.expected_name_value(item.span(), None);
614 continue;
615 };
616
617 let path = item.path().word_sym();
618 let Some(value) = item.args().name_value() else {
619 cx.expected_name_value(item.span(), path);
620 continue;
621 };
622
623 let mut apply = |s: SanitizerSet| {
624 let is_on = match value.value_as_str() {
625 Some(sym::on) => true,
626 Some(sym::off) => false,
627 Some(_) => {
628 cx.expected_specific_argument_strings(
629 value.value_span,
630 &[sym::on, sym::off],
631 );
632 return;
633 }
634 None => {
635 cx.expected_string_literal(value.value_span, Some(value.value_as_lit()));
636 return;
637 }
638 };
639
640 if is_on {
641 on_set |= s;
642 } else {
643 off_set |= s;
644 }
645 };
646
647 match path {
648 Some(sym::address) | Some(sym::kernel_address) => {
649 apply(SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS)
650 }
651 Some(sym::cfi) => apply(SanitizerSet::CFI),
652 Some(sym::kcfi) => apply(SanitizerSet::KCFI),
653 Some(sym::memory) => apply(SanitizerSet::MEMORY),
654 Some(sym::memtag) => apply(SanitizerSet::MEMTAG),
655 Some(sym::shadow_call_stack) => apply(SanitizerSet::SHADOWCALLSTACK),
656 Some(sym::thread) => apply(SanitizerSet::THREAD),
657 Some(sym::hwaddress) => apply(SanitizerSet::HWADDRESS),
658 Some(sym::realtime) => match value.value_as_str() {
659 Some(sym::nonblocking) => rtsan = Some(RtsanSetting::Nonblocking),
660 Some(sym::blocking) => rtsan = Some(RtsanSetting::Blocking),
661 Some(sym::caller) => rtsan = Some(RtsanSetting::Caller),
662 _ => {
663 cx.expected_specific_argument_strings(
664 value.value_span,
665 &[sym::nonblocking, sym::blocking, sym::caller],
666 );
667 }
668 },
669 _ => {
670 cx.expected_specific_argument_strings(
671 item.path().span(),
672 &[
673 sym::address,
674 sym::cfi,
675 sym::kcfi,
676 sym::memory,
677 sym::memtag,
678 sym::shadow_call_stack,
679 sym::thread,
680 sym::hwaddress,
681 sym::realtime,
682 ],
683 );
684 continue;
685 }
686 }
687 }
688
689 Some(AttributeKind::Sanitize { on_set, off_set, rtsan, span: cx.attr_span })
690 }
691}
692
693pub(crate) struct ThreadLocalParser;
694
695impl<S: Stage> NoArgsAttributeParser<S> for ThreadLocalParser {
696 const PATH: &[Symbol] = &[sym::thread_local];
697 const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
698 const ALLOWED_TARGETS: AllowedTargets =
699 AllowedTargets::AllowList(&[Allow(Target::Static), Allow(Target::ForeignStatic)]);
700 const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::ThreadLocal;
701}
702
703pub(crate) struct RustcPassIndirectlyInNonRusticAbisParser;
704
705impl<S: Stage> NoArgsAttributeParser<S> for RustcPassIndirectlyInNonRusticAbisParser {
706 const PATH: &[Symbol] = &[sym::rustc_pass_indirectly_in_non_rustic_abis];
707 const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
708 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]);
709 const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcPassIndirectlyInNonRusticAbis;
710}
711
712pub(crate) struct EiiExternItemParser;
713
714impl<S: Stage> NoArgsAttributeParser<S> for EiiExternItemParser {
715 const PATH: &[Symbol] = &[sym::rustc_eii_extern_item];
716 const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
717 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::ForeignFn)]);
718 const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::EiiExternItem;
719}