rustc_attr_parsing/attributes/
codegen_attrs.rs

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