1use rustc_hir::attrs::{CoverageAttrKind, OptimizeAttr, RtsanSetting, SanitizerSet, UsedBy};
2use rustc_session::errors::feature_err;
3use rustc_span::edition::Edition::Edition2024;
4
5use super::prelude::*;
6use crate::attributes::AttributeSafety;
7use crate::session_diagnostics::{
8 EmptyExportName, NakedFunctionIncompatibleAttribute, NullOnExport, NullOnObjcClass,
9 NullOnObjcSelector, ObjcClassExpectedStringLiteral, ObjcSelectorExpectedStringLiteral,
10};
11use crate::target_checking::Policy::AllowSilent;
12
13pub(crate) struct OptimizeParser;
14
15impl SingleAttributeParser for OptimizeParser {
16 const PATH: &[Symbol] = &[sym::optimize];
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 = ::rustc_feature::AttributeTemplate {
word: false,
list: Some(&["size", "speed", "none"]),
one_of: &[],
name_value_str: None,
docs: None,
}template!(List: &["size", "speed", "none"]);
25
26 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
27 let single = cx.expect_single_element_list(args, cx.attr_span)?;
28
29 let res = match single.meta_item().and_then(|i| i.path().word().map(|i| i.name)) {
30 Some(sym::size) => OptimizeAttr::Size,
31 Some(sym::speed) => OptimizeAttr::Speed,
32 Some(sym::none) => OptimizeAttr::DoNotOptimize,
33 _ => {
34 cx.adcx()
35 .expected_specific_argument(single.span(), &[sym::size, sym::speed, sym::none]);
36 OptimizeAttr::Default
37 }
38 };
39
40 Some(AttributeKind::Optimize(res, cx.attr_span))
41 }
42}
43
44pub(crate) struct ColdParser;
45
46impl NoArgsAttributeParser for ColdParser {
47 const PATH: &[Symbol] = &[sym::cold];
48 const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn;
49 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[
50 Allow(Target::Fn),
51 Allow(Target::Method(MethodKind::Trait { body: true })),
52 Allow(Target::Method(MethodKind::TraitImpl)),
53 Allow(Target::Method(MethodKind::Inherent)),
54 Allow(Target::ForeignFn),
55 Allow(Target::Closure),
56 ]);
57 const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::Cold;
58}
59
60pub(crate) struct CoverageParser;
61
62impl SingleAttributeParser for CoverageParser {
63 const PATH: &[Symbol] = &[sym::coverage];
64 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
65 Allow(Target::Fn),
66 Allow(Target::Closure),
67 Allow(Target::Method(MethodKind::Trait { body: true })),
68 Allow(Target::Method(MethodKind::TraitImpl)),
69 Allow(Target::Method(MethodKind::Inherent)),
70 Allow(Target::Impl { of_trait: true }),
71 Allow(Target::Impl { of_trait: false }),
72 Allow(Target::Mod),
73 Allow(Target::Crate),
74 ]);
75 const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
word: false,
list: None,
one_of: &[sym::off, sym::on],
name_value_str: None,
docs: None,
}template!(OneOf: &[sym::off, sym::on]);
76
77 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
78 let arg = cx.expect_single_element_list(args, cx.attr_span)?;
79
80 let mut fail_incorrect_argument =
81 |span| cx.adcx().expected_specific_argument(span, &[sym::on, sym::off]);
82
83 let Some(arg) = arg.meta_item() else {
84 fail_incorrect_argument(arg.span());
85 return None;
86 };
87
88 let kind = match arg.path().word_sym() {
89 Some(sym::off) => CoverageAttrKind::Off,
90 Some(sym::on) => CoverageAttrKind::On,
91 None | Some(_) => {
92 fail_incorrect_argument(arg.span());
93 return None;
94 }
95 };
96
97 Some(AttributeKind::Coverage(kind))
98 }
99}
100
101pub(crate) struct ExportNameParser;
102
103impl SingleAttributeParser for ExportNameParser {
104 const PATH: &[rustc_span::Symbol] = &[sym::export_name];
105 const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError;
106 const SAFETY: AttributeSafety = AttributeSafety::Unsafe { unsafe_since: Some(Edition2024) };
107 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
108 Allow(Target::Static),
109 Allow(Target::Fn),
110 Allow(Target::Method(MethodKind::Inherent)),
111 Allow(Target::Method(MethodKind::Trait { body: true })),
112 Allow(Target::Method(MethodKind::TraitImpl)),
113 Warn(Target::Field),
114 Warn(Target::Arm),
115 Warn(Target::MacroDef),
116 Warn(Target::MacroCall),
117 ]);
118 const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
word: false,
list: None,
one_of: &[],
name_value_str: Some(&["name"]),
docs: None,
}template!(NameValueStr: "name");
119
120 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
121 let nv = cx.expect_name_value(args, cx.attr_span, None)?;
122 let name = cx.expect_string_literal(nv)?;
123 if name.as_str().contains('\0') {
124 cx.emit_err(NullOnExport { span: cx.attr_span });
127 return None;
128 }
129 if name.is_empty() {
130 cx.emit_err(EmptyExportName { span: cx.attr_span });
133 return None;
134 }
135 Some(AttributeKind::ExportName { name, span: cx.attr_span })
136 }
137}
138
139pub(crate) struct RustcObjcClassParser;
140
141impl SingleAttributeParser for RustcObjcClassParser {
142 const PATH: &[rustc_span::Symbol] = &[sym::rustc_objc_class];
143 const ALLOWED_TARGETS: AllowedTargets =
144 AllowedTargets::AllowList(&[Allow(Target::ForeignStatic)]);
145 const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
word: false,
list: None,
one_of: &[],
name_value_str: Some(&["ClassName"]),
docs: None,
}template!(NameValueStr: "ClassName");
146
147 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
148 let nv = cx.expect_name_value(args, cx.attr_span, None)?;
149 let Some(classname) = nv.value_as_str() else {
150 cx.emit_err(ObjcClassExpectedStringLiteral { span: nv.value_span });
154 return None;
155 };
156 if classname.as_str().contains('\0') {
157 cx.emit_err(NullOnObjcClass { span: nv.value_span });
160 return None;
161 }
162 Some(AttributeKind::RustcObjcClass { classname })
163 }
164}
165
166pub(crate) struct RustcObjcSelectorParser;
167
168impl SingleAttributeParser for RustcObjcSelectorParser {
169 const PATH: &[rustc_span::Symbol] = &[sym::rustc_objc_selector];
170 const ALLOWED_TARGETS: AllowedTargets =
171 AllowedTargets::AllowList(&[Allow(Target::ForeignStatic)]);
172 const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
word: false,
list: None,
one_of: &[],
name_value_str: Some(&["methodName"]),
docs: None,
}template!(NameValueStr: "methodName");
173
174 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
175 let nv = cx.expect_name_value(args, cx.attr_span, None)?;
176 let Some(methname) = nv.value_as_str() else {
177 cx.emit_err(ObjcSelectorExpectedStringLiteral { span: nv.value_span });
181 return None;
182 };
183 if methname.as_str().contains('\0') {
184 cx.emit_err(NullOnObjcSelector { span: nv.value_span });
187 return None;
188 }
189 Some(AttributeKind::RustcObjcSelector { methname })
190 }
191}
192
193#[derive(#[automatically_derived]
impl ::core::default::Default for NakedParser {
#[inline]
fn default() -> NakedParser {
NakedParser { span: ::core::default::Default::default() }
}
}Default)]
194pub(crate) struct NakedParser {
195 span: Option<Span>,
196}
197
198impl AttributeParser for NakedParser {
199 const ATTRIBUTES: AcceptMapping<Self> =
200 &[(&[sym::naked], ::rustc_feature::AttributeTemplate {
word: true,
list: None,
one_of: &[],
name_value_str: None,
docs: None,
}template!(Word), |this, cx, args| {
201 let Some(()) = cx.expect_no_args(args) else {
202 return;
203 };
204
205 if let Some(earlier) = this.span {
206 let span = cx.attr_span;
207 cx.warn_unused_duplicate(earlier, span);
208 } else {
209 this.span = Some(cx.attr_span);
210 }
211 })];
212 const SAFETY: AttributeSafety = AttributeSafety::Unsafe { unsafe_since: None };
213 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
214 Allow(Target::Fn),
215 Allow(Target::Method(MethodKind::Inherent)),
216 Allow(Target::Method(MethodKind::Trait { body: true })),
217 Allow(Target::Method(MethodKind::TraitImpl)),
218 Warn(Target::MacroCall),
219 ]);
220
221 fn finalize(self, cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {
222 const ALLOW_LIST: &[rustc_span::Symbol] = &[
235 sym::cfg_trace,
237 sym::cfg_attr_trace,
238 sym::test,
240 sym::ignore,
241 sym::should_panic,
242 sym::bench,
243 sym::allow,
245 sym::warn,
246 sym::deny,
247 sym::forbid,
248 sym::deprecated,
249 sym::must_use,
250 sym::cold,
252 sym::export_name,
253 sym::link_section,
254 sym::linkage,
255 sym::no_mangle,
256 sym::instruction_set,
257 sym::repr,
258 sym::rustc_std_internal_symbol,
259 sym::rustc_align,
261 sym::rustc_align_static,
262 sym::naked,
264 sym::doc,
266 ];
267
268 let span = self.span?;
269
270 let Some(tools) = cx.tools else {
271 {
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("tools required while parsing attributes")));
};unreachable!("tools required while parsing attributes");
272 };
273
274 'outer: for other_attr in cx.all_attrs {
276 for allowed_attr in ALLOW_LIST {
277 if other_attr
278 .segments()
279 .next()
280 .is_some_and(|i| tools.iter().any(|tool| tool.name == i.name))
281 {
282 continue 'outer;
285 }
286 if other_attr.word_is(*allowed_attr) {
287 continue 'outer;
290 }
291
292 if other_attr.word_is(sym::target_feature) {
293 if !cx.features().naked_functions_target_feature() {
294 feature_err(
295 &cx.sess(),
296 sym::naked_functions_target_feature,
297 other_attr.span(),
298 "`#[target_feature(/* ... */)]` is currently unstable on `#[naked]` functions",
299 ).emit();
300 }
301
302 continue 'outer;
303 }
304 }
305
306 cx.emit_err(NakedFunctionIncompatibleAttribute {
307 span: other_attr.span(),
308 naked_span: span,
309 attr: other_attr.get_attribute_path().to_string(),
310 });
311 }
312
313 Some(AttributeKind::Naked(span))
314 }
315}
316
317pub(crate) struct TrackCallerParser;
318impl NoArgsAttributeParser for TrackCallerParser {
319 const PATH: &[Symbol] = &[sym::track_caller];
320 const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn;
321 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
322 Allow(Target::Fn),
323 Allow(Target::Method(MethodKind::Inherent)),
324 Allow(Target::Method(MethodKind::Trait { body: true })),
325 Allow(Target::Method(MethodKind::TraitImpl)),
326 Allow(Target::Method(MethodKind::Trait { body: false })), Allow(Target::ForeignFn),
328 Allow(Target::Closure),
329 Warn(Target::MacroDef),
330 Warn(Target::Arm),
331 Warn(Target::Field),
332 Warn(Target::MacroCall),
333 ]);
334 const CREATE: fn(Span) -> AttributeKind = AttributeKind::TrackCaller;
335}
336
337pub(crate) struct NoMangleParser;
338impl NoArgsAttributeParser for NoMangleParser {
339 const PATH: &[Symbol] = &[sym::no_mangle];
340 const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn;
341 const SAFETY: AttributeSafety = AttributeSafety::Unsafe { unsafe_since: Some(Edition2024) };
342 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[
343 Allow(Target::Fn),
344 Allow(Target::Static),
345 Allow(Target::Method(MethodKind::Inherent)),
346 Allow(Target::Method(MethodKind::TraitImpl)),
347 AllowSilent(Target::Const), Error(Target::Closure),
349 ]);
350 const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoMangle;
351}
352
353#[derive(#[automatically_derived]
impl ::core::default::Default for UsedParser {
#[inline]
fn default() -> UsedParser {
UsedParser {
first_compiler: ::core::default::Default::default(),
first_linker: ::core::default::Default::default(),
first_default: ::core::default::Default::default(),
}
}
}Default)]
354pub(crate) struct UsedParser {
355 first_compiler: Option<Span>,
356 first_linker: Option<Span>,
357 first_default: Option<Span>,
358}
359
360impl AttributeParser for UsedParser {
365 const ATTRIBUTES: AcceptMapping<Self> = &[(
366 &[sym::used],
367 ::rustc_feature::AttributeTemplate {
word: true,
list: Some(&["compiler", "linker"]),
one_of: &[],
name_value_str: None,
docs: None,
}template!(Word, List: &["compiler", "linker"]),
368 |group: &mut Self, cx, args| {
369 let used_by = match args {
370 ArgParser::NoArgs => UsedBy::Default,
371 ArgParser::List(list) => {
372 let Some(l) = cx.expect_single(list) else {
373 return;
374 };
375
376 match l.meta_item().and_then(|i| i.path().word_sym()) {
377 Some(sym::compiler) => {
378 if !cx.features().used_with_arg() {
379 feature_err(
380 &cx.sess(),
381 sym::used_with_arg,
382 cx.attr_span,
383 "`#[used(compiler)]` is currently unstable",
384 )
385 .emit();
386 }
387 UsedBy::Compiler
388 }
389 Some(sym::linker) => {
390 if !cx.features().used_with_arg() {
391 feature_err(
392 &cx.sess(),
393 sym::used_with_arg,
394 cx.attr_span,
395 "`#[used(linker)]` is currently unstable",
396 )
397 .emit();
398 }
399 UsedBy::Linker
400 }
401 _ => {
402 cx.adcx().expected_specific_argument(
403 l.span(),
404 &[sym::compiler, sym::linker],
405 );
406 return;
407 }
408 }
409 }
410 ArgParser::NameValue(_) => return,
411 };
412
413 let attr_span = cx.attr_span;
414
415 let target = match used_by {
419 UsedBy::Compiler => &mut group.first_compiler,
420 UsedBy::Linker => {
421 if let Some(prev) = group.first_default {
422 cx.warn_unused_duplicate(prev, attr_span);
423 return;
424 }
425 &mut group.first_linker
426 }
427 UsedBy::Default => {
428 if let Some(prev) = group.first_linker {
429 cx.warn_unused_duplicate(prev, attr_span);
430 return;
431 }
432 &mut group.first_default
433 }
434 };
435
436 if let Some(prev) = *target {
437 cx.warn_unused_duplicate(prev, attr_span);
438 } else {
439 *target = Some(attr_span);
440 }
441 },
442 )];
443 const ALLOWED_TARGETS: AllowedTargets =
444 AllowedTargets::AllowList(&[Allow(Target::Static), Warn(Target::MacroCall)]);
445
446 fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {
447 Some(match (self.first_compiler, self.first_linker, self.first_default) {
450 (_, Some(_), _) => AttributeKind::Used { used_by: UsedBy::Linker },
451 (Some(_), _, _) => AttributeKind::Used { used_by: UsedBy::Compiler },
452 (_, _, Some(_)) => AttributeKind::Used { used_by: UsedBy::Default },
453 (None, None, None) => return None,
454 })
455 }
456}
457
458fn parse_tf_attribute(
459 cx: &mut AcceptContext<'_, '_>,
460 args: &ArgParser,
461) -> impl IntoIterator<Item = (Symbol, Span)> {
462 let mut features = Vec::new();
463 let Some(list) = cx.expect_list(args, cx.attr_span) else {
464 return features;
465 };
466 if list.is_empty() {
467 let attr_span = cx.attr_span;
468 cx.adcx().warn_empty_attribute(attr_span);
469 return features;
470 }
471 for item in list.mixed() {
472 let Some((ident, value)) = cx.expect_name_value(item, item.span(), Some(sym::enable))
473 else {
474 return features;
475 };
476
477 if ident.name != sym::enable {
479 cx.adcx().expected_specific_argument(ident.span, &[sym::enable]);
480 return features;
481 }
482
483 let Some(value_str) = cx.expect_string_literal(value) else {
485 return features;
486 };
487 for feature in value_str.as_str().split(",") {
488 features.push((Symbol::intern(feature), item.span()));
489 }
490 }
491 features
492}
493
494pub(crate) struct TargetFeatureParser;
495
496impl CombineAttributeParser for TargetFeatureParser {
497 type Item = (Symbol, Span);
498 const PATH: &[Symbol] = &[sym::target_feature];
499 const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {
500 features: items,
501 attr_span: span,
502 was_forced: false,
503 };
504 const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
word: false,
list: Some(&["enable = \"feat1, feat2\""]),
one_of: &[],
name_value_str: None,
docs: None,
}template!(List: &["enable = \"feat1, feat2\""]);
505
506 fn extend(
507 cx: &mut AcceptContext<'_, '_>,
508 args: &ArgParser,
509 ) -> impl IntoIterator<Item = Self::Item> {
510 parse_tf_attribute(cx, args)
511 }
512
513 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
514 Allow(Target::Fn),
515 Allow(Target::Method(MethodKind::Inherent)),
516 Allow(Target::Method(MethodKind::Trait { body: true })),
517 Allow(Target::Method(MethodKind::TraitImpl)),
518 Warn(Target::Statement),
519 Warn(Target::Field),
520 Warn(Target::Arm),
521 Warn(Target::MacroDef),
522 Warn(Target::MacroCall),
523 ]);
524}
525
526pub(crate) struct ForceTargetFeatureParser;
527
528impl CombineAttributeParser for ForceTargetFeatureParser {
529 type Item = (Symbol, Span);
530 const PATH: &[Symbol] = &[sym::force_target_feature];
531 const SAFETY: AttributeSafety = AttributeSafety::Unsafe { unsafe_since: None };
532 const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {
533 features: items,
534 attr_span: span,
535 was_forced: true,
536 };
537 const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
word: false,
list: Some(&["enable = \"feat1, feat2\""]),
one_of: &[],
name_value_str: None,
docs: None,
}template!(List: &["enable = \"feat1, feat2\""]);
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 ]);
544
545 fn extend(
546 cx: &mut AcceptContext<'_, '_>,
547 args: &ArgParser,
548 ) -> impl IntoIterator<Item = Self::Item> {
549 parse_tf_attribute(cx, args)
550 }
551}
552
553pub(crate) struct SanitizeParser;
554
555impl SingleAttributeParser for SanitizeParser {
556 const PATH: &[Symbol] = &[sym::sanitize];
557
558 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
560
561 const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
word: false,
list: Some(&[r#"address = "on|off""#, r#"kernel_address = "on|off""#,
r#"cfi = "on|off""#, r#"hwaddress = "on|off""#,
r#"kernel_hwaddress = "on|off""#, r#"kcfi = "on|off""#,
r#"memory = "on|off""#, r#"memtag = "on|off""#,
r#"shadow_call_stack = "on|off""#, r#"thread = "on|off""#,
r#"realtime = "nonblocking|blocking|caller""#]),
one_of: &[],
name_value_str: None,
docs: None,
}template!(List: &[
562 r#"address = "on|off""#,
563 r#"kernel_address = "on|off""#,
564 r#"cfi = "on|off""#,
565 r#"hwaddress = "on|off""#,
566 r#"kernel_hwaddress = "on|off""#,
567 r#"kcfi = "on|off""#,
568 r#"memory = "on|off""#,
569 r#"memtag = "on|off""#,
570 r#"shadow_call_stack = "on|off""#,
571 r#"thread = "on|off""#,
572 r#"realtime = "nonblocking|blocking|caller""#,
573 ]);
574
575 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
576 let list = cx.expect_list(args, cx.attr_span)?;
577
578 let mut on_set = SanitizerSet::empty();
579 let mut off_set = SanitizerSet::empty();
580 let mut rtsan = None;
581
582 for item in list.mixed() {
583 let Some((ident, value)) = cx.expect_name_value(item, item.span(), None) else {
584 continue;
585 };
586
587 let mut apply = |s: SanitizerSet| {
588 let is_on = match value.value_as_str() {
589 Some(sym::on) => true,
590 Some(sym::off) => false,
591 Some(_) => {
592 cx.adcx().expected_specific_argument_strings(
593 value.value_span,
594 &[sym::on, sym::off],
595 );
596 return;
597 }
598 None => {
599 cx.adcx().expected_specific_argument_strings(
600 value.value_span,
601 &[sym::on, sym::off],
602 );
603 return;
604 }
605 };
606
607 if is_on {
608 on_set |= s;
609 } else {
610 off_set |= s;
611 }
612 };
613
614 match ident.name {
615 sym::address | sym::kernel_address => {
616 apply(SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS)
617 }
618 sym::cfi => apply(SanitizerSet::CFI),
619 sym::kcfi => apply(SanitizerSet::KCFI),
620 sym::memory => apply(SanitizerSet::MEMORY),
621 sym::memtag => apply(SanitizerSet::MEMTAG),
622 sym::shadow_call_stack => apply(SanitizerSet::SHADOWCALLSTACK),
623 sym::thread => apply(SanitizerSet::THREAD),
624 sym::hwaddress | sym::kernel_hwaddress => {
625 apply(SanitizerSet::HWADDRESS | SanitizerSet::KERNELHWADDRESS)
626 }
627 sym::realtime => match value.value_as_str() {
628 Some(sym::nonblocking) => rtsan = Some(RtsanSetting::Nonblocking),
629 Some(sym::blocking) => rtsan = Some(RtsanSetting::Blocking),
630 Some(sym::caller) => rtsan = Some(RtsanSetting::Caller),
631 _ => {
632 cx.adcx().expected_specific_argument_strings(
633 value.value_span,
634 &[sym::nonblocking, sym::blocking, sym::caller],
635 );
636 }
637 },
638 _ => {
639 cx.adcx().expected_specific_argument_strings(
640 ident.span,
641 &[
642 sym::address,
643 sym::kernel_address,
644 sym::cfi,
645 sym::kcfi,
646 sym::memory,
647 sym::memtag,
648 sym::shadow_call_stack,
649 sym::thread,
650 sym::hwaddress,
651 sym::kernel_hwaddress,
652 sym::realtime,
653 ],
654 );
655 continue;
656 }
657 }
658 }
659
660 Some(AttributeKind::Sanitize { on_set, off_set, rtsan, span: cx.attr_span })
661 }
662}
663
664pub(crate) struct ThreadLocalParser;
665
666impl NoArgsAttributeParser for ThreadLocalParser {
667 const PATH: &[Symbol] = &[sym::thread_local];
668 const ALLOWED_TARGETS: AllowedTargets =
669 AllowedTargets::AllowList(&[Allow(Target::Static), Allow(Target::ForeignStatic)]);
670 const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::ThreadLocal;
671}
672
673pub(crate) struct RustcPassIndirectlyInNonRusticAbisParser;
674
675impl NoArgsAttributeParser for RustcPassIndirectlyInNonRusticAbisParser {
676 const PATH: &[Symbol] = &[sym::rustc_pass_indirectly_in_non_rustic_abis];
677 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]);
678 const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcPassIndirectlyInNonRusticAbis;
679}
680
681pub(crate) struct RustcEiiForeignItemParser;
682
683impl NoArgsAttributeParser for RustcEiiForeignItemParser {
684 const PATH: &[Symbol] = &[sym::rustc_eii_foreign_item];
685 const ALLOWED_TARGETS: AllowedTargets =
686 AllowedTargets::AllowList(&[Allow(Target::ForeignFn), Allow(Target::ForeignStatic)]);
687 const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcEiiForeignItem;
688}
689
690pub(crate) struct PatchableFunctionEntryParser;
691
692impl SingleAttributeParser for PatchableFunctionEntryParser {
693 const PATH: &[Symbol] = &[sym::patchable_function_entry];
694 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]);
695 const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
word: false,
list: Some(&["prefix_nops = m, entry_nops = n"]),
one_of: &[],
name_value_str: None,
docs: None,
}template!(List: &["prefix_nops = m, entry_nops = n"]);
696
697 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
698 let meta_item_list = cx.expect_list(args, cx.attr_span)?;
699
700 let mut prefix = None;
701 let mut entry = None;
702
703 if meta_item_list.len() == 0 {
704 cx.adcx().expected_at_least_one_argument(meta_item_list.span);
705 return None;
706 }
707
708 let mut errored = false;
709
710 for item in meta_item_list.mixed() {
711 let Some((ident, value)) = cx.expect_name_value(item, item.span(), None) else {
712 continue;
713 };
714
715 let attrib_to_write = match ident.name {
716 sym::prefix_nops => {
717 if prefix.is_some() {
719 errored = true;
720 cx.adcx().duplicate_key(ident.span, sym::prefix_nops);
721 continue;
722 }
723 &mut prefix
724 }
725 sym::entry_nops => {
726 if entry.is_some() {
728 errored = true;
729 cx.adcx().duplicate_key(ident.span, sym::entry_nops);
730 continue;
731 }
732 &mut entry
733 }
734 _ => {
735 errored = true;
736 cx.adcx().expected_specific_argument(
737 ident.span,
738 &[sym::prefix_nops, sym::entry_nops],
739 );
740 continue;
741 }
742 };
743
744 let rustc_ast::LitKind::Int(val, _) = value.value_as_lit().kind else {
745 errored = true;
746 cx.adcx().expected_integer_literal(value.value_span);
747 continue;
748 };
749
750 let Ok(val) = val.get().try_into() else {
751 errored = true;
752 cx.adcx().expected_integer_literal_in_range(
753 value.value_span,
754 u8::MIN as isize,
755 u8::MAX as isize,
756 );
757 continue;
758 };
759
760 *attrib_to_write = Some(val);
761 }
762
763 if errored {
764 None
765 } else {
766 Some(AttributeKind::PatchableFunctionEntry {
767 prefix: prefix.unwrap_or(0),
768 entry: entry.unwrap_or(0),
769 })
770 }
771 }
772}