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