Skip to main content

rustc_attr_parsing/attributes/
codegen_attrs.rs

1use rustc_feature::AttributeStability;
2use rustc_hir::attrs::{CoverageAttrKind, OptimizeAttr, RtsanSetting, SanitizerSet, UsedBy};
3use rustc_session::errors::feature_err;
4use rustc_span::edition::Edition::Edition2024;
5
6use super::prelude::*;
7use crate::attributes::AttributeSafety;
8use crate::session_diagnostics::{
9    EmptyExportName, NakedFunctionIncompatibleAttribute, NullOnExport, NullOnObjcClass,
10    NullOnObjcSelector, ObjcClassExpectedStringLiteral, ObjcSelectorExpectedStringLiteral,
11};
12use crate::target_checking::Policy::AllowSilent;
13
14pub(crate) struct OptimizeParser;
15
16impl SingleAttributeParser for OptimizeParser {
17    const PATH: &[Symbol] = &[sym::optimize];
18    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
19        Allow(Target::Fn),
20        Allow(Target::Closure),
21        Allow(Target::Method(MethodKind::Trait { body: true })),
22        Allow(Target::Method(MethodKind::TraitImpl)),
23        Allow(Target::Method(MethodKind::Inherent)),
24    ]);
25    const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
    word: false,
    list: Some(&["size", "speed", "none"]),
    one_of: &[],
    name_value_str: None,
    docs: None,
}template!(List: &["size", "speed", "none"]);
26    const STABILITY: AttributeStability = AttributeStability::Unstable {
    gate_name: rustc_span::sym::optimize_attribute,
    gate_check: rustc_feature::Features::optimize_attribute,
    notes: &[],
}unstable!(optimize_attribute);
27
28    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
29        let single = cx.expect_single_element_list(args, cx.attr_span)?;
30
31        let res = match single.meta_item_no_args().and_then(|i| i.path().word().map(|i| i.name)) {
32            Some(sym::size) => OptimizeAttr::Size,
33            Some(sym::speed) => OptimizeAttr::Speed,
34            Some(sym::none) => OptimizeAttr::DoNotOptimize,
35            _ => {
36                cx.adcx()
37                    .expected_specific_argument(single.span(), &[sym::size, sym::speed, sym::none]);
38                OptimizeAttr::Default
39            }
40        };
41
42        Some(AttributeKind::Optimize(res, cx.attr_span))
43    }
44}
45
46pub(crate) struct ColdParser;
47
48impl NoArgsAttributeParser for ColdParser {
49    const PATH: &[Symbol] = &[sym::cold];
50    const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn;
51    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[
52        Allow(Target::Fn),
53        Allow(Target::Method(MethodKind::Trait { body: true })),
54        Allow(Target::Method(MethodKind::TraitImpl)),
55        Allow(Target::Method(MethodKind::Inherent)),
56        Allow(Target::ForeignFn),
57        Allow(Target::Closure),
58    ]);
59    const STABILITY: AttributeStability = AttributeStability::Stable;
60    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::Cold;
61}
62
63pub(crate) struct CoverageParser;
64
65impl SingleAttributeParser for CoverageParser {
66    const PATH: &[Symbol] = &[sym::coverage];
67    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
68        Allow(Target::Fn),
69        Allow(Target::Closure),
70        Allow(Target::Method(MethodKind::Trait { body: true })),
71        Allow(Target::Method(MethodKind::TraitImpl)),
72        Allow(Target::Method(MethodKind::Inherent)),
73        Allow(Target::Impl { of_trait: true }),
74        Allow(Target::Impl { of_trait: false }),
75        Allow(Target::Mod),
76        Allow(Target::Crate),
77    ]);
78    const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
    word: false,
    list: None,
    one_of: &[sym::off, sym::on],
    name_value_str: None,
    docs: None,
}template!(OneOf: &[sym::off, sym::on]);
79    const STABILITY: AttributeStability = AttributeStability::Unstable {
    gate_name: rustc_span::sym::coverage_attribute,
    gate_check: rustc_feature::Features::coverage_attribute,
    notes: &[],
}unstable!(coverage_attribute);
80
81    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
82        let arg = cx.expect_single_element_list(args, cx.attr_span)?;
83
84        let mut fail_incorrect_argument =
85            |span| cx.adcx().expected_specific_argument(span, &[sym::on, sym::off]);
86
87        let Some(arg) = arg.meta_item_no_args() else {
88            fail_incorrect_argument(arg.span());
89            return None;
90        };
91
92        let kind = match arg.path().word_sym() {
93            Some(sym::off) => CoverageAttrKind::Off,
94            Some(sym::on) => CoverageAttrKind::On,
95            None | Some(_) => {
96                fail_incorrect_argument(arg.span());
97                return None;
98            }
99        };
100
101        Some(AttributeKind::Coverage(kind))
102    }
103}
104
105pub(crate) struct ExportNameParser;
106
107impl SingleAttributeParser for ExportNameParser {
108    const PATH: &[rustc_span::Symbol] = &[sym::export_name];
109    const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError;
110    const SAFETY: AttributeSafety = AttributeSafety::Unsafe { unsafe_since: Some(Edition2024) };
111    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
112        Allow(Target::Static),
113        Allow(Target::Fn),
114        Allow(Target::Method(MethodKind::Inherent)),
115        Allow(Target::Method(MethodKind::Trait { body: true })),
116        Allow(Target::Method(MethodKind::TraitImpl)),
117        Warn(Target::Field),
118        Warn(Target::Arm),
119        Warn(Target::MacroDef),
120        Warn(Target::MacroCall),
121    ]);
122    const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
    word: false,
    list: None,
    one_of: &[],
    name_value_str: Some(&["name"]),
    docs: None,
}template!(NameValueStr: "name");
123    const STABILITY: AttributeStability = AttributeStability::Stable;
124
125    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
126        let nv = cx.expect_name_value(args, cx.attr_span, None)?;
127        let name = cx.expect_string_literal(nv)?;
128        if name.as_str().contains('\0') {
129            // `#[export_name = ...]` will be converted to a null-terminated string,
130            // so it may not contain any null characters.
131            cx.emit_err(NullOnExport { span: cx.attr_span });
132            return None;
133        }
134        if name.is_empty() {
135            // LLVM will make up a name if the empty string is given, but that name will be
136            // inconsistent between compilation units, causing linker errors.
137            cx.emit_err(EmptyExportName { span: cx.attr_span });
138            return None;
139        }
140        Some(AttributeKind::ExportName { name, span: cx.attr_span })
141    }
142}
143
144pub(crate) struct RustcObjcClassParser;
145
146impl SingleAttributeParser for RustcObjcClassParser {
147    const PATH: &[rustc_span::Symbol] = &[sym::rustc_objc_class];
148    const ALLOWED_TARGETS: AllowedTargets =
149        AllowedTargets::AllowList(&[Allow(Target::ForeignStatic)]);
150    const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
    word: false,
    list: None,
    one_of: &[],
    name_value_str: Some(&["ClassName"]),
    docs: None,
}template!(NameValueStr: "ClassName");
151    const STABILITY: AttributeStability = AttributeStability::Unstable {
    gate_name: rustc_span::sym::rustc_attrs,
    gate_check: rustc_feature::Features::rustc_attrs,
    notes: &[],
}unstable!(rustc_attrs);
152
153    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
154        let nv = cx.expect_name_value(args, cx.attr_span, None)?;
155        let Some(classname) = nv.value_as_str() else {
156            // `#[rustc_objc_class = ...]` is expected to be used as an implementation detail
157            // inside a standard library macro, but `cx.expected_string_literal` exposes too much.
158            // Use a custom error message instead.
159            cx.emit_err(ObjcClassExpectedStringLiteral { span: nv.value_span });
160            return None;
161        };
162        if classname.as_str().contains('\0') {
163            // `#[rustc_objc_class = ...]` will be converted to a null-terminated string,
164            // so it may not contain any null characters.
165            cx.emit_err(NullOnObjcClass { span: nv.value_span });
166            return None;
167        }
168        Some(AttributeKind::RustcObjcClass { classname })
169    }
170}
171
172pub(crate) struct RustcObjcSelectorParser;
173
174impl SingleAttributeParser for RustcObjcSelectorParser {
175    const PATH: &[rustc_span::Symbol] = &[sym::rustc_objc_selector];
176    const ALLOWED_TARGETS: AllowedTargets =
177        AllowedTargets::AllowList(&[Allow(Target::ForeignStatic)]);
178    const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
    word: false,
    list: None,
    one_of: &[],
    name_value_str: Some(&["methodName"]),
    docs: None,
}template!(NameValueStr: "methodName");
179    const STABILITY: AttributeStability = AttributeStability::Unstable {
    gate_name: rustc_span::sym::rustc_attrs,
    gate_check: rustc_feature::Features::rustc_attrs,
    notes: &[],
}unstable!(rustc_attrs);
180
181    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
182        let nv = cx.expect_name_value(args, cx.attr_span, None)?;
183        let Some(methname) = nv.value_as_str() else {
184            // `#[rustc_objc_selector = ...]` is expected to be used as an implementation detail
185            // inside a standard library macro, but `cx.expected_string_literal` exposes too much.
186            // Use a custom error message instead.
187            cx.emit_err(ObjcSelectorExpectedStringLiteral { span: nv.value_span });
188            return None;
189        };
190        if methname.as_str().contains('\0') {
191            // `#[rustc_objc_selector = ...]` will be converted to a null-terminated string,
192            // so it may not contain any null characters.
193            cx.emit_err(NullOnObjcSelector { span: nv.value_span });
194            return None;
195        }
196        Some(AttributeKind::RustcObjcSelector { methname })
197    }
198}
199
200#[derive(#[automatically_derived]
impl ::core::default::Default for NakedParser {
    #[inline]
    fn default() -> NakedParser {
        NakedParser { span: ::core::default::Default::default() }
    }
}Default)]
201pub(crate) struct NakedParser {
202    span: Option<Span>,
203}
204
205impl AttributeParser for NakedParser {
206    const ATTRIBUTES: AcceptMapping<Self> =
207        &[(&[sym::naked], ::rustc_feature::AttributeTemplate {
    word: true,
    list: None,
    one_of: &[],
    name_value_str: None,
    docs: None,
}template!(Word), AttributeStability::Stable, |this, cx, args| {
208            let Some(()) = cx.expect_no_args(args) else {
209                return;
210            };
211
212            if let Some(earlier) = this.span {
213                let span = cx.attr_span;
214                cx.warn_unused_duplicate(earlier, span);
215            } else {
216                this.span = Some(cx.attr_span);
217            }
218        })];
219    const SAFETY: AttributeSafety = AttributeSafety::Unsafe { unsafe_since: None };
220    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
221        Allow(Target::Fn),
222        Allow(Target::Method(MethodKind::Inherent)),
223        Allow(Target::Method(MethodKind::Trait { body: true })),
224        Allow(Target::Method(MethodKind::TraitImpl)),
225        Warn(Target::MacroCall),
226    ]);
227
228    fn finalize(self, cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {
229        // FIXME(jdonszelmann): upgrade this list to *parsed* attributes
230        // once all of these have parsed forms. That'd make the check much nicer...
231        //
232        // many attributes don't make sense in combination with #[naked].
233        // Notable attributes that are incompatible with `#[naked]` are:
234        //
235        // * `#[inline]`
236        // * `#[track_caller]`
237        // * `#[test]`, `#[ignore]`, `#[should_panic]`
238        //
239        // NOTE: when making changes to this list, check that `error_codes/E0736.md` remains
240        // accurate.
241        const ALLOW_LIST: &[rustc_span::Symbol] = &[
242            // conditional compilation
243            sym::cfg_trace,
244            sym::cfg_attr_trace,
245            // testing (allowed here so better errors can be generated in `rustc_builtin_macros::test`)
246            sym::test,
247            sym::ignore,
248            sym::should_panic,
249            sym::bench,
250            // diagnostics
251            sym::allow,
252            sym::warn,
253            sym::deny,
254            sym::forbid,
255            sym::deprecated,
256            sym::must_use,
257            // abi, linking and FFI
258            sym::cold,
259            sym::export_name,
260            sym::link_section,
261            sym::linkage,
262            sym::no_mangle,
263            sym::instruction_set,
264            sym::repr,
265            sym::rustc_std_internal_symbol,
266            // FIXME(#82232, #143834): temporarily renamed to mitigate `#[align]` nameres ambiguity
267            sym::rustc_align,
268            sym::rustc_align_static,
269            // obviously compatible with self
270            sym::naked,
271            // documentation
272            sym::doc,
273        ];
274
275        let span = self.span?;
276
277        let Some(tools) = cx.tools else {
278            {
    ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
            format_args!("tools required while parsing attributes")));
};unreachable!("tools required while parsing attributes");
279        };
280
281        // only if we found a naked attribute do we do the somewhat expensive check
282        'outer: for other_attr in cx.all_attrs {
283            for allowed_attr in ALLOW_LIST {
284                if other_attr
285                    .segments()
286                    .next()
287                    .is_some_and(|i| tools.iter().any(|tool| tool.name == i.name))
288                {
289                    // effectively skips the error message  being emitted below
290                    // if it's a tool attribute
291                    continue 'outer;
292                }
293                if other_attr.word_is(*allowed_attr) {
294                    // effectively skips the error message  being emitted below
295                    // if its an allowed attribute
296                    continue 'outer;
297                }
298
299                if other_attr.word_is(sym::target_feature) {
300                    if !cx.features().naked_functions_target_feature() {
301                        feature_err(
302                            &cx.sess(),
303                            sym::naked_functions_target_feature,
304                            other_attr.span(),
305                            "`#[target_feature(/* ... */)]` is currently unstable on `#[naked]` functions",
306                        ).emit();
307                    }
308
309                    continue 'outer;
310                }
311            }
312
313            cx.emit_err(NakedFunctionIncompatibleAttribute {
314                span: other_attr.span(),
315                naked_span: span,
316                attr: other_attr.get_attribute_path().to_string(),
317            });
318        }
319
320        Some(AttributeKind::Naked(span))
321    }
322}
323
324pub(crate) struct TrackCallerParser;
325impl NoArgsAttributeParser for TrackCallerParser {
326    const PATH: &[Symbol] = &[sym::track_caller];
327    const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn;
328    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
329        Allow(Target::Fn),
330        Allow(Target::Method(MethodKind::Inherent)),
331        Allow(Target::Method(MethodKind::Trait { body: true })),
332        Allow(Target::Method(MethodKind::TraitImpl)),
333        Allow(Target::Method(MethodKind::Trait { body: false })), // `#[track_caller]` is inherited from trait methods
334        Allow(Target::ForeignFn),
335        Allow(Target::Closure),
336        Warn(Target::MacroDef),
337        Warn(Target::Arm),
338        Warn(Target::Field),
339        Warn(Target::MacroCall),
340    ]);
341    const STABILITY: AttributeStability = AttributeStability::Stable;
342    const CREATE: fn(Span) -> AttributeKind = AttributeKind::TrackCaller;
343}
344
345pub(crate) struct NoMangleParser;
346impl NoArgsAttributeParser for NoMangleParser {
347    const PATH: &[Symbol] = &[sym::no_mangle];
348    const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn;
349    const SAFETY: AttributeSafety = AttributeSafety::Unsafe { unsafe_since: Some(Edition2024) };
350    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[
351        Allow(Target::Fn),
352        Allow(Target::Static),
353        Allow(Target::Method(MethodKind::Inherent)),
354        Allow(Target::Method(MethodKind::TraitImpl)),
355        AllowSilent(Target::Const), // Handled in the `InvalidNoMangleItems` pass
356        Error(Target::Closure),
357    ]);
358    const STABILITY: AttributeStability = AttributeStability::Stable;
359    const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoMangle;
360}
361
362#[derive(#[automatically_derived]
impl ::core::default::Default for UsedParser {
    #[inline]
    fn default() -> UsedParser {
        UsedParser {
            first_compiler: ::core::default::Default::default(),
            first_linker: ::core::default::Default::default(),
            first_default: ::core::default::Default::default(),
        }
    }
}Default)]
363pub(crate) struct UsedParser {
364    first_compiler: Option<Span>,
365    first_linker: Option<Span>,
366    first_default: Option<Span>,
367}
368
369// A custom `AttributeParser` is used rather than a Simple attribute parser because
370// - Specifying two `#[used]` attributes is a warning (but will be an error in the future)
371// - But specifying two conflicting attributes: `#[used(compiler)]` and `#[used(linker)]` is already an error today
372// We can change this to a Simple parser once the warning becomes an error
373impl AttributeParser for UsedParser {
374    const ATTRIBUTES: AcceptMapping<Self> = &[(
375        &[sym::used],
376        ::rustc_feature::AttributeTemplate {
    word: true,
    list: Some(&["compiler", "linker"]),
    one_of: &[],
    name_value_str: None,
    docs: None,
}template!(Word, List: &["compiler", "linker"]),
377        AttributeStability::Stable,
378        |group: &mut Self, cx, args| {
379            let used_by = match args {
380                ArgParser::NoArgs => UsedBy::Default,
381                ArgParser::List(list) => {
382                    let Some(l) = cx.expect_single(list) else {
383                        return;
384                    };
385
386                    match l.meta_item_no_args().and_then(|i| i.path().word_sym()) {
387                        Some(sym::compiler) => {
388                            if !cx.features().used_with_arg() {
389                                feature_err(
390                                    &cx.sess(),
391                                    sym::used_with_arg,
392                                    cx.attr_span,
393                                    "`#[used(compiler)]` is currently unstable",
394                                )
395                                .emit();
396                            }
397                            UsedBy::Compiler
398                        }
399                        Some(sym::linker) => {
400                            if !cx.features().used_with_arg() {
401                                feature_err(
402                                    &cx.sess(),
403                                    sym::used_with_arg,
404                                    cx.attr_span,
405                                    "`#[used(linker)]` is currently unstable",
406                                )
407                                .emit();
408                            }
409                            UsedBy::Linker
410                        }
411                        _ => {
412                            cx.adcx().expected_specific_argument(
413                                l.span(),
414                                &[sym::compiler, sym::linker],
415                            );
416                            return;
417                        }
418                    }
419                }
420                ArgParser::NameValue(_) => return,
421            };
422
423            let attr_span = cx.attr_span;
424
425            // `#[used]` is interpreted as `#[used(linker)]` (though depending on target OS the
426            // circumstances are more complicated). While we're checking `used_by`, also report
427            // these cross-`UsedBy` duplicates to warn.
428            let target = match used_by {
429                UsedBy::Compiler => &mut group.first_compiler,
430                UsedBy::Linker => {
431                    if let Some(prev) = group.first_default {
432                        cx.warn_unused_duplicate(prev, attr_span);
433                        return;
434                    }
435                    &mut group.first_linker
436                }
437                UsedBy::Default => {
438                    if let Some(prev) = group.first_linker {
439                        cx.warn_unused_duplicate(prev, attr_span);
440                        return;
441                    }
442                    &mut group.first_default
443                }
444            };
445
446            if let Some(prev) = *target {
447                cx.warn_unused_duplicate(prev, attr_span);
448            } else {
449                *target = Some(attr_span);
450            }
451        },
452    )];
453    const ALLOWED_TARGETS: AllowedTargets =
454        AllowedTargets::AllowList(&[Allow(Target::Static), Warn(Target::MacroCall)]);
455
456    fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {
457        // If a specific form of `used` is specified, it takes precedence over generic `#[used]`.
458        // If both `linker` and `compiler` are specified, use `linker`.
459        Some(match (self.first_compiler, self.first_linker, self.first_default) {
460            (_, Some(_), _) => AttributeKind::Used { used_by: UsedBy::Linker },
461            (Some(_), _, _) => AttributeKind::Used { used_by: UsedBy::Compiler },
462            (_, _, Some(_)) => AttributeKind::Used { used_by: UsedBy::Default },
463            (None, None, None) => return None,
464        })
465    }
466}
467
468fn parse_tf_attribute(
469    cx: &mut AcceptContext<'_, '_>,
470    args: &ArgParser,
471) -> impl IntoIterator<Item = (Symbol, Span)> {
472    let mut features = Vec::new();
473    let Some(list) = cx.expect_list(args, cx.attr_span) else {
474        return features;
475    };
476    if list.is_empty() {
477        let attr_span = cx.attr_span;
478        cx.adcx().warn_empty_attribute(attr_span);
479        return features;
480    }
481    for item in list.mixed() {
482        let Some((ident, value)) = cx.expect_name_value(item, item.span(), Some(sym::enable))
483        else {
484            return features;
485        };
486
487        // Validate name
488        if ident.name != sym::enable {
489            cx.adcx().expected_specific_argument(ident.span, &[sym::enable]);
490            return features;
491        }
492
493        // Use value
494        let Some(value_str) = cx.expect_string_literal(value) else {
495            return features;
496        };
497        for feature in value_str.as_str().split(",") {
498            features.push((Symbol::intern(feature), item.span()));
499        }
500    }
501    features
502}
503
504pub(crate) struct TargetFeatureParser;
505
506impl CombineAttributeParser for TargetFeatureParser {
507    type Item = (Symbol, Span);
508    const PATH: &[Symbol] = &[sym::target_feature];
509    const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {
510        features: items,
511        attr_span: span,
512        was_forced: false,
513    };
514    const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
    word: false,
    list: Some(&["enable = \"feat1, feat2\""]),
    one_of: &[],
    name_value_str: None,
    docs: None,
}template!(List: &["enable = \"feat1, feat2\""]);
515    const STABILITY: AttributeStability = AttributeStability::Stable;
516
517    fn extend(
518        cx: &mut AcceptContext<'_, '_>,
519        args: &ArgParser,
520    ) -> impl IntoIterator<Item = Self::Item> {
521        parse_tf_attribute(cx, args)
522    }
523
524    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
525        Allow(Target::Fn),
526        Allow(Target::Method(MethodKind::Inherent)),
527        Allow(Target::Method(MethodKind::Trait { body: true })),
528        Allow(Target::Method(MethodKind::TraitImpl)),
529        Warn(Target::Statement),
530        Warn(Target::Field),
531        Warn(Target::Arm),
532        Warn(Target::MacroDef),
533        Warn(Target::MacroCall),
534    ]);
535}
536
537pub(crate) struct ForceTargetFeatureParser;
538
539impl CombineAttributeParser for ForceTargetFeatureParser {
540    type Item = (Symbol, Span);
541    const PATH: &[Symbol] = &[sym::force_target_feature];
542    const SAFETY: AttributeSafety = AttributeSafety::Unsafe { unsafe_since: None };
543    const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {
544        features: items,
545        attr_span: span,
546        was_forced: true,
547    };
548    const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
    word: false,
    list: Some(&["enable = \"feat1, feat2\""]),
    one_of: &[],
    name_value_str: None,
    docs: None,
}template!(List: &["enable = \"feat1, feat2\""]);
549    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
550        Allow(Target::Fn),
551        Allow(Target::Method(MethodKind::Inherent)),
552        Allow(Target::Method(MethodKind::Trait { body: true })),
553        Allow(Target::Method(MethodKind::TraitImpl)),
554    ]);
555    const STABILITY: AttributeStability = AttributeStability::Unstable {
    gate_name: rustc_span::sym::effective_target_features,
    gate_check: rustc_feature::Features::effective_target_features,
    notes: &[],
}unstable!(effective_target_features);
556
557    fn extend(
558        cx: &mut AcceptContext<'_, '_>,
559        args: &ArgParser,
560    ) -> impl IntoIterator<Item = Self::Item> {
561        parse_tf_attribute(cx, args)
562    }
563}
564
565pub(crate) struct SanitizeParser;
566
567impl SingleAttributeParser for SanitizeParser {
568    const PATH: &[Symbol] = &[sym::sanitize];
569    // FIXME: still checked in check_attrs.rs
570    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
571    const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
    word: false,
    list: Some(&[r#"address = "on|off""#, r#"kernel_address = "on|off""#,
                    r#"cfi = "on|off""#, r#"hwaddress = "on|off""#,
                    r#"kernel_hwaddress = "on|off""#, r#"kcfi = "on|off""#,
                    r#"memory = "on|off""#, r#"memtag = "on|off""#,
                    r#"shadow_call_stack = "on|off""#, r#"thread = "on|off""#,
                    r#"realtime = "nonblocking|blocking|caller""#]),
    one_of: &[],
    name_value_str: None,
    docs: None,
}template!(List: &[
572        r#"address = "on|off""#,
573        r#"kernel_address = "on|off""#,
574        r#"cfi = "on|off""#,
575        r#"hwaddress = "on|off""#,
576        r#"kernel_hwaddress = "on|off""#,
577        r#"kcfi = "on|off""#,
578        r#"memory = "on|off""#,
579        r#"memtag = "on|off""#,
580        r#"shadow_call_stack = "on|off""#,
581        r#"thread = "on|off""#,
582        r#"realtime = "nonblocking|blocking|caller""#,
583    ]);
584    const STABILITY: AttributeStability = AttributeStability::Unstable {
    gate_name: rustc_span::sym::sanitize,
    gate_check: rustc_feature::Features::sanitize,
    notes: &[],
}unstable!(sanitize);
585
586    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
587        let list = cx.expect_list(args, cx.attr_span)?;
588
589        let mut on_set = SanitizerSet::empty();
590        let mut off_set = SanitizerSet::empty();
591        let mut rtsan = None;
592
593        for item in list.mixed() {
594            let Some((ident, value)) = cx.expect_name_value(item, item.span(), None) else {
595                continue;
596            };
597
598            let mut apply = |s: SanitizerSet| {
599                let is_on = match value.value_as_str() {
600                    Some(sym::on) => true,
601                    Some(sym::off) => false,
602                    Some(_) => {
603                        cx.adcx().expected_specific_argument_strings(
604                            value.value_span,
605                            &[sym::on, sym::off],
606                        );
607                        return;
608                    }
609                    None => {
610                        cx.adcx().expected_specific_argument_strings(
611                            value.value_span,
612                            &[sym::on, sym::off],
613                        );
614                        return;
615                    }
616                };
617
618                if is_on {
619                    on_set |= s;
620                } else {
621                    off_set |= s;
622                }
623            };
624
625            match ident.name {
626                sym::address | sym::kernel_address => {
627                    apply(SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS)
628                }
629                sym::cfi => apply(SanitizerSet::CFI),
630                sym::kcfi => apply(SanitizerSet::KCFI),
631                sym::memory => apply(SanitizerSet::MEMORY),
632                sym::memtag => apply(SanitizerSet::MEMTAG),
633                sym::shadow_call_stack => apply(SanitizerSet::SHADOWCALLSTACK),
634                sym::thread => apply(SanitizerSet::THREAD),
635                sym::hwaddress | sym::kernel_hwaddress => {
636                    apply(SanitizerSet::HWADDRESS | SanitizerSet::KERNELHWADDRESS)
637                }
638                sym::realtime => match value.value_as_str() {
639                    Some(sym::nonblocking) => rtsan = Some(RtsanSetting::Nonblocking),
640                    Some(sym::blocking) => rtsan = Some(RtsanSetting::Blocking),
641                    Some(sym::caller) => rtsan = Some(RtsanSetting::Caller),
642                    _ => {
643                        cx.adcx().expected_specific_argument_strings(
644                            value.value_span,
645                            &[sym::nonblocking, sym::blocking, sym::caller],
646                        );
647                    }
648                },
649                _ => {
650                    cx.adcx().expected_specific_argument_strings(
651                        ident.span,
652                        &[
653                            sym::address,
654                            sym::kernel_address,
655                            sym::cfi,
656                            sym::kcfi,
657                            sym::memory,
658                            sym::memtag,
659                            sym::shadow_call_stack,
660                            sym::thread,
661                            sym::hwaddress,
662                            sym::kernel_hwaddress,
663                            sym::realtime,
664                        ],
665                    );
666                    continue;
667                }
668            }
669        }
670
671        Some(AttributeKind::Sanitize { on_set, off_set, rtsan, span: cx.attr_span })
672    }
673}
674
675pub(crate) struct ThreadLocalParser;
676
677impl NoArgsAttributeParser for ThreadLocalParser {
678    const PATH: &[Symbol] = &[sym::thread_local];
679    const ALLOWED_TARGETS: AllowedTargets =
680        AllowedTargets::AllowList(&[Allow(Target::Static), Allow(Target::ForeignStatic)]);
681    const STABILITY: AttributeStability = AttributeStability::Unstable {
    gate_name: rustc_span::sym::thread_local,
    gate_check: rustc_feature::Features::thread_local,
    notes: &[],
}unstable!(thread_local);
682    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::ThreadLocal;
683}
684
685pub(crate) struct RustcPassIndirectlyInNonRusticAbisParser;
686
687impl NoArgsAttributeParser for RustcPassIndirectlyInNonRusticAbisParser {
688    const PATH: &[Symbol] = &[sym::rustc_pass_indirectly_in_non_rustic_abis];
689    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]);
690    const STABILITY: AttributeStability = AttributeStability::Unstable {
    gate_name: rustc_span::sym::rustc_attrs,
    gate_check: rustc_feature::Features::rustc_attrs,
    notes: &[],
}unstable!(rustc_attrs);
691    const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcPassIndirectlyInNonRusticAbis;
692}
693
694pub(crate) struct RustcEiiForeignItemParser;
695
696impl NoArgsAttributeParser for RustcEiiForeignItemParser {
697    const PATH: &[Symbol] = &[sym::rustc_eii_foreign_item];
698    const ALLOWED_TARGETS: AllowedTargets =
699        AllowedTargets::AllowList(&[Allow(Target::ForeignFn), Allow(Target::ForeignStatic)]);
700    const STABILITY: AttributeStability = AttributeStability::Unstable {
    gate_name: rustc_span::sym::eii_internals,
    gate_check: rustc_feature::Features::eii_internals,
    notes: &[],
}unstable!(eii_internals);
701    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcEiiForeignItem;
702}
703
704pub(crate) struct PatchableFunctionEntryParser;
705
706impl SingleAttributeParser for PatchableFunctionEntryParser {
707    const PATH: &[Symbol] = &[sym::patchable_function_entry];
708    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]);
709    const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
    word: false,
    list: Some(&["prefix_nops = m, entry_nops = n"]),
    one_of: &[],
    name_value_str: None,
    docs: None,
}template!(List: &["prefix_nops = m, entry_nops = n"]);
710    const STABILITY: AttributeStability = AttributeStability::Unstable {
    gate_name: rustc_span::sym::patchable_function_entry,
    gate_check: rustc_feature::Features::patchable_function_entry,
    notes: &[],
}unstable!(patchable_function_entry);
711
712    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
713        let meta_item_list = cx.expect_list(args, cx.attr_span)?;
714
715        let mut prefix = None;
716        let mut entry = None;
717
718        if meta_item_list.len() == 0 {
719            cx.adcx().expected_at_least_one_argument(meta_item_list.span);
720            return None;
721        }
722
723        let mut errored = false;
724
725        for item in meta_item_list.mixed() {
726            let Some((ident, value)) = cx.expect_name_value(item, item.span(), None) else {
727                continue;
728            };
729
730            let attrib_to_write = match ident.name {
731                sym::prefix_nops => {
732                    // Duplicate prefixes are not allowed
733                    if prefix.is_some() {
734                        errored = true;
735                        cx.adcx().duplicate_key(ident.span, sym::prefix_nops);
736                        continue;
737                    }
738                    &mut prefix
739                }
740                sym::entry_nops => {
741                    // Duplicate entries are not allowed
742                    if entry.is_some() {
743                        errored = true;
744                        cx.adcx().duplicate_key(ident.span, sym::entry_nops);
745                        continue;
746                    }
747                    &mut entry
748                }
749                _ => {
750                    errored = true;
751                    cx.adcx().expected_specific_argument(
752                        ident.span,
753                        &[sym::prefix_nops, sym::entry_nops],
754                    );
755                    continue;
756                }
757            };
758
759            let rustc_ast::LitKind::Int(val, _) = value.value_as_lit().kind else {
760                errored = true;
761                cx.adcx().expected_integer_literal(value.value_span);
762                continue;
763            };
764
765            let Ok(val) = val.get().try_into() else {
766                errored = true;
767                cx.adcx().expected_integer_literal_in_range(
768                    value.value_span,
769                    u8::MIN as isize,
770                    u8::MAX as isize,
771                );
772                continue;
773            };
774
775            *attrib_to_write = Some(val);
776        }
777
778        if errored {
779            None
780        } else {
781            Some(AttributeKind::PatchableFunctionEntry {
782                prefix: prefix.unwrap_or(0),
783                entry: entry.unwrap_or(0),
784            })
785        }
786    }
787}