Info: Wildcard imports (`use some::path::*;`) can obscure the origin of names and lead to conflicts. Prefer importing specific items explicitly.
use super::prelude::*;
1use rustc_hir::attrs::{CoverageAttrKind, OptimizeAttr, RtsanSetting, SanitizerSet, UsedBy};2use rustc_session::errors::feature_err;3use rustc_span::edition::Edition::Edition2024;45use super::prelude::*;6use crate::attributes::AttributeSafety;7use crate::session_diagnostics::{8 NakedFunctionIncompatibleAttribute, NullOnExport, NullOnObjcClass, NullOnObjcSelector,9 ObjcClassExpectedStringLiteral, ObjcSelectorExpectedStringLiteral,10};11use crate::target_checking::Policy::AllowSilent;1213pub(crate) struct OptimizeParser;1415impl SingleAttributeParser for OptimizeParser {16 const PATH: &[Symbol] = &[sym::optimize];17 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[18 Allow(Target::Fn),19 Allow(Target::Closure),20 Allow(Target::Method(MethodKind::Trait { body: true })),21 Allow(Target::Method(MethodKind::TraitImpl)),22 Allow(Target::Method(MethodKind::Inherent)),23 ]);24 const TEMPLATE: AttributeTemplate = template!(List: &["size", "speed", "none"]);2526 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {27 let single = cx.expect_single_element_list(args, cx.attr_span)?;2829 let res = match single.meta_item().and_then(|i| i.path().word().map(|i| i.name)) {30 Some(sym::size) => OptimizeAttr::Size,31 Some(sym::speed) => OptimizeAttr::Speed,32 Some(sym::none) => OptimizeAttr::DoNotOptimize,33 _ => {34 cx.adcx()35 .expected_specific_argument(single.span(), &[sym::size, sym::speed, sym::none]);36 OptimizeAttr::Default37 }38 };3940 Some(AttributeKind::Optimize(res, cx.attr_span))41 }42}4344pub(crate) struct ColdParser;4546impl NoArgsAttributeParser for ColdParser {47 const PATH: &[Symbol] = &[sym::cold];48 const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn;49 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[50 Allow(Target::Fn),51 Allow(Target::Method(MethodKind::Trait { body: true })),52 Allow(Target::Method(MethodKind::TraitImpl)),53 Allow(Target::Method(MethodKind::Inherent)),54 Allow(Target::ForeignFn),55 Allow(Target::Closure),56 ]);57 const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::Cold;58}5960pub(crate) struct CoverageParser;6162impl SingleAttributeParser for CoverageParser {63 const PATH: &[Symbol] = &[sym::coverage];64 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[65 Allow(Target::Fn),66 Allow(Target::Closure),67 Allow(Target::Method(MethodKind::Trait { body: true })),68 Allow(Target::Method(MethodKind::TraitImpl)),69 Allow(Target::Method(MethodKind::Inherent)),70 Allow(Target::Impl { of_trait: true }),71 Allow(Target::Impl { of_trait: false }),72 Allow(Target::Mod),73 Allow(Target::Crate),74 ]);75 const TEMPLATE: AttributeTemplate = template!(OneOf: &[sym::off, sym::on]);7677 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {78 let arg = cx.expect_single_element_list(args, cx.attr_span)?;7980 let mut fail_incorrect_argument =81 |span| cx.adcx().expected_specific_argument(span, &[sym::on, sym::off]);8283 let Some(arg) = arg.meta_item() else {84 fail_incorrect_argument(arg.span());85 return None;86 };8788 let kind = match arg.path().word_sym() {89 Some(sym::off) => CoverageAttrKind::Off,90 Some(sym::on) => CoverageAttrKind::On,91 None | Some(_) => {92 fail_incorrect_argument(arg.span());93 return None;94 }95 };9697 Some(AttributeKind::Coverage(kind))98 }99}100101pub(crate) struct ExportNameParser;102103impl SingleAttributeParser for ExportNameParser {104 const PATH: &[rustc_span::Symbol] = &[sym::export_name];105 const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError;106 const SAFETY: AttributeSafety = AttributeSafety::Unsafe { unsafe_since: Some(Edition2024) };107 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[108 Allow(Target::Static),109 Allow(Target::Fn),110 Allow(Target::Method(MethodKind::Inherent)),111 Allow(Target::Method(MethodKind::Trait { body: true })),112 Allow(Target::Method(MethodKind::TraitImpl)),113 Warn(Target::Field),114 Warn(Target::Arm),115 Warn(Target::MacroDef),116 Warn(Target::MacroCall),117 ]);118 const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name");119120 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {121 let nv = cx.expect_name_value(args, cx.attr_span, None)?;122 let Some(name) = nv.value_as_str() else {123 cx.adcx().expected_string_literal(nv.value_span, Some(nv.value_as_lit()));124 return None;125 };126 if name.as_str().contains('\0') {127 // `#[export_name = ...]` will be converted to a null-terminated string,128 // so it may not contain any null characters.129 cx.emit_err(NullOnExport { span: cx.attr_span });130 return None;131 }132 Some(AttributeKind::ExportName { name, span: cx.attr_span })133 }134}135136pub(crate) struct RustcObjcClassParser;137138impl SingleAttributeParser for RustcObjcClassParser {139 const PATH: &[rustc_span::Symbol] = &[sym::rustc_objc_class];140 const ALLOWED_TARGETS: AllowedTargets =141 AllowedTargets::AllowList(&[Allow(Target::ForeignStatic)]);142 const TEMPLATE: AttributeTemplate = template!(NameValueStr: "ClassName");143144 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {145 let nv = cx.expect_name_value(args, cx.attr_span, None)?;146 let Some(classname) = nv.value_as_str() else {147 // `#[rustc_objc_class = ...]` is expected to be used as an implementation detail148 // inside a standard library macro, but `cx.expected_string_literal` exposes too much.149 // Use a custom error message instead.150 cx.emit_err(ObjcClassExpectedStringLiteral { span: nv.value_span });151 return None;152 };153 if classname.as_str().contains('\0') {154 // `#[rustc_objc_class = ...]` will be converted to a null-terminated string,155 // so it may not contain any null characters.156 cx.emit_err(NullOnObjcClass { span: nv.value_span });157 return None;158 }159 Some(AttributeKind::RustcObjcClass { classname })160 }161}162163pub(crate) struct RustcObjcSelectorParser;164165impl SingleAttributeParser for RustcObjcSelectorParser {166 const PATH: &[rustc_span::Symbol] = &[sym::rustc_objc_selector];167 const ALLOWED_TARGETS: AllowedTargets =168 AllowedTargets::AllowList(&[Allow(Target::ForeignStatic)]);169 const TEMPLATE: AttributeTemplate = template!(NameValueStr: "methodName");170171 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {172 let nv = cx.expect_name_value(args, cx.attr_span, None)?;173 let Some(methname) = nv.value_as_str() else {174 // `#[rustc_objc_selector = ...]` is expected to be used as an implementation detail175 // inside a standard library macro, but `cx.expected_string_literal` exposes too much.176 // Use a custom error message instead.177 cx.emit_err(ObjcSelectorExpectedStringLiteral { span: nv.value_span });178 return None;179 };180 if methname.as_str().contains('\0') {181 // `#[rustc_objc_selector = ...]` will be converted to a null-terminated string,182 // so it may not contain any null characters.183 cx.emit_err(NullOnObjcSelector { span: nv.value_span });184 return None;185 }186 Some(AttributeKind::RustcObjcSelector { methname })187 }188}189190#[derive(Default)]191pub(crate) struct NakedParser {192 span: Option<Span>,193}194195impl AttributeParser for NakedParser {196 const ATTRIBUTES: AcceptMapping<Self> =197 &[(&[sym::naked], template!(Word), |this, cx, args| {198 let Some(()) = cx.expect_no_args(args) else {199 return;200 };201202 if let Some(earlier) = this.span {203 let span = cx.attr_span;204 cx.warn_unused_duplicate(earlier, span);205 } else {206 this.span = Some(cx.attr_span);207 }208 })];209 const SAFETY: AttributeSafety = AttributeSafety::Unsafe { unsafe_since: None };210 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[211 Allow(Target::Fn),212 Allow(Target::Method(MethodKind::Inherent)),213 Allow(Target::Method(MethodKind::Trait { body: true })),214 Allow(Target::Method(MethodKind::TraitImpl)),215 Warn(Target::MacroCall),216 ]);217218 fn finalize(self, cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {219 // FIXME(jdonszelmann): upgrade this list to *parsed* attributes220 // once all of these have parsed forms. That'd make the check much nicer...221 //222 // many attributes don't make sense in combination with #[naked].223 // Notable attributes that are incompatible with `#[naked]` are:224 //225 // * `#[inline]`226 // * `#[track_caller]`227 // * `#[test]`, `#[ignore]`, `#[should_panic]`228 //229 // NOTE: when making changes to this list, check that `error_codes/E0736.md` remains230 // accurate.231 const ALLOW_LIST: &[rustc_span::Symbol] = &[232 // conditional compilation233 sym::cfg_trace,234 sym::cfg_attr_trace,235 // testing (allowed here so better errors can be generated in `rustc_builtin_macros::test`)236 sym::test,237 sym::ignore,238 sym::should_panic,239 sym::bench,240 // diagnostics241 sym::allow,242 sym::warn,243 sym::deny,244 sym::forbid,245 sym::deprecated,246 sym::must_use,247 // abi, linking and FFI248 sym::cold,249 sym::export_name,250 sym::link_section,251 sym::linkage,252 sym::no_mangle,253 sym::instruction_set,254 sym::repr,255 sym::rustc_std_internal_symbol,256 // FIXME(#82232, #143834): temporarily renamed to mitigate `#[align]` nameres ambiguity257 sym::rustc_align,258 sym::rustc_align_static,259 // obviously compatible with self260 sym::naked,261 // documentation262 sym::doc,263 ];264265 let span = self.span?;266267 let Some(tools) = cx.tools else {268 unreachable!("tools required while parsing attributes");269 };270271 // only if we found a naked attribute do we do the somewhat expensive check272 'outer: for other_attr in cx.all_attrs {273 for allowed_attr in ALLOW_LIST {274 if other_attr275 .segments()276 .next()277 .is_some_and(|i| tools.iter().any(|tool| tool.name == i.name))278 {279 // effectively skips the error message being emitted below280 // if it's a tool attribute281 continue 'outer;282 }283 if other_attr.word_is(*allowed_attr) {284 // effectively skips the error message being emitted below285 // if its an allowed attribute286 continue 'outer;287 }288289 if other_attr.word_is(sym::target_feature) {290 if !cx.features().naked_functions_target_feature() {291 feature_err(292 &cx.sess(),293 sym::naked_functions_target_feature,294 other_attr.span(),295 "`#[target_feature(/* ... */)]` is currently unstable on `#[naked]` functions",296 ).emit();297 }298299 continue 'outer;300 }301 }302303 cx.emit_err(NakedFunctionIncompatibleAttribute {304 span: other_attr.span(),305 naked_span: span,306 attr: other_attr.get_attribute_path().to_string(),307 });308 }309310 Some(AttributeKind::Naked(span))311 }312}313314pub(crate) struct TrackCallerParser;315impl NoArgsAttributeParser for TrackCallerParser {316 const PATH: &[Symbol] = &[sym::track_caller];317 const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn;318 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[319 Allow(Target::Fn),320 Allow(Target::Method(MethodKind::Inherent)),321 Allow(Target::Method(MethodKind::Trait { body: true })),322 Allow(Target::Method(MethodKind::TraitImpl)),323 Allow(Target::Method(MethodKind::Trait { body: false })), // `#[track_caller]` is inherited from trait methods324 Allow(Target::ForeignFn),325 Allow(Target::Closure),326 Warn(Target::MacroDef),327 Warn(Target::Arm),328 Warn(Target::Field),329 Warn(Target::MacroCall),330 ]);331 const CREATE: fn(Span) -> AttributeKind = AttributeKind::TrackCaller;332}333334pub(crate) struct NoMangleParser;335impl NoArgsAttributeParser for NoMangleParser {336 const PATH: &[Symbol] = &[sym::no_mangle];337 const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn;338 const SAFETY: AttributeSafety = AttributeSafety::Unsafe { unsafe_since: Some(Edition2024) };339 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[340 Allow(Target::Fn),341 Allow(Target::Static),342 Allow(Target::Method(MethodKind::Inherent)),343 Allow(Target::Method(MethodKind::TraitImpl)),344 AllowSilent(Target::Const), // Handled in the `InvalidNoMangleItems` pass345 Error(Target::Closure),346 ]);347 const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoMangle;348}349350#[derive(Default)]351pub(crate) struct UsedParser {352 first_compiler: Option<Span>,353 first_linker: Option<Span>,354 first_default: Option<Span>,355}356357// A custom `AttributeParser` is used rather than a Simple attribute parser because358// - Specifying two `#[used]` attributes is a warning (but will be an error in the future)359// - But specifying two conflicting attributes: `#[used(compiler)]` and `#[used(linker)]` is already an error today360// We can change this to a Simple parser once the warning becomes an error361impl AttributeParser for UsedParser {362 const ATTRIBUTES: AcceptMapping<Self> = &[(363 &[sym::used],364 template!(Word, List: &["compiler", "linker"]),365 |group: &mut Self, cx, args| {366 let used_by = match args {367 ArgParser::NoArgs => UsedBy::Default,368 ArgParser::List(list) => {369 let Some(l) = cx.expect_single(list) else {370 return;371 };372373 match l.meta_item().and_then(|i| i.path().word_sym()) {374 Some(sym::compiler) => {375 if !cx.features().used_with_arg() {376 feature_err(377 &cx.sess(),378 sym::used_with_arg,379 cx.attr_span,380 "`#[used(compiler)]` is currently unstable",381 )382 .emit();383 }384 UsedBy::Compiler385 }386 Some(sym::linker) => {387 if !cx.features().used_with_arg() {388 feature_err(389 &cx.sess(),390 sym::used_with_arg,391 cx.attr_span,392 "`#[used(linker)]` is currently unstable",393 )394 .emit();395 }396 UsedBy::Linker397 }398 _ => {399 cx.adcx().expected_specific_argument(400 l.span(),401 &[sym::compiler, sym::linker],402 );403 return;404 }405 }406 }407 ArgParser::NameValue(_) => return,408 };409410 let attr_span = cx.attr_span;411412 // `#[used]` is interpreted as `#[used(linker)]` (though depending on target OS the413 // circumstances are more complicated). While we're checking `used_by`, also report414 // these cross-`UsedBy` duplicates to warn.415 let target = match used_by {416 UsedBy::Compiler => &mut group.first_compiler,417 UsedBy::Linker => {418 if let Some(prev) = group.first_default {419 cx.warn_unused_duplicate(prev, attr_span);420 return;421 }422 &mut group.first_linker423 }424 UsedBy::Default => {425 if let Some(prev) = group.first_linker {426 cx.warn_unused_duplicate(prev, attr_span);427 return;428 }429 &mut group.first_default430 }431 };432433 if let Some(prev) = *target {434 cx.warn_unused_duplicate(prev, attr_span);435 } else {436 *target = Some(attr_span);437 }438 },439 )];440 const ALLOWED_TARGETS: AllowedTargets =441 AllowedTargets::AllowList(&[Allow(Target::Static), Warn(Target::MacroCall)]);442443 fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {444 // If a specific form of `used` is specified, it takes precedence over generic `#[used]`.445 // If both `linker` and `compiler` are specified, use `linker`.446 Some(match (self.first_compiler, self.first_linker, self.first_default) {447 (_, Some(_), _) => AttributeKind::Used { used_by: UsedBy::Linker },448 (Some(_), _, _) => AttributeKind::Used { used_by: UsedBy::Compiler },449 (_, _, Some(_)) => AttributeKind::Used { used_by: UsedBy::Default },450 (None, None, None) => return None,451 })452 }453}454455fn parse_tf_attribute(456 cx: &mut AcceptContext<'_, '_>,457 args: &ArgParser,458) -> impl IntoIterator<Item = (Symbol, Span)> {459 let mut features = Vec::new();460 let Some(list) = cx.expect_list(args, cx.attr_span) else {461 return features;462 };463 if list.is_empty() {464 let attr_span = cx.attr_span;465 cx.adcx().warn_empty_attribute(attr_span);466 return features;467 }468 for item in list.mixed() {469 let Some((ident, value)) = cx.expect_name_value(item, item.span(), Some(sym::enable))470 else {471 return features;472 };473474 // Validate name475 if ident.name != sym::enable {476 cx.adcx().expected_specific_argument(ident.span, &[sym::enable]);477 return features;478 }479480 // Use value481 let Some(value_str) = value.value_as_str() else {482 cx.adcx().expected_string_literal(value.value_span, Some(value.value_as_lit()));483 return features;484 };485 for feature in value_str.as_str().split(",") {486 features.push((Symbol::intern(feature), item.span()));487 }488 }489 features490}491492pub(crate) struct TargetFeatureParser;493494impl CombineAttributeParser for TargetFeatureParser {495 type Item = (Symbol, Span);496 const PATH: &[Symbol] = &[sym::target_feature];497 const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {498 features: items,499 attr_span: span,500 was_forced: false,501 };502 const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]);503504 fn extend(505 cx: &mut AcceptContext<'_, '_>,506 args: &ArgParser,507 ) -> impl IntoIterator<Item = Self::Item> {508 parse_tf_attribute(cx, args)509 }510511 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[512 Allow(Target::Fn),513 Allow(Target::Method(MethodKind::Inherent)),514 Allow(Target::Method(MethodKind::Trait { body: true })),515 Allow(Target::Method(MethodKind::TraitImpl)),516 Warn(Target::Statement),517 Warn(Target::Field),518 Warn(Target::Arm),519 Warn(Target::MacroDef),520 Warn(Target::MacroCall),521 ]);522}523524pub(crate) struct ForceTargetFeatureParser;525526impl CombineAttributeParser for ForceTargetFeatureParser {527 type Item = (Symbol, Span);528 const PATH: &[Symbol] = &[sym::force_target_feature];529 const SAFETY: AttributeSafety = AttributeSafety::Unsafe { unsafe_since: None };530 const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {531 features: items,532 attr_span: span,533 was_forced: true,534 };535 const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]);536 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 ]);542543 fn extend(544 cx: &mut AcceptContext<'_, '_>,545 args: &ArgParser,546 ) -> impl IntoIterator<Item = Self::Item> {547 parse_tf_attribute(cx, args)548 }549}550551pub(crate) struct SanitizeParser;552553impl SingleAttributeParser for SanitizeParser {554 const PATH: &[Symbol] = &[sym::sanitize];555556 // FIXME: still checked in check_attrs.rs557 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);558559 const TEMPLATE: AttributeTemplate = template!(List: &[560 r#"address = "on|off""#,561 r#"kernel_address = "on|off""#,562 r#"cfi = "on|off""#,563 r#"hwaddress = "on|off""#,564 r#"kernel_hwaddress = "on|off""#,565 r#"kcfi = "on|off""#,566 r#"memory = "on|off""#,567 r#"memtag = "on|off""#,568 r#"shadow_call_stack = "on|off""#,569 r#"thread = "on|off""#,570 r#"realtime = "nonblocking|blocking|caller""#,571 ]);572573 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {574 let list = cx.expect_list(args, cx.attr_span)?;575576 let mut on_set = SanitizerSet::empty();577 let mut off_set = SanitizerSet::empty();578 let mut rtsan = None;579580 for item in list.mixed() {581 let Some((ident, value)) = cx.expect_name_value(item, item.span(), None) else {582 continue;583 };584585 let mut apply = |s: SanitizerSet| {586 let is_on = match value.value_as_str() {587 Some(sym::on) => true,588 Some(sym::off) => false,589 Some(_) => {590 cx.adcx().expected_specific_argument_strings(591 value.value_span,592 &[sym::on, sym::off],593 );594 return;595 }596 None => {597 cx.adcx()598 .expected_string_literal(value.value_span, Some(value.value_as_lit()));599 return;600 }601 };602603 if is_on {604 on_set |= s;605 } else {606 off_set |= s;607 }608 };609610 match ident.name {611 sym::address | sym::kernel_address => {612 apply(SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS)613 }614 sym::cfi => apply(SanitizerSet::CFI),615 sym::kcfi => apply(SanitizerSet::KCFI),616 sym::memory => apply(SanitizerSet::MEMORY),617 sym::memtag => apply(SanitizerSet::MEMTAG),618 sym::shadow_call_stack => apply(SanitizerSet::SHADOWCALLSTACK),619 sym::thread => apply(SanitizerSet::THREAD),620 sym::hwaddress | sym::kernel_hwaddress => {621 apply(SanitizerSet::HWADDRESS | SanitizerSet::KERNELHWADDRESS)622 }623 sym::realtime => match value.value_as_str() {624 Some(sym::nonblocking) => rtsan = Some(RtsanSetting::Nonblocking),625 Some(sym::blocking) => rtsan = Some(RtsanSetting::Blocking),626 Some(sym::caller) => rtsan = Some(RtsanSetting::Caller),627 _ => {628 cx.adcx().expected_specific_argument_strings(629 value.value_span,630 &[sym::nonblocking, sym::blocking, sym::caller],631 );632 }633 },634 _ => {635 cx.adcx().expected_specific_argument_strings(636 ident.span,637 &[638 sym::address,639 sym::kernel_address,640 sym::cfi,641 sym::kcfi,642 sym::memory,643 sym::memtag,644 sym::shadow_call_stack,645 sym::thread,646 sym::hwaddress,647 sym::kernel_hwaddress,648 sym::realtime,649 ],650 );651 continue;652 }653 }654 }655656 Some(AttributeKind::Sanitize { on_set, off_set, rtsan, span: cx.attr_span })657 }658}659660pub(crate) struct ThreadLocalParser;661662impl NoArgsAttributeParser for ThreadLocalParser {663 const PATH: &[Symbol] = &[sym::thread_local];664 const ALLOWED_TARGETS: AllowedTargets =665 AllowedTargets::AllowList(&[Allow(Target::Static), Allow(Target::ForeignStatic)]);666 const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::ThreadLocal;667}668669pub(crate) struct RustcPassIndirectlyInNonRusticAbisParser;670671impl NoArgsAttributeParser for RustcPassIndirectlyInNonRusticAbisParser {672 const PATH: &[Symbol] = &[sym::rustc_pass_indirectly_in_non_rustic_abis];673 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]);674 const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcPassIndirectlyInNonRusticAbis;675}676677pub(crate) struct RustcEiiForeignItemParser;678679impl NoArgsAttributeParser for RustcEiiForeignItemParser {680 const PATH: &[Symbol] = &[sym::rustc_eii_foreign_item];681 const ALLOWED_TARGETS: AllowedTargets =682 AllowedTargets::AllowList(&[Allow(Target::ForeignFn), Allow(Target::ForeignStatic)]);683 const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcEiiForeignItem;684}685686pub(crate) struct PatchableFunctionEntryParser;687688impl SingleAttributeParser for PatchableFunctionEntryParser {689 const PATH: &[Symbol] = &[sym::patchable_function_entry];690 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]);691 const TEMPLATE: AttributeTemplate = template!(List: &["prefix_nops = m, entry_nops = n"]);692693 fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {694 let meta_item_list = cx.expect_list(args, cx.attr_span)?;695696 let mut prefix = None;697 let mut entry = None;698699 if meta_item_list.len() == 0 {700 cx.adcx().expected_at_least_one_argument(meta_item_list.span);701 return None;702 }703704 let mut errored = false;705706 for item in meta_item_list.mixed() {707 let Some((ident, value)) = cx.expect_name_value(item, item.span(), None) else {708 continue;709 };710711 let attrib_to_write = match ident.name {712 sym::prefix_nops => {713 // Duplicate prefixes are not allowed714 if prefix.is_some() {715 errored = true;716 cx.adcx().duplicate_key(ident.span, sym::prefix_nops);717 continue;718 }719 &mut prefix720 }721 sym::entry_nops => {722 // Duplicate entries are not allowed723 if entry.is_some() {724 errored = true;725 cx.adcx().duplicate_key(ident.span, sym::entry_nops);726 continue;727 }728 &mut entry729 }730 _ => {731 errored = true;732 cx.adcx().expected_specific_argument(733 ident.span,734 &[sym::prefix_nops, sym::entry_nops],735 );736 continue;737 }738 };739740 let rustc_ast::LitKind::Int(val, _) = value.value_as_lit().kind else {741 errored = true;742 cx.adcx().expected_integer_literal(value.value_span);743 continue;744 };745746 let Ok(val) = val.get().try_into() else {747 errored = true;748 cx.adcx().expected_integer_literal_in_range(749 value.value_span,750 u8::MIN as isize,751 u8::MAX as isize,752 );753 continue;754 };755756 *attrib_to_write = Some(val);757 }758759 if errored {760 None761 } else {762 Some(AttributeKind::PatchableFunctionEntry {763 prefix: prefix.unwrap_or(0),764 entry: entry.unwrap_or(0),765 })766 }767 }768}
Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.