Skip to main content

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