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