rustc_attr_parsing/attributes/
codegen_attrs.rs

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