Skip to main content

rustc_attr_parsing/attributes/
codegen_attrs.rs

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