1use rustc_feature::AttributeStability;
2use rustc_hir::attrs::{
3 CoverageAttrKind, InstrumentFnAttr, OptimizeAttr, RtsanSetting, SanitizerSet, UsedBy,
4};
5use rustc_session::errors::feature_err;
6use rustc_span::edition::Edition::Edition2024;
7
8use super::prelude::*;
9use crate::attributes::AttributeSafety;
10use crate::session_diagnostics::{
11 EmptyExportName, EmptySection, NakedFunctionIncompatibleAttribute, NullOnExport,
12 NullOnObjcClass, NullOnObjcSelector, NullOnSection, ObjcClassExpectedStringLiteral,
13 ObjcSelectorExpectedStringLiteral, SanitizeInvalidStatic, TargetFeatureOnLangItem,
14};
15use crate::target_checking::Policy::AllowSilent;
16
17pub(crate) struct OptimizeParser;
18
19impl SingleAttributeParser for OptimizeParser {
20 const PATH: &[Symbol] = &[sym::optimize];
21 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[
22 Allow(Target::Fn),
23 Allow(Target::Closure),
24 Allow(Target::Method(MethodKind::Trait { body: true })),
25 Allow(Target::Method(MethodKind::TraitImpl)),
26 Allow(Target::Method(MethodKind::Inherent)),
27 ]);
28 const TEMPLATE: AttributeTemplate = crate::AttributeTemplate {
word: false,
list: Some(&["size", "speed", "none"]),
one_of: &[],
name_value_str: None,
docs: None,
}template!(List: &["size", "speed", "none"]);
29 const STABILITY: AttributeStability = AttributeStability::Unstable {
gate_name: rustc_span::sym::optimize_attribute,
gate_check: rustc_feature::Features::optimize_attribute,
notes: &[],
}unstable!(optimize_attribute);
30
31 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
32 let single = cx.expect_single_element_list(args, cx.attr_span)?;
33
34 let res = match single.meta_item_no_args().and_then(|i| i.path().word().map(|i| i.name)) {
35 Some(sym::size) => OptimizeAttr::Size,
36 Some(sym::speed) => OptimizeAttr::Speed,
37 Some(sym::none) => OptimizeAttr::DoNotOptimize,
38 _ => {
39 cx.adcx()
40 .expected_specific_argument(single.span(), &[sym::size, sym::speed, sym::none]);
41 OptimizeAttr::Default
42 }
43 };
44
45 Some(AttributeKind::Optimize(res, cx.attr_span))
46 }
47}
48
49pub(crate) struct ColdParser;
50
51impl NoArgsAttributeParser for ColdParser {
52 const PATH: &[Symbol] = &[sym::cold];
53 const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn;
54 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowListWarnRest(&[
55 Allow(Target::Fn),
56 Allow(Target::Method(MethodKind::Trait { body: true })),
57 Allow(Target::Method(MethodKind::TraitImpl)),
58 Allow(Target::Method(MethodKind::Inherent)),
59 Allow(Target::ForeignFn),
60 Allow(Target::Closure),
61 ]);
62 const STABILITY: AttributeStability = AttributeStability::Stable;
63 const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::Cold;
64}
65
66pub(crate) struct CoverageParser;
67
68impl SingleAttributeParser for CoverageParser {
69 const PATH: &[Symbol] = &[sym::coverage];
70 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[
71 Allow(Target::Fn),
72 Allow(Target::Closure),
73 Allow(Target::Method(MethodKind::Trait { body: true })),
74 Allow(Target::Method(MethodKind::TraitImpl)),
75 Allow(Target::Method(MethodKind::Inherent)),
76 Allow(Target::Impl { of_trait: true }),
77 Allow(Target::Impl { of_trait: false }),
78 Allow(Target::Mod),
79 Allow(Target::Crate),
80 ]);
81 const TEMPLATE: AttributeTemplate = crate::AttributeTemplate {
word: false,
list: None,
one_of: &[sym::off, sym::on],
name_value_str: None,
docs: None,
}template!(OneOf: &[sym::off, sym::on]);
82 const STABILITY: AttributeStability = AttributeStability::Unstable {
gate_name: rustc_span::sym::coverage_attribute,
gate_check: rustc_feature::Features::coverage_attribute,
notes: &[],
}unstable!(coverage_attribute);
83
84 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
85 let arg = cx.expect_single_element_list(args, cx.attr_span)?;
86
87 let mut fail_incorrect_argument =
88 |span| cx.adcx().expected_specific_argument(span, &[sym::on, sym::off]);
89
90 let Some(arg) = arg.meta_item_no_args() else {
91 fail_incorrect_argument(arg.span());
92 return None;
93 };
94
95 let kind = match arg.path().word_sym() {
96 Some(sym::off) => CoverageAttrKind::Off,
97 Some(sym::on) => CoverageAttrKind::On,
98 None | Some(_) => {
99 fail_incorrect_argument(arg.span());
100 return None;
101 }
102 };
103
104 Some(AttributeKind::Coverage(kind))
105 }
106}
107
108pub(crate) struct ExportNameParser;
109
110impl SingleAttributeParser for ExportNameParser {
111 const PATH: &[rustc_span::Symbol] = &[sym::export_name];
112 const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError;
113 const SAFETY: AttributeSafety = AttributeSafety::Unsafe {
114 note: "the linker's behavior with multiple libraries exporting duplicate symbol names is undefined and Rust cannot provide guarantees when you manually override them",
115 unsafe_since: Some(Edition2024),
116 };
117 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[
118 Allow(Target::Static),
119 Allow(Target::Fn),
120 Allow(Target::Method(MethodKind::Inherent)),
121 Allow(Target::Method(MethodKind::Trait { body: true })),
122 Allow(Target::Method(MethodKind::TraitImpl)),
123 Warn(Target::Field),
124 Warn(Target::Arm),
125 Warn(Target::MacroDef),
126 Warn(Target::MacroCall),
127 ]);
128 const TEMPLATE: AttributeTemplate = crate::AttributeTemplate {
word: false,
list: None,
one_of: &[],
name_value_str: Some(&["name"]),
docs: None,
}template!(NameValueStr: "name");
129 const STABILITY: AttributeStability = AttributeStability::Stable;
130
131 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
132 let nv = cx.expect_name_value(args, cx.attr_span, None)?;
133 let name = cx.expect_string_literal(nv)?;
134 if name.as_str().contains('\0') {
135 cx.emit_err(NullOnExport { span: cx.attr_span });
138 return None;
139 }
140 if name.is_empty() {
141 cx.emit_err(EmptyExportName { span: cx.attr_span });
144 return None;
145 }
146 Some(AttributeKind::ExportName { name, span: cx.attr_span })
147 }
148}
149
150pub(crate) struct RustcObjcClassParser;
151
152impl SingleAttributeParser for RustcObjcClassParser {
153 const PATH: &[rustc_span::Symbol] = &[sym::rustc_objc_class];
154 const ALLOWED_TARGETS: AllowedTargets<'_> =
155 AllowedTargets::AllowList(&[Allow(Target::ForeignStatic)]);
156 const TEMPLATE: AttributeTemplate = crate::AttributeTemplate {
word: false,
list: None,
one_of: &[],
name_value_str: Some(&["ClassName"]),
docs: None,
}template!(NameValueStr: "ClassName");
157 const STABILITY: AttributeStability = AttributeStability::Unstable {
gate_name: rustc_span::sym::rustc_attrs,
gate_check: rustc_feature::Features::rustc_attrs,
notes: &[],
}unstable!(rustc_attrs);
158
159 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
160 let nv = cx.expect_name_value(args, cx.attr_span, None)?;
161 let Some(classname) = nv.value_as_str() else {
162 cx.emit_err(ObjcClassExpectedStringLiteral { span: nv.value_span });
166 return None;
167 };
168 if classname.as_str().contains('\0') {
169 cx.emit_err(NullOnObjcClass { span: nv.value_span });
172 return None;
173 }
174 Some(AttributeKind::RustcObjcClass { classname })
175 }
176}
177
178pub(crate) struct RustcObjcSelectorParser;
179
180impl SingleAttributeParser for RustcObjcSelectorParser {
181 const PATH: &[rustc_span::Symbol] = &[sym::rustc_objc_selector];
182 const ALLOWED_TARGETS: AllowedTargets<'_> =
183 AllowedTargets::AllowList(&[Allow(Target::ForeignStatic)]);
184 const TEMPLATE: AttributeTemplate = crate::AttributeTemplate {
word: false,
list: None,
one_of: &[],
name_value_str: Some(&["methodName"]),
docs: None,
}template!(NameValueStr: "methodName");
185 const STABILITY: AttributeStability = AttributeStability::Unstable {
gate_name: rustc_span::sym::rustc_attrs,
gate_check: rustc_feature::Features::rustc_attrs,
notes: &[],
}unstable!(rustc_attrs);
186
187 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
188 let nv = cx.expect_name_value(args, cx.attr_span, None)?;
189 let Some(methname) = nv.value_as_str() else {
190 cx.emit_err(ObjcSelectorExpectedStringLiteral { span: nv.value_span });
194 return None;
195 };
196 if methname.as_str().contains('\0') {
197 cx.emit_err(NullOnObjcSelector { span: nv.value_span });
200 return None;
201 }
202 Some(AttributeKind::RustcObjcSelector { methname })
203 }
204}
205
206#[derive(#[automatically_derived]
impl ::core::default::Default for NakedParser {
#[inline]
fn default() -> NakedParser {
NakedParser { span: ::core::default::Default::default() }
}
}Default)]
207pub(crate) struct NakedParser {
208 span: Option<Span>,
209}
210
211impl AttributeParser for NakedParser {
212 const ATTRIBUTES: AcceptMapping<Self> =
213 &[(&[sym::naked], crate::AttributeTemplate {
word: true,
list: None,
one_of: &[],
name_value_str: None,
docs: None,
}template!(Word), AttributeStability::Stable, |this, cx, args| {
214 let Some(()) = cx.expect_no_args(args) else {
215 return;
216 };
217
218 if let Some(earlier) = this.span {
219 let span = cx.attr_span;
220 cx.warn_unused_duplicate(earlier, span);
221 } else {
222 this.span = Some(cx.attr_span);
223 }
224 })];
225 const SAFETY: AttributeSafety = AttributeSafety::Unsafe {
226 note: "the `#[naked]` attribute adds the safety obligation that the function's body must respect the function’s calling convention, uphold its signature, and either return or diverge (i.e., not fall through past the end of the assembly code).",
227 unsafe_since: None,
228 };
229 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[
230 Allow(Target::Fn),
231 Allow(Target::Method(MethodKind::Inherent)),
232 Allow(Target::Method(MethodKind::Trait { body: true })),
233 Allow(Target::Method(MethodKind::TraitImpl)),
234 Warn(Target::MacroCall),
235 ]);
236
237 fn finalize(self, cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {
238 const ALLOW_LIST: &[rustc_span::Symbol] = &[
251 sym::cfg_trace,
253 sym::cfg_attr_trace,
254 sym::test,
256 sym::ignore,
257 sym::should_panic,
258 sym::bench,
259 sym::allow,
261 sym::warn,
262 sym::deny,
263 sym::forbid,
264 sym::deprecated,
265 sym::must_use,
266 sym::cold,
268 sym::export_name,
269 sym::link_section,
270 sym::linkage,
271 sym::no_mangle,
272 sym::instruction_set,
273 sym::repr,
274 sym::rustc_std_internal_symbol,
275 sym::rustc_align,
277 sym::rustc_align_static,
278 sym::naked,
280 sym::doc,
282 ];
283
284 let span = self.span?;
285
286 let Some(tools) = cx.tools else {
287 {
::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");
288 };
289
290 'outer: for other_attr in cx.all_attrs {
292 for allowed_attr in ALLOW_LIST {
293 if other_attr
294 .segments()
295 .next()
296 .is_some_and(|i| tools.iter().any(|tool| tool.name == i.name))
297 {
298 continue 'outer;
301 }
302 if other_attr.word_is(*allowed_attr) {
303 continue 'outer;
306 }
307
308 if other_attr.word_is(sym::target_feature) {
309 if !cx.features().naked_functions_target_feature() {
310 feature_err(
311 &cx.sess(),
312 sym::naked_functions_target_feature,
313 other_attr.span(),
314 "`#[target_feature(/* ... */)]` is currently unstable on `#[naked]` functions",
315 ).emit();
316 }
317
318 continue 'outer;
319 }
320 }
321
322 cx.emit_err(NakedFunctionIncompatibleAttribute {
323 span: other_attr.span(),
324 naked_span: span,
325 attr: other_attr.get_attribute_path().to_string(),
326 });
327 }
328
329 Some(AttributeKind::Naked(span))
330 }
331}
332
333pub(crate) struct TrackCallerParser;
334impl NoArgsAttributeParser for TrackCallerParser {
335 const PATH: &[Symbol] = &[sym::track_caller];
336 const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn;
337 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[
338 Allow(Target::Fn),
339 Allow(Target::Method(MethodKind::Inherent)),
340 Allow(Target::Method(MethodKind::Trait { body: true })),
341 Allow(Target::Method(MethodKind::TraitImpl)),
342 Allow(Target::Method(MethodKind::Trait { body: false })), Allow(Target::ForeignFn),
344 Allow(Target::Closure),
345 Warn(Target::MacroDef),
346 Warn(Target::Arm),
347 Warn(Target::Field),
348 Warn(Target::MacroCall),
349 ]);
350 const STABILITY: AttributeStability = AttributeStability::Stable;
351 const CREATE: fn(Span) -> AttributeKind = AttributeKind::TrackCaller;
352}
353
354pub(crate) struct NoMangleParser;
355impl NoArgsAttributeParser for NoMangleParser {
356 const PATH: &[Symbol] = &[sym::no_mangle];
357 const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn;
358 const SAFETY: AttributeSafety = AttributeSafety::Unsafe {
359 note: "the linker's behavior with multiple libraries exporting duplicate symbol names is undefined and Rust cannot provide guarantees when you manually override them",
360 unsafe_since: Some(Edition2024),
361 };
362 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowListWarnRest(&[
363 Allow(Target::Fn),
364 Allow(Target::Static),
365 Allow(Target::Method(MethodKind::Inherent)),
366 Allow(Target::Method(MethodKind::TraitImpl)),
367 AllowSilent(Target::Const), Error(Target::Closure),
369 ]);
370 const STABILITY: AttributeStability = AttributeStability::Stable;
371 const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoMangle;
372}
373
374#[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)]
375pub(crate) struct UsedParser {
376 first_compiler: Option<Span>,
377 first_linker: Option<Span>,
378 first_default: Option<Span>,
379}
380
381impl AttributeParser for UsedParser {
386 const ATTRIBUTES: AcceptMapping<Self> = &[(
387 &[sym::used],
388 crate::AttributeTemplate {
word: true,
list: Some(&["compiler", "linker"]),
one_of: &[],
name_value_str: None,
docs: None,
}template!(Word, List: &["compiler", "linker"]),
389 AttributeStability::Stable,
390 |group: &mut Self, cx, args| {
391 let used_by = match args {
392 ArgParser::NoArgs => UsedBy::Default,
393 ArgParser::List(list) => {
394 let Some(l) = cx.expect_single(list) else {
395 return;
396 };
397
398 match l.meta_item_no_args().and_then(|i| i.path().word_sym()) {
399 Some(sym::compiler) => {
400 if !cx.features().used_with_arg() {
401 feature_err(
402 &cx.sess(),
403 sym::used_with_arg,
404 cx.attr_span,
405 "`#[used(compiler)]` is currently unstable",
406 )
407 .emit();
408 }
409 UsedBy::Compiler
410 }
411 Some(sym::linker) => {
412 if !cx.features().used_with_arg() {
413 feature_err(
414 &cx.sess(),
415 sym::used_with_arg,
416 cx.attr_span,
417 "`#[used(linker)]` is currently unstable",
418 )
419 .emit();
420 }
421 UsedBy::Linker
422 }
423 _ => {
424 cx.adcx().expected_specific_argument(
425 l.span(),
426 &[sym::compiler, sym::linker],
427 );
428 return;
429 }
430 }
431 }
432 ArgParser::NameValue(_) => return,
433 };
434
435 let attr_span = cx.attr_span;
436
437 let target = match used_by {
441 UsedBy::Compiler => &mut group.first_compiler,
442 UsedBy::Linker => {
443 if let Some(prev) = group.first_default {
444 cx.warn_unused_duplicate(prev, attr_span);
445 return;
446 }
447 &mut group.first_linker
448 }
449 UsedBy::Default => {
450 if let Some(prev) = group.first_linker {
451 cx.warn_unused_duplicate(prev, attr_span);
452 return;
453 }
454 &mut group.first_default
455 }
456 };
457
458 if let Some(prev) = *target {
459 cx.warn_unused_duplicate(prev, attr_span);
460 } else {
461 *target = Some(attr_span);
462 }
463 },
464 )];
465 const ALLOWED_TARGETS: AllowedTargets<'_> =
466 AllowedTargets::AllowList(&[Allow(Target::Static), Warn(Target::MacroCall)]);
467
468 fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {
469 Some(match (self.first_compiler, self.first_linker, self.first_default) {
472 (_, Some(_), _) => AttributeKind::Used { used_by: UsedBy::Linker },
473 (Some(_), _, _) => AttributeKind::Used { used_by: UsedBy::Compiler },
474 (_, _, Some(_)) => AttributeKind::Used { used_by: UsedBy::Default },
475 (None, None, None) => return None,
476 })
477 }
478}
479
480fn parse_tf_attribute(
481 cx: &mut AcceptContext<'_, '_>,
482 args: &ArgParser,
483) -> impl IntoIterator<Item = (Symbol, Span)> {
484 let mut features = Vec::new();
485 let Some(list) = cx.expect_list(args, cx.attr_span) else {
486 return features;
487 };
488 if list.is_empty() {
489 let attr_span = cx.attr_span;
490 cx.adcx().warn_empty_attribute(attr_span);
491 return features;
492 }
493 for item in list.mixed() {
494 let Some((ident, value)) = cx.expect_name_value(item, item.span(), Some(sym::enable))
495 else {
496 return features;
497 };
498
499 if ident.name != sym::enable {
501 cx.adcx().expected_specific_argument(ident.span, &[sym::enable]);
502 return features;
503 }
504
505 let Some(value_str) = cx.expect_string_literal(value) else {
507 return features;
508 };
509 for feature in value_str.as_str().split(",") {
510 features.push((Symbol::intern(feature), item.span()));
511 }
512 }
513 features
514}
515
516pub(crate) struct TargetFeatureParser;
517
518impl CombineAttributeParser for TargetFeatureParser {
519 type Item = (Symbol, Span);
520 const PATH: &[Symbol] = &[sym::target_feature];
521 const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {
522 features: items,
523 attr_span: span,
524 was_forced: false,
525 };
526 const TEMPLATE: AttributeTemplate = crate::AttributeTemplate {
word: false,
list: Some(&["enable = \"feat1, feat2\""]),
one_of: &[],
name_value_str: None,
docs: None,
}template!(List: &["enable = \"feat1, feat2\""]);
527 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[
528 Allow(Target::Fn),
529 Allow(Target::Method(MethodKind::Inherent)),
530 Allow(Target::Method(MethodKind::Trait { body: true })),
531 Allow(Target::Method(MethodKind::TraitImpl)),
532 Warn(Target::Statement),
533 Warn(Target::Field),
534 Warn(Target::Arm),
535 Warn(Target::MacroDef),
536 Warn(Target::MacroCall),
537 ]);
538 const STABILITY: AttributeStability = AttributeStability::Stable;
539
540 fn extend(
541 cx: &mut AcceptContext<'_, '_>,
542 args: &ArgParser,
543 ) -> impl IntoIterator<Item = Self::Item> {
544 parse_tf_attribute(cx, args)
545 }
546
547 fn finalize_check(cx: &FinalizeContext<'_, '_>, attr_span: Span) {
548 if !cx.sess().target.is_like_wasm && !cx.sess().opts.actually_rustdoc {
551 let lang_kind = cx
553 .all_attrs
554 .iter()
555 .find_map(|a| [sym::panic_handler, sym::lang].into_iter().find(|&s| a.word_is(s)));
556 if let Some(kind) = lang_kind {
557 cx.emit_err(TargetFeatureOnLangItem { attr_span, kind, item_span: cx.target_span });
558 }
559 }
560 }
561}
562
563pub(crate) struct ForceTargetFeatureParser;
564
565impl CombineAttributeParser for ForceTargetFeatureParser {
566 type Item = (Symbol, Span);
567 const PATH: &[Symbol] = &[sym::force_target_feature];
568 const SAFETY: AttributeSafety = AttributeSafety::Unsafe {
569 note: "a function with the signature of the function the attribute is applied to must only be callable if the force-enabled features are guaranteed to be present",
570 unsafe_since: None,
571 };
572 const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {
573 features: items,
574 attr_span: span,
575 was_forced: true,
576 };
577 const TEMPLATE: AttributeTemplate = crate::AttributeTemplate {
word: false,
list: Some(&["enable = \"feat1, feat2\""]),
one_of: &[],
name_value_str: None,
docs: None,
}template!(List: &["enable = \"feat1, feat2\""]);
578 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[
579 Allow(Target::Fn),
580 Allow(Target::Method(MethodKind::Inherent)),
581 Allow(Target::Method(MethodKind::Trait { body: true })),
582 Allow(Target::Method(MethodKind::TraitImpl)),
583 ]);
584 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);
585
586 fn extend(
587 cx: &mut AcceptContext<'_, '_>,
588 args: &ArgParser,
589 ) -> impl IntoIterator<Item = Self::Item> {
590 parse_tf_attribute(cx, args)
591 }
592}
593
594pub(crate) struct InstrumentFnParser;
595
596impl SingleAttributeParser for InstrumentFnParser {
597 const PATH: &[Symbol] = &[sym::instrument_fn];
598 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[
599 Allow(Target::Fn),
600 Allow(Target::Method(MethodKind::Inherent)),
601 Allow(Target::Method(MethodKind::Trait { body: true })),
602 Allow(Target::Method(MethodKind::TraitImpl)),
603 ]);
604 const TEMPLATE: AttributeTemplate = crate::AttributeTemplate {
word: false,
list: None,
one_of: &[],
name_value_str: Some(&["on|off"]),
docs: None,
}template!(NameValueStr: "on|off");
605 const STABILITY: AttributeStability = AttributeStability::Unstable {
gate_name: rustc_span::sym::instrument_fn,
gate_check: rustc_feature::Features::instrument_fn,
notes: &[],
}unstable!(instrument_fn);
606
607 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
608 let instrument = match args {
609 ArgParser::NameValue(nv) => match nv.value_as_str() {
610 Some(sym::on) => Some(AttributeKind::InstrumentFn(InstrumentFnAttr::On)),
611 Some(sym::off) => Some(AttributeKind::InstrumentFn(InstrumentFnAttr::Off)),
612 _ => {
613 cx.adcx()
614 .expected_specific_argument_strings(nv.value_span, &[sym::on, sym::off]);
615 None
616 }
617 },
618 ArgParser::List(l) => {
619 cx.adcx().expected_single_argument(l.span, l.len());
620 None
621 }
622 ArgParser::NoArgs => {
623 let span = cx.attr_span;
624 cx.adcx().expected_specific_argument_strings(span, &[sym::on, sym::off]);
625 None
626 }
627 };
628 instrument
629 }
630}
631
632pub(crate) struct SanitizeParser;
633
634impl SingleAttributeParser for SanitizeParser {
635 const PATH: &[Symbol] = &[sym::sanitize];
636 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[
637 Allow(Target::Fn),
638 Allow(Target::Closure),
639 Allow(Target::Method(MethodKind::Inherent)),
640 Allow(Target::Method(MethodKind::Trait { body: true })),
641 Allow(Target::Method(MethodKind::TraitImpl)),
642 Allow(Target::Impl { of_trait: false }),
643 Allow(Target::Impl { of_trait: true }),
644 Allow(Target::Mod),
645 Allow(Target::Crate),
646 Allow(Target::Static),
647 ]);
648 const TEMPLATE: AttributeTemplate = crate::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: &[
649 r#"address = "on|off""#,
650 r#"kernel_address = "on|off""#,
651 r#"cfi = "on|off""#,
652 r#"hwaddress = "on|off""#,
653 r#"kernel_hwaddress = "on|off""#,
654 r#"kcfi = "on|off""#,
655 r#"memory = "on|off""#,
656 r#"memtag = "on|off""#,
657 r#"shadow_call_stack = "on|off""#,
658 r#"thread = "on|off""#,
659 r#"realtime = "nonblocking|blocking|caller""#,
660 ]);
661 const STABILITY: AttributeStability = AttributeStability::Unstable {
gate_name: rustc_span::sym::sanitize,
gate_check: rustc_feature::Features::sanitize,
notes: &[],
}unstable!(sanitize);
662
663 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
664 let list = cx.expect_list(args, cx.attr_span)?;
665
666 let mut on_set = SanitizerSet::empty();
667 let mut off_set = SanitizerSet::empty();
668 let mut rtsan = None;
669
670 for item in list.mixed() {
671 let Some((ident, value)) = cx.expect_name_value(item, item.span(), None) else {
672 continue;
673 };
674
675 let mut apply = |s: SanitizerSet| {
676 let is_on = match value.value_as_str() {
677 Some(sym::on) => true,
678 Some(sym::off) => false,
679 Some(_) => {
680 cx.adcx().expected_specific_argument_strings(
681 value.value_span,
682 &[sym::on, sym::off],
683 );
684 return;
685 }
686 None => {
687 cx.adcx().expected_specific_argument_strings(
688 value.value_span,
689 &[sym::on, sym::off],
690 );
691 return;
692 }
693 };
694
695 if is_on {
696 on_set |= s;
697 } else {
698 off_set |= s;
699 }
700 };
701
702 match ident.name {
703 sym::address | sym::kernel_address => {
704 apply(SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS)
705 }
706 sym::cfi => apply(SanitizerSet::CFI),
707 sym::kcfi => apply(SanitizerSet::KCFI),
708 sym::memory => apply(SanitizerSet::MEMORY),
709 sym::memtag => apply(SanitizerSet::MEMTAG),
710 sym::shadow_call_stack => apply(SanitizerSet::SHADOWCALLSTACK),
711 sym::thread => apply(SanitizerSet::THREAD),
712 sym::hwaddress | sym::kernel_hwaddress => {
713 apply(SanitizerSet::HWADDRESS | SanitizerSet::KERNELHWADDRESS)
714 }
715 sym::realtime => match value.value_as_str() {
716 Some(sym::nonblocking) => rtsan = Some(RtsanSetting::Nonblocking),
717 Some(sym::blocking) => rtsan = Some(RtsanSetting::Blocking),
718 Some(sym::caller) => rtsan = Some(RtsanSetting::Caller),
719 _ => {
720 cx.adcx().expected_specific_argument_strings(
721 value.value_span,
722 &[sym::nonblocking, sym::blocking, sym::caller],
723 );
724 }
725 },
726 _ => {
727 cx.adcx().expected_specific_argument_strings(
728 ident.span,
729 &[
730 sym::address,
731 sym::kernel_address,
732 sym::cfi,
733 sym::kcfi,
734 sym::memory,
735 sym::memtag,
736 sym::shadow_call_stack,
737 sym::thread,
738 sym::hwaddress,
739 sym::kernel_hwaddress,
740 sym::realtime,
741 ],
742 );
743 continue;
744 }
745 }
746 }
747
748 let all_set_except_address =
750 (on_set | off_set) & !(SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS);
751 if cx.target == Target::Static
752 && let Some(set) = all_set_except_address.iter().next()
753 {
754 cx.emit_err(SanitizeInvalidStatic {
755 span: cx.attr_span,
756 field: set.as_str().expect("Since this `SanitizerSet` is returned from an iterator, exactly one field is set")
757 });
758 }
759
760 Some(AttributeKind::Sanitize { on_set, off_set, rtsan, span: cx.attr_span })
761 }
762}
763
764pub(crate) struct ThreadLocalParser;
765
766impl NoArgsAttributeParser for ThreadLocalParser {
767 const PATH: &[Symbol] = &[sym::thread_local];
768 const ALLOWED_TARGETS: AllowedTargets<'_> =
769 AllowedTargets::AllowList(&[Allow(Target::Static), Allow(Target::ForeignStatic)]);
770 const STABILITY: AttributeStability = AttributeStability::Unstable {
gate_name: rustc_span::sym::thread_local,
gate_check: rustc_feature::Features::thread_local,
notes: &[],
}unstable!(thread_local);
771 const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::ThreadLocal;
772}
773
774pub(crate) struct RustcPassIndirectlyInNonRusticAbisParser;
775
776impl NoArgsAttributeParser for RustcPassIndirectlyInNonRusticAbisParser {
777 const PATH: &[Symbol] = &[sym::rustc_pass_indirectly_in_non_rustic_abis];
778 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Struct)]);
779 const STABILITY: AttributeStability = AttributeStability::Unstable {
gate_name: rustc_span::sym::rustc_attrs,
gate_check: rustc_feature::Features::rustc_attrs,
notes: &[],
}unstable!(rustc_attrs);
780 const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcPassIndirectlyInNonRusticAbis;
781}
782
783pub(crate) struct RustcEiiForeignItemParser;
784
785impl NoArgsAttributeParser for RustcEiiForeignItemParser {
786 const PATH: &[Symbol] = &[sym::rustc_eii_foreign_item];
787 const ALLOWED_TARGETS: AllowedTargets<'_> =
788 AllowedTargets::AllowList(&[Allow(Target::ForeignFn), Allow(Target::ForeignStatic)]);
789 const STABILITY: AttributeStability = AttributeStability::Unstable {
gate_name: rustc_span::sym::eii_internals,
gate_check: rustc_feature::Features::eii_internals,
notes: &[],
}unstable!(eii_internals);
790 const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcEiiForeignItem;
791}
792
793pub(crate) struct PatchableFunctionEntryParser;
794
795impl SingleAttributeParser for PatchableFunctionEntryParser {
796 const PATH: &[Symbol] = &[sym::patchable_function_entry];
797 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Fn)]);
798 const TEMPLATE: AttributeTemplate =
799 crate::AttributeTemplate {
word: false,
list: Some(&["prefix_nops = m, entry_nops = n, section = \"section\""]),
one_of: &[],
name_value_str: None,
docs: None,
}template!(List: &["prefix_nops = m, entry_nops = n, section = \"section\""]);
800 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);
801
802 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
803 let meta_item_list = cx.expect_list(args, cx.attr_span)?;
804
805 let mut prefix = None;
806 let mut entry = None;
807 let mut section = None;
808
809 if meta_item_list.len() == 0 {
810 cx.adcx().expected_at_least_one_argument(meta_item_list.span);
811 return None;
812 }
813
814 for item in meta_item_list.mixed() {
815 let Some((ident, value)) = cx.expect_name_value(item, item.span(), None) else {
816 return None;
817 };
818
819 let attrib_to_write = match ident.name {
820 sym::prefix_nops => {
821 if prefix.is_some() {
823 cx.adcx().duplicate_key(ident.span, sym::prefix_nops);
824 return None;
825 }
826 &mut prefix
827 }
828 sym::entry_nops => {
829 if entry.is_some() {
831 cx.adcx().duplicate_key(ident.span, sym::entry_nops);
832 return None;
833 }
834 &mut entry
835 }
836 sym::section => {
837 if section.is_some() {
839 cx.adcx().duplicate_key(ident.span, sym::section);
840 return None;
841 }
842 let Some(value_str) = value.value_as_str() else {
844 cx.adcx().expect_string_literal(value);
845 return None;
846 };
847 if value_str.as_str().contains('\0') {
849 cx.emit_err(NullOnSection { span: value.value_span });
850 }
851 if value_str.is_empty() {
854 cx.emit_err(EmptySection { span: value.value_span });
855 }
856 section = Some(value_str);
857 continue;
859 }
860 _ => {
861 cx.adcx().expected_specific_argument(
862 ident.span,
863 &[sym::prefix_nops, sym::entry_nops],
864 );
865 return None;
866 }
867 };
868
869 let rustc_ast::LitKind::Int(val, _) = value.value_as_lit().kind else {
870 cx.adcx().expected_integer_literal(value.value_span);
871 return None;
872 };
873
874 let Ok(val) = val.get().try_into() else {
875 cx.adcx().expected_integer_literal_in_range(
876 value.value_span,
877 u8::MIN as isize,
878 u8::MAX as isize,
879 );
880 return None;
881 };
882
883 *attrib_to_write = Some(val);
884 }
885
886 Some(AttributeKind::PatchableFunctionEntry { prefix, entry, section })
887 }
888}