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

Code quality findings 8

Warning: '.expect()' will panic with a custom message on None/Err. While better than unwrap() for debugging, prefer non-panicking error handling in production code (match, if let, ?).
warning correctness expect-usage
field: set.as_str().expect("Since this `SanitizerSet` is returned from an iterator, exactly one field is set")
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_no_args().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
match args {
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
ArgParser::NameValue(nv) => match nv.value_as_str() {
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 is_on = match value.value_as_str() {
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.