compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs RUST 769 lines View on github.com → Search inside
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}

Code quality findings 4

Info: Wildcard imports (`use some::path::*;`) can obscure the origin of names and lead to conflicts. Prefer importing specific items explicitly.
info maintainability wildcard-import
use super::prelude::*;
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
let res = match single.meta_item().and_then(|i| i.path().word().map(|i| i.name)) {
Performance Info: Calling .push() repeatedly inside a loop without prior capacity reservation can lead to multiple reallocations. Consider using `Vec::with_capacity(n)` or `vec.reserve(n)` if the approximate number of elements is known.
info performance push-without-reserve
features.push((Symbol::intern(feature), item.span()));
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info correctness match-wildcard
sym::realtime => match value.value_as_str() {

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.