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