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