Skip to main content

rustc_attr_parsing/attributes/
codegen_attrs.rs

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