Skip to main content

rustc_attr_parsing/attributes/
codegen_attrs.rs

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