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.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<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.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(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) = cx.expect_single(list) else {
375 return;
376 };
377
378 match l.meta_item().and_then(|i| i.path().word_sym()) {
379 Some(sym::compiler) => {
380 if !cx.features().used_with_arg() {
381 feature_err(
382 &cx.sess(),
383 sym::used_with_arg,
384 cx.attr_span,
385 "`#[used(compiler)]` is currently unstable",
386 )
387 .emit();
388 }
389 UsedBy::Compiler
390 }
391 Some(sym::linker) => {
392 if !cx.features().used_with_arg() {
393 feature_err(
394 &cx.sess(),
395 sym::used_with_arg,
396 cx.attr_span,
397 "`#[used(linker)]` is currently unstable",
398 )
399 .emit();
400 }
401 UsedBy::Linker
402 }
403 _ => {
404 cx.adcx().expected_specific_argument(
405 l.span(),
406 &[sym::compiler, sym::linker],
407 );
408 return;
409 }
410 }
411 }
412 ArgParser::NameValue(_) => return,
413 };
414
415 let attr_span = cx.attr_span;
416
417 let target = match used_by {
421 UsedBy::Compiler => &mut group.first_compiler,
422 UsedBy::Linker => {
423 if let Some(prev) = group.first_default {
424 cx.warn_unused_duplicate(prev, attr_span);
425 return;
426 }
427 &mut group.first_linker
428 }
429 UsedBy::Default => {
430 if let Some(prev) = group.first_linker {
431 cx.warn_unused_duplicate(prev, attr_span);
432 return;
433 }
434 &mut group.first_default
435 }
436 };
437
438 if let Some(prev) = *target {
439 cx.warn_unused_duplicate(prev, attr_span);
440 } else {
441 *target = Some(attr_span);
442 }
443 },
444 )];
445 const ALLOWED_TARGETS: AllowedTargets =
446 AllowedTargets::AllowList(&[Allow(Target::Static), Warn(Target::MacroCall)]);
447
448 fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
449 Some(match (self.first_compiler, self.first_linker, self.first_default) {
452 (_, Some(span), _) => AttributeKind::Used { used_by: UsedBy::Linker, span },
453 (Some(span), _, _) => AttributeKind::Used { used_by: UsedBy::Compiler, span },
454 (_, _, Some(span)) => AttributeKind::Used { used_by: UsedBy::Default, span },
455 (None, None, None) => return None,
456 })
457 }
458}
459
460fn parse_tf_attribute<S: Stage>(
461 cx: &mut AcceptContext<'_, '_, S>,
462 args: &ArgParser,
463) -> impl IntoIterator<Item = (Symbol, Span)> {
464 let mut features = Vec::new();
465 let Some(list) = cx.expect_list(args, cx.attr_span) else {
466 return features;
467 };
468 if list.is_empty() {
469 let attr_span = cx.attr_span;
470 cx.adcx().warn_empty_attribute(attr_span);
471 return features;
472 }
473 for item in list.mixed() {
474 let Some(name_value) = item.meta_item() else {
475 cx.adcx().expected_name_value(item.span(), Some(sym::enable));
476 return features;
477 };
478
479 let Some(name) = name_value.path().word_sym() else {
481 cx.adcx().expected_name_value(name_value.path().span(), Some(sym::enable));
482 return features;
483 };
484 if name != sym::enable {
485 cx.adcx().expected_name_value(name_value.path().span(), Some(sym::enable));
486 return features;
487 }
488
489 let Some(name_value) = name_value.args().name_value() else {
491 cx.adcx().expected_name_value(item.span(), Some(sym::enable));
492 return features;
493 };
494 let Some(value_str) = name_value.value_as_str() else {
495 cx.adcx()
496 .expected_string_literal(name_value.value_span, Some(name_value.value_as_lit()));
497 return features;
498 };
499 for feature in value_str.as_str().split(",") {
500 features.push((Symbol::intern(feature), item.span()));
501 }
502 }
503 features
504}
505
506pub(crate) struct TargetFeatureParser;
507
508impl<S: Stage> CombineAttributeParser<S> for TargetFeatureParser {
509 type Item = (Symbol, Span);
510 const PATH: &[Symbol] = &[sym::target_feature];
511 const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {
512 features: items,
513 attr_span: span,
514 was_forced: false,
515 };
516 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\""]);
517
518 fn extend(
519 cx: &mut AcceptContext<'_, '_, S>,
520 args: &ArgParser,
521 ) -> impl IntoIterator<Item = Self::Item> {
522 parse_tf_attribute(cx, args)
523 }
524
525 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
526 Allow(Target::Fn),
527 Allow(Target::Method(MethodKind::Inherent)),
528 Allow(Target::Method(MethodKind::Trait { body: true })),
529 Allow(Target::Method(MethodKind::TraitImpl)),
530 Warn(Target::Statement),
531 Warn(Target::Field),
532 Warn(Target::Arm),
533 Warn(Target::MacroDef),
534 Warn(Target::MacroCall),
535 ]);
536}
537
538pub(crate) struct ForceTargetFeatureParser;
539
540impl<S: Stage> CombineAttributeParser<S> for ForceTargetFeatureParser {
541 type Item = (Symbol, Span);
542 const PATH: &[Symbol] = &[sym::force_target_feature];
543 const SAFETY: AttributeSafety = AttributeSafety::Unsafe { unsafe_since: None };
544 const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {
545 features: items,
546 attr_span: span,
547 was_forced: true,
548 };
549 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\""]);
550 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
551 Allow(Target::Fn),
552 Allow(Target::Method(MethodKind::Inherent)),
553 Allow(Target::Method(MethodKind::Trait { body: true })),
554 Allow(Target::Method(MethodKind::TraitImpl)),
555 ]);
556
557 fn extend(
558 cx: &mut AcceptContext<'_, '_, S>,
559 args: &ArgParser,
560 ) -> impl IntoIterator<Item = Self::Item> {
561 parse_tf_attribute(cx, args)
562 }
563}
564
565pub(crate) struct SanitizeParser;
566
567impl<S: Stage> SingleAttributeParser<S> for SanitizeParser {
568 const PATH: &[Symbol] = &[sym::sanitize];
569
570 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
572
573 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: &[
574 r#"address = "on|off""#,
575 r#"kernel_address = "on|off""#,
576 r#"cfi = "on|off""#,
577 r#"hwaddress = "on|off""#,
578 r#"kernel_hwaddress = "on|off""#,
579 r#"kcfi = "on|off""#,
580 r#"memory = "on|off""#,
581 r#"memtag = "on|off""#,
582 r#"shadow_call_stack = "on|off""#,
583 r#"thread = "on|off""#,
584 r#"realtime = "nonblocking|blocking|caller""#,
585 ]);
586
587 fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
588 let list = cx.expect_list(args, cx.attr_span)?;
589
590 let mut on_set = SanitizerSet::empty();
591 let mut off_set = SanitizerSet::empty();
592 let mut rtsan = None;
593
594 for item in list.mixed() {
595 let Some(item) = item.meta_item() else {
596 cx.adcx().expected_name_value(item.span(), None);
597 continue;
598 };
599
600 let path = item.path().word_sym();
601 let Some(value) = item.args().name_value() else {
602 cx.adcx().expected_name_value(item.span(), path);
603 continue;
604 };
605
606 let mut apply = |s: SanitizerSet| {
607 let is_on = match value.value_as_str() {
608 Some(sym::on) => true,
609 Some(sym::off) => false,
610 Some(_) => {
611 cx.adcx().expected_specific_argument_strings(
612 value.value_span,
613 &[sym::on, sym::off],
614 );
615 return;
616 }
617 None => {
618 cx.adcx()
619 .expected_string_literal(value.value_span, Some(value.value_as_lit()));
620 return;
621 }
622 };
623
624 if is_on {
625 on_set |= s;
626 } else {
627 off_set |= s;
628 }
629 };
630
631 match path {
632 Some(sym::address) | Some(sym::kernel_address) => {
633 apply(SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS)
634 }
635 Some(sym::cfi) => apply(SanitizerSet::CFI),
636 Some(sym::kcfi) => apply(SanitizerSet::KCFI),
637 Some(sym::memory) => apply(SanitizerSet::MEMORY),
638 Some(sym::memtag) => apply(SanitizerSet::MEMTAG),
639 Some(sym::shadow_call_stack) => apply(SanitizerSet::SHADOWCALLSTACK),
640 Some(sym::thread) => apply(SanitizerSet::THREAD),
641 Some(sym::hwaddress) | Some(sym::kernel_hwaddress) => {
642 apply(SanitizerSet::HWADDRESS | SanitizerSet::KERNELHWADDRESS)
643 }
644 Some(sym::realtime) => match value.value_as_str() {
645 Some(sym::nonblocking) => rtsan = Some(RtsanSetting::Nonblocking),
646 Some(sym::blocking) => rtsan = Some(RtsanSetting::Blocking),
647 Some(sym::caller) => rtsan = Some(RtsanSetting::Caller),
648 _ => {
649 cx.adcx().expected_specific_argument_strings(
650 value.value_span,
651 &[sym::nonblocking, sym::blocking, sym::caller],
652 );
653 }
654 },
655 _ => {
656 cx.adcx().expected_specific_argument_strings(
657 item.path().span(),
658 &[
659 sym::address,
660 sym::kernel_address,
661 sym::cfi,
662 sym::kcfi,
663 sym::memory,
664 sym::memtag,
665 sym::shadow_call_stack,
666 sym::thread,
667 sym::hwaddress,
668 sym::kernel_hwaddress,
669 sym::realtime,
670 ],
671 );
672 continue;
673 }
674 }
675 }
676
677 Some(AttributeKind::Sanitize { on_set, off_set, rtsan, span: cx.attr_span })
678 }
679}
680
681pub(crate) struct ThreadLocalParser;
682
683impl<S: Stage> NoArgsAttributeParser<S> for ThreadLocalParser {
684 const PATH: &[Symbol] = &[sym::thread_local];
685 const ALLOWED_TARGETS: AllowedTargets =
686 AllowedTargets::AllowList(&[Allow(Target::Static), Allow(Target::ForeignStatic)]);
687 const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::ThreadLocal;
688}
689
690pub(crate) struct RustcPassIndirectlyInNonRusticAbisParser;
691
692impl<S: Stage> NoArgsAttributeParser<S> for RustcPassIndirectlyInNonRusticAbisParser {
693 const PATH: &[Symbol] = &[sym::rustc_pass_indirectly_in_non_rustic_abis];
694 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]);
695 const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcPassIndirectlyInNonRusticAbis;
696}
697
698pub(crate) struct RustcEiiForeignItemParser;
699
700impl<S: Stage> NoArgsAttributeParser<S> for RustcEiiForeignItemParser {
701 const PATH: &[Symbol] = &[sym::rustc_eii_foreign_item];
702 const ALLOWED_TARGETS: AllowedTargets =
703 AllowedTargets::AllowList(&[Allow(Target::ForeignFn), Allow(Target::ForeignStatic)]);
704 const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcEiiForeignItem;
705}
706
707pub(crate) struct PatchableFunctionEntryParser;
708
709impl<S: Stage> SingleAttributeParser<S> for PatchableFunctionEntryParser {
710 const PATH: &[Symbol] = &[sym::patchable_function_entry];
711 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]);
712 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"]);
713
714 fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
715 let meta_item_list = cx.expect_list(args, cx.attr_span)?;
716
717 let mut prefix = None;
718 let mut entry = None;
719
720 if meta_item_list.len() == 0 {
721 cx.adcx().expected_at_least_one_argument(meta_item_list.span);
722 return None;
723 }
724
725 let mut errored = false;
726
727 for item in meta_item_list.mixed() {
728 let Some(meta_item) = item.meta_item() else {
729 errored = true;
730 cx.adcx().expected_name_value(item.span(), None);
731 continue;
732 };
733
734 let Some(name_value_lit) = meta_item.args().name_value() else {
735 errored = true;
736 cx.adcx().expected_name_value(item.span(), None);
737 continue;
738 };
739
740 let attrib_to_write = match meta_item.ident().map(|ident| ident.name) {
741 Some(sym::prefix_nops) => {
742 if prefix.is_some() {
744 errored = true;
745 cx.adcx().duplicate_key(meta_item.path().span(), sym::prefix_nops);
746 continue;
747 }
748 &mut prefix
749 }
750 Some(sym::entry_nops) => {
751 if entry.is_some() {
753 errored = true;
754 cx.adcx().duplicate_key(meta_item.path().span(), sym::entry_nops);
755 continue;
756 }
757 &mut entry
758 }
759 _ => {
760 errored = true;
761 cx.adcx().expected_specific_argument(
762 meta_item.path().span(),
763 &[sym::prefix_nops, sym::entry_nops],
764 );
765 continue;
766 }
767 };
768
769 let rustc_ast::LitKind::Int(val, _) = name_value_lit.value_as_lit().kind else {
770 errored = true;
771 cx.adcx().expected_integer_literal(name_value_lit.value_span);
772 continue;
773 };
774
775 let Ok(val) = val.get().try_into() else {
776 errored = true;
777 cx.adcx().expected_integer_literal_in_range(
778 name_value_lit.value_span,
779 u8::MIN as isize,
780 u8::MAX as isize,
781 );
782 continue;
783 };
784
785 *attrib_to_write = Some(val);
786 }
787
788 if errored {
789 None
790 } else {
791 Some(AttributeKind::PatchableFunctionEntry {
792 prefix: prefix.unwrap_or(0),
793 entry: entry.unwrap_or(0),
794 })
795 }
796 }
797}