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;89use super::prelude::*;10use crate::attributes::AttributeSafety;11use crate::diagnostics::{12 EmptyExportName, EmptySection, NakedFunctionIncompatibleAttribute, NullOnExport,13 NullOnObjcClass, NullOnObjcSelector, NullOnSection, ObjcClassExpectedStringLiteral,14 ObjcSelectorExpectedStringLiteral, SanitizeInvalidStatic, TargetFeatureOnLangItem,15 TrackCallerOnLangItem,16};17use crate::target_checking::Policy::AllowSilent;1819pub(crate) struct OptimizeParser;2021impl SingleAttributeParser for OptimizeParser {22 const PATH: &[Symbol] = &[sym::optimize];23 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[24 Allow(Target::Fn),25 Allow(Target::Closure),26 Allow(Target::Method(MethodKind::Trait { body: true })),27 Allow(Target::Method(MethodKind::TraitImpl)),28 Allow(Target::Method(MethodKind::Inherent)),29 ]);30 const TEMPLATE: AttributeTemplate = template!(List: &["size", "speed", "none"]);31 const STABILITY: AttributeStability = unstable!(optimize_attribute);3233 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {34 let single = cx.expect_single_element_list(args, cx.attr_span)?;3536 let res = match single.meta_item_no_args().and_then(|i| i.path().word().map(|i| i.name)) {37 Some(sym::size) => OptimizeAttr::Size,38 Some(sym::speed) => OptimizeAttr::Speed,39 Some(sym::none) => OptimizeAttr::DoNotOptimize,40 _ => {41 cx.adcx()42 .expected_specific_argument(single.span(), &[sym::size, sym::speed, sym::none]);43 OptimizeAttr::Default44 }45 };4647 Some(AttributeKind::Optimize(res, cx.attr_span))48 }49}5051pub(crate) struct ColdParser;5253impl NoArgsAttributeParser for ColdParser {54 const PATH: &[Symbol] = &[sym::cold];55 const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn;56 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowListWarnRest(&[57 Allow(Target::Fn),58 Allow(Target::Method(MethodKind::Trait { body: true })),59 Allow(Target::Method(MethodKind::TraitImpl)),60 Allow(Target::Method(MethodKind::Inherent)),61 Allow(Target::ForeignFn),62 Allow(Target::Closure),63 ]);64 const STABILITY: AttributeStability = AttributeStability::Stable;65 const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::Cold;66}6768pub(crate) struct CoverageParser;6970impl SingleAttributeParser for CoverageParser {71 const PATH: &[Symbol] = &[sym::coverage];72 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[73 Allow(Target::Fn),74 Allow(Target::Closure),75 Allow(Target::Method(MethodKind::Trait { body: true })),76 Allow(Target::Method(MethodKind::TraitImpl)),77 Allow(Target::Method(MethodKind::Inherent)),78 Allow(Target::Impl { of_trait: true }),79 Allow(Target::Impl { of_trait: false }),80 Allow(Target::Mod),81 Allow(Target::Crate),82 ]);83 const TEMPLATE: AttributeTemplate = template!(OneOf: &[sym::off, sym::on]);84 const STABILITY: AttributeStability = unstable!(coverage_attribute);8586 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {87 let arg = cx.expect_single_element_list(args, cx.attr_span)?;8889 let mut fail_incorrect_argument =90 |span| cx.adcx().expected_specific_argument(span, &[sym::on, sym::off]);9192 let Some(arg) = arg.meta_item_no_args() else {93 fail_incorrect_argument(arg.span());94 return None;95 };9697 let kind = match arg.path().word_sym() {98 Some(sym::off) => CoverageAttrKind::Off,99 Some(sym::on) => CoverageAttrKind::On,100 None | Some(_) => {101 fail_incorrect_argument(arg.span());102 return None;103 }104 };105106 Some(AttributeKind::Coverage(kind))107 }108}109110pub(crate) struct ExportNameParser;111112impl SingleAttributeParser for ExportNameParser {113 const PATH: &[rustc_span::Symbol] = &[sym::export_name];114 const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError;115 const SAFETY: AttributeSafety = AttributeSafety::Unsafe {116 note: "the linker's behavior with multiple libraries exporting duplicate symbol names is undefined and Rust cannot provide guarantees when you manually override them",117 unsafe_since: Some(Edition2024),118 };119 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[120 Allow(Target::Static),121 Allow(Target::Fn),122 Allow(Target::Method(MethodKind::Inherent)),123 Allow(Target::Method(MethodKind::Trait { body: true })),124 Allow(Target::Method(MethodKind::TraitImpl)),125 Warn(Target::Field),126 Warn(Target::Arm),127 Warn(Target::MacroDef),128 Warn(Target::MacroCall),129 ]);130 const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name");131 const STABILITY: AttributeStability = AttributeStability::Stable;132133 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {134 let nv = cx.expect_name_value(args, cx.attr_span, None)?;135 let name = cx.expect_string_literal(nv)?;136 if name.as_str().contains('\0') {137 // `#[export_name = ...]` will be converted to a null-terminated string,138 // so it may not contain any null characters.139 cx.emit_err(NullOnExport { span: cx.attr_span });140 return None;141 }142 if name.is_empty() {143 // LLVM will make up a name if the empty string is given, but that name will be144 // inconsistent between compilation units, causing linker errors.145 cx.emit_err(EmptyExportName { span: cx.attr_span });146 return None;147 }148 Some(AttributeKind::ExportName { name, span: cx.attr_span })149 }150}151152pub(crate) struct RustcObjcClassParser;153154impl SingleAttributeParser for RustcObjcClassParser {155 const PATH: &[rustc_span::Symbol] = &[sym::rustc_objc_class];156 const ALLOWED_TARGETS: AllowedTargets<'_> =157 AllowedTargets::AllowList(&[Allow(Target::ForeignStatic)]);158 const TEMPLATE: AttributeTemplate = template!(NameValueStr: "ClassName");159 const STABILITY: AttributeStability = unstable!(rustc_attrs);160161 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {162 let nv = cx.expect_name_value(args, cx.attr_span, None)?;163 let Some(classname) = nv.value_as_str() else {164 // `#[rustc_objc_class = ...]` is expected to be used as an implementation detail165 // inside a standard library macro, but `cx.expected_string_literal` exposes too much.166 // Use a custom error message instead.167 cx.emit_err(ObjcClassExpectedStringLiteral { span: nv.value_span });168 return None;169 };170 if classname.as_str().contains('\0') {171 // `#[rustc_objc_class = ...]` will be converted to a null-terminated string,172 // so it may not contain any null characters.173 cx.emit_err(NullOnObjcClass { span: nv.value_span });174 return None;175 }176 Some(AttributeKind::RustcObjcClass { classname })177 }178}179180pub(crate) struct RustcObjcSelectorParser;181182impl SingleAttributeParser for RustcObjcSelectorParser {183 const PATH: &[rustc_span::Symbol] = &[sym::rustc_objc_selector];184 const ALLOWED_TARGETS: AllowedTargets<'_> =185 AllowedTargets::AllowList(&[Allow(Target::ForeignStatic)]);186 const TEMPLATE: AttributeTemplate = template!(NameValueStr: "methodName");187 const STABILITY: AttributeStability = unstable!(rustc_attrs);188189 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {190 let nv = cx.expect_name_value(args, cx.attr_span, None)?;191 let Some(methname) = nv.value_as_str() else {192 // `#[rustc_objc_selector = ...]` is expected to be used as an implementation detail193 // inside a standard library macro, but `cx.expected_string_literal` exposes too much.194 // Use a custom error message instead.195 cx.emit_err(ObjcSelectorExpectedStringLiteral { span: nv.value_span });196 return None;197 };198 if methname.as_str().contains('\0') {199 // `#[rustc_objc_selector = ...]` will be converted to a null-terminated string,200 // so it may not contain any null characters.201 cx.emit_err(NullOnObjcSelector { span: nv.value_span });202 return None;203 }204 Some(AttributeKind::RustcObjcSelector { methname })205 }206}207208#[derive(Default)]209pub(crate) struct NakedParser {210 span: Option<Span>,211}212213impl AttributeParser for NakedParser {214 const ATTRIBUTES: AcceptMapping<Self> =215 &[(&[sym::naked], template!(Word), AttributeStability::Stable, |this, cx, args| {216 let Some(()) = cx.expect_no_args(args) else {217 return;218 };219220 if let Some(earlier) = this.span {221 let span = cx.attr_span;222 cx.warn_unused_duplicate(earlier, span);223 } else {224 this.span = Some(cx.attr_span);225 }226 })];227 const SAFETY: AttributeSafety = AttributeSafety::Unsafe {228 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).",229 unsafe_since: None,230 };231 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[232 Allow(Target::Fn),233 Allow(Target::Method(MethodKind::Inherent)),234 Allow(Target::Method(MethodKind::Trait { body: true })),235 Allow(Target::Method(MethodKind::TraitImpl)),236 Warn(Target::MacroCall),237 ]);238239 fn finalize(self, cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {240 // FIXME(jdonszelmann): upgrade this list to *parsed* attributes241 // once all of these have parsed forms. That'd make the check much nicer...242 //243 // many attributes don't make sense in combination with #[naked].244 // Notable attributes that are incompatible with `#[naked]` are:245 //246 // * `#[inline]`247 // * `#[track_caller]`248 // * `#[test]`, `#[ignore]`, `#[should_panic]`249 //250 // NOTE: when making changes to this list, check that `error_codes/E0736.md` remains251 // accurate.252 const ALLOW_LIST: &[rustc_span::Symbol] = &[253 // testing (allowed here so better errors can be generated in `rustc_builtin_macros::test`)254 sym::test,255 sym::ignore,256 sym::should_panic,257 sym::bench,258 // diagnostics259 sym::allow,260 sym::warn,261 sym::deny,262 sym::forbid,263 sym::deprecated,264 sym::must_use,265 // abi, linking and FFI266 sym::cold,267 sym::export_name,268 sym::link_section,269 sym::linkage,270 sym::no_mangle,271 sym::instruction_set,272 sym::repr,273 sym::rustc_std_internal_symbol,274 // FIXME(#82232, #143834): temporarily renamed to mitigate `#[align]` nameres ambiguity275 sym::rustc_align,276 sym::rustc_align_static,277 // obviously compatible with self278 sym::naked,279 // documentation280 sym::doc,281 ];282283 let span = self.span?;284285 let Some(tools) = cx.attr_tools else {286 unreachable!("tools required while parsing attributes");287 };288289 // only if we found a naked attribute do we do the somewhat expensive check290 'outer: for other_attr in cx.all_attrs {291 for allowed_attr in ALLOW_LIST {292 if other_attr293 .segments()294 .next()295 .is_some_and(|i| tools.iter().any(|tool| tool.name == i.name))296 {297 // effectively skips the error message being emitted below298 // if it's a tool attribute299 continue 'outer;300 }301 if other_attr.word_is(*allowed_attr) {302 // effectively skips the error message being emitted below303 // if its an allowed attribute304 continue 'outer;305 }306307 if other_attr.word_is(sym::target_feature) {308 if !cx.features().naked_functions_target_feature() {309 feature_err(310 cx.sess(),311 sym::naked_functions_target_feature,312 other_attr.span(),313 "`#[target_feature(/* ... */)]` is currently unstable on `#[naked]` functions",314 ).emit();315 }316317 continue 'outer;318 }319 }320321 cx.emit_err(NakedFunctionIncompatibleAttribute {322 span: other_attr.span(),323 naked_span: span,324 attr: other_attr.get_attribute_path().to_string(),325 });326 }327328 Some(AttributeKind::Naked(span))329 }330}331332pub(crate) struct TrackCallerParser;333impl NoArgsAttributeParser for TrackCallerParser {334 const PATH: &[Symbol] = &[sym::track_caller];335 const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn;336 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[337 Allow(Target::Fn),338 Allow(Target::Method(MethodKind::Inherent)),339 Allow(Target::Method(MethodKind::Trait { body: true })),340 Allow(Target::Method(MethodKind::TraitImpl)),341 Allow(Target::Method(MethodKind::Trait { body: false })), // `#[track_caller]` is inherited from trait methods342 Allow(Target::ForeignFn),343 Allow(Target::Closure),344 Warn(Target::MacroDef),345 Warn(Target::Arm),346 Warn(Target::Field),347 Warn(Target::MacroCall),348 ]);349 const STABILITY: AttributeStability = AttributeStability::Stable;350 const CREATE: fn(Span) -> AttributeKind = AttributeKind::TrackCaller;351352 fn finalize_check(cx: &FinalizeCheckContext<'_, '_>, attr_span: Span) {353 match cx.target {354 Target::Fn => {355 // `#[track_caller]` is not valid on weak lang items because they are called via356 // `extern` declarations and `#[track_caller]` would alter their ABI.357 if let Some(item) = find_attr!(cx.parsed_attrs, Lang(item) => item)358 && item.is_weak()359 {360 cx.emit_err(TrackCallerOnLangItem {361 attr_span,362 name: item.name(),363 sig_span: cx.target_span,364 });365 }366 }367 _ => {}368 }369 }370}371372pub(crate) struct NoMangleParser;373impl NoArgsAttributeParser for NoMangleParser {374 const PATH: &[Symbol] = &[sym::no_mangle];375 const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn;376 const SAFETY: AttributeSafety = AttributeSafety::Unsafe {377 note: "the linker's behavior with multiple libraries exporting duplicate symbol names is undefined and Rust cannot provide guarantees when you manually override them",378 unsafe_since: Some(Edition2024),379 };380 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowListWarnRest(&[381 Allow(Target::Fn),382 Allow(Target::Static),383 Allow(Target::Method(MethodKind::Inherent)),384 Allow(Target::Method(MethodKind::TraitImpl)),385 AllowSilent(Target::Const), // Handled in the `InvalidNoMangleItems` pass386 Error(Target::Closure),387 ]);388 const STABILITY: AttributeStability = AttributeStability::Stable;389 const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoMangle;390}391392#[derive(Default)]393pub(crate) struct UsedParser {394 first_compiler: Option<Span>,395 first_linker: Option<Span>,396 first_default: Option<Span>,397}398399// A custom `AttributeParser` is used rather than a Simple attribute parser because400// - Specifying two `#[used]` attributes is a warning (but will be an error in the future)401// - But specifying two conflicting attributes: `#[used(compiler)]` and `#[used(linker)]` is already an error today402// We can change this to a Simple parser once the warning becomes an error403impl AttributeParser for UsedParser {404 const ATTRIBUTES: AcceptMapping<Self> = &[(405 &[sym::used],406 template!(Word, List: &["compiler", "linker"]),407 AttributeStability::Stable,408 |group: &mut Self, cx, args| {409 let used_by = match args {410 ArgParser::NoArgs => UsedBy::Default,411 ArgParser::List(list) => {412 let Some(l) = cx.expect_single(list) else {413 return;414 };415416 match l.meta_item_no_args().and_then(|i| i.path().word_sym()) {417 Some(sym::compiler) => {418 if !cx.features().used_with_arg() {419 feature_err(420 cx.sess(),421 sym::used_with_arg,422 cx.attr_span,423 "`#[used(compiler)]` is currently unstable",424 )425 .emit();426 }427 UsedBy::Compiler428 }429 Some(sym::linker) => {430 if !cx.features().used_with_arg() {431 feature_err(432 cx.sess(),433 sym::used_with_arg,434 cx.attr_span,435 "`#[used(linker)]` is currently unstable",436 )437 .emit();438 }439 UsedBy::Linker440 }441 _ => {442 cx.adcx().expected_specific_argument(443 l.span(),444 &[sym::compiler, sym::linker],445 );446 return;447 }448 }449 }450 ArgParser::NameValue(_) => return,451 };452453 let attr_span = cx.attr_span;454455 // `#[used]` is interpreted as `#[used(linker)]` (though depending on target OS the456 // circumstances are more complicated). While we're checking `used_by`, also report457 // these cross-`UsedBy` duplicates to warn.458 let target = match used_by {459 UsedBy::Compiler => &mut group.first_compiler,460 UsedBy::Linker => {461 if let Some(prev) = group.first_default {462 cx.warn_unused_duplicate(prev, attr_span);463 return;464 }465 &mut group.first_linker466 }467 UsedBy::Default => {468 if let Some(prev) = group.first_linker {469 cx.warn_unused_duplicate(prev, attr_span);470 return;471 }472 &mut group.first_default473 }474 };475476 if let Some(prev) = *target {477 cx.warn_unused_duplicate(prev, attr_span);478 } else {479 *target = Some(attr_span);480 }481 },482 )];483 const ALLOWED_TARGETS: AllowedTargets<'_> =484 AllowedTargets::AllowList(&[Allow(Target::Static), Warn(Target::MacroCall)]);485486 fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {487 // If a specific form of `used` is specified, it takes precedence over generic `#[used]`.488 // If both `linker` and `compiler` are specified, use `linker`.489 Some(match (self.first_compiler, self.first_linker, self.first_default) {490 (_, Some(_), _) => AttributeKind::Used { used_by: UsedBy::Linker },491 (Some(_), _, _) => AttributeKind::Used { used_by: UsedBy::Compiler },492 (_, _, Some(_)) => AttributeKind::Used { used_by: UsedBy::Default },493 (None, None, None) => return None,494 })495 }496}497498fn parse_tf_attribute(499 cx: &mut AcceptContext<'_, '_>,500 args: &ArgParser,501) -> impl IntoIterator<Item = (Symbol, Span)> {502 let mut features = Vec::new();503 let Some(list) = cx.expect_list(args, cx.attr_span) else {504 return features;505 };506 if list.is_empty() {507 let attr_span = cx.attr_span;508 cx.adcx().warn_empty_attribute(attr_span);509 return features;510 }511 for item in list.mixed() {512 let Some((ident, value)) = cx.expect_name_value(item, item.span(), Some(sym::enable))513 else {514 return features;515 };516517 // Validate name518 if ident.name != sym::enable {519 cx.adcx().expected_specific_argument(ident.span, &[sym::enable]);520 return features;521 }522523 // Use value524 let Some(value_str) = cx.expect_string_literal(value) else {525 return features;526 };527 for feature in value_str.as_str().split(',') {528 features.push((Symbol::intern(feature), item.span()));529 }530 }531 features532}533534pub(crate) struct TargetFeatureParser;535536impl CombineAttributeParser for TargetFeatureParser {537 type Item = (Symbol, Span);538 const PATH: &[Symbol] = &[sym::target_feature];539 const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {540 features: items,541 attr_span: span,542 was_forced: false,543 };544 const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]);545 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[546 Allow(Target::Fn),547 Allow(Target::Method(MethodKind::Inherent)),548 Allow(Target::Method(MethodKind::Trait { body: true })),549 Allow(Target::Method(MethodKind::TraitImpl)),550 Warn(Target::Statement),551 Warn(Target::Field),552 Warn(Target::Arm),553 Warn(Target::MacroDef),554 Warn(Target::MacroCall),555 ]);556 const STABILITY: AttributeStability = AttributeStability::Stable;557558 fn extend(559 cx: &mut AcceptContext<'_, '_>,560 args: &ArgParser,561 ) -> impl IntoIterator<Item = Self::Item> {562 parse_tf_attribute(cx, args)563 }564565 fn finalize_check(cx: &FinalizeCheckContext<'_, '_>, attr_span: Span) {566 // `#[target_feature]` is incompatible with lang item functions,567 // except on WASM where calling target-feature functions is safe (see #84988).568 if !cx.sess().target.is_like_wasm && !cx.sess().opts.actually_rustdoc {569 // `#[panic_handler]` is checked first so it takes priority in the diagnostic.570 let lang_kind = cx571 .all_attrs572 .iter()573 .find_map(|a| [sym::panic_handler, sym::lang].into_iter().find(|&s| a.word_is(s)));574 if let Some(kind) = lang_kind {575 cx.emit_err(TargetFeatureOnLangItem { attr_span, kind, item_span: cx.target_span });576 }577 }578 }579}580581pub(crate) struct ForceTargetFeatureParser;582583impl CombineAttributeParser for ForceTargetFeatureParser {584 type Item = (Symbol, Span);585 const PATH: &[Symbol] = &[sym::force_target_feature];586 const SAFETY: AttributeSafety = AttributeSafety::Unsafe {587 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",588 unsafe_since: None,589 };590 const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {591 features: items,592 attr_span: span,593 was_forced: true,594 };595 const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]);596 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[597 Allow(Target::Fn),598 Allow(Target::Method(MethodKind::Inherent)),599 Allow(Target::Method(MethodKind::Trait { body: true })),600 Allow(Target::Method(MethodKind::TraitImpl)),601 ]);602 const STABILITY: AttributeStability = unstable!(effective_target_features);603604 fn extend(605 cx: &mut AcceptContext<'_, '_>,606 args: &ArgParser,607 ) -> impl IntoIterator<Item = Self::Item> {608 parse_tf_attribute(cx, args)609 }610}611612pub(crate) struct InstrumentFnParser;613614impl SingleAttributeParser for InstrumentFnParser {615 const PATH: &[Symbol] = &[sym::instrument_fn];616 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[617 Allow(Target::Fn),618 Allow(Target::Method(MethodKind::Inherent)),619 Allow(Target::Method(MethodKind::Trait { body: true })),620 Allow(Target::Method(MethodKind::TraitImpl)),621 ]);622 const TEMPLATE: AttributeTemplate = template!(NameValueStr: "on|off");623 const STABILITY: AttributeStability = unstable!(instrument_fn);624625 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {626 match args {627 ArgParser::NameValue(nv) => match nv.value_as_str() {628 Some(sym::on) => Some(AttributeKind::InstrumentFn(InstrumentFnAttr::On)),629 Some(sym::off) => Some(AttributeKind::InstrumentFn(InstrumentFnAttr::Off)),630 _ => {631 cx.adcx()632 .expected_specific_argument_strings(nv.value_span, &[sym::on, sym::off]);633 None634 }635 },636 ArgParser::List(l) => {637 cx.adcx().expected_single_argument(l.span, l.len());638 None639 }640 ArgParser::NoArgs => {641 let span = cx.attr_span;642 cx.adcx().expected_specific_argument_strings(span, &[sym::on, sym::off]);643 None644 }645 }646 }647}648649pub(crate) struct SanitizeParser;650651impl SingleAttributeParser for SanitizeParser {652 const PATH: &[Symbol] = &[sym::sanitize];653 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[654 Allow(Target::Fn),655 Allow(Target::Closure),656 Allow(Target::Method(MethodKind::Inherent)),657 Allow(Target::Method(MethodKind::Trait { body: true })),658 Allow(Target::Method(MethodKind::TraitImpl)),659 Allow(Target::Impl { of_trait: false }),660 Allow(Target::Impl { of_trait: true }),661 Allow(Target::Mod),662 Allow(Target::Crate),663 Allow(Target::Static),664 ]);665 const TEMPLATE: AttributeTemplate = template!(List: &[666 r#"address = "on|off""#,667 r#"kernel_address = "on|off""#,668 r#"cfi = "on|off""#,669 r#"hwaddress = "on|off""#,670 r#"kernel_hwaddress = "on|off""#,671 r#"kcfi = "on|off""#,672 r#"memory = "on|off""#,673 r#"memtag = "on|off""#,674 r#"shadow_call_stack = "on|off""#,675 r#"thread = "on|off""#,676 r#"realtime = "nonblocking|blocking|caller""#,677 ]);678 const STABILITY: AttributeStability = unstable!(sanitize);679680 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {681 let list = cx.expect_list(args, cx.attr_span)?;682683 let mut on_set = SanitizerSet::empty();684 let mut off_set = SanitizerSet::empty();685 let mut rtsan = None;686687 for item in list.mixed() {688 let Some((ident, value)) = cx.expect_name_value(item, item.span(), None) else {689 continue;690 };691692 let mut apply = |s: SanitizerSet| {693 let is_on = match value.value_as_str() {694 Some(sym::on) => true,695 Some(sym::off) => false,696 _ => {697 cx.adcx().expected_specific_argument_strings(698 value.value_span,699 &[sym::on, sym::off],700 );701 return;702 }703 };704705 if is_on {706 on_set |= s;707 } else {708 off_set |= s;709 }710 };711712 match ident.name {713 sym::address | sym::kernel_address => {714 apply(SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS)715 }716 sym::cfi => apply(SanitizerSet::CFI),717 sym::kcfi => apply(SanitizerSet::KCFI),718 sym::memory => apply(SanitizerSet::MEMORY),719 sym::memtag => apply(SanitizerSet::MEMTAG),720 sym::shadow_call_stack => apply(SanitizerSet::SHADOWCALLSTACK),721 sym::thread => apply(SanitizerSet::THREAD),722 sym::hwaddress | sym::kernel_hwaddress => {723 apply(SanitizerSet::HWADDRESS | SanitizerSet::KERNELHWADDRESS)724 }725 sym::realtime => match value.value_as_str() {726 Some(sym::nonblocking) => rtsan = Some(RtsanSetting::Nonblocking),727 Some(sym::blocking) => rtsan = Some(RtsanSetting::Blocking),728 Some(sym::caller) => rtsan = Some(RtsanSetting::Caller),729 _ => {730 cx.adcx().expected_specific_argument_strings(731 value.value_span,732 &[sym::nonblocking, sym::blocking, sym::caller],733 );734 }735 },736 _ => {737 cx.adcx().expected_specific_argument_strings(738 ident.span,739 &[740 sym::address,741 sym::kernel_address,742 sym::cfi,743 sym::kcfi,744 sym::memory,745 sym::memtag,746 sym::shadow_call_stack,747 sym::thread,748 sym::hwaddress,749 sym::kernel_hwaddress,750 sym::realtime,751 ],752 );753 }754 }755 }756757 // The sanitizer attribute is only allowed on statics, if only address bits are set758 let all_set_except_address =759 (on_set | off_set) & !(SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS);760 if cx.target == Target::Static761 && let Some(set) = all_set_except_address.iter().next()762 {763 cx.emit_err(SanitizeInvalidStatic {764 span: cx.attr_span,765 field: set.as_str().expect("Since this `SanitizerSet` is returned from an iterator, exactly one field is set")766 });767 }768769 Some(AttributeKind::Sanitize { on_set, off_set, rtsan, span: cx.attr_span })770 }771}772773pub(crate) struct ThreadLocalParser;774775impl NoArgsAttributeParser for ThreadLocalParser {776 const PATH: &[Symbol] = &[sym::thread_local];777 const ALLOWED_TARGETS: AllowedTargets<'_> =778 AllowedTargets::AllowList(&[Allow(Target::Static), Allow(Target::ForeignStatic)]);779 const STABILITY: AttributeStability = unstable!(thread_local);780 const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::ThreadLocal;781}782783pub(crate) struct RustcPassIndirectlyInNonRusticAbisParser;784785impl NoArgsAttributeParser for RustcPassIndirectlyInNonRusticAbisParser {786 const PATH: &[Symbol] = &[sym::rustc_pass_indirectly_in_non_rustic_abis];787 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Struct)]);788 const STABILITY: AttributeStability = unstable!(rustc_attrs);789 const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcPassIndirectlyInNonRusticAbis;790}791792pub(crate) struct RustcEiiForeignItemParser;793794impl NoArgsAttributeParser for RustcEiiForeignItemParser {795 const PATH: &[Symbol] = &[sym::rustc_eii_foreign_item];796 const ALLOWED_TARGETS: AllowedTargets<'_> =797 AllowedTargets::AllowList(&[Allow(Target::ForeignFn), Allow(Target::ForeignStatic)]);798 const STABILITY: AttributeStability = unstable!(eii_internals);799 const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcEiiForeignItem;800}801802pub(crate) struct PatchableFunctionEntryParser;803804impl SingleAttributeParser for PatchableFunctionEntryParser {805 const PATH: &[Symbol] = &[sym::patchable_function_entry];806 const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Fn)]);807 const TEMPLATE: AttributeTemplate =808 template!(List: &["prefix_nops = m, entry_nops = n, section = \"section\""]);809 const STABILITY: AttributeStability = unstable!(patchable_function_entry);810811 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {812 let meta_item_list = cx.expect_list(args, cx.attr_span)?;813814 let mut prefix = None;815 let mut entry = None;816 let mut section = None;817818 if meta_item_list.len() == 0 {819 cx.adcx().expected_at_least_one_argument(meta_item_list.span);820 return None;821 }822823 for item in meta_item_list.mixed() {824 let (ident, value) = cx.expect_name_value(item, item.span(), None)?;825826 let attrib_to_write = match ident.name {827 sym::prefix_nops => {828 // Duplicate prefixes are not allowed829 if prefix.is_some() {830 cx.adcx().duplicate_key(ident.span, sym::prefix_nops);831 return None;832 }833 &mut prefix834 }835 sym::entry_nops => {836 // Duplicate entries are not allowed837 if entry.is_some() {838 cx.adcx().duplicate_key(ident.span, sym::entry_nops);839 return None;840 }841 &mut entry842 }843 sym::section => {844 // Duplicate entries are not allowed845 if section.is_some() {846 cx.adcx().duplicate_key(ident.span, sym::section);847 return None;848 }849 // Only a string type value is allowed.850 let Some(value_str) = value.value_as_str() else {851 cx.adcx().expect_string_literal(value);852 return None;853 };854 // The section name does not allow null characters.855 if value_str.as_str().contains('\0') {856 cx.emit_err(NullOnSection { span: value.value_span });857 }858 // The section name is not allowed to be empty, LLVM does859 // not allow them.860 if value_str.is_empty() {861 cx.emit_err(EmptySection { span: value.value_span });862 }863 section = Some(value_str);864 // Integer parsing is not needed, process next item.865 continue;866 }867 _ => {868 cx.adcx().expected_specific_argument(869 ident.span,870 &[sym::prefix_nops, sym::entry_nops],871 );872 return None;873 }874 };875876 let rustc_ast::LitKind::Int(val, _) = value.value_as_lit().kind else {877 cx.adcx().expected_integer_literal(value.value_span);878 return None;879 };880881 let Ok(val) = val.get().try_into() else {882 cx.adcx().expected_integer_literal_in_range(883 value.value_span,884 u8::MIN as isize,885 u8::MAX as isize,886 );887 return None;888 };889890 *attrib_to_write = Some(val);891 }892893 Some(AttributeKind::PatchableFunctionEntry { prefix, entry, section })894 }895}