1use rustc_attr_ir::{
2 CoverageAttrKind, InstrumentFnAttr, OptimizeAttr, RtsanSetting, UsedBy, find_attr,
3};
4use rustc_feature::AttributeStability;
5use rustc_session::diagnostics::feature_err;
6use rustc_span::edition::Edition::Edition2024;
7use rustc_structures::SanitizerSet;
8
9use super::prelude::*;
10use crate::attributes::AttributeSafety;
11use crate::context::FinalizeCheckFn;
12use crate::diagnostics::{
13 EmptyExportName, EmptySection, NakedFunctionIncompatibleAttribute, NullOnExport,
14 NullOnObjcClass, NullOnObjcSelector, NullOnSection, ObjcClassExpectedStringLiteral,
15 ObjcSelectorExpectedStringLiteral, SanitizeInvalidStatic, TargetFeatureOnLangItem,
16 TrackCallerOnLangItem,
17};
18use crate::target_checking::Policy::AllowSilent;
19
20pub(crate) struct OptimizeParser;
21
22impl SingleAttributeParser for OptimizeParser {
23 const PATH: &[Symbol] = &[sym::optimize];
24 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[
25 Allow(Target::Fn),
26 Allow(Target::Closure),
27 Allow(Target::Method(MethodKind::Trait { body: true })),
28 Allow(Target::Method(MethodKind::TraitImpl)),
29 Allow(Target::Method(MethodKind::Inherent)),
30 ]);
31 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"]);
32 const STABILITY: AttributeStability = AttributeStability::Stable;
33
34 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
35 let single = cx.expect_single_element_list(args, cx.attr_span)?;
36
37 let res = match single.meta_item_no_args().and_then(|i| i.path().word().map(|i| i.name)) {
38 Some(sym::size) => OptimizeAttr::Size,
39 Some(sym::speed) => OptimizeAttr::Speed,
40 Some(sym::none) => OptimizeAttr::DoNotOptimize,
41 _ => {
42 cx.adcx()
43 .expected_specific_argument(single.span(), &[sym::size, sym::speed, sym::none]);
44 OptimizeAttr::Default
45 }
46 };
47
48 Some(AttributeKind::Optimize(res, cx.attr_span))
49 }
50}
51
52pub(crate) struct ColdParser;
53
54impl NoArgsAttributeParser for ColdParser {
55 const PATH: &[Symbol] = &[sym::cold];
56 const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn;
57 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowListWarnRest(&[
58 Allow(Target::Fn),
59 Allow(Target::Method(MethodKind::Trait { body: true })),
60 Allow(Target::Method(MethodKind::TraitImpl)),
61 Allow(Target::Method(MethodKind::Inherent)),
62 Allow(Target::ForeignFn),
63 Allow(Target::Closure),
64 ]);
65 const STABILITY: AttributeStability = AttributeStability::Stable;
66 const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::Cold;
67}
68
69pub(crate) struct CoverageParser;
70
71impl SingleAttributeParser for CoverageParser {
72 const PATH: &[Symbol] = &[sym::coverage];
73 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[
74 Allow(Target::Fn),
75 Allow(Target::Closure),
76 Allow(Target::Method(MethodKind::Trait { body: true })),
77 Allow(Target::Method(MethodKind::TraitImpl)),
78 Allow(Target::Method(MethodKind::Inherent)),
79 Allow(Target::Impl { of_trait: true }),
80 Allow(Target::Impl { of_trait: false }),
81 Allow(Target::Mod),
82 Allow(Target::Crate),
83 ]);
84 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]);
85 const STABILITY: AttributeStability = {
_ = rustc_feature::Features::coverage_attribute;
AttributeStability::Unstable {
gate_name: rustc_span::sym::coverage_attribute,
notes: &[],
}
}unstable!(coverage_attribute);
86
87 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
88 let arg = cx.expect_single_element_list(args, cx.attr_span)?;
89
90 let mut fail_incorrect_argument =
91 |span| cx.adcx().expected_specific_argument(span, &[sym::on, sym::off]);
92
93 let Some(arg) = arg.meta_item_no_args() else {
94 fail_incorrect_argument(arg.span());
95 return None;
96 };
97
98 let kind = match arg.path().word_sym() {
99 Some(sym::off) => CoverageAttrKind::Off,
100 Some(sym::on) => CoverageAttrKind::On,
101 None | Some(_) => {
102 fail_incorrect_argument(arg.span());
103 return None;
104 }
105 };
106
107 Some(AttributeKind::Coverage(kind))
108 }
109}
110
111pub(crate) struct ExportNameParser;
112
113impl SingleAttributeParser for ExportNameParser {
114 const PATH: &[rustc_span::Symbol] = &[sym::export_name];
115 const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError;
116 const SAFETY: AttributeSafety = AttributeSafety::Unsafe {
117 note: "the linker's behavior with multiple libraries exporting duplicate symbol names is undefined and Rust cannot provide guarantees when you manually override them",
118 unsafe_since: Some(Edition2024),
119 };
120 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[
121 Allow(Target::Static),
122 Allow(Target::Fn),
123 Allow(Target::Method(MethodKind::Inherent)),
124 Allow(Target::Method(MethodKind::Trait { body: true })),
125 Allow(Target::Method(MethodKind::TraitImpl)),
126 Warn(Target::Field),
127 Warn(Target::Arm),
128 Warn(Target::MacroDef),
129 Warn(Target::MacroCall),
130 ]);
131 const TEMPLATE: AttributeTemplate = crate::AttributeTemplate {
word: false,
list: None,
one_of: &[],
name_value_str: Some(&["name"]),
docs: None,
}template!(NameValueStr: "name");
132 const STABILITY: AttributeStability = AttributeStability::Stable;
133
134 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
135 let nv = cx.expect_name_value(args, cx.attr_span, None)?;
136 let name = cx.expect_string_literal(nv)?;
137 if name.as_str().contains('\0') {
138 cx.emit_err(NullOnExport { span: cx.attr_span });
141 return None;
142 }
143 if name.is_empty() {
144 cx.emit_err(EmptyExportName { span: cx.attr_span });
147 return None;
148 }
149 Some(AttributeKind::ExportName { name, span: cx.attr_span })
150 }
151}
152
153pub(crate) struct RustcObjcClassParser;
154
155impl SingleAttributeParser for RustcObjcClassParser {
156 const PATH: &[rustc_span::Symbol] = &[sym::rustc_objc_class];
157 const ALLOWED_TARGETS: AllowedTargets<'_> =
158 AllowedTargets::AllowList(&[Allow(Target::ForeignStatic)]);
159 const TEMPLATE: AttributeTemplate = crate::AttributeTemplate {
word: false,
list: None,
one_of: &[],
name_value_str: Some(&["ClassName"]),
docs: None,
}template!(NameValueStr: "ClassName");
160 const STABILITY: AttributeStability = {
_ = rustc_feature::Features::rustc_attrs;
AttributeStability::Unstable {
gate_name: rustc_span::sym::rustc_attrs,
notes: &[],
}
}unstable!(rustc_attrs);
161
162 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
163 let nv = cx.expect_name_value(args, cx.attr_span, None)?;
164 let Some(classname) = nv.value_as_str() else {
165 cx.emit_err(ObjcClassExpectedStringLiteral { span: nv.value_span });
169 return None;
170 };
171 if classname.as_str().contains('\0') {
172 cx.emit_err(NullOnObjcClass { span: nv.value_span });
175 return None;
176 }
177 Some(AttributeKind::RustcObjcClass { classname })
178 }
179}
180
181pub(crate) struct RustcObjcSelectorParser;
182
183impl SingleAttributeParser for RustcObjcSelectorParser {
184 const PATH: &[rustc_span::Symbol] = &[sym::rustc_objc_selector];
185 const ALLOWED_TARGETS: AllowedTargets<'_> =
186 AllowedTargets::AllowList(&[Allow(Target::ForeignStatic)]);
187 const TEMPLATE: AttributeTemplate = crate::AttributeTemplate {
word: false,
list: None,
one_of: &[],
name_value_str: Some(&["methodName"]),
docs: None,
}template!(NameValueStr: "methodName");
188 const STABILITY: AttributeStability = {
_ = rustc_feature::Features::rustc_attrs;
AttributeStability::Unstable {
gate_name: rustc_span::sym::rustc_attrs,
notes: &[],
}
}unstable!(rustc_attrs);
189
190 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
191 let nv = cx.expect_name_value(args, cx.attr_span, None)?;
192 let Some(methname) = nv.value_as_str() else {
193 cx.emit_err(ObjcSelectorExpectedStringLiteral { span: nv.value_span });
197 return None;
198 };
199 if methname.as_str().contains('\0') {
200 cx.emit_err(NullOnObjcSelector { span: nv.value_span });
203 return None;
204 }
205 Some(AttributeKind::RustcObjcSelector { methname })
206 }
207}
208
209#[derive(#[automatically_derived]impl ::core::default::Default for NakedParser {
#[inline]
fn default() -> Self {
NakedParser { span: ::core::default::Default::default() }
}
}Default)]
210pub(crate) struct NakedParser {
211 span: Option<Span>,
212}
213
214impl AttributeParser for NakedParser {
215 const ATTRIBUTES: AcceptMapping<Self> =
216 &[(&[sym::naked], crate::AttributeTemplate {
word: true,
list: None,
one_of: &[],
name_value_str: None,
docs: None,
}template!(Word), AttributeStability::Stable, |this, cx, args| {
217 let Some(()) = cx.expect_no_args(args) else {
218 return;
219 };
220
221 if let Some(earlier) = this.span {
222 let span = cx.attr_span;
223 cx.warn_unused_duplicate(earlier, span);
224 } else {
225 this.span = Some(cx.attr_span);
226 }
227 })];
228 const SAFETY: AttributeSafety = AttributeSafety::Unsafe {
229 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).",
230 unsafe_since: None,
231 };
232 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[
233 Allow(Target::Fn),
234 Allow(Target::Method(MethodKind::Inherent)),
235 Allow(Target::Method(MethodKind::Trait { body: true })),
236 Allow(Target::Method(MethodKind::TraitImpl)),
237 Warn(Target::MacroCall),
238 ]);
239
240 fn finalize(self, cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {
241 const ALLOW_LIST: &[rustc_span::Symbol] = &[
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.attr_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 fn deferred_finalize_check(&self) -> Option<(FinalizeCheckFn, Span)> {
333 Some((
337 |cx, _| match cx.target {
338 Target::Fn
339 | Target::Method(
340 MethodKind::Trait { body: true } | MethodKind::TraitImpl | MethodKind::Inherent,
341 ) => {
342 let fn_sig =
343 cx.ast_target.get_fn_sig().expect("missing fn signature for AST target");
344 let Some(abi) = cx.ast_target.get_abi() else {
345 return;
346 };
347
348 if abi.is_rustic_abi() && !cx.features().naked_functions_rustic_abi() {
349 feature_err(
350 cx.sess(),
351 sym::naked_functions_rustic_abi,
352 fn_sig.span,
353 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("`#[naked]` is currently unstable on `extern {0}` functions",
abi))
})format!(
354 "`#[naked]` is currently unstable on `extern {abi}` functions",
355 ),
356 )
357 .emit();
358 }
359 }
360 _ => {}
361 },
362 self.span?,
363 ))
364 }
365}
366
367pub(crate) struct TrackCallerParser;
368impl NoArgsAttributeParser for TrackCallerParser {
369 const PATH: &[Symbol] = &[sym::track_caller];
370 const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn;
371 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[
372 Allow(Target::Fn),
373 Allow(Target::Method(MethodKind::Inherent)),
374 Allow(Target::Method(MethodKind::Trait { body: true })),
375 Allow(Target::Method(MethodKind::TraitImpl)),
376 Allow(Target::Method(MethodKind::Trait { body: false })), Allow(Target::ForeignFn),
378 Allow(Target::Closure),
379 Warn(Target::MacroDef),
380 Warn(Target::Arm),
381 Warn(Target::Field),
382 Warn(Target::MacroCall),
383 ]);
384 const STABILITY: AttributeStability = AttributeStability::Stable;
385 const CREATE: fn(Span) -> AttributeKind = AttributeKind::TrackCaller;
386
387 fn finalize_check(cx: &mut FinalizeCheckContext<'_, '_>, attr_span: Span) {
388 match cx.target {
389 Target::Fn => {
390 if let Some(item) = {
'done:
{
for i in cx.parsed_attrs {
#[allow(unused_imports)]
use ::rustc_attr_ir::AttributeKind::*;
let i: &::rustc_attr_ir::Attribute = i;
match i {
::rustc_attr_ir::Attribute::Parsed(Lang(item)) => {
break 'done Some(item);
}
::rustc_attr_ir::Attribute::Unparsed(..) =>
{}
#[deny(unreachable_patterns)]
_ => {}
}
}
None
}
}find_attr!(cx.parsed_attrs, Lang(item) => item)
393 && item.is_weak()
394 {
395 cx.emit_err(TrackCallerOnLangItem {
396 attr_span,
397 name: item.name(),
398 sig_span: cx.target_span,
399 });
400 }
401 }
402 Target::Closure if !cx.features().closure_track_caller() => {
403 feature_err(
404 cx.sess(),
405 sym::closure_track_caller,
406 attr_span,
407 "`#[track_caller]` on closures is currently unstable",
408 )
409 .emit();
410 }
411 _ => {}
412 }
413 }
414}
415
416pub(crate) struct NoMangleParser;
417impl NoArgsAttributeParser for NoMangleParser {
418 const PATH: &[Symbol] = &[sym::no_mangle];
419 const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn;
420 const SAFETY: AttributeSafety = AttributeSafety::Unsafe {
421 note: "the linker's behavior with multiple libraries exporting duplicate symbol names is undefined and Rust cannot provide guarantees when you manually override them",
422 unsafe_since: Some(Edition2024),
423 };
424 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowListWarnRest(&[
425 Allow(Target::Fn),
426 Allow(Target::Static),
427 Allow(Target::Method(MethodKind::Inherent)),
428 Allow(Target::Method(MethodKind::TraitImpl)),
429 AllowSilent(Target::Const), Error(Target::Closure),
431 ]);
432 const STABILITY: AttributeStability = AttributeStability::Stable;
433 const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoMangle;
434}
435
436#[derive(#[automatically_derived]impl ::core::default::Default for UsedParser {
#[inline]
fn default() -> Self {
UsedParser {
first_compiler: ::core::default::Default::default(),
first_linker: ::core::default::Default::default(),
first_default: ::core::default::Default::default(),
}
}
}Default)]
437pub(crate) struct UsedParser {
438 first_compiler: Option<Span>,
439 first_linker: Option<Span>,
440 first_default: Option<Span>,
441}
442
443impl AttributeParser for UsedParser {
448 const ATTRIBUTES: AcceptMapping<Self> = &[(
449 &[sym::used],
450 crate::AttributeTemplate {
word: true,
list: Some(&["compiler", "linker"]),
one_of: &[],
name_value_str: None,
docs: None,
}template!(Word, List: &["compiler", "linker"]),
451 AttributeStability::Stable,
452 |group: &mut Self, cx, args| {
453 let used_by = match args {
454 ArgParser::NoArgs => UsedBy::Default,
455 ArgParser::List(list) => {
456 let Some(l) = cx.expect_single(list) else {
457 return;
458 };
459
460 match l.meta_item_no_args().and_then(|i| i.path().word_sym()) {
461 Some(sym::compiler) => {
462 if !cx.features().used_with_arg() {
463 feature_err(
464 cx.sess(),
465 sym::used_with_arg,
466 cx.attr_span,
467 "`#[used(compiler)]` is currently unstable",
468 )
469 .emit();
470 }
471 UsedBy::Compiler
472 }
473 Some(sym::linker) => {
474 if !cx.features().used_with_arg() {
475 feature_err(
476 cx.sess(),
477 sym::used_with_arg,
478 cx.attr_span,
479 "`#[used(linker)]` is currently unstable",
480 )
481 .emit();
482 }
483 UsedBy::Linker
484 }
485 _ => {
486 cx.adcx().expected_specific_argument(
487 l.span(),
488 &[sym::compiler, sym::linker],
489 );
490 return;
491 }
492 }
493 }
494 ArgParser::NameValue(_) => return,
495 };
496
497 let attr_span = cx.attr_span;
498
499 let target = match used_by {
503 UsedBy::Compiler => &mut group.first_compiler,
504 UsedBy::Linker => {
505 if let Some(prev) = group.first_default {
506 cx.warn_unused_duplicate(prev, attr_span);
507 return;
508 }
509 &mut group.first_linker
510 }
511 UsedBy::Default => {
512 if let Some(prev) = group.first_linker {
513 cx.warn_unused_duplicate(prev, attr_span);
514 return;
515 }
516 &mut group.first_default
517 }
518 };
519
520 if let Some(prev) = *target {
521 cx.warn_unused_duplicate(prev, attr_span);
522 } else {
523 *target = Some(attr_span);
524 }
525 },
526 )];
527 const ALLOWED_TARGETS: AllowedTargets<'_> =
528 AllowedTargets::AllowList(&[Allow(Target::Static), Warn(Target::MacroCall)]);
529
530 fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {
531 Some(match (self.first_compiler, self.first_linker, self.first_default) {
534 (_, Some(_), _) => AttributeKind::Used { used_by: UsedBy::Linker },
535 (Some(_), _, _) => AttributeKind::Used { used_by: UsedBy::Compiler },
536 (_, _, Some(_)) => AttributeKind::Used { used_by: UsedBy::Default },
537 (None, None, None) => return None,
538 })
539 }
540}
541
542fn parse_tf_attribute(
543 cx: &mut AcceptContext<'_, '_>,
544 args: &ArgParser,
545) -> impl IntoIterator<Item = (Symbol, Span)> {
546 let mut features = Vec::new();
547 let Some(list) = cx.expect_list(args, cx.attr_span) else {
548 return features;
549 };
550 if list.is_empty() {
551 let attr_span = cx.attr_span;
552 cx.adcx().warn_empty_attribute(attr_span);
553 return features;
554 }
555 for item in list.mixed() {
556 let Some((ident, value)) = cx.expect_name_value(item, item.span(), Some(sym::enable))
557 else {
558 return features;
559 };
560
561 if ident.name != sym::enable {
563 cx.adcx().expected_specific_argument(ident.span, &[sym::enable]);
564 return features;
565 }
566
567 let Some(value_str) = cx.expect_string_literal(value) else {
569 return features;
570 };
571 for feature in value_str.as_str().split(',') {
572 features.push((Symbol::intern(feature), item.span()));
573 }
574 }
575 features
576}
577
578pub(crate) struct TargetFeatureParser;
579
580impl CombineAttributeParser for TargetFeatureParser {
581 type Item = (Symbol, Span);
582 const PATH: &[Symbol] = &[sym::target_feature];
583 const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {
584 features: items,
585 attr_span: span,
586 was_forced: false,
587 };
588 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\""]);
589 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[
590 Allow(Target::Fn),
591 Allow(Target::Method(MethodKind::Inherent)),
592 Allow(Target::Method(MethodKind::Trait { body: true })),
593 Allow(Target::Method(MethodKind::TraitImpl)),
594 Warn(Target::Statement),
595 Warn(Target::Field),
596 Warn(Target::Arm),
597 Warn(Target::MacroDef),
598 Warn(Target::MacroCall),
599 ]);
600 const STABILITY: AttributeStability = AttributeStability::Stable;
601
602 fn extend(
603 cx: &mut AcceptContext<'_, '_>,
604 args: &ArgParser,
605 ) -> impl IntoIterator<Item = Self::Item> {
606 parse_tf_attribute(cx, args)
607 }
608
609 fn finalize_check(cx: &mut FinalizeCheckContext<'_, '_>, attr_span: Span) {
610 if !cx.sess().target.is_like_wasm && !cx.sess().opts.actually_rustdoc {
613 let lang_kind = cx
615 .all_attrs
616 .iter()
617 .find_map(|a| [sym::panic_handler, sym::lang].into_iter().find(|&s| a.word_is(s)));
618 if let Some(kind) = lang_kind {
619 cx.emit_err(TargetFeatureOnLangItem { attr_span, kind, item_span: cx.target_span });
620 }
621 }
622 }
623}
624
625pub(crate) struct ForceTargetFeatureParser;
626
627impl CombineAttributeParser for ForceTargetFeatureParser {
628 type Item = (Symbol, Span);
629 const PATH: &[Symbol] = &[sym::force_target_feature];
630 const SAFETY: AttributeSafety = AttributeSafety::Unsafe {
631 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",
632 unsafe_since: None,
633 };
634 const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {
635 features: items,
636 attr_span: span,
637 was_forced: true,
638 };
639 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\""]);
640 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[
641 Allow(Target::Fn),
642 Allow(Target::Method(MethodKind::Inherent)),
643 Allow(Target::Method(MethodKind::Trait { body: true })),
644 Allow(Target::Method(MethodKind::TraitImpl)),
645 ]);
646 const STABILITY: AttributeStability = {
_ = rustc_feature::Features::effective_target_features;
AttributeStability::Unstable {
gate_name: rustc_span::sym::effective_target_features,
notes: &[],
}
}unstable!(effective_target_features);
647
648 fn extend(
649 cx: &mut AcceptContext<'_, '_>,
650 args: &ArgParser,
651 ) -> impl IntoIterator<Item = Self::Item> {
652 parse_tf_attribute(cx, args)
653 }
654}
655
656pub(crate) struct InstrumentFnParser;
657
658impl SingleAttributeParser for InstrumentFnParser {
659 const PATH: &[Symbol] = &[sym::instrument_fn];
660 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[
661 Allow(Target::Fn),
662 Allow(Target::Method(MethodKind::Inherent)),
663 Allow(Target::Method(MethodKind::Trait { body: true })),
664 Allow(Target::Method(MethodKind::TraitImpl)),
665 ]);
666 const TEMPLATE: AttributeTemplate = crate::AttributeTemplate {
word: false,
list: None,
one_of: &[],
name_value_str: Some(&["on|off"]),
docs: None,
}template!(NameValueStr: "on|off");
667 const STABILITY: AttributeStability = {
_ = rustc_feature::Features::instrument_fn;
AttributeStability::Unstable {
gate_name: rustc_span::sym::instrument_fn,
notes: &[],
}
}unstable!(instrument_fn);
668
669 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
670 match args {
671 ArgParser::NameValue(nv) => match nv.value_as_str() {
672 Some(sym::on) => Some(AttributeKind::InstrumentFn(InstrumentFnAttr::On)),
673 Some(sym::off) => Some(AttributeKind::InstrumentFn(InstrumentFnAttr::Off)),
674 _ => {
675 cx.adcx()
676 .expected_specific_argument_strings(nv.value_span, &[sym::on, sym::off]);
677 None
678 }
679 },
680 ArgParser::List(l) => {
681 cx.adcx().expected_single_argument(l.span, l.len());
682 None
683 }
684 ArgParser::NoArgs => {
685 let span = cx.attr_span;
686 cx.adcx().expected_specific_argument_strings(span, &[sym::on, sym::off]);
687 None
688 }
689 }
690 }
691}
692
693pub(crate) struct SanitizeParser;
694
695impl SingleAttributeParser for SanitizeParser {
696 const PATH: &[Symbol] = &[sym::sanitize];
697 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[
698 Allow(Target::Fn),
699 Allow(Target::Closure),
700 Allow(Target::Method(MethodKind::Inherent)),
701 Allow(Target::Method(MethodKind::Trait { body: true })),
702 Allow(Target::Method(MethodKind::TraitImpl)),
703 Allow(Target::Impl { of_trait: false }),
704 Allow(Target::Impl { of_trait: true }),
705 Allow(Target::Mod),
706 Allow(Target::Crate),
707 Allow(Target::Static),
708 ]);
709 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#"safestack = "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: &[
710 r#"address = "on|off""#,
711 r#"kernel_address = "on|off""#,
712 r#"cfi = "on|off""#,
713 r#"hwaddress = "on|off""#,
714 r#"kernel_hwaddress = "on|off""#,
715 r#"kcfi = "on|off""#,
716 r#"memory = "on|off""#,
717 r#"memtag = "on|off""#,
718 r#"safestack = "on|off""#,
719 r#"shadow_call_stack = "on|off""#,
720 r#"thread = "on|off""#,
721 r#"realtime = "nonblocking|blocking|caller""#,
722 ]);
723 const STABILITY: AttributeStability = {
_ = rustc_feature::Features::sanitize;
AttributeStability::Unstable {
gate_name: rustc_span::sym::sanitize,
notes: &[],
}
}unstable!(sanitize);
724
725 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
726 let list = cx.expect_list(args, cx.attr_span)?;
727
728 let mut on_set = SanitizerSet::empty();
729 let mut off_set = SanitizerSet::empty();
730 let mut rtsan = None;
731
732 for item in list.mixed() {
733 let Some((ident, value)) = cx.expect_name_value(item, item.span(), None) else {
734 continue;
735 };
736
737 let mut apply = |s: SanitizerSet| {
738 let is_on = match value.value_as_str() {
739 Some(sym::on) => true,
740 Some(sym::off) => false,
741 _ => {
742 cx.adcx().expected_specific_argument_strings(
743 value.value_span,
744 &[sym::on, sym::off],
745 );
746 return;
747 }
748 };
749
750 if is_on {
751 on_set |= s;
752 } else {
753 off_set |= s;
754 }
755 };
756
757 match ident.name {
758 sym::address | sym::kernel_address => {
759 apply(SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS)
760 }
761 sym::cfi => apply(SanitizerSet::CFI),
762 sym::kcfi => apply(SanitizerSet::KCFI),
763 sym::memory => apply(SanitizerSet::MEMORY),
764 sym::memtag => apply(SanitizerSet::MEMTAG),
765 sym::safestack => apply(SanitizerSet::SAFESTACK),
766 sym::shadow_call_stack => apply(SanitizerSet::SHADOWCALLSTACK),
767 sym::thread => apply(SanitizerSet::THREAD),
768 sym::hwaddress | sym::kernel_hwaddress => {
769 apply(SanitizerSet::HWADDRESS | SanitizerSet::KERNELHWADDRESS)
770 }
771 sym::realtime => match value.value_as_str() {
772 Some(sym::nonblocking) => rtsan = Some(RtsanSetting::Nonblocking),
773 Some(sym::blocking) => rtsan = Some(RtsanSetting::Blocking),
774 Some(sym::caller) => rtsan = Some(RtsanSetting::Caller),
775 _ => {
776 cx.adcx().expected_specific_argument_strings(
777 value.value_span,
778 &[sym::nonblocking, sym::blocking, sym::caller],
779 );
780 }
781 },
782 _ => {
783 cx.adcx().expected_specific_argument_strings(
784 ident.span,
785 &[
786 sym::address,
787 sym::kernel_address,
788 sym::cfi,
789 sym::kcfi,
790 sym::memory,
791 sym::memtag,
792 sym::safestack,
793 sym::shadow_call_stack,
794 sym::thread,
795 sym::hwaddress,
796 sym::kernel_hwaddress,
797 sym::realtime,
798 ],
799 );
800 }
801 }
802 }
803
804 let all_set_except_address =
806 (on_set | off_set) & !(SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS);
807 if cx.target == Target::Static
808 && let Some(set) = all_set_except_address.iter().next()
809 {
810 cx.emit_err(SanitizeInvalidStatic {
811 span: cx.attr_span,
812 field: set.as_str().expect("Since this `SanitizerSet` is returned from an iterator, exactly one field is set")
813 });
814 }
815
816 Some(AttributeKind::Sanitize { on_set, off_set, rtsan, span: cx.attr_span })
817 }
818}
819
820pub(crate) struct ThreadLocalParser;
821
822impl NoArgsAttributeParser for ThreadLocalParser {
823 const PATH: &[Symbol] = &[sym::thread_local];
824 const ALLOWED_TARGETS: AllowedTargets<'_> =
825 AllowedTargets::AllowList(&[Allow(Target::Static), Allow(Target::ForeignStatic)]);
826 const STABILITY: AttributeStability = {
_ = rustc_feature::Features::thread_local;
AttributeStability::Unstable {
gate_name: rustc_span::sym::thread_local,
notes: &[],
}
}unstable!(thread_local);
827 const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::ThreadLocal;
828}
829
830pub(crate) struct RustcPassIndirectlyInNonRusticAbisParser;
831
832impl NoArgsAttributeParser for RustcPassIndirectlyInNonRusticAbisParser {
833 const PATH: &[Symbol] = &[sym::rustc_pass_indirectly_in_non_rustic_abis];
834 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Struct)]);
835 const STABILITY: AttributeStability = {
_ = rustc_feature::Features::rustc_attrs;
AttributeStability::Unstable {
gate_name: rustc_span::sym::rustc_attrs,
notes: &[],
}
}unstable!(rustc_attrs);
836 const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcPassIndirectlyInNonRusticAbis;
837}
838
839pub(crate) struct RustcEiiForeignItemParser;
840
841impl NoArgsAttributeParser for RustcEiiForeignItemParser {
842 const PATH: &[Symbol] = &[sym::rustc_eii_foreign_item];
843 const ALLOWED_TARGETS: AllowedTargets<'_> =
844 AllowedTargets::AllowList(&[Allow(Target::ForeignFn), Allow(Target::ForeignStatic)]);
845 const STABILITY: AttributeStability = {
_ = rustc_feature::Features::eii_internals;
AttributeStability::Unstable {
gate_name: rustc_span::sym::eii_internals,
notes: &[],
}
}unstable!(eii_internals);
846 const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcEiiForeignItem;
847}
848
849pub(crate) struct PatchableFunctionEntryParser;
850
851impl SingleAttributeParser for PatchableFunctionEntryParser {
852 const PATH: &[Symbol] = &[sym::patchable_function_entry];
853 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Fn)]);
854 const TEMPLATE: AttributeTemplate =
855 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\""]);
856 const STABILITY: AttributeStability = {
_ = rustc_feature::Features::patchable_function_entry;
AttributeStability::Unstable {
gate_name: rustc_span::sym::patchable_function_entry,
notes: &[],
}
}unstable!(patchable_function_entry);
857
858 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
859 let meta_item_list = cx.expect_list(args, cx.attr_span)?;
860
861 let mut prefix = None;
862 let mut entry = None;
863 let mut section = None;
864
865 if meta_item_list.len() == 0 {
866 cx.adcx().expected_at_least_one_argument(meta_item_list.span);
867 return None;
868 }
869
870 for item in meta_item_list.mixed() {
871 let (ident, value) = cx.expect_name_value(item, item.span(), None)?;
872
873 let attrib_to_write = match ident.name {
874 sym::prefix_nops => {
875 if prefix.is_some() {
877 cx.adcx().duplicate_key(ident.span, sym::prefix_nops);
878 return None;
879 }
880 &mut prefix
881 }
882 sym::entry_nops => {
883 if entry.is_some() {
885 cx.adcx().duplicate_key(ident.span, sym::entry_nops);
886 return None;
887 }
888 &mut entry
889 }
890 sym::section => {
891 if section.is_some() {
893 cx.adcx().duplicate_key(ident.span, sym::section);
894 return None;
895 }
896 let Some(value_str) = value.value_as_str() else {
898 cx.adcx().expect_string_literal(value);
899 return None;
900 };
901 if value_str.as_str().contains('\0') {
903 cx.emit_err(NullOnSection { span: value.value_span });
904 }
905 if value_str.is_empty() {
908 cx.emit_err(EmptySection { span: value.value_span });
909 }
910 section = Some(value_str);
911 continue;
913 }
914 _ => {
915 cx.adcx().expected_specific_argument(
916 ident.span,
917 &[sym::prefix_nops, sym::entry_nops],
918 );
919 return None;
920 }
921 };
922
923 let rustc_ast::LitKind::Int(val, _) = value.value_as_lit().kind else {
924 cx.adcx().expected_integer_literal(value.value_span);
925 return None;
926 };
927
928 let Ok(val) = val.get().try_into() else {
929 cx.adcx().expected_integer_literal_in_range(
930 value.value_span,
931 u8::MIN as isize,
932 u8::MAX as isize,
933 );
934 return None;
935 };
936
937 *attrib_to_write = Some(val);
938 }
939
940 Some(AttributeKind::PatchableFunctionEntry { prefix, entry, section })
941 }
942}