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 NakedFunctionIncompatibleAttribute, NullOnExport, NullOnObjcClass, NullOnObjcSelector,
9 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 Some(name) = nv.value_as_str() else {
123 cx.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
124 return None;
125 };
126 if name.as_str().contains('\0') {
127 cx.emit_err(NullOnExport { span: cx.attr_span });
130 return None;
131 }
132 Some(AttributeKind::ExportName { name, span: cx.attr_span })
133 }
134}
135
136pub(crate) struct RustcObjcClassParser;
137
138impl SingleAttributeParser for RustcObjcClassParser {
139 const PATH: &[rustc_span::Symbol] = &[sym::rustc_objc_class];
140 const ALLOWED_TARGETS: AllowedTargets =
141 AllowedTargets::AllowList(&[Allow(Target::ForeignStatic)]);
142 const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
word: false,
list: None,
one_of: &[],
name_value_str: Some(&["ClassName"]),
docs: None,
}template!(NameValueStr: "ClassName");
143
144 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
145 let nv = cx.expect_name_value(args, cx.attr_span, None)?;
146 let Some(classname) = nv.value_as_str() else {
147 cx.emit_err(ObjcClassExpectedStringLiteral { span: nv.value_span });
151 return None;
152 };
153 if classname.as_str().contains('\0') {
154 cx.emit_err(NullOnObjcClass { span: nv.value_span });
157 return None;
158 }
159 Some(AttributeKind::RustcObjcClass { classname })
160 }
161}
162
163pub(crate) struct RustcObjcSelectorParser;
164
165impl SingleAttributeParser for RustcObjcSelectorParser {
166 const PATH: &[rustc_span::Symbol] = &[sym::rustc_objc_selector];
167 const ALLOWED_TARGETS: AllowedTargets =
168 AllowedTargets::AllowList(&[Allow(Target::ForeignStatic)]);
169 const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
word: false,
list: None,
one_of: &[],
name_value_str: Some(&["methodName"]),
docs: None,
}template!(NameValueStr: "methodName");
170
171 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
172 let nv = cx.expect_name_value(args, cx.attr_span, None)?;
173 let Some(methname) = nv.value_as_str() else {
174 cx.emit_err(ObjcSelectorExpectedStringLiteral { span: nv.value_span });
178 return None;
179 };
180 if methname.as_str().contains('\0') {
181 cx.emit_err(NullOnObjcSelector { span: nv.value_span });
184 return None;
185 }
186 Some(AttributeKind::RustcObjcSelector { methname })
187 }
188}
189
190#[derive(#[automatically_derived]
impl ::core::default::Default for NakedParser {
#[inline]
fn default() -> NakedParser {
NakedParser { span: ::core::default::Default::default() }
}
}Default)]
191pub(crate) struct NakedParser {
192 span: Option<Span>,
193}
194
195impl AttributeParser for NakedParser {
196 const ATTRIBUTES: AcceptMapping<Self> =
197 &[(&[sym::naked], ::rustc_feature::AttributeTemplate {
word: true,
list: None,
one_of: &[],
name_value_str: None,
docs: None,
}template!(Word), |this, cx, args| {
198 let Some(()) = cx.expect_no_args(args) else {
199 return;
200 };
201
202 if let Some(earlier) = this.span {
203 let span = cx.attr_span;
204 cx.warn_unused_duplicate(earlier, span);
205 } else {
206 this.span = Some(cx.attr_span);
207 }
208 })];
209 const SAFETY: AttributeSafety = AttributeSafety::Unsafe { unsafe_since: None };
210 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
211 Allow(Target::Fn),
212 Allow(Target::Method(MethodKind::Inherent)),
213 Allow(Target::Method(MethodKind::Trait { body: true })),
214 Allow(Target::Method(MethodKind::TraitImpl)),
215 Warn(Target::MacroCall),
216 ]);
217
218 fn finalize(self, cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {
219 const ALLOW_LIST: &[rustc_span::Symbol] = &[
232 sym::cfg_trace,
234 sym::cfg_attr_trace,
235 sym::test,
237 sym::ignore,
238 sym::should_panic,
239 sym::bench,
240 sym::allow,
242 sym::warn,
243 sym::deny,
244 sym::forbid,
245 sym::deprecated,
246 sym::must_use,
247 sym::cold,
249 sym::export_name,
250 sym::link_section,
251 sym::linkage,
252 sym::no_mangle,
253 sym::instruction_set,
254 sym::repr,
255 sym::rustc_std_internal_symbol,
256 sym::rustc_align,
258 sym::rustc_align_static,
259 sym::naked,
261 sym::doc,
263 ];
264
265 let span = self.span?;
266
267 let Some(tools) = cx.tools else {
268 {
::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");
269 };
270
271 'outer: for other_attr in cx.all_attrs {
273 for allowed_attr in ALLOW_LIST {
274 if other_attr
275 .segments()
276 .next()
277 .is_some_and(|i| tools.iter().any(|tool| tool.name == i.name))
278 {
279 continue 'outer;
282 }
283 if other_attr.word_is(*allowed_attr) {
284 continue 'outer;
287 }
288
289 if other_attr.word_is(sym::target_feature) {
290 if !cx.features().naked_functions_target_feature() {
291 feature_err(
292 &cx.sess(),
293 sym::naked_functions_target_feature,
294 other_attr.span(),
295 "`#[target_feature(/* ... */)]` is currently unstable on `#[naked]` functions",
296 ).emit();
297 }
298
299 continue 'outer;
300 }
301 }
302
303 cx.emit_err(NakedFunctionIncompatibleAttribute {
304 span: other_attr.span(),
305 naked_span: span,
306 attr: other_attr.get_attribute_path().to_string(),
307 });
308 }
309
310 Some(AttributeKind::Naked(span))
311 }
312}
313
314pub(crate) struct TrackCallerParser;
315impl NoArgsAttributeParser for TrackCallerParser {
316 const PATH: &[Symbol] = &[sym::track_caller];
317 const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn;
318 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
319 Allow(Target::Fn),
320 Allow(Target::Method(MethodKind::Inherent)),
321 Allow(Target::Method(MethodKind::Trait { body: true })),
322 Allow(Target::Method(MethodKind::TraitImpl)),
323 Allow(Target::Method(MethodKind::Trait { body: false })), Allow(Target::ForeignFn),
325 Allow(Target::Closure),
326 Warn(Target::MacroDef),
327 Warn(Target::Arm),
328 Warn(Target::Field),
329 Warn(Target::MacroCall),
330 ]);
331 const CREATE: fn(Span) -> AttributeKind = AttributeKind::TrackCaller;
332}
333
334pub(crate) struct NoMangleParser;
335impl NoArgsAttributeParser for NoMangleParser {
336 const PATH: &[Symbol] = &[sym::no_mangle];
337 const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn;
338 const SAFETY: AttributeSafety = AttributeSafety::Unsafe { unsafe_since: Some(Edition2024) };
339 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[
340 Allow(Target::Fn),
341 Allow(Target::Static),
342 Allow(Target::Method(MethodKind::Inherent)),
343 Allow(Target::Method(MethodKind::TraitImpl)),
344 AllowSilent(Target::Const), Error(Target::Closure),
346 ]);
347 const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoMangle;
348}
349
350#[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)]
351pub(crate) struct UsedParser {
352 first_compiler: Option<Span>,
353 first_linker: Option<Span>,
354 first_default: Option<Span>,
355}
356
357impl AttributeParser for UsedParser {
362 const ATTRIBUTES: AcceptMapping<Self> = &[(
363 &[sym::used],
364 ::rustc_feature::AttributeTemplate {
word: true,
list: Some(&["compiler", "linker"]),
one_of: &[],
name_value_str: None,
docs: None,
}template!(Word, List: &["compiler", "linker"]),
365 |group: &mut Self, cx, args| {
366 let used_by = match args {
367 ArgParser::NoArgs => UsedBy::Default,
368 ArgParser::List(list) => {
369 let Some(l) = cx.expect_single(list) else {
370 return;
371 };
372
373 match l.meta_item().and_then(|i| i.path().word_sym()) {
374 Some(sym::compiler) => {
375 if !cx.features().used_with_arg() {
376 feature_err(
377 &cx.sess(),
378 sym::used_with_arg,
379 cx.attr_span,
380 "`#[used(compiler)]` is currently unstable",
381 )
382 .emit();
383 }
384 UsedBy::Compiler
385 }
386 Some(sym::linker) => {
387 if !cx.features().used_with_arg() {
388 feature_err(
389 &cx.sess(),
390 sym::used_with_arg,
391 cx.attr_span,
392 "`#[used(linker)]` is currently unstable",
393 )
394 .emit();
395 }
396 UsedBy::Linker
397 }
398 _ => {
399 cx.adcx().expected_specific_argument(
400 l.span(),
401 &[sym::compiler, sym::linker],
402 );
403 return;
404 }
405 }
406 }
407 ArgParser::NameValue(_) => return,
408 };
409
410 let attr_span = cx.attr_span;
411
412 let target = match used_by {
416 UsedBy::Compiler => &mut group.first_compiler,
417 UsedBy::Linker => {
418 if let Some(prev) = group.first_default {
419 cx.warn_unused_duplicate(prev, attr_span);
420 return;
421 }
422 &mut group.first_linker
423 }
424 UsedBy::Default => {
425 if let Some(prev) = group.first_linker {
426 cx.warn_unused_duplicate(prev, attr_span);
427 return;
428 }
429 &mut group.first_default
430 }
431 };
432
433 if let Some(prev) = *target {
434 cx.warn_unused_duplicate(prev, attr_span);
435 } else {
436 *target = Some(attr_span);
437 }
438 },
439 )];
440 const ALLOWED_TARGETS: AllowedTargets =
441 AllowedTargets::AllowList(&[Allow(Target::Static), Warn(Target::MacroCall)]);
442
443 fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {
444 Some(match (self.first_compiler, self.first_linker, self.first_default) {
447 (_, Some(_), _) => AttributeKind::Used { used_by: UsedBy::Linker },
448 (Some(_), _, _) => AttributeKind::Used { used_by: UsedBy::Compiler },
449 (_, _, Some(_)) => AttributeKind::Used { used_by: UsedBy::Default },
450 (None, None, None) => return None,
451 })
452 }
453}
454
455fn parse_tf_attribute(
456 cx: &mut AcceptContext<'_, '_>,
457 args: &ArgParser,
458) -> impl IntoIterator<Item = (Symbol, Span)> {
459 let mut features = Vec::new();
460 let Some(list) = cx.expect_list(args, cx.attr_span) else {
461 return features;
462 };
463 if list.is_empty() {
464 let attr_span = cx.attr_span;
465 cx.adcx().warn_empty_attribute(attr_span);
466 return features;
467 }
468 for item in list.mixed() {
469 let Some((ident, value)) = cx.expect_name_value(item, item.span(), Some(sym::enable))
470 else {
471 return features;
472 };
473
474 if ident.name != sym::enable {
476 cx.adcx().expected_specific_argument(ident.span, &[sym::enable]);
477 return features;
478 }
479
480 let Some(value_str) = value.value_as_str() else {
482 cx.adcx().expected_string_literal(value.value_span, Some(value.value_as_lit()));
483 return features;
484 };
485 for feature in value_str.as_str().split(",") {
486 features.push((Symbol::intern(feature), item.span()));
487 }
488 }
489 features
490}
491
492pub(crate) struct TargetFeatureParser;
493
494impl CombineAttributeParser for TargetFeatureParser {
495 type Item = (Symbol, Span);
496 const PATH: &[Symbol] = &[sym::target_feature];
497 const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {
498 features: items,
499 attr_span: span,
500 was_forced: false,
501 };
502 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\""]);
503
504 fn extend(
505 cx: &mut AcceptContext<'_, '_>,
506 args: &ArgParser,
507 ) -> impl IntoIterator<Item = Self::Item> {
508 parse_tf_attribute(cx, args)
509 }
510
511 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
512 Allow(Target::Fn),
513 Allow(Target::Method(MethodKind::Inherent)),
514 Allow(Target::Method(MethodKind::Trait { body: true })),
515 Allow(Target::Method(MethodKind::TraitImpl)),
516 Warn(Target::Statement),
517 Warn(Target::Field),
518 Warn(Target::Arm),
519 Warn(Target::MacroDef),
520 Warn(Target::MacroCall),
521 ]);
522}
523
524pub(crate) struct ForceTargetFeatureParser;
525
526impl CombineAttributeParser for ForceTargetFeatureParser {
527 type Item = (Symbol, Span);
528 const PATH: &[Symbol] = &[sym::force_target_feature];
529 const SAFETY: AttributeSafety = AttributeSafety::Unsafe { unsafe_since: None };
530 const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {
531 features: items,
532 attr_span: span,
533 was_forced: true,
534 };
535 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\""]);
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 ]);
542
543 fn extend(
544 cx: &mut AcceptContext<'_, '_>,
545 args: &ArgParser,
546 ) -> impl IntoIterator<Item = Self::Item> {
547 parse_tf_attribute(cx, args)
548 }
549}
550
551pub(crate) struct SanitizeParser;
552
553impl SingleAttributeParser for SanitizeParser {
554 const PATH: &[Symbol] = &[sym::sanitize];
555
556 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
558
559 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: &[
560 r#"address = "on|off""#,
561 r#"kernel_address = "on|off""#,
562 r#"cfi = "on|off""#,
563 r#"hwaddress = "on|off""#,
564 r#"kernel_hwaddress = "on|off""#,
565 r#"kcfi = "on|off""#,
566 r#"memory = "on|off""#,
567 r#"memtag = "on|off""#,
568 r#"shadow_call_stack = "on|off""#,
569 r#"thread = "on|off""#,
570 r#"realtime = "nonblocking|blocking|caller""#,
571 ]);
572
573 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
574 let list = cx.expect_list(args, cx.attr_span)?;
575
576 let mut on_set = SanitizerSet::empty();
577 let mut off_set = SanitizerSet::empty();
578 let mut rtsan = None;
579
580 for item in list.mixed() {
581 let Some((ident, value)) = cx.expect_name_value(item, item.span(), None) else {
582 continue;
583 };
584
585 let mut apply = |s: SanitizerSet| {
586 let is_on = match value.value_as_str() {
587 Some(sym::on) => true,
588 Some(sym::off) => false,
589 Some(_) => {
590 cx.adcx().expected_specific_argument_strings(
591 value.value_span,
592 &[sym::on, sym::off],
593 );
594 return;
595 }
596 None => {
597 cx.adcx()
598 .expected_string_literal(value.value_span, Some(value.value_as_lit()));
599 return;
600 }
601 };
602
603 if is_on {
604 on_set |= s;
605 } else {
606 off_set |= s;
607 }
608 };
609
610 match ident.name {
611 sym::address | sym::kernel_address => {
612 apply(SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS)
613 }
614 sym::cfi => apply(SanitizerSet::CFI),
615 sym::kcfi => apply(SanitizerSet::KCFI),
616 sym::memory => apply(SanitizerSet::MEMORY),
617 sym::memtag => apply(SanitizerSet::MEMTAG),
618 sym::shadow_call_stack => apply(SanitizerSet::SHADOWCALLSTACK),
619 sym::thread => apply(SanitizerSet::THREAD),
620 sym::hwaddress | sym::kernel_hwaddress => {
621 apply(SanitizerSet::HWADDRESS | SanitizerSet::KERNELHWADDRESS)
622 }
623 sym::realtime => match value.value_as_str() {
624 Some(sym::nonblocking) => rtsan = Some(RtsanSetting::Nonblocking),
625 Some(sym::blocking) => rtsan = Some(RtsanSetting::Blocking),
626 Some(sym::caller) => rtsan = Some(RtsanSetting::Caller),
627 _ => {
628 cx.adcx().expected_specific_argument_strings(
629 value.value_span,
630 &[sym::nonblocking, sym::blocking, sym::caller],
631 );
632 }
633 },
634 _ => {
635 cx.adcx().expected_specific_argument_strings(
636 ident.span,
637 &[
638 sym::address,
639 sym::kernel_address,
640 sym::cfi,
641 sym::kcfi,
642 sym::memory,
643 sym::memtag,
644 sym::shadow_call_stack,
645 sym::thread,
646 sym::hwaddress,
647 sym::kernel_hwaddress,
648 sym::realtime,
649 ],
650 );
651 continue;
652 }
653 }
654 }
655
656 Some(AttributeKind::Sanitize { on_set, off_set, rtsan, span: cx.attr_span })
657 }
658}
659
660pub(crate) struct ThreadLocalParser;
661
662impl NoArgsAttributeParser for ThreadLocalParser {
663 const PATH: &[Symbol] = &[sym::thread_local];
664 const ALLOWED_TARGETS: AllowedTargets =
665 AllowedTargets::AllowList(&[Allow(Target::Static), Allow(Target::ForeignStatic)]);
666 const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::ThreadLocal;
667}
668
669pub(crate) struct RustcPassIndirectlyInNonRusticAbisParser;
670
671impl NoArgsAttributeParser for RustcPassIndirectlyInNonRusticAbisParser {
672 const PATH: &[Symbol] = &[sym::rustc_pass_indirectly_in_non_rustic_abis];
673 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]);
674 const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcPassIndirectlyInNonRusticAbis;
675}
676
677pub(crate) struct RustcEiiForeignItemParser;
678
679impl NoArgsAttributeParser for RustcEiiForeignItemParser {
680 const PATH: &[Symbol] = &[sym::rustc_eii_foreign_item];
681 const ALLOWED_TARGETS: AllowedTargets =
682 AllowedTargets::AllowList(&[Allow(Target::ForeignFn), Allow(Target::ForeignStatic)]);
683 const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcEiiForeignItem;
684}
685
686pub(crate) struct PatchableFunctionEntryParser;
687
688impl SingleAttributeParser for PatchableFunctionEntryParser {
689 const PATH: &[Symbol] = &[sym::patchable_function_entry];
690 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]);
691 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"]);
692
693 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
694 let meta_item_list = cx.expect_list(args, cx.attr_span)?;
695
696 let mut prefix = None;
697 let mut entry = None;
698
699 if meta_item_list.len() == 0 {
700 cx.adcx().expected_at_least_one_argument(meta_item_list.span);
701 return None;
702 }
703
704 let mut errored = false;
705
706 for item in meta_item_list.mixed() {
707 let Some((ident, value)) = cx.expect_name_value(item, item.span(), None) else {
708 continue;
709 };
710
711 let attrib_to_write = match ident.name {
712 sym::prefix_nops => {
713 if prefix.is_some() {
715 errored = true;
716 cx.adcx().duplicate_key(ident.span, sym::prefix_nops);
717 continue;
718 }
719 &mut prefix
720 }
721 sym::entry_nops => {
722 if entry.is_some() {
724 errored = true;
725 cx.adcx().duplicate_key(ident.span, sym::entry_nops);
726 continue;
727 }
728 &mut entry
729 }
730 _ => {
731 errored = true;
732 cx.adcx().expected_specific_argument(
733 ident.span,
734 &[sym::prefix_nops, sym::entry_nops],
735 );
736 continue;
737 }
738 };
739
740 let rustc_ast::LitKind::Int(val, _) = value.value_as_lit().kind else {
741 errored = true;
742 cx.adcx().expected_integer_literal(value.value_span);
743 continue;
744 };
745
746 let Ok(val) = val.get().try_into() else {
747 errored = true;
748 cx.adcx().expected_integer_literal_in_range(
749 value.value_span,
750 u8::MIN as isize,
751 u8::MAX as isize,
752 );
753 continue;
754 };
755
756 *attrib_to_write = Some(val);
757 }
758
759 if errored {
760 None
761 } else {
762 Some(AttributeKind::PatchableFunctionEntry {
763 prefix: prefix.unwrap_or(0),
764 entry: entry.unwrap_or(0),
765 })
766 }
767 }
768}