Skip to main content

rustc_attr_parsing/attributes/
codegen_attrs.rs

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