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;78use super::prelude::*;9use crate::attributes::AttributeSafety;10use crate::session_diagnostics::{11 EmptyExportName, NakedFunctionIncompatibleAttribute, NullOnExport, NullOnObjcClass,12 NullOnObjcSelector, ObjcClassExpectedStringLiteral, ObjcSelectorExpectedStringLiteral,13 SanitizeInvalidStatic,14};15use crate::target_checking::Policy::AllowSilent;1617pub(crate) struct OptimizeParser;1819impl 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 = template!(List: &["size", "speed", "none"]);29 const STABILITY: AttributeStability = unstable!(optimize_attribute);3031 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {32 let single = cx.expect_single_element_list(args, cx.attr_span)?;3334 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::Default42 }43 };4445 Some(AttributeKind::Optimize(res, cx.attr_span))46 }47}4849pub(crate) struct ColdParser;5051impl 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}6566pub(crate) struct CoverageParser;6768impl 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 = template!(OneOf: &[sym::off, sym::on]);82 const STABILITY: AttributeStability = unstable!(coverage_attribute);8384 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {85 let arg = cx.expect_single_element_list(args, cx.attr_span)?;8687 let mut fail_incorrect_argument =88 |span| cx.adcx().expected_specific_argument(span, &[sym::on, sym::off]);8990 let Some(arg) = arg.meta_item_no_args() else {91 fail_incorrect_argument(arg.span());92 return None;93 };9495 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 };103104 Some(AttributeKind::Coverage(kind))105 }106}107108pub(crate) struct ExportNameParser;109110impl 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 = template!(NameValueStr: "name");129 const STABILITY: AttributeStability = AttributeStability::Stable;130131 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 // `#[export_name = ...]` will be converted to a null-terminated string,136 // so it may not contain any null characters.137 cx.emit_err(NullOnExport { span: cx.attr_span });138 return None;139 }140 if name.is_empty() {141 // LLVM will make up a name if the empty string is given, but that name will be142 // inconsistent between compilation units, causing linker errors.143 cx.emit_err(EmptyExportName { span: cx.attr_span });144 return None;145 }146 Some(AttributeKind::ExportName { name, span: cx.attr_span })147 }148}149150pub(crate) struct RustcObjcClassParser;151152impl 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 = template!(NameValueStr: "ClassName");157 const STABILITY: AttributeStability = unstable!(rustc_attrs);158159 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 // `#[rustc_objc_class = ...]` is expected to be used as an implementation detail163 // inside a standard library macro, but `cx.expected_string_literal` exposes too much.164 // Use a custom error message instead.165 cx.emit_err(ObjcClassExpectedStringLiteral { span: nv.value_span });166 return None;167 };168 if classname.as_str().contains('\0') {169 // `#[rustc_objc_class = ...]` will be converted to a null-terminated string,170 // so it may not contain any null characters.171 cx.emit_err(NullOnObjcClass { span: nv.value_span });172 return None;173 }174 Some(AttributeKind::RustcObjcClass { classname })175 }176}177178pub(crate) struct RustcObjcSelectorParser;179180impl 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 = template!(NameValueStr: "methodName");185 const STABILITY: AttributeStability = unstable!(rustc_attrs);186187 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 // `#[rustc_objc_selector = ...]` is expected to be used as an implementation detail191 // inside a standard library macro, but `cx.expected_string_literal` exposes too much.192 // Use a custom error message instead.193 cx.emit_err(ObjcSelectorExpectedStringLiteral { span: nv.value_span });194 return None;195 };196 if methname.as_str().contains('\0') {197 // `#[rustc_objc_selector = ...]` will be converted to a null-terminated string,198 // so it may not contain any null characters.199 cx.emit_err(NullOnObjcSelector { span: nv.value_span });200 return None;201 }202 Some(AttributeKind::RustcObjcSelector { methname })203 }204}205206#[derive(Default)]207pub(crate) struct NakedParser {208 span: Option<Span>,209}210211impl AttributeParser for NakedParser {212 const ATTRIBUTES: AcceptMapping<Self> =213 &[(&[sym::naked], template!(Word), AttributeStability::Stable, |this, cx, args| {214 let Some(()) = cx.expect_no_args(args) else {215 return;216 };217218 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 ]);236237 fn finalize(self, cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {238 // FIXME(jdonszelmann): upgrade this list to *parsed* attributes239 // once all of these have parsed forms. That'd make the check much nicer...240 //241 // many attributes don't make sense in combination with #[naked].242 // Notable attributes that are incompatible with `#[naked]` are:243 //244 // * `#[inline]`245 // * `#[track_caller]`246 // * `#[test]`, `#[ignore]`, `#[should_panic]`247 //248 // NOTE: when making changes to this list, check that `error_codes/E0736.md` remains249 // accurate.250 const ALLOW_LIST: &[rustc_span::Symbol] = &[251 // conditional compilation252 sym::cfg_trace,253 sym::cfg_attr_trace,254 // testing (allowed here so better errors can be generated in `rustc_builtin_macros::test`)255 sym::test,256 sym::ignore,257 sym::should_panic,258 sym::bench,259 // diagnostics260 sym::allow,261 sym::warn,262 sym::deny,263 sym::forbid,264 sym::deprecated,265 sym::must_use,266 // abi, linking and FFI267 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 // FIXME(#82232, #143834): temporarily renamed to mitigate `#[align]` nameres ambiguity276 sym::rustc_align,277 sym::rustc_align_static,278 // obviously compatible with self279 sym::naked,280 // documentation281 sym::doc,282 ];283284 let span = self.span?;285286 let Some(tools) = cx.tools else {287 unreachable!("tools required while parsing attributes");288 };289290 // only if we found a naked attribute do we do the somewhat expensive check291 'outer: for other_attr in cx.all_attrs {292 for allowed_attr in ALLOW_LIST {293 if other_attr294 .segments()295 .next()296 .is_some_and(|i| tools.iter().any(|tool| tool.name == i.name))297 {298 // effectively skips the error message being emitted below299 // if it's a tool attribute300 continue 'outer;301 }302 if other_attr.word_is(*allowed_attr) {303 // effectively skips the error message being emitted below304 // if its an allowed attribute305 continue 'outer;306 }307308 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 }317318 continue 'outer;319 }320 }321322 cx.emit_err(NakedFunctionIncompatibleAttribute {323 span: other_attr.span(),324 naked_span: span,325 attr: other_attr.get_attribute_path().to_string(),326 });327 }328329 Some(AttributeKind::Naked(span))330 }331}332333pub(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 })), // `#[track_caller]` is inherited from trait methods343 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}353354pub(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), // Handled in the `InvalidNoMangleItems` pass368 Error(Target::Closure),369 ]);370 const STABILITY: AttributeStability = AttributeStability::Stable;371 const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoMangle;372}373374#[derive(Default)]375pub(crate) struct UsedParser {376 first_compiler: Option<Span>,377 first_linker: Option<Span>,378 first_default: Option<Span>,379}380381// A custom `AttributeParser` is used rather than a Simple attribute parser because382// - Specifying two `#[used]` attributes is a warning (but will be an error in the future)383// - But specifying two conflicting attributes: `#[used(compiler)]` and `#[used(linker)]` is already an error today384// We can change this to a Simple parser once the warning becomes an error385impl AttributeParser for UsedParser {386 const ATTRIBUTES: AcceptMapping<Self> = &[(387 &[sym::used],388 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 };397398 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::Compiler410 }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::Linker422 }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 };434435 let attr_span = cx.attr_span;436437 // `#[used]` is interpreted as `#[used(linker)]` (though depending on target OS the438 // circumstances are more complicated). While we're checking `used_by`, also report439 // these cross-`UsedBy` duplicates to warn.440 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_linker448 }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_default455 }456 };457458 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)]);467468 fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {469 // If a specific form of `used` is specified, it takes precedence over generic `#[used]`.470 // If both `linker` and `compiler` are specified, use `linker`.471 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}479480fn 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 };498499 // Validate name500 if ident.name != sym::enable {501 cx.adcx().expected_specific_argument(ident.span, &[sym::enable]);502 return features;503 }504505 // Use value506 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 features514}515516pub(crate) struct TargetFeatureParser;517518impl 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 = template!(List: &["enable = \"feat1, feat2\""]);527 const STABILITY: AttributeStability = AttributeStability::Stable;528529 fn extend(530 cx: &mut AcceptContext<'_, '_>,531 args: &ArgParser,532 ) -> impl IntoIterator<Item = Self::Item> {533 parse_tf_attribute(cx, args)534 }535536 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[537 Allow(Target::Fn),538 Allow(Target::Method(MethodKind::Inherent)),539 Allow(Target::Method(MethodKind::Trait { body: true })),540 Allow(Target::Method(MethodKind::TraitImpl)),541 Warn(Target::Statement),542 Warn(Target::Field),543 Warn(Target::Arm),544 Warn(Target::MacroDef),545 Warn(Target::MacroCall),546 ]);547}548549pub(crate) struct ForceTargetFeatureParser;550551impl CombineAttributeParser for ForceTargetFeatureParser {552 type Item = (Symbol, Span);553 const PATH: &[Symbol] = &[sym::force_target_feature];554 const SAFETY: AttributeSafety = AttributeSafety::Unsafe {555 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",556 unsafe_since: None,557 };558 const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {559 features: items,560 attr_span: span,561 was_forced: true,562 };563 const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]);564 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[565 Allow(Target::Fn),566 Allow(Target::Method(MethodKind::Inherent)),567 Allow(Target::Method(MethodKind::Trait { body: true })),568 Allow(Target::Method(MethodKind::TraitImpl)),569 ]);570 const STABILITY: AttributeStability = unstable!(effective_target_features);571572 fn extend(573 cx: &mut AcceptContext<'_, '_>,574 args: &ArgParser,575 ) -> impl IntoIterator<Item = Self::Item> {576 parse_tf_attribute(cx, args)577 }578}579580pub(crate) struct InstrumentFnParser;581582impl SingleAttributeParser for InstrumentFnParser {583 const PATH: &[Symbol] = &[sym::instrument_fn];584 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[585 Allow(Target::Fn),586 Allow(Target::Method(MethodKind::Inherent)),587 Allow(Target::Method(MethodKind::Trait { body: true })),588 Allow(Target::Method(MethodKind::TraitImpl)),589 ]);590 const TEMPLATE: AttributeTemplate = template!(NameValueStr: "on|off");591 const STABILITY: AttributeStability = unstable!(instrument_fn);592593 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {594 let instrument = match args {595 ArgParser::NameValue(nv) => match nv.value_as_str() {596 Some(sym::on) => Some(AttributeKind::InstrumentFn(InstrumentFnAttr::On)),597 Some(sym::off) => Some(AttributeKind::InstrumentFn(InstrumentFnAttr::Off)),598 _ => {599 cx.adcx()600 .expected_specific_argument_strings(nv.value_span, &[sym::on, sym::off]);601 None602 }603 },604 ArgParser::List(l) => {605 cx.adcx().expected_single_argument(l.span, l.len());606 None607 }608 ArgParser::NoArgs => {609 let span = cx.attr_span;610 cx.adcx().expected_specific_argument_strings(span, &[sym::on, sym::off]);611 None612 }613 };614 instrument615 }616}617618pub(crate) struct SanitizeParser;619620impl SingleAttributeParser for SanitizeParser {621 const PATH: &[Symbol] = &[sym::sanitize];622 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[623 Allow(Target::Fn),624 Allow(Target::Closure),625 Allow(Target::Method(MethodKind::Inherent)),626 Allow(Target::Method(MethodKind::Trait { body: true })),627 Allow(Target::Method(MethodKind::TraitImpl)),628 Allow(Target::Impl { of_trait: false }),629 Allow(Target::Impl { of_trait: true }),630 Allow(Target::Mod),631 Allow(Target::Crate),632 Allow(Target::Static),633 ]);634 const TEMPLATE: AttributeTemplate = template!(List: &[635 r#"address = "on|off""#,636 r#"kernel_address = "on|off""#,637 r#"cfi = "on|off""#,638 r#"hwaddress = "on|off""#,639 r#"kernel_hwaddress = "on|off""#,640 r#"kcfi = "on|off""#,641 r#"memory = "on|off""#,642 r#"memtag = "on|off""#,643 r#"shadow_call_stack = "on|off""#,644 r#"thread = "on|off""#,645 r#"realtime = "nonblocking|blocking|caller""#,646 ]);647 const STABILITY: AttributeStability = unstable!(sanitize);648649 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {650 let list = cx.expect_list(args, cx.attr_span)?;651652 let mut on_set = SanitizerSet::empty();653 let mut off_set = SanitizerSet::empty();654 let mut rtsan = None;655656 for item in list.mixed() {657 let Some((ident, value)) = cx.expect_name_value(item, item.span(), None) else {658 continue;659 };660661 let mut apply = |s: SanitizerSet| {662 let is_on = match value.value_as_str() {663 Some(sym::on) => true,664 Some(sym::off) => false,665 Some(_) => {666 cx.adcx().expected_specific_argument_strings(667 value.value_span,668 &[sym::on, sym::off],669 );670 return;671 }672 None => {673 cx.adcx().expected_specific_argument_strings(674 value.value_span,675 &[sym::on, sym::off],676 );677 return;678 }679 };680681 if is_on {682 on_set |= s;683 } else {684 off_set |= s;685 }686 };687688 match ident.name {689 sym::address | sym::kernel_address => {690 apply(SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS)691 }692 sym::cfi => apply(SanitizerSet::CFI),693 sym::kcfi => apply(SanitizerSet::KCFI),694 sym::memory => apply(SanitizerSet::MEMORY),695 sym::memtag => apply(SanitizerSet::MEMTAG),696 sym::shadow_call_stack => apply(SanitizerSet::SHADOWCALLSTACK),697 sym::thread => apply(SanitizerSet::THREAD),698 sym::hwaddress | sym::kernel_hwaddress => {699 apply(SanitizerSet::HWADDRESS | SanitizerSet::KERNELHWADDRESS)700 }701 sym::realtime => match value.value_as_str() {702 Some(sym::nonblocking) => rtsan = Some(RtsanSetting::Nonblocking),703 Some(sym::blocking) => rtsan = Some(RtsanSetting::Blocking),704 Some(sym::caller) => rtsan = Some(RtsanSetting::Caller),705 _ => {706 cx.adcx().expected_specific_argument_strings(707 value.value_span,708 &[sym::nonblocking, sym::blocking, sym::caller],709 );710 }711 },712 _ => {713 cx.adcx().expected_specific_argument_strings(714 ident.span,715 &[716 sym::address,717 sym::kernel_address,718 sym::cfi,719 sym::kcfi,720 sym::memory,721 sym::memtag,722 sym::shadow_call_stack,723 sym::thread,724 sym::hwaddress,725 sym::kernel_hwaddress,726 sym::realtime,727 ],728 );729 continue;730 }731 }732 }733734 // The sanitizer attribute is only allowed on statics, if only address bits are set735 let all_set_except_address =736 (on_set | off_set) & !(SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS);737 if cx.target == Target::Static738 && let Some(set) = all_set_except_address.iter().next()739 {740 cx.emit_err(SanitizeInvalidStatic {741 span: cx.attr_span,742 field: set.as_str().expect("Since this `SanitizerSet` is returned from an iterator, exactly one field is set")743 });744 }745746 Some(AttributeKind::Sanitize { on_set, off_set, rtsan, span: cx.attr_span })747 }748}749750pub(crate) struct ThreadLocalParser;751752impl NoArgsAttributeParser for ThreadLocalParser {753 const PATH: &[Symbol] = &[sym::thread_local];754 const ALLOWED_TARGETS: AllowedTargets =755 AllowedTargets::AllowList(&[Allow(Target::Static), Allow(Target::ForeignStatic)]);756 const STABILITY: AttributeStability = unstable!(thread_local);757 const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::ThreadLocal;758}759760pub(crate) struct RustcPassIndirectlyInNonRusticAbisParser;761762impl NoArgsAttributeParser for RustcPassIndirectlyInNonRusticAbisParser {763 const PATH: &[Symbol] = &[sym::rustc_pass_indirectly_in_non_rustic_abis];764 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]);765 const STABILITY: AttributeStability = unstable!(rustc_attrs);766 const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcPassIndirectlyInNonRusticAbis;767}768769pub(crate) struct RustcEiiForeignItemParser;770771impl NoArgsAttributeParser for RustcEiiForeignItemParser {772 const PATH: &[Symbol] = &[sym::rustc_eii_foreign_item];773 const ALLOWED_TARGETS: AllowedTargets =774 AllowedTargets::AllowList(&[Allow(Target::ForeignFn), Allow(Target::ForeignStatic)]);775 const STABILITY: AttributeStability = unstable!(eii_internals);776 const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcEiiForeignItem;777}778779pub(crate) struct PatchableFunctionEntryParser;780781impl SingleAttributeParser for PatchableFunctionEntryParser {782 const PATH: &[Symbol] = &[sym::patchable_function_entry];783 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]);784 const TEMPLATE: AttributeTemplate = template!(List: &["prefix_nops = m, entry_nops = n"]);785 const STABILITY: AttributeStability = unstable!(patchable_function_entry);786787 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {788 let meta_item_list = cx.expect_list(args, cx.attr_span)?;789790 let mut prefix = None;791 let mut entry = None;792793 if meta_item_list.len() == 0 {794 cx.adcx().expected_at_least_one_argument(meta_item_list.span);795 return None;796 }797798 let mut errored = false;799800 for item in meta_item_list.mixed() {801 let Some((ident, value)) = cx.expect_name_value(item, item.span(), None) else {802 continue;803 };804805 let attrib_to_write = match ident.name {806 sym::prefix_nops => {807 // Duplicate prefixes are not allowed808 if prefix.is_some() {809 errored = true;810 cx.adcx().duplicate_key(ident.span, sym::prefix_nops);811 continue;812 }813 &mut prefix814 }815 sym::entry_nops => {816 // Duplicate entries are not allowed817 if entry.is_some() {818 errored = true;819 cx.adcx().duplicate_key(ident.span, sym::entry_nops);820 continue;821 }822 &mut entry823 }824 _ => {825 errored = true;826 cx.adcx().expected_specific_argument(827 ident.span,828 &[sym::prefix_nops, sym::entry_nops],829 );830 continue;831 }832 };833834 let rustc_ast::LitKind::Int(val, _) = value.value_as_lit().kind else {835 errored = true;836 cx.adcx().expected_integer_literal(value.value_span);837 continue;838 };839840 let Ok(val) = val.get().try_into() else {841 errored = true;842 cx.adcx().expected_integer_literal_in_range(843 value.value_span,844 u8::MIN as isize,845 u8::MAX as isize,846 );847 continue;848 };849850 *attrib_to_write = Some(val);851 }852853 if errored {854 None855 } else {856 Some(AttributeKind::PatchableFunctionEntry {857 prefix: prefix.unwrap_or(0),858 entry: entry.unwrap_or(0),859 })860 }861 }862}