compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs RUST 863 lines View on github.com → Search inside
1use rustc_feature::AttributeStability;2use rustc_hir::attrs::{3    CoverageAttrKind, InstrumentFnAttr, OptimizeAttr, RtsanSetting, SanitizerSet, UsedBy,4};5use rustc_session::errors::feature_err;6use rustc_span::edition::Edition::Edition2024;78use super::prelude::*;9use crate::attributes::AttributeSafety;10use crate::session_diagnostics::{11    EmptyExportName, NakedFunctionIncompatibleAttribute, NullOnExport, NullOnObjcClass,12    NullOnObjcSelector, ObjcClassExpectedStringLiteral, ObjcSelectorExpectedStringLiteral,13    SanitizeInvalidStatic,14};15use crate::target_checking::Policy::AllowSilent;1617pub(crate) struct OptimizeParser;1819impl SingleAttributeParser for OptimizeParser {20    const PATH: &[Symbol] = &[sym::optimize];21    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[22        Allow(Target::Fn),23        Allow(Target::Closure),24        Allow(Target::Method(MethodKind::Trait { body: true })),25        Allow(Target::Method(MethodKind::TraitImpl)),26        Allow(Target::Method(MethodKind::Inherent)),27    ]);28    const TEMPLATE: AttributeTemplate = template!(List: &["size", "speed", "none"]);29    const STABILITY: AttributeStability = unstable!(optimize_attribute);3031    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {32        let single = cx.expect_single_element_list(args, cx.attr_span)?;3334        let res = match single.meta_item_no_args().and_then(|i| i.path().word().map(|i| i.name)) {35            Some(sym::size) => OptimizeAttr::Size,36            Some(sym::speed) => OptimizeAttr::Speed,37            Some(sym::none) => OptimizeAttr::DoNotOptimize,38            _ => {39                cx.adcx()40                    .expected_specific_argument(single.span(), &[sym::size, sym::speed, sym::none]);41                OptimizeAttr::Default42            }43        };4445        Some(AttributeKind::Optimize(res, cx.attr_span))46    }47}4849pub(crate) struct ColdParser;5051impl NoArgsAttributeParser for ColdParser {52    const PATH: &[Symbol] = &[sym::cold];53    const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn;54    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[55        Allow(Target::Fn),56        Allow(Target::Method(MethodKind::Trait { body: true })),57        Allow(Target::Method(MethodKind::TraitImpl)),58        Allow(Target::Method(MethodKind::Inherent)),59        Allow(Target::ForeignFn),60        Allow(Target::Closure),61    ]);62    const STABILITY: AttributeStability = AttributeStability::Stable;63    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::Cold;64}6566pub(crate) struct CoverageParser;6768impl SingleAttributeParser for CoverageParser {69    const PATH: &[Symbol] = &[sym::coverage];70    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[71        Allow(Target::Fn),72        Allow(Target::Closure),73        Allow(Target::Method(MethodKind::Trait { body: true })),74        Allow(Target::Method(MethodKind::TraitImpl)),75        Allow(Target::Method(MethodKind::Inherent)),76        Allow(Target::Impl { of_trait: true }),77        Allow(Target::Impl { of_trait: false }),78        Allow(Target::Mod),79        Allow(Target::Crate),80    ]);81    const TEMPLATE: AttributeTemplate = template!(OneOf: &[sym::off, sym::on]);82    const STABILITY: AttributeStability = unstable!(coverage_attribute);8384    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {85        let arg = cx.expect_single_element_list(args, cx.attr_span)?;8687        let mut fail_incorrect_argument =88            |span| cx.adcx().expected_specific_argument(span, &[sym::on, sym::off]);8990        let Some(arg) = arg.meta_item_no_args() else {91            fail_incorrect_argument(arg.span());92            return None;93        };9495        let kind = match arg.path().word_sym() {96            Some(sym::off) => CoverageAttrKind::Off,97            Some(sym::on) => CoverageAttrKind::On,98            None | Some(_) => {99                fail_incorrect_argument(arg.span());100                return None;101            }102        };103104        Some(AttributeKind::Coverage(kind))105    }106}107108pub(crate) struct ExportNameParser;109110impl SingleAttributeParser for ExportNameParser {111    const PATH: &[rustc_span::Symbol] = &[sym::export_name];112    const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError;113    const SAFETY: AttributeSafety = AttributeSafety::Unsafe {114        note: "the linker's behavior with multiple libraries exporting duplicate symbol names is undefined and Rust cannot provide guarantees when you manually override them",115        unsafe_since: Some(Edition2024),116    };117    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[118        Allow(Target::Static),119        Allow(Target::Fn),120        Allow(Target::Method(MethodKind::Inherent)),121        Allow(Target::Method(MethodKind::Trait { body: true })),122        Allow(Target::Method(MethodKind::TraitImpl)),123        Warn(Target::Field),124        Warn(Target::Arm),125        Warn(Target::MacroDef),126        Warn(Target::MacroCall),127    ]);128    const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name");129    const STABILITY: AttributeStability = AttributeStability::Stable;130131    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {132        let nv = cx.expect_name_value(args, cx.attr_span, None)?;133        let name = cx.expect_string_literal(nv)?;134        if name.as_str().contains('\0') {135            // `#[export_name = ...]` will be converted to a null-terminated string,136            // so it may not contain any null characters.137            cx.emit_err(NullOnExport { span: cx.attr_span });138            return None;139        }140        if name.is_empty() {141            // LLVM will make up a name if the empty string is given, but that name will be142            // inconsistent between compilation units, causing linker errors.143            cx.emit_err(EmptyExportName { span: cx.attr_span });144            return None;145        }146        Some(AttributeKind::ExportName { name, span: cx.attr_span })147    }148}149150pub(crate) struct RustcObjcClassParser;151152impl SingleAttributeParser for RustcObjcClassParser {153    const PATH: &[rustc_span::Symbol] = &[sym::rustc_objc_class];154    const ALLOWED_TARGETS: AllowedTargets =155        AllowedTargets::AllowList(&[Allow(Target::ForeignStatic)]);156    const TEMPLATE: AttributeTemplate = template!(NameValueStr: "ClassName");157    const STABILITY: AttributeStability = unstable!(rustc_attrs);158159    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {160        let nv = cx.expect_name_value(args, cx.attr_span, None)?;161        let Some(classname) = nv.value_as_str() else {162            // `#[rustc_objc_class = ...]` is expected to be used as an implementation detail163            // inside a standard library macro, but `cx.expected_string_literal` exposes too much.164            // Use a custom error message instead.165            cx.emit_err(ObjcClassExpectedStringLiteral { span: nv.value_span });166            return None;167        };168        if classname.as_str().contains('\0') {169            // `#[rustc_objc_class = ...]` will be converted to a null-terminated string,170            // so it may not contain any null characters.171            cx.emit_err(NullOnObjcClass { span: nv.value_span });172            return None;173        }174        Some(AttributeKind::RustcObjcClass { classname })175    }176}177178pub(crate) struct RustcObjcSelectorParser;179180impl SingleAttributeParser for RustcObjcSelectorParser {181    const PATH: &[rustc_span::Symbol] = &[sym::rustc_objc_selector];182    const ALLOWED_TARGETS: AllowedTargets =183        AllowedTargets::AllowList(&[Allow(Target::ForeignStatic)]);184    const TEMPLATE: AttributeTemplate = template!(NameValueStr: "methodName");185    const STABILITY: AttributeStability = unstable!(rustc_attrs);186187    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {188        let nv = cx.expect_name_value(args, cx.attr_span, None)?;189        let Some(methname) = nv.value_as_str() else {190            // `#[rustc_objc_selector = ...]` is expected to be used as an implementation detail191            // inside a standard library macro, but `cx.expected_string_literal` exposes too much.192            // Use a custom error message instead.193            cx.emit_err(ObjcSelectorExpectedStringLiteral { span: nv.value_span });194            return None;195        };196        if methname.as_str().contains('\0') {197            // `#[rustc_objc_selector = ...]` will be converted to a null-terminated string,198            // so it may not contain any null characters.199            cx.emit_err(NullOnObjcSelector { span: nv.value_span });200            return None;201        }202        Some(AttributeKind::RustcObjcSelector { methname })203    }204}205206#[derive(Default)]207pub(crate) struct NakedParser {208    span: Option<Span>,209}210211impl AttributeParser for NakedParser {212    const ATTRIBUTES: AcceptMapping<Self> =213        &[(&[sym::naked], template!(Word), AttributeStability::Stable, |this, cx, args| {214            let Some(()) = cx.expect_no_args(args) else {215                return;216            };217218            if let Some(earlier) = this.span {219                let span = cx.attr_span;220                cx.warn_unused_duplicate(earlier, span);221            } else {222                this.span = Some(cx.attr_span);223            }224        })];225    const SAFETY: AttributeSafety = AttributeSafety::Unsafe {226        note: "the `#[naked]` attribute adds the safety obligation that the function's body must respect the function’s calling convention, uphold its signature, and either return or diverge (i.e., not fall through past the end of the assembly code).",227        unsafe_since: None,228    };229    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[230        Allow(Target::Fn),231        Allow(Target::Method(MethodKind::Inherent)),232        Allow(Target::Method(MethodKind::Trait { body: true })),233        Allow(Target::Method(MethodKind::TraitImpl)),234        Warn(Target::MacroCall),235    ]);236237    fn finalize(self, cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {238        // FIXME(jdonszelmann): upgrade this list to *parsed* attributes239        // once all of these have parsed forms. That'd make the check much nicer...240        //241        // many attributes don't make sense in combination with #[naked].242        // Notable attributes that are incompatible with `#[naked]` are:243        //244        // * `#[inline]`245        // * `#[track_caller]`246        // * `#[test]`, `#[ignore]`, `#[should_panic]`247        //248        // NOTE: when making changes to this list, check that `error_codes/E0736.md` remains249        // accurate.250        const ALLOW_LIST: &[rustc_span::Symbol] = &[251            // conditional compilation252            sym::cfg_trace,253            sym::cfg_attr_trace,254            // testing (allowed here so better errors can be generated in `rustc_builtin_macros::test`)255            sym::test,256            sym::ignore,257            sym::should_panic,258            sym::bench,259            // diagnostics260            sym::allow,261            sym::warn,262            sym::deny,263            sym::forbid,264            sym::deprecated,265            sym::must_use,266            // abi, linking and FFI267            sym::cold,268            sym::export_name,269            sym::link_section,270            sym::linkage,271            sym::no_mangle,272            sym::instruction_set,273            sym::repr,274            sym::rustc_std_internal_symbol,275            // FIXME(#82232, #143834): temporarily renamed to mitigate `#[align]` nameres ambiguity276            sym::rustc_align,277            sym::rustc_align_static,278            // obviously compatible with self279            sym::naked,280            // documentation281            sym::doc,282        ];283284        let span = self.span?;285286        let Some(tools) = cx.tools else {287            unreachable!("tools required while parsing attributes");288        };289290        // only if we found a naked attribute do we do the somewhat expensive check291        'outer: for other_attr in cx.all_attrs {292            for allowed_attr in ALLOW_LIST {293                if other_attr294                    .segments()295                    .next()296                    .is_some_and(|i| tools.iter().any(|tool| tool.name == i.name))297                {298                    // effectively skips the error message  being emitted below299                    // if it's a tool attribute300                    continue 'outer;301                }302                if other_attr.word_is(*allowed_attr) {303                    // effectively skips the error message  being emitted below304                    // if its an allowed attribute305                    continue 'outer;306                }307308                if other_attr.word_is(sym::target_feature) {309                    if !cx.features().naked_functions_target_feature() {310                        feature_err(311                            &cx.sess(),312                            sym::naked_functions_target_feature,313                            other_attr.span(),314                            "`#[target_feature(/* ... */)]` is currently unstable on `#[naked]` functions",315                        ).emit();316                    }317318                    continue 'outer;319                }320            }321322            cx.emit_err(NakedFunctionIncompatibleAttribute {323                span: other_attr.span(),324                naked_span: span,325                attr: other_attr.get_attribute_path().to_string(),326            });327        }328329        Some(AttributeKind::Naked(span))330    }331}332333pub(crate) struct TrackCallerParser;334impl NoArgsAttributeParser for TrackCallerParser {335    const PATH: &[Symbol] = &[sym::track_caller];336    const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn;337    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[338        Allow(Target::Fn),339        Allow(Target::Method(MethodKind::Inherent)),340        Allow(Target::Method(MethodKind::Trait { body: true })),341        Allow(Target::Method(MethodKind::TraitImpl)),342        Allow(Target::Method(MethodKind::Trait { body: false })), // `#[track_caller]` is inherited from trait methods343        Allow(Target::ForeignFn),344        Allow(Target::Closure),345        Warn(Target::MacroDef),346        Warn(Target::Arm),347        Warn(Target::Field),348        Warn(Target::MacroCall),349    ]);350    const STABILITY: AttributeStability = AttributeStability::Stable;351    const CREATE: fn(Span) -> AttributeKind = AttributeKind::TrackCaller;352}353354pub(crate) struct NoMangleParser;355impl NoArgsAttributeParser for NoMangleParser {356    const PATH: &[Symbol] = &[sym::no_mangle];357    const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn;358    const SAFETY: AttributeSafety = AttributeSafety::Unsafe {359        note: "the linker's behavior with multiple libraries exporting duplicate symbol names is undefined and Rust cannot provide guarantees when you manually override them",360        unsafe_since: Some(Edition2024),361    };362    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[363        Allow(Target::Fn),364        Allow(Target::Static),365        Allow(Target::Method(MethodKind::Inherent)),366        Allow(Target::Method(MethodKind::TraitImpl)),367        AllowSilent(Target::Const), // Handled in the `InvalidNoMangleItems` pass368        Error(Target::Closure),369    ]);370    const STABILITY: AttributeStability = AttributeStability::Stable;371    const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoMangle;372}373374#[derive(Default)]375pub(crate) struct UsedParser {376    first_compiler: Option<Span>,377    first_linker: Option<Span>,378    first_default: Option<Span>,379}380381// A custom `AttributeParser` is used rather than a Simple attribute parser because382// - Specifying two `#[used]` attributes is a warning (but will be an error in the future)383// - But specifying two conflicting attributes: `#[used(compiler)]` and `#[used(linker)]` is already an error today384// We can change this to a Simple parser once the warning becomes an error385impl AttributeParser for UsedParser {386    const ATTRIBUTES: AcceptMapping<Self> = &[(387        &[sym::used],388        template!(Word, List: &["compiler", "linker"]),389        AttributeStability::Stable,390        |group: &mut Self, cx, args| {391            let used_by = match args {392                ArgParser::NoArgs => UsedBy::Default,393                ArgParser::List(list) => {394                    let Some(l) = cx.expect_single(list) else {395                        return;396                    };397398                    match l.meta_item_no_args().and_then(|i| i.path().word_sym()) {399                        Some(sym::compiler) => {400                            if !cx.features().used_with_arg() {401                                feature_err(402                                    &cx.sess(),403                                    sym::used_with_arg,404                                    cx.attr_span,405                                    "`#[used(compiler)]` is currently unstable",406                                )407                                .emit();408                            }409                            UsedBy::Compiler410                        }411                        Some(sym::linker) => {412                            if !cx.features().used_with_arg() {413                                feature_err(414                                    &cx.sess(),415                                    sym::used_with_arg,416                                    cx.attr_span,417                                    "`#[used(linker)]` is currently unstable",418                                )419                                .emit();420                            }421                            UsedBy::Linker422                        }423                        _ => {424                            cx.adcx().expected_specific_argument(425                                l.span(),426                                &[sym::compiler, sym::linker],427                            );428                            return;429                        }430                    }431                }432                ArgParser::NameValue(_) => return,433            };434435            let attr_span = cx.attr_span;436437            // `#[used]` is interpreted as `#[used(linker)]` (though depending on target OS the438            // circumstances are more complicated). While we're checking `used_by`, also report439            // these cross-`UsedBy` duplicates to warn.440            let target = match used_by {441                UsedBy::Compiler => &mut group.first_compiler,442                UsedBy::Linker => {443                    if let Some(prev) = group.first_default {444                        cx.warn_unused_duplicate(prev, attr_span);445                        return;446                    }447                    &mut group.first_linker448                }449                UsedBy::Default => {450                    if let Some(prev) = group.first_linker {451                        cx.warn_unused_duplicate(prev, attr_span);452                        return;453                    }454                    &mut group.first_default455                }456            };457458            if let Some(prev) = *target {459                cx.warn_unused_duplicate(prev, attr_span);460            } else {461                *target = Some(attr_span);462            }463        },464    )];465    const ALLOWED_TARGETS: AllowedTargets =466        AllowedTargets::AllowList(&[Allow(Target::Static), Warn(Target::MacroCall)]);467468    fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {469        // If a specific form of `used` is specified, it takes precedence over generic `#[used]`.470        // If both `linker` and `compiler` are specified, use `linker`.471        Some(match (self.first_compiler, self.first_linker, self.first_default) {472            (_, Some(_), _) => AttributeKind::Used { used_by: UsedBy::Linker },473            (Some(_), _, _) => AttributeKind::Used { used_by: UsedBy::Compiler },474            (_, _, Some(_)) => AttributeKind::Used { used_by: UsedBy::Default },475            (None, None, None) => return None,476        })477    }478}479480fn parse_tf_attribute(481    cx: &mut AcceptContext<'_, '_>,482    args: &ArgParser,483) -> impl IntoIterator<Item = (Symbol, Span)> {484    let mut features = Vec::new();485    let Some(list) = cx.expect_list(args, cx.attr_span) else {486        return features;487    };488    if list.is_empty() {489        let attr_span = cx.attr_span;490        cx.adcx().warn_empty_attribute(attr_span);491        return features;492    }493    for item in list.mixed() {494        let Some((ident, value)) = cx.expect_name_value(item, item.span(), Some(sym::enable))495        else {496            return features;497        };498499        // Validate name500        if ident.name != sym::enable {501            cx.adcx().expected_specific_argument(ident.span, &[sym::enable]);502            return features;503        }504505        // Use value506        let Some(value_str) = cx.expect_string_literal(value) else {507            return features;508        };509        for feature in value_str.as_str().split(",") {510            features.push((Symbol::intern(feature), item.span()));511        }512    }513    features514}515516pub(crate) struct TargetFeatureParser;517518impl CombineAttributeParser for TargetFeatureParser {519    type Item = (Symbol, Span);520    const PATH: &[Symbol] = &[sym::target_feature];521    const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {522        features: items,523        attr_span: span,524        was_forced: false,525    };526    const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]);527    const STABILITY: AttributeStability = AttributeStability::Stable;528529    fn extend(530        cx: &mut AcceptContext<'_, '_>,531        args: &ArgParser,532    ) -> impl IntoIterator<Item = Self::Item> {533        parse_tf_attribute(cx, args)534    }535536    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[537        Allow(Target::Fn),538        Allow(Target::Method(MethodKind::Inherent)),539        Allow(Target::Method(MethodKind::Trait { body: true })),540        Allow(Target::Method(MethodKind::TraitImpl)),541        Warn(Target::Statement),542        Warn(Target::Field),543        Warn(Target::Arm),544        Warn(Target::MacroDef),545        Warn(Target::MacroCall),546    ]);547}548549pub(crate) struct ForceTargetFeatureParser;550551impl CombineAttributeParser for ForceTargetFeatureParser {552    type Item = (Symbol, Span);553    const PATH: &[Symbol] = &[sym::force_target_feature];554    const SAFETY: AttributeSafety = AttributeSafety::Unsafe {555        note: "a function with the signature of the function the attribute is applied to must only be callable if the force-enabled features are guaranteed to be present",556        unsafe_since: None,557    };558    const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {559        features: items,560        attr_span: span,561        was_forced: true,562    };563    const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]);564    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[565        Allow(Target::Fn),566        Allow(Target::Method(MethodKind::Inherent)),567        Allow(Target::Method(MethodKind::Trait { body: true })),568        Allow(Target::Method(MethodKind::TraitImpl)),569    ]);570    const STABILITY: AttributeStability = unstable!(effective_target_features);571572    fn extend(573        cx: &mut AcceptContext<'_, '_>,574        args: &ArgParser,575    ) -> impl IntoIterator<Item = Self::Item> {576        parse_tf_attribute(cx, args)577    }578}579580pub(crate) struct InstrumentFnParser;581582impl SingleAttributeParser for InstrumentFnParser {583    const PATH: &[Symbol] = &[sym::instrument_fn];584    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[585        Allow(Target::Fn),586        Allow(Target::Method(MethodKind::Inherent)),587        Allow(Target::Method(MethodKind::Trait { body: true })),588        Allow(Target::Method(MethodKind::TraitImpl)),589    ]);590    const TEMPLATE: AttributeTemplate = template!(NameValueStr: "on|off");591    const STABILITY: AttributeStability = unstable!(instrument_fn);592593    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {594        let instrument = match args {595            ArgParser::NameValue(nv) => match nv.value_as_str() {596                Some(sym::on) => Some(AttributeKind::InstrumentFn(InstrumentFnAttr::On)),597                Some(sym::off) => Some(AttributeKind::InstrumentFn(InstrumentFnAttr::Off)),598                _ => {599                    cx.adcx()600                        .expected_specific_argument_strings(nv.value_span, &[sym::on, sym::off]);601                    None602                }603            },604            ArgParser::List(l) => {605                cx.adcx().expected_single_argument(l.span, l.len());606                None607            }608            ArgParser::NoArgs => {609                let span = cx.attr_span;610                cx.adcx().expected_specific_argument_strings(span, &[sym::on, sym::off]);611                None612            }613        };614        instrument615    }616}617618pub(crate) struct SanitizeParser;619620impl SingleAttributeParser for SanitizeParser {621    const PATH: &[Symbol] = &[sym::sanitize];622    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[623        Allow(Target::Fn),624        Allow(Target::Closure),625        Allow(Target::Method(MethodKind::Inherent)),626        Allow(Target::Method(MethodKind::Trait { body: true })),627        Allow(Target::Method(MethodKind::TraitImpl)),628        Allow(Target::Impl { of_trait: false }),629        Allow(Target::Impl { of_trait: true }),630        Allow(Target::Mod),631        Allow(Target::Crate),632        Allow(Target::Static),633    ]);634    const TEMPLATE: AttributeTemplate = template!(List: &[635        r#"address = "on|off""#,636        r#"kernel_address = "on|off""#,637        r#"cfi = "on|off""#,638        r#"hwaddress = "on|off""#,639        r#"kernel_hwaddress = "on|off""#,640        r#"kcfi = "on|off""#,641        r#"memory = "on|off""#,642        r#"memtag = "on|off""#,643        r#"shadow_call_stack = "on|off""#,644        r#"thread = "on|off""#,645        r#"realtime = "nonblocking|blocking|caller""#,646    ]);647    const STABILITY: AttributeStability = unstable!(sanitize);648649    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {650        let list = cx.expect_list(args, cx.attr_span)?;651652        let mut on_set = SanitizerSet::empty();653        let mut off_set = SanitizerSet::empty();654        let mut rtsan = None;655656        for item in list.mixed() {657            let Some((ident, value)) = cx.expect_name_value(item, item.span(), None) else {658                continue;659            };660661            let mut apply = |s: SanitizerSet| {662                let is_on = match value.value_as_str() {663                    Some(sym::on) => true,664                    Some(sym::off) => false,665                    Some(_) => {666                        cx.adcx().expected_specific_argument_strings(667                            value.value_span,668                            &[sym::on, sym::off],669                        );670                        return;671                    }672                    None => {673                        cx.adcx().expected_specific_argument_strings(674                            value.value_span,675                            &[sym::on, sym::off],676                        );677                        return;678                    }679                };680681                if is_on {682                    on_set |= s;683                } else {684                    off_set |= s;685                }686            };687688            match ident.name {689                sym::address | sym::kernel_address => {690                    apply(SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS)691                }692                sym::cfi => apply(SanitizerSet::CFI),693                sym::kcfi => apply(SanitizerSet::KCFI),694                sym::memory => apply(SanitizerSet::MEMORY),695                sym::memtag => apply(SanitizerSet::MEMTAG),696                sym::shadow_call_stack => apply(SanitizerSet::SHADOWCALLSTACK),697                sym::thread => apply(SanitizerSet::THREAD),698                sym::hwaddress | sym::kernel_hwaddress => {699                    apply(SanitizerSet::HWADDRESS | SanitizerSet::KERNELHWADDRESS)700                }701                sym::realtime => match value.value_as_str() {702                    Some(sym::nonblocking) => rtsan = Some(RtsanSetting::Nonblocking),703                    Some(sym::blocking) => rtsan = Some(RtsanSetting::Blocking),704                    Some(sym::caller) => rtsan = Some(RtsanSetting::Caller),705                    _ => {706                        cx.adcx().expected_specific_argument_strings(707                            value.value_span,708                            &[sym::nonblocking, sym::blocking, sym::caller],709                        );710                    }711                },712                _ => {713                    cx.adcx().expected_specific_argument_strings(714                        ident.span,715                        &[716                            sym::address,717                            sym::kernel_address,718                            sym::cfi,719                            sym::kcfi,720                            sym::memory,721                            sym::memtag,722                            sym::shadow_call_stack,723                            sym::thread,724                            sym::hwaddress,725                            sym::kernel_hwaddress,726                            sym::realtime,727                        ],728                    );729                    continue;730                }731            }732        }733734        // The sanitizer attribute is only allowed on statics, if only address bits are set735        let all_set_except_address =736            (on_set | off_set) & !(SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS);737        if cx.target == Target::Static738            && let Some(set) = all_set_except_address.iter().next()739        {740            cx.emit_err(SanitizeInvalidStatic {741                span: cx.attr_span,742                field: set.as_str().expect("Since this `SanitizerSet` is returned from an iterator, exactly one field is set")743            });744        }745746        Some(AttributeKind::Sanitize { on_set, off_set, rtsan, span: cx.attr_span })747    }748}749750pub(crate) struct ThreadLocalParser;751752impl NoArgsAttributeParser for ThreadLocalParser {753    const PATH: &[Symbol] = &[sym::thread_local];754    const ALLOWED_TARGETS: AllowedTargets =755        AllowedTargets::AllowList(&[Allow(Target::Static), Allow(Target::ForeignStatic)]);756    const STABILITY: AttributeStability = unstable!(thread_local);757    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::ThreadLocal;758}759760pub(crate) struct RustcPassIndirectlyInNonRusticAbisParser;761762impl NoArgsAttributeParser for RustcPassIndirectlyInNonRusticAbisParser {763    const PATH: &[Symbol] = &[sym::rustc_pass_indirectly_in_non_rustic_abis];764    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]);765    const STABILITY: AttributeStability = unstable!(rustc_attrs);766    const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcPassIndirectlyInNonRusticAbis;767}768769pub(crate) struct RustcEiiForeignItemParser;770771impl NoArgsAttributeParser for RustcEiiForeignItemParser {772    const PATH: &[Symbol] = &[sym::rustc_eii_foreign_item];773    const ALLOWED_TARGETS: AllowedTargets =774        AllowedTargets::AllowList(&[Allow(Target::ForeignFn), Allow(Target::ForeignStatic)]);775    const STABILITY: AttributeStability = unstable!(eii_internals);776    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcEiiForeignItem;777}778779pub(crate) struct PatchableFunctionEntryParser;780781impl SingleAttributeParser for PatchableFunctionEntryParser {782    const PATH: &[Symbol] = &[sym::patchable_function_entry];783    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]);784    const TEMPLATE: AttributeTemplate = template!(List: &["prefix_nops = m, entry_nops = n"]);785    const STABILITY: AttributeStability = unstable!(patchable_function_entry);786787    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {788        let meta_item_list = cx.expect_list(args, cx.attr_span)?;789790        let mut prefix = None;791        let mut entry = None;792793        if meta_item_list.len() == 0 {794            cx.adcx().expected_at_least_one_argument(meta_item_list.span);795            return None;796        }797798        let mut errored = false;799800        for item in meta_item_list.mixed() {801            let Some((ident, value)) = cx.expect_name_value(item, item.span(), None) else {802                continue;803            };804805            let attrib_to_write = match ident.name {806                sym::prefix_nops => {807                    // Duplicate prefixes are not allowed808                    if prefix.is_some() {809                        errored = true;810                        cx.adcx().duplicate_key(ident.span, sym::prefix_nops);811                        continue;812                    }813                    &mut prefix814                }815                sym::entry_nops => {816                    // Duplicate entries are not allowed817                    if entry.is_some() {818                        errored = true;819                        cx.adcx().duplicate_key(ident.span, sym::entry_nops);820                        continue;821                    }822                    &mut entry823                }824                _ => {825                    errored = true;826                    cx.adcx().expected_specific_argument(827                        ident.span,828                        &[sym::prefix_nops, sym::entry_nops],829                    );830                    continue;831                }832            };833834            let rustc_ast::LitKind::Int(val, _) = value.value_as_lit().kind else {835                errored = true;836                cx.adcx().expected_integer_literal(value.value_span);837                continue;838            };839840            let Ok(val) = val.get().try_into() else {841                errored = true;842                cx.adcx().expected_integer_literal_in_range(843                    value.value_span,844                    u8::MIN as isize,845                    u8::MAX as isize,846                );847                continue;848            };849850            *attrib_to_write = Some(val);851        }852853        if errored {854            None855        } else {856            Some(AttributeKind::PatchableFunctionEntry {857                prefix: prefix.unwrap_or(0),858                entry: entry.unwrap_or(0),859            })860        }861    }862}

Code quality findings 7

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
let instrument = 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
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.