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::diagnostics::feature_err;
6use rustc_span::edition::Edition::Edition2024;
7
8use super::prelude::*;
9use crate::attributes::AttributeSafety;
10use crate::session_diagnostics::{
11    EmptyExportName, EmptySection, NakedFunctionIncompatibleAttribute, NullOnExport,
12    NullOnObjcClass, NullOnObjcSelector, NullOnSection, ObjcClassExpectedStringLiteral,
13    ObjcSelectorExpectedStringLiteral, SanitizeInvalidStatic, TargetFeatureOnLangItem,
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            // testing (allowed here so better errors can be generated in `rustc_builtin_macros::test`)
252            sym::test,
253            sym::ignore,
254            sym::should_panic,
255            sym::bench,
256            // diagnostics
257            sym::allow,
258            sym::warn,
259            sym::deny,
260            sym::forbid,
261            sym::deprecated,
262            sym::must_use,
263            // abi, linking and FFI
264            sym::cold,
265            sym::export_name,
266            sym::link_section,
267            sym::linkage,
268            sym::no_mangle,
269            sym::instruction_set,
270            sym::repr,
271            sym::rustc_std_internal_symbol,
272            // FIXME(#82232, #143834): temporarily renamed to mitigate `#[align]` nameres ambiguity
273            sym::rustc_align,
274            sym::rustc_align_static,
275            // obviously compatible with self
276            sym::naked,
277            // documentation
278            sym::doc,
279        ];
280
281        let span = self.span?;
282
283        let Some(tools) = cx.tools else {
284            {
    ::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");
285        };
286
287        // only if we found a naked attribute do we do the somewhat expensive check
288        'outer: for other_attr in cx.all_attrs {
289            for allowed_attr in ALLOW_LIST {
290                if other_attr
291                    .segments()
292                    .next()
293                    .is_some_and(|i| tools.iter().any(|tool| tool.name == i.name))
294                {
295                    // effectively skips the error message  being emitted below
296                    // if it's a tool attribute
297                    continue 'outer;
298                }
299                if other_attr.word_is(*allowed_attr) {
300                    // effectively skips the error message  being emitted below
301                    // if its an allowed attribute
302                    continue 'outer;
303                }
304
305                if other_attr.word_is(sym::target_feature) {
306                    if !cx.features().naked_functions_target_feature() {
307                        feature_err(
308                            &cx.sess(),
309                            sym::naked_functions_target_feature,
310                            other_attr.span(),
311                            "`#[target_feature(/* ... */)]` is currently unstable on `#[naked]` functions",
312                        ).emit();
313                    }
314
315                    continue 'outer;
316                }
317            }
318
319            cx.emit_err(NakedFunctionIncompatibleAttribute {
320                span: other_attr.span(),
321                naked_span: span,
322                attr: other_attr.get_attribute_path().to_string(),
323            });
324        }
325
326        Some(AttributeKind::Naked(span))
327    }
328}
329
330pub(crate) struct TrackCallerParser;
331impl NoArgsAttributeParser for TrackCallerParser {
332    const PATH: &[Symbol] = &[sym::track_caller];
333    const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn;
334    const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[
335        Allow(Target::Fn),
336        Allow(Target::Method(MethodKind::Inherent)),
337        Allow(Target::Method(MethodKind::Trait { body: true })),
338        Allow(Target::Method(MethodKind::TraitImpl)),
339        Allow(Target::Method(MethodKind::Trait { body: false })), // `#[track_caller]` is inherited from trait methods
340        Allow(Target::ForeignFn),
341        Allow(Target::Closure),
342        Warn(Target::MacroDef),
343        Warn(Target::Arm),
344        Warn(Target::Field),
345        Warn(Target::MacroCall),
346    ]);
347    const STABILITY: AttributeStability = AttributeStability::Stable;
348    const CREATE: fn(Span) -> AttributeKind = AttributeKind::TrackCaller;
349}
350
351pub(crate) struct NoMangleParser;
352impl NoArgsAttributeParser for NoMangleParser {
353    const PATH: &[Symbol] = &[sym::no_mangle];
354    const ON_DUPLICATE: OnDuplicate = OnDuplicate::Warn;
355    const SAFETY: AttributeSafety = AttributeSafety::Unsafe {
356        note: "the linker's behavior with multiple libraries exporting duplicate symbol names is undefined and Rust cannot provide guarantees when you manually override them",
357        unsafe_since: Some(Edition2024),
358    };
359    const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowListWarnRest(&[
360        Allow(Target::Fn),
361        Allow(Target::Static),
362        Allow(Target::Method(MethodKind::Inherent)),
363        Allow(Target::Method(MethodKind::TraitImpl)),
364        AllowSilent(Target::Const), // Handled in the `InvalidNoMangleItems` pass
365        Error(Target::Closure),
366    ]);
367    const STABILITY: AttributeStability = AttributeStability::Stable;
368    const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoMangle;
369}
370
371#[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)]
372pub(crate) struct UsedParser {
373    first_compiler: Option<Span>,
374    first_linker: Option<Span>,
375    first_default: Option<Span>,
376}
377
378// A custom `AttributeParser` is used rather than a Simple attribute parser because
379// - Specifying two `#[used]` attributes is a warning (but will be an error in the future)
380// - But specifying two conflicting attributes: `#[used(compiler)]` and `#[used(linker)]` is already an error today
381// We can change this to a Simple parser once the warning becomes an error
382impl AttributeParser for UsedParser {
383    const ATTRIBUTES: AcceptMapping<Self> = &[(
384        &[sym::used],
385        crate::AttributeTemplate {
    word: true,
    list: Some(&["compiler", "linker"]),
    one_of: &[],
    name_value_str: None,
    docs: None,
}template!(Word, List: &["compiler", "linker"]),
386        AttributeStability::Stable,
387        |group: &mut Self, cx, args| {
388            let used_by = match args {
389                ArgParser::NoArgs => UsedBy::Default,
390                ArgParser::List(list) => {
391                    let Some(l) = cx.expect_single(list) else {
392                        return;
393                    };
394
395                    match l.meta_item_no_args().and_then(|i| i.path().word_sym()) {
396                        Some(sym::compiler) => {
397                            if !cx.features().used_with_arg() {
398                                feature_err(
399                                    &cx.sess(),
400                                    sym::used_with_arg,
401                                    cx.attr_span,
402                                    "`#[used(compiler)]` is currently unstable",
403                                )
404                                .emit();
405                            }
406                            UsedBy::Compiler
407                        }
408                        Some(sym::linker) => {
409                            if !cx.features().used_with_arg() {
410                                feature_err(
411                                    &cx.sess(),
412                                    sym::used_with_arg,
413                                    cx.attr_span,
414                                    "`#[used(linker)]` is currently unstable",
415                                )
416                                .emit();
417                            }
418                            UsedBy::Linker
419                        }
420                        _ => {
421                            cx.adcx().expected_specific_argument(
422                                l.span(),
423                                &[sym::compiler, sym::linker],
424                            );
425                            return;
426                        }
427                    }
428                }
429                ArgParser::NameValue(_) => return,
430            };
431
432            let attr_span = cx.attr_span;
433
434            // `#[used]` is interpreted as `#[used(linker)]` (though depending on target OS the
435            // circumstances are more complicated). While we're checking `used_by`, also report
436            // these cross-`UsedBy` duplicates to warn.
437            let target = match used_by {
438                UsedBy::Compiler => &mut group.first_compiler,
439                UsedBy::Linker => {
440                    if let Some(prev) = group.first_default {
441                        cx.warn_unused_duplicate(prev, attr_span);
442                        return;
443                    }
444                    &mut group.first_linker
445                }
446                UsedBy::Default => {
447                    if let Some(prev) = group.first_linker {
448                        cx.warn_unused_duplicate(prev, attr_span);
449                        return;
450                    }
451                    &mut group.first_default
452                }
453            };
454
455            if let Some(prev) = *target {
456                cx.warn_unused_duplicate(prev, attr_span);
457            } else {
458                *target = Some(attr_span);
459            }
460        },
461    )];
462    const ALLOWED_TARGETS: AllowedTargets<'_> =
463        AllowedTargets::AllowList(&[Allow(Target::Static), Warn(Target::MacroCall)]);
464
465    fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option<AttributeKind> {
466        // If a specific form of `used` is specified, it takes precedence over generic `#[used]`.
467        // If both `linker` and `compiler` are specified, use `linker`.
468        Some(match (self.first_compiler, self.first_linker, self.first_default) {
469            (_, Some(_), _) => AttributeKind::Used { used_by: UsedBy::Linker },
470            (Some(_), _, _) => AttributeKind::Used { used_by: UsedBy::Compiler },
471            (_, _, Some(_)) => AttributeKind::Used { used_by: UsedBy::Default },
472            (None, None, None) => return None,
473        })
474    }
475}
476
477fn parse_tf_attribute(
478    cx: &mut AcceptContext<'_, '_>,
479    args: &ArgParser,
480) -> impl IntoIterator<Item = (Symbol, Span)> {
481    let mut features = Vec::new();
482    let Some(list) = cx.expect_list(args, cx.attr_span) else {
483        return features;
484    };
485    if list.is_empty() {
486        let attr_span = cx.attr_span;
487        cx.adcx().warn_empty_attribute(attr_span);
488        return features;
489    }
490    for item in list.mixed() {
491        let Some((ident, value)) = cx.expect_name_value(item, item.span(), Some(sym::enable))
492        else {
493            return features;
494        };
495
496        // Validate name
497        if ident.name != sym::enable {
498            cx.adcx().expected_specific_argument(ident.span, &[sym::enable]);
499            return features;
500        }
501
502        // Use value
503        let Some(value_str) = cx.expect_string_literal(value) else {
504            return features;
505        };
506        for feature in value_str.as_str().split(",") {
507            features.push((Symbol::intern(feature), item.span()));
508        }
509    }
510    features
511}
512
513pub(crate) struct TargetFeatureParser;
514
515impl CombineAttributeParser for TargetFeatureParser {
516    type Item = (Symbol, Span);
517    const PATH: &[Symbol] = &[sym::target_feature];
518    const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {
519        features: items,
520        attr_span: span,
521        was_forced: false,
522    };
523    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\""]);
524    const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[
525        Allow(Target::Fn),
526        Allow(Target::Method(MethodKind::Inherent)),
527        Allow(Target::Method(MethodKind::Trait { body: true })),
528        Allow(Target::Method(MethodKind::TraitImpl)),
529        Warn(Target::Statement),
530        Warn(Target::Field),
531        Warn(Target::Arm),
532        Warn(Target::MacroDef),
533        Warn(Target::MacroCall),
534    ]);
535    const STABILITY: AttributeStability = AttributeStability::Stable;
536
537    fn extend(
538        cx: &mut AcceptContext<'_, '_>,
539        args: &ArgParser,
540    ) -> impl IntoIterator<Item = Self::Item> {
541        parse_tf_attribute(cx, args)
542    }
543
544    fn finalize_check(cx: &FinalizeCheckContext<'_, '_>, attr_span: Span) {
545        // `#[target_feature]` is incompatible with lang item functions,
546        // except on WASM where calling target-feature functions is safe (see #84988).
547        if !cx.sess().target.is_like_wasm && !cx.sess().opts.actually_rustdoc {
548            // `#[panic_handler]` is checked first so it takes priority in the diagnostic.
549            let lang_kind = cx
550                .all_attrs
551                .iter()
552                .find_map(|a| [sym::panic_handler, sym::lang].into_iter().find(|&s| a.word_is(s)));
553            if let Some(kind) = lang_kind {
554                cx.emit_err(TargetFeatureOnLangItem { attr_span, kind, item_span: cx.target_span });
555            }
556        }
557    }
558}
559
560pub(crate) struct ForceTargetFeatureParser;
561
562impl CombineAttributeParser for ForceTargetFeatureParser {
563    type Item = (Symbol, Span);
564    const PATH: &[Symbol] = &[sym::force_target_feature];
565    const SAFETY: AttributeSafety = AttributeSafety::Unsafe {
566        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",
567        unsafe_since: None,
568    };
569    const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {
570        features: items,
571        attr_span: span,
572        was_forced: true,
573    };
574    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\""]);
575    const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[
576        Allow(Target::Fn),
577        Allow(Target::Method(MethodKind::Inherent)),
578        Allow(Target::Method(MethodKind::Trait { body: true })),
579        Allow(Target::Method(MethodKind::TraitImpl)),
580    ]);
581    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);
582
583    fn extend(
584        cx: &mut AcceptContext<'_, '_>,
585        args: &ArgParser,
586    ) -> impl IntoIterator<Item = Self::Item> {
587        parse_tf_attribute(cx, args)
588    }
589}
590
591pub(crate) struct InstrumentFnParser;
592
593impl SingleAttributeParser for InstrumentFnParser {
594    const PATH: &[Symbol] = &[sym::instrument_fn];
595    const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[
596        Allow(Target::Fn),
597        Allow(Target::Method(MethodKind::Inherent)),
598        Allow(Target::Method(MethodKind::Trait { body: true })),
599        Allow(Target::Method(MethodKind::TraitImpl)),
600    ]);
601    const TEMPLATE: AttributeTemplate = crate::AttributeTemplate {
    word: false,
    list: None,
    one_of: &[],
    name_value_str: Some(&["on|off"]),
    docs: None,
}template!(NameValueStr: "on|off");
602    const STABILITY: AttributeStability = AttributeStability::Unstable {
    gate_name: rustc_span::sym::instrument_fn,
    gate_check: rustc_feature::Features::instrument_fn,
    notes: &[],
}unstable!(instrument_fn);
603
604    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
605        let instrument = match args {
606            ArgParser::NameValue(nv) => match nv.value_as_str() {
607                Some(sym::on) => Some(AttributeKind::InstrumentFn(InstrumentFnAttr::On)),
608                Some(sym::off) => Some(AttributeKind::InstrumentFn(InstrumentFnAttr::Off)),
609                _ => {
610                    cx.adcx()
611                        .expected_specific_argument_strings(nv.value_span, &[sym::on, sym::off]);
612                    None
613                }
614            },
615            ArgParser::List(l) => {
616                cx.adcx().expected_single_argument(l.span, l.len());
617                None
618            }
619            ArgParser::NoArgs => {
620                let span = cx.attr_span;
621                cx.adcx().expected_specific_argument_strings(span, &[sym::on, sym::off]);
622                None
623            }
624        };
625        instrument
626    }
627}
628
629pub(crate) struct SanitizeParser;
630
631impl SingleAttributeParser for SanitizeParser {
632    const PATH: &[Symbol] = &[sym::sanitize];
633    const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[
634        Allow(Target::Fn),
635        Allow(Target::Closure),
636        Allow(Target::Method(MethodKind::Inherent)),
637        Allow(Target::Method(MethodKind::Trait { body: true })),
638        Allow(Target::Method(MethodKind::TraitImpl)),
639        Allow(Target::Impl { of_trait: false }),
640        Allow(Target::Impl { of_trait: true }),
641        Allow(Target::Mod),
642        Allow(Target::Crate),
643        Allow(Target::Static),
644    ]);
645    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: &[
646        r#"address = "on|off""#,
647        r#"kernel_address = "on|off""#,
648        r#"cfi = "on|off""#,
649        r#"hwaddress = "on|off""#,
650        r#"kernel_hwaddress = "on|off""#,
651        r#"kcfi = "on|off""#,
652        r#"memory = "on|off""#,
653        r#"memtag = "on|off""#,
654        r#"shadow_call_stack = "on|off""#,
655        r#"thread = "on|off""#,
656        r#"realtime = "nonblocking|blocking|caller""#,
657    ]);
658    const STABILITY: AttributeStability = AttributeStability::Unstable {
    gate_name: rustc_span::sym::sanitize,
    gate_check: rustc_feature::Features::sanitize,
    notes: &[],
}unstable!(sanitize);
659
660    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
661        let list = cx.expect_list(args, cx.attr_span)?;
662
663        let mut on_set = SanitizerSet::empty();
664        let mut off_set = SanitizerSet::empty();
665        let mut rtsan = None;
666
667        for item in list.mixed() {
668            let Some((ident, value)) = cx.expect_name_value(item, item.span(), None) else {
669                continue;
670            };
671
672            let mut apply = |s: SanitizerSet| {
673                let is_on = match value.value_as_str() {
674                    Some(sym::on) => true,
675                    Some(sym::off) => false,
676                    Some(_) => {
677                        cx.adcx().expected_specific_argument_strings(
678                            value.value_span,
679                            &[sym::on, sym::off],
680                        );
681                        return;
682                    }
683                    None => {
684                        cx.adcx().expected_specific_argument_strings(
685                            value.value_span,
686                            &[sym::on, sym::off],
687                        );
688                        return;
689                    }
690                };
691
692                if is_on {
693                    on_set |= s;
694                } else {
695                    off_set |= s;
696                }
697            };
698
699            match ident.name {
700                sym::address | sym::kernel_address => {
701                    apply(SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS)
702                }
703                sym::cfi => apply(SanitizerSet::CFI),
704                sym::kcfi => apply(SanitizerSet::KCFI),
705                sym::memory => apply(SanitizerSet::MEMORY),
706                sym::memtag => apply(SanitizerSet::MEMTAG),
707                sym::shadow_call_stack => apply(SanitizerSet::SHADOWCALLSTACK),
708                sym::thread => apply(SanitizerSet::THREAD),
709                sym::hwaddress | sym::kernel_hwaddress => {
710                    apply(SanitizerSet::HWADDRESS | SanitizerSet::KERNELHWADDRESS)
711                }
712                sym::realtime => match value.value_as_str() {
713                    Some(sym::nonblocking) => rtsan = Some(RtsanSetting::Nonblocking),
714                    Some(sym::blocking) => rtsan = Some(RtsanSetting::Blocking),
715                    Some(sym::caller) => rtsan = Some(RtsanSetting::Caller),
716                    _ => {
717                        cx.adcx().expected_specific_argument_strings(
718                            value.value_span,
719                            &[sym::nonblocking, sym::blocking, sym::caller],
720                        );
721                    }
722                },
723                _ => {
724                    cx.adcx().expected_specific_argument_strings(
725                        ident.span,
726                        &[
727                            sym::address,
728                            sym::kernel_address,
729                            sym::cfi,
730                            sym::kcfi,
731                            sym::memory,
732                            sym::memtag,
733                            sym::shadow_call_stack,
734                            sym::thread,
735                            sym::hwaddress,
736                            sym::kernel_hwaddress,
737                            sym::realtime,
738                        ],
739                    );
740                    continue;
741                }
742            }
743        }
744
745        // The sanitizer attribute is only allowed on statics, if only address bits are set
746        let all_set_except_address =
747            (on_set | off_set) & !(SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS);
748        if cx.target == Target::Static
749            && let Some(set) = all_set_except_address.iter().next()
750        {
751            cx.emit_err(SanitizeInvalidStatic {
752                span: cx.attr_span,
753                field: set.as_str().expect("Since this `SanitizerSet` is returned from an iterator, exactly one field is set")
754            });
755        }
756
757        Some(AttributeKind::Sanitize { on_set, off_set, rtsan, span: cx.attr_span })
758    }
759}
760
761pub(crate) struct ThreadLocalParser;
762
763impl NoArgsAttributeParser for ThreadLocalParser {
764    const PATH: &[Symbol] = &[sym::thread_local];
765    const ALLOWED_TARGETS: AllowedTargets<'_> =
766        AllowedTargets::AllowList(&[Allow(Target::Static), Allow(Target::ForeignStatic)]);
767    const STABILITY: AttributeStability = AttributeStability::Unstable {
    gate_name: rustc_span::sym::thread_local,
    gate_check: rustc_feature::Features::thread_local,
    notes: &[],
}unstable!(thread_local);
768    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::ThreadLocal;
769}
770
771pub(crate) struct RustcPassIndirectlyInNonRusticAbisParser;
772
773impl NoArgsAttributeParser for RustcPassIndirectlyInNonRusticAbisParser {
774    const PATH: &[Symbol] = &[sym::rustc_pass_indirectly_in_non_rustic_abis];
775    const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Struct)]);
776    const STABILITY: AttributeStability = AttributeStability::Unstable {
    gate_name: rustc_span::sym::rustc_attrs,
    gate_check: rustc_feature::Features::rustc_attrs,
    notes: &[],
}unstable!(rustc_attrs);
777    const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcPassIndirectlyInNonRusticAbis;
778}
779
780pub(crate) struct RustcEiiForeignItemParser;
781
782impl NoArgsAttributeParser for RustcEiiForeignItemParser {
783    const PATH: &[Symbol] = &[sym::rustc_eii_foreign_item];
784    const ALLOWED_TARGETS: AllowedTargets<'_> =
785        AllowedTargets::AllowList(&[Allow(Target::ForeignFn), Allow(Target::ForeignStatic)]);
786    const STABILITY: AttributeStability = AttributeStability::Unstable {
    gate_name: rustc_span::sym::eii_internals,
    gate_check: rustc_feature::Features::eii_internals,
    notes: &[],
}unstable!(eii_internals);
787    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcEiiForeignItem;
788}
789
790pub(crate) struct PatchableFunctionEntryParser;
791
792impl SingleAttributeParser for PatchableFunctionEntryParser {
793    const PATH: &[Symbol] = &[sym::patchable_function_entry];
794    const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Fn)]);
795    const TEMPLATE: AttributeTemplate =
796        crate::AttributeTemplate {
    word: false,
    list: Some(&["prefix_nops = m, entry_nops = n, section = \"section\""]),
    one_of: &[],
    name_value_str: None,
    docs: None,
}template!(List: &["prefix_nops = m, entry_nops = n, section = \"section\""]);
797    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);
798
799    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
800        let meta_item_list = cx.expect_list(args, cx.attr_span)?;
801
802        let mut prefix = None;
803        let mut entry = None;
804        let mut section = None;
805
806        if meta_item_list.len() == 0 {
807            cx.adcx().expected_at_least_one_argument(meta_item_list.span);
808            return None;
809        }
810
811        for item in meta_item_list.mixed() {
812            let Some((ident, value)) = cx.expect_name_value(item, item.span(), None) else {
813                return None;
814            };
815
816            let attrib_to_write = match ident.name {
817                sym::prefix_nops => {
818                    // Duplicate prefixes are not allowed
819                    if prefix.is_some() {
820                        cx.adcx().duplicate_key(ident.span, sym::prefix_nops);
821                        return None;
822                    }
823                    &mut prefix
824                }
825                sym::entry_nops => {
826                    // Duplicate entries are not allowed
827                    if entry.is_some() {
828                        cx.adcx().duplicate_key(ident.span, sym::entry_nops);
829                        return None;
830                    }
831                    &mut entry
832                }
833                sym::section => {
834                    // Duplicate entries are not allowed
835                    if section.is_some() {
836                        cx.adcx().duplicate_key(ident.span, sym::section);
837                        return None;
838                    }
839                    // Only a string type value is allowed.
840                    let Some(value_str) = value.value_as_str() else {
841                        cx.adcx().expect_string_literal(value);
842                        return None;
843                    };
844                    // The section name does not allow null characters.
845                    if value_str.as_str().contains('\0') {
846                        cx.emit_err(NullOnSection { span: value.value_span });
847                    }
848                    // The section name is not allowed to be empty, LLVM does
849                    // not allow them.
850                    if value_str.is_empty() {
851                        cx.emit_err(EmptySection { span: value.value_span });
852                    }
853                    section = Some(value_str);
854                    // Integer parsing is not needed, process next item.
855                    continue;
856                }
857                _ => {
858                    cx.adcx().expected_specific_argument(
859                        ident.span,
860                        &[sym::prefix_nops, sym::entry_nops],
861                    );
862                    return None;
863                }
864            };
865
866            let rustc_ast::LitKind::Int(val, _) = value.value_as_lit().kind else {
867                cx.adcx().expected_integer_literal(value.value_span);
868                return None;
869            };
870
871            let Ok(val) = val.get().try_into() else {
872                cx.adcx().expected_integer_literal_in_range(
873                    value.value_span,
874                    u8::MIN as isize,
875                    u8::MAX as isize,
876                );
877                return None;
878            };
879
880            *attrib_to_write = Some(val);
881        }
882
883        Some(AttributeKind::PatchableFunctionEntry { prefix, entry, section })
884    }
885}