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            let attr_span = cx.attr_span;
28            cx.adcx().expected_list(attr_span, args);
29            return None;
30        };
31
32        let Some(single) = list.single() else {
33            cx.adcx().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.adcx()
43                    .expected_specific_argument(single.span(), &[sym::size, sym::speed, sym::none]);
44                OptimizeAttr::Default
45            }
46        };
47
48        Some(AttributeKind::Optimize(res, cx.attr_span))
49    }
50}
51
52pub(crate) struct ColdParser;
53
54impl<S: Stage> NoArgsAttributeParser<S> for ColdParser {
55    const PATH: &[Symbol] = &[sym::cold];
56    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
57    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[
58        Allow(Target::Fn),
59        Allow(Target::Method(MethodKind::Trait { body: true })),
60        Allow(Target::Method(MethodKind::TraitImpl)),
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 ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
73    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
74        Allow(Target::Fn),
75        Allow(Target::Closure),
76        Allow(Target::Method(MethodKind::Trait { body: true })),
77        Allow(Target::Method(MethodKind::TraitImpl)),
78        Allow(Target::Method(MethodKind::Inherent)),
79        Allow(Target::Impl { of_trait: true }),
80        Allow(Target::Impl { of_trait: false }),
81        Allow(Target::Mod),
82        Allow(Target::Crate),
83    ]);
84    const TEMPLATE: AttributeTemplate = ::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]);
85
86    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
87        let Some(args) = args.list() else {
88            let attr_span = cx.attr_span;
89            cx.adcx().expected_specific_argument_and_list(attr_span, &[sym::on, sym::off]);
90            return None;
91        };
92
93        let Some(arg) = args.single() else {
94            cx.adcx().expected_single_argument(args.span);
95            return None;
96        };
97
98        let mut fail_incorrect_argument =
99            |span| cx.adcx().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 ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
124    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
125        Allow(Target::Static),
126        Allow(Target::Fn),
127        Allow(Target::Method(MethodKind::Inherent)),
128        Allow(Target::Method(MethodKind::Trait { body: true })),
129        Allow(Target::Method(MethodKind::TraitImpl)),
130        Warn(Target::Field),
131        Warn(Target::Arm),
132        Warn(Target::MacroDef),
133        Warn(Target::MacroCall),
134    ]);
135    const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
    word: false,
    list: None,
    one_of: &[],
    name_value_str: Some(&["name"]),
    docs: None,
}template!(NameValueStr: "name");
136
137    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
138        let Some(nv) = args.name_value() else {
139            let attr_span = cx.attr_span;
140            cx.adcx().expected_name_value(attr_span, None);
141            return None;
142        };
143        let Some(name) = nv.value_as_str() else {
144            cx.adcx().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 RustcObjcClassParser;
158
159impl<S: Stage> SingleAttributeParser<S> for RustcObjcClassParser {
160    const PATH: &[rustc_span::Symbol] = &[sym::rustc_objc_class];
161    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
162    const ALLOWED_TARGETS: AllowedTargets =
163        AllowedTargets::AllowList(&[Allow(Target::ForeignStatic)]);
164    const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
    word: false,
    list: None,
    one_of: &[],
    name_value_str: Some(&["ClassName"]),
    docs: None,
}template!(NameValueStr: "ClassName");
165
166    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
167        let Some(nv) = args.name_value() else {
168            let attr_span = cx.attr_span;
169            cx.adcx().expected_name_value(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 implementation 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::RustcObjcClass { classname, span: cx.attr_span })
186    }
187}
188
189pub(crate) struct RustcObjcSelectorParser;
190
191impl<S: Stage> SingleAttributeParser<S> for RustcObjcSelectorParser {
192    const PATH: &[rustc_span::Symbol] = &[sym::rustc_objc_selector];
193    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
194    const ALLOWED_TARGETS: AllowedTargets =
195        AllowedTargets::AllowList(&[Allow(Target::ForeignStatic)]);
196    const TEMPLATE: AttributeTemplate = ::rustc_feature::AttributeTemplate {
    word: false,
    list: None,
    one_of: &[],
    name_value_str: Some(&["methodName"]),
    docs: None,
}template!(NameValueStr: "methodName");
197
198    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
199        let Some(nv) = args.name_value() else {
200            let attr_span = cx.attr_span;
201            cx.adcx().expected_name_value(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 implementation 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::RustcObjcSelector { methname, span: cx.attr_span })
218    }
219}
220
221#[derive(#[automatically_derived]
impl ::core::default::Default for NakedParser {
    #[inline]
    fn default() -> NakedParser {
        NakedParser { span: ::core::default::Default::default() }
    }
}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], ::rustc_feature::AttributeTemplate {
    word: true,
    list: None,
    one_of: &[],
    name_value_str: None,
    docs: None,
}template!(Word), |this, cx, args| {
229            if let Err(span) = args.no_args() {
230                cx.adcx().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        let Some(tools) = cx.tools else {
299            {
    ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
            format_args!("tools required while parsing attributes")));
};unreachable!("tools required while parsing attributes");
300        };
301
302        let tools = tools.iter().map(|tool| tool.name).collect::<Vec<_>>();
303        // only if we found a naked attribute do we do the somewhat expensive check
304        'outer: for other_attr in cx.all_attrs {
305            for allowed_attr in ALLOW_LIST {
306                if other_attr.segments().next().is_some_and(|i| tools.contains(&i.name)) {
307                    // effectively skips the error message  being emitted below
308                    // if it's a tool attribute
309                    continue 'outer;
310                }
311                if other_attr.word_is(*allowed_attr) {
312                    // effectively skips the error message  being emitted below
313                    // if its an allowed attribute
314                    continue 'outer;
315                }
316
317                if other_attr.word_is(sym::target_feature) {
318                    if !cx.features().naked_functions_target_feature() {
319                        feature_err(
320                            &cx.sess(),
321                            sym::naked_functions_target_feature,
322                            other_attr.span(),
323                            "`#[target_feature(/* ... */)]` is currently unstable on `#[naked]` functions",
324                        ).emit();
325                    }
326
327                    continue 'outer;
328                }
329            }
330
331            cx.emit_err(NakedFunctionIncompatibleAttribute {
332                span: other_attr.span(),
333                naked_span: span,
334                attr: other_attr.get_attribute_path().to_string(),
335            });
336        }
337
338        Some(AttributeKind::Naked(span))
339    }
340}
341
342pub(crate) struct TrackCallerParser;
343impl<S: Stage> NoArgsAttributeParser<S> for TrackCallerParser {
344    const PATH: &[Symbol] = &[sym::track_caller];
345    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
346    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
347        Allow(Target::Fn),
348        Allow(Target::Method(MethodKind::Inherent)),
349        Allow(Target::Method(MethodKind::Trait { body: true })),
350        Allow(Target::Method(MethodKind::TraitImpl)),
351        Allow(Target::Method(MethodKind::Trait { body: false })), // `#[track_caller]` is inherited from trait methods
352        Allow(Target::ForeignFn),
353        Allow(Target::Closure),
354        Warn(Target::MacroDef),
355        Warn(Target::Arm),
356        Warn(Target::Field),
357        Warn(Target::MacroCall),
358    ]);
359    const CREATE: fn(Span) -> AttributeKind = AttributeKind::TrackCaller;
360}
361
362pub(crate) struct NoMangleParser;
363impl<S: Stage> NoArgsAttributeParser<S> for NoMangleParser {
364    const PATH: &[Symbol] = &[sym::no_mangle];
365    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
366    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[
367        Allow(Target::Fn),
368        Allow(Target::Static),
369        Allow(Target::Method(MethodKind::Inherent)),
370        Allow(Target::Method(MethodKind::TraitImpl)),
371        AllowSilent(Target::Const), // Handled in the `InvalidNoMangleItems` pass
372        Error(Target::Closure),
373    ]);
374    const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoMangle;
375}
376
377#[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)]
378pub(crate) struct UsedParser {
379    first_compiler: Option<Span>,
380    first_linker: Option<Span>,
381    first_default: Option<Span>,
382}
383
384// A custom `AttributeParser` is used rather than a Simple attribute parser because
385// - Specifying two `#[used]` attributes is a warning (but will be an error in the future)
386// - But specifying two conflicting attributes: `#[used(compiler)]` and `#[used(linker)]` is already an error today
387// We can change this to a Simple parser once the warning becomes an error
388impl<S: Stage> AttributeParser<S> for UsedParser {
389    const ATTRIBUTES: AcceptMapping<Self, S> = &[(
390        &[sym::used],
391        ::rustc_feature::AttributeTemplate {
    word: true,
    list: Some(&["compiler", "linker"]),
    one_of: &[],
    name_value_str: None,
    docs: None,
}template!(Word, List: &["compiler", "linker"]),
392        |group: &mut Self, cx, args| {
393            let used_by = match args {
394                ArgParser::NoArgs => UsedBy::Default,
395                ArgParser::List(list) => {
396                    let Some(l) = list.single() else {
397                        cx.adcx().expected_single_argument(list.span);
398                        return;
399                    };
400
401                    match l.meta_item().and_then(|i| i.path().word_sym()) {
402                        Some(sym::compiler) => {
403                            if !cx.features().used_with_arg() {
404                                feature_err(
405                                    &cx.sess(),
406                                    sym::used_with_arg,
407                                    cx.attr_span,
408                                    "`#[used(compiler)]` is currently unstable",
409                                )
410                                .emit();
411                            }
412                            UsedBy::Compiler
413                        }
414                        Some(sym::linker) => {
415                            if !cx.features().used_with_arg() {
416                                feature_err(
417                                    &cx.sess(),
418                                    sym::used_with_arg,
419                                    cx.attr_span,
420                                    "`#[used(linker)]` is currently unstable",
421                                )
422                                .emit();
423                            }
424                            UsedBy::Linker
425                        }
426                        _ => {
427                            cx.adcx().expected_specific_argument(
428                                l.span(),
429                                &[sym::compiler, sym::linker],
430                            );
431                            return;
432                        }
433                    }
434                }
435                ArgParser::NameValue(_) => return,
436            };
437
438            let attr_span = cx.attr_span;
439
440            // `#[used]` is interpreted as `#[used(linker)]` (though depending on target OS the
441            // circumstances are more complicated). While we're checking `used_by`, also report
442            // these cross-`UsedBy` duplicates to warn.
443            let target = match used_by {
444                UsedBy::Compiler => &mut group.first_compiler,
445                UsedBy::Linker => {
446                    if let Some(prev) = group.first_default {
447                        cx.warn_unused_duplicate(prev, attr_span);
448                        return;
449                    }
450                    &mut group.first_linker
451                }
452                UsedBy::Default => {
453                    if let Some(prev) = group.first_linker {
454                        cx.warn_unused_duplicate(prev, attr_span);
455                        return;
456                    }
457                    &mut group.first_default
458                }
459            };
460
461            if let Some(prev) = *target {
462                cx.warn_unused_duplicate(prev, attr_span);
463            } else {
464                *target = Some(attr_span);
465            }
466        },
467    )];
468    const ALLOWED_TARGETS: AllowedTargets =
469        AllowedTargets::AllowList(&[Allow(Target::Static), Warn(Target::MacroCall)]);
470
471    fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
472        // If a specific form of `used` is specified, it takes precedence over generic `#[used]`.
473        // If both `linker` and `compiler` are specified, use `linker`.
474        Some(match (self.first_compiler, self.first_linker, self.first_default) {
475            (_, Some(span), _) => AttributeKind::Used { used_by: UsedBy::Linker, span },
476            (Some(span), _, _) => AttributeKind::Used { used_by: UsedBy::Compiler, span },
477            (_, _, Some(span)) => AttributeKind::Used { used_by: UsedBy::Default, span },
478            (None, None, None) => return None,
479        })
480    }
481}
482
483fn parse_tf_attribute<S: Stage>(
484    cx: &mut AcceptContext<'_, '_, S>,
485    args: &ArgParser,
486) -> impl IntoIterator<Item = (Symbol, Span)> {
487    let mut features = Vec::new();
488    let ArgParser::List(list) = args else {
489        let attr_span = cx.attr_span;
490        cx.adcx().expected_list(attr_span, args);
491        return features;
492    };
493    if list.is_empty() {
494        let attr_span = cx.attr_span;
495        cx.adcx().warn_empty_attribute(attr_span);
496        return features;
497    }
498    for item in list.mixed() {
499        let Some(name_value) = item.meta_item() else {
500            cx.adcx().expected_name_value(item.span(), Some(sym::enable));
501            return features;
502        };
503
504        // Validate name
505        let Some(name) = name_value.path().word_sym() else {
506            cx.adcx().expected_name_value(name_value.path().span(), Some(sym::enable));
507            return features;
508        };
509        if name != sym::enable {
510            cx.adcx().expected_name_value(name_value.path().span(), Some(sym::enable));
511            return features;
512        }
513
514        // Use value
515        let Some(name_value) = name_value.args().name_value() else {
516            cx.adcx().expected_name_value(item.span(), Some(sym::enable));
517            return features;
518        };
519        let Some(value_str) = name_value.value_as_str() else {
520            cx.adcx()
521                .expected_string_literal(name_value.value_span, Some(name_value.value_as_lit()));
522            return features;
523        };
524        for feature in value_str.as_str().split(",") {
525            features.push((Symbol::intern(feature), item.span()));
526        }
527    }
528    features
529}
530
531pub(crate) struct TargetFeatureParser;
532
533impl<S: Stage> CombineAttributeParser<S> for TargetFeatureParser {
534    type Item = (Symbol, Span);
535    const PATH: &[Symbol] = &[sym::target_feature];
536    const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {
537        features: items,
538        attr_span: span,
539        was_forced: false,
540    };
541    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\""]);
542
543    fn extend(
544        cx: &mut AcceptContext<'_, '_, S>,
545        args: &ArgParser,
546    ) -> impl IntoIterator<Item = Self::Item> {
547        parse_tf_attribute(cx, args)
548    }
549
550    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
551        Allow(Target::Fn),
552        Allow(Target::Method(MethodKind::Inherent)),
553        Allow(Target::Method(MethodKind::Trait { body: true })),
554        Allow(Target::Method(MethodKind::TraitImpl)),
555        Warn(Target::Statement),
556        Warn(Target::Field),
557        Warn(Target::Arm),
558        Warn(Target::MacroDef),
559        Warn(Target::MacroCall),
560    ]);
561}
562
563pub(crate) struct ForceTargetFeatureParser;
564
565impl<S: Stage> CombineAttributeParser<S> for ForceTargetFeatureParser {
566    type Item = (Symbol, Span);
567    const PATH: &[Symbol] = &[sym::force_target_feature];
568    const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {
569        features: items,
570        attr_span: span,
571        was_forced: true,
572    };
573    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\""]);
574    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
575        Allow(Target::Fn),
576        Allow(Target::Method(MethodKind::Inherent)),
577        Allow(Target::Method(MethodKind::Trait { body: true })),
578        Allow(Target::Method(MethodKind::TraitImpl)),
579    ]);
580
581    fn extend(
582        cx: &mut AcceptContext<'_, '_, S>,
583        args: &ArgParser,
584    ) -> impl IntoIterator<Item = Self::Item> {
585        parse_tf_attribute(cx, args)
586    }
587}
588
589pub(crate) struct SanitizeParser;
590
591impl<S: Stage> SingleAttributeParser<S> for SanitizeParser {
592    const PATH: &[Symbol] = &[sym::sanitize];
593
594    // FIXME: still checked in check_attrs.rs
595    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
596
597    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: &[
598        r#"address = "on|off""#,
599        r#"kernel_address = "on|off""#,
600        r#"cfi = "on|off""#,
601        r#"hwaddress = "on|off""#,
602        r#"kernel_hwaddress = "on|off""#,
603        r#"kcfi = "on|off""#,
604        r#"memory = "on|off""#,
605        r#"memtag = "on|off""#,
606        r#"shadow_call_stack = "on|off""#,
607        r#"thread = "on|off""#,
608        r#"realtime = "nonblocking|blocking|caller""#,
609    ]);
610
611    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
612
613    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
614        let Some(list) = args.list() else {
615            let attr_span = cx.attr_span;
616            cx.adcx().expected_list(attr_span, args);
617            return None;
618        };
619
620        let mut on_set = SanitizerSet::empty();
621        let mut off_set = SanitizerSet::empty();
622        let mut rtsan = None;
623
624        for item in list.mixed() {
625            let Some(item) = item.meta_item() else {
626                cx.adcx().expected_name_value(item.span(), None);
627                continue;
628            };
629
630            let path = item.path().word_sym();
631            let Some(value) = item.args().name_value() else {
632                cx.adcx().expected_name_value(item.span(), path);
633                continue;
634            };
635
636            let mut apply = |s: SanitizerSet| {
637                let is_on = match value.value_as_str() {
638                    Some(sym::on) => true,
639                    Some(sym::off) => false,
640                    Some(_) => {
641                        cx.adcx().expected_specific_argument_strings(
642                            value.value_span,
643                            &[sym::on, sym::off],
644                        );
645                        return;
646                    }
647                    None => {
648                        cx.adcx()
649                            .expected_string_literal(value.value_span, Some(value.value_as_lit()));
650                        return;
651                    }
652                };
653
654                if is_on {
655                    on_set |= s;
656                } else {
657                    off_set |= s;
658                }
659            };
660
661            match path {
662                Some(sym::address) | Some(sym::kernel_address) => {
663                    apply(SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS)
664                }
665                Some(sym::cfi) => apply(SanitizerSet::CFI),
666                Some(sym::kcfi) => apply(SanitizerSet::KCFI),
667                Some(sym::memory) => apply(SanitizerSet::MEMORY),
668                Some(sym::memtag) => apply(SanitizerSet::MEMTAG),
669                Some(sym::shadow_call_stack) => apply(SanitizerSet::SHADOWCALLSTACK),
670                Some(sym::thread) => apply(SanitizerSet::THREAD),
671                Some(sym::hwaddress) | Some(sym::kernel_hwaddress) => {
672                    apply(SanitizerSet::HWADDRESS | SanitizerSet::KERNELHWADDRESS)
673                }
674                Some(sym::realtime) => match value.value_as_str() {
675                    Some(sym::nonblocking) => rtsan = Some(RtsanSetting::Nonblocking),
676                    Some(sym::blocking) => rtsan = Some(RtsanSetting::Blocking),
677                    Some(sym::caller) => rtsan = Some(RtsanSetting::Caller),
678                    _ => {
679                        cx.adcx().expected_specific_argument_strings(
680                            value.value_span,
681                            &[sym::nonblocking, sym::blocking, sym::caller],
682                        );
683                    }
684                },
685                _ => {
686                    cx.adcx().expected_specific_argument_strings(
687                        item.path().span(),
688                        &[
689                            sym::address,
690                            sym::kernel_address,
691                            sym::cfi,
692                            sym::kcfi,
693                            sym::memory,
694                            sym::memtag,
695                            sym::shadow_call_stack,
696                            sym::thread,
697                            sym::hwaddress,
698                            sym::kernel_hwaddress,
699                            sym::realtime,
700                        ],
701                    );
702                    continue;
703                }
704            }
705        }
706
707        Some(AttributeKind::Sanitize { on_set, off_set, rtsan, span: cx.attr_span })
708    }
709}
710
711pub(crate) struct ThreadLocalParser;
712
713impl<S: Stage> NoArgsAttributeParser<S> for ThreadLocalParser {
714    const PATH: &[Symbol] = &[sym::thread_local];
715    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
716    const ALLOWED_TARGETS: AllowedTargets =
717        AllowedTargets::AllowList(&[Allow(Target::Static), Allow(Target::ForeignStatic)]);
718    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::ThreadLocal;
719}
720
721pub(crate) struct RustcPassIndirectlyInNonRusticAbisParser;
722
723impl<S: Stage> NoArgsAttributeParser<S> for RustcPassIndirectlyInNonRusticAbisParser {
724    const PATH: &[Symbol] = &[sym::rustc_pass_indirectly_in_non_rustic_abis];
725    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
726    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]);
727    const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcPassIndirectlyInNonRusticAbis;
728}
729
730pub(crate) struct RustcEiiForeignItemParser;
731
732impl<S: Stage> NoArgsAttributeParser<S> for RustcEiiForeignItemParser {
733    const PATH: &[Symbol] = &[sym::rustc_eii_foreign_item];
734    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
735    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::ForeignFn)]);
736    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcEiiForeignItem;
737}
738
739pub(crate) struct PatchableFunctionEntryParser;
740
741impl<S: Stage> SingleAttributeParser<S> for PatchableFunctionEntryParser {
742    const PATH: &[Symbol] = &[sym::patchable_function_entry];
743    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
744    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]);
745    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"]);
746
747    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
748        let Some(meta_item_list) = args.list() else {
749            let attr_span = cx.attr_span;
750            cx.adcx().expected_list(attr_span, args);
751            return None;
752        };
753
754        let mut prefix = None;
755        let mut entry = None;
756
757        if meta_item_list.len() == 0 {
758            cx.adcx().expected_list(meta_item_list.span, args);
759            return None;
760        }
761
762        let mut errored = false;
763
764        for item in meta_item_list.mixed() {
765            let Some(meta_item) = item.meta_item() else {
766                errored = true;
767                cx.adcx().expected_name_value(item.span(), None);
768                continue;
769            };
770
771            let Some(name_value_lit) = meta_item.args().name_value() else {
772                errored = true;
773                cx.adcx().expected_name_value(item.span(), None);
774                continue;
775            };
776
777            let attrib_to_write = match meta_item.ident().map(|ident| ident.name) {
778                Some(sym::prefix_nops) => {
779                    // Duplicate prefixes are not allowed
780                    if prefix.is_some() {
781                        errored = true;
782                        cx.adcx().duplicate_key(meta_item.path().span(), sym::prefix_nops);
783                        continue;
784                    }
785                    &mut prefix
786                }
787                Some(sym::entry_nops) => {
788                    // Duplicate entries are not allowed
789                    if entry.is_some() {
790                        errored = true;
791                        cx.adcx().duplicate_key(meta_item.path().span(), sym::entry_nops);
792                        continue;
793                    }
794                    &mut entry
795                }
796                _ => {
797                    errored = true;
798                    cx.adcx().expected_specific_argument(
799                        meta_item.path().span(),
800                        &[sym::prefix_nops, sym::entry_nops],
801                    );
802                    continue;
803                }
804            };
805
806            let rustc_ast::LitKind::Int(val, _) = name_value_lit.value_as_lit().kind else {
807                errored = true;
808                cx.adcx().expected_integer_literal(name_value_lit.value_span);
809                continue;
810            };
811
812            let Ok(val) = val.get().try_into() else {
813                errored = true;
814                cx.adcx().expected_integer_literal_in_range(
815                    name_value_lit.value_span,
816                    u8::MIN as isize,
817                    u8::MAX as isize,
818                );
819                continue;
820            };
821
822            *attrib_to_write = Some(val);
823        }
824
825        if errored {
826            None
827        } else {
828            Some(AttributeKind::PatchableFunctionEntry {
829                prefix: prefix.unwrap_or(0),
830                entry: entry.unwrap_or(0),
831            })
832        }
833    }
834}