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, 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            // 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 ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[
528        Allow(Target::Fn),
529        Allow(Target::Method(MethodKind::Inherent)),
530        Allow(Target::Method(MethodKind::Trait { body: true })),
531        Allow(Target::Method(MethodKind::TraitImpl)),
532        Warn(Target::Statement),
533        Warn(Target::Field),
534        Warn(Target::Arm),
535        Warn(Target::MacroDef),
536        Warn(Target::MacroCall),
537    ]);
538    const STABILITY: AttributeStability = AttributeStability::Stable;
539
540    fn extend(
541        cx: &mut AcceptContext<'_, '_>,
542        args: &ArgParser,
543    ) -> impl IntoIterator<Item = Self::Item> {
544        parse_tf_attribute(cx, args)
545    }
546
547    fn finalize_check(cx: &FinalizeContext<'_, '_>, attr_span: Span) {
548        // `#[target_feature]` is incompatible with lang item functions,
549        // except on WASM where calling target-feature functions is safe (see #84988).
550        if !cx.sess().target.is_like_wasm && !cx.sess().opts.actually_rustdoc {
551            // `#[panic_handler]` is checked first so it takes priority in the diagnostic.
552            let lang_kind = cx
553                .all_attrs
554                .iter()
555                .find_map(|a| [sym::panic_handler, sym::lang].into_iter().find(|&s| a.word_is(s)));
556            if let Some(kind) = lang_kind {
557                cx.emit_err(TargetFeatureOnLangItem { attr_span, kind, item_span: cx.target_span });
558            }
559        }
560    }
561}
562
563pub(crate) struct ForceTargetFeatureParser;
564
565impl CombineAttributeParser for ForceTargetFeatureParser {
566    type Item = (Symbol, Span);
567    const PATH: &[Symbol] = &[sym::force_target_feature];
568    const SAFETY: AttributeSafety = AttributeSafety::Unsafe {
569        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",
570        unsafe_since: None,
571    };
572    const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {
573        features: items,
574        attr_span: span,
575        was_forced: true,
576    };
577    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\""]);
578    const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[
579        Allow(Target::Fn),
580        Allow(Target::Method(MethodKind::Inherent)),
581        Allow(Target::Method(MethodKind::Trait { body: true })),
582        Allow(Target::Method(MethodKind::TraitImpl)),
583    ]);
584    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);
585
586    fn extend(
587        cx: &mut AcceptContext<'_, '_>,
588        args: &ArgParser,
589    ) -> impl IntoIterator<Item = Self::Item> {
590        parse_tf_attribute(cx, args)
591    }
592}
593
594pub(crate) struct InstrumentFnParser;
595
596impl SingleAttributeParser for InstrumentFnParser {
597    const PATH: &[Symbol] = &[sym::instrument_fn];
598    const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[
599        Allow(Target::Fn),
600        Allow(Target::Method(MethodKind::Inherent)),
601        Allow(Target::Method(MethodKind::Trait { body: true })),
602        Allow(Target::Method(MethodKind::TraitImpl)),
603    ]);
604    const TEMPLATE: AttributeTemplate = crate::AttributeTemplate {
    word: false,
    list: None,
    one_of: &[],
    name_value_str: Some(&["on|off"]),
    docs: None,
}template!(NameValueStr: "on|off");
605    const STABILITY: AttributeStability = AttributeStability::Unstable {
    gate_name: rustc_span::sym::instrument_fn,
    gate_check: rustc_feature::Features::instrument_fn,
    notes: &[],
}unstable!(instrument_fn);
606
607    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
608        let instrument = match args {
609            ArgParser::NameValue(nv) => match nv.value_as_str() {
610                Some(sym::on) => Some(AttributeKind::InstrumentFn(InstrumentFnAttr::On)),
611                Some(sym::off) => Some(AttributeKind::InstrumentFn(InstrumentFnAttr::Off)),
612                _ => {
613                    cx.adcx()
614                        .expected_specific_argument_strings(nv.value_span, &[sym::on, sym::off]);
615                    None
616                }
617            },
618            ArgParser::List(l) => {
619                cx.adcx().expected_single_argument(l.span, l.len());
620                None
621            }
622            ArgParser::NoArgs => {
623                let span = cx.attr_span;
624                cx.adcx().expected_specific_argument_strings(span, &[sym::on, sym::off]);
625                None
626            }
627        };
628        instrument
629    }
630}
631
632pub(crate) struct SanitizeParser;
633
634impl SingleAttributeParser for SanitizeParser {
635    const PATH: &[Symbol] = &[sym::sanitize];
636    const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[
637        Allow(Target::Fn),
638        Allow(Target::Closure),
639        Allow(Target::Method(MethodKind::Inherent)),
640        Allow(Target::Method(MethodKind::Trait { body: true })),
641        Allow(Target::Method(MethodKind::TraitImpl)),
642        Allow(Target::Impl { of_trait: false }),
643        Allow(Target::Impl { of_trait: true }),
644        Allow(Target::Mod),
645        Allow(Target::Crate),
646        Allow(Target::Static),
647    ]);
648    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: &[
649        r#"address = "on|off""#,
650        r#"kernel_address = "on|off""#,
651        r#"cfi = "on|off""#,
652        r#"hwaddress = "on|off""#,
653        r#"kernel_hwaddress = "on|off""#,
654        r#"kcfi = "on|off""#,
655        r#"memory = "on|off""#,
656        r#"memtag = "on|off""#,
657        r#"shadow_call_stack = "on|off""#,
658        r#"thread = "on|off""#,
659        r#"realtime = "nonblocking|blocking|caller""#,
660    ]);
661    const STABILITY: AttributeStability = AttributeStability::Unstable {
    gate_name: rustc_span::sym::sanitize,
    gate_check: rustc_feature::Features::sanitize,
    notes: &[],
}unstable!(sanitize);
662
663    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
664        let list = cx.expect_list(args, cx.attr_span)?;
665
666        let mut on_set = SanitizerSet::empty();
667        let mut off_set = SanitizerSet::empty();
668        let mut rtsan = None;
669
670        for item in list.mixed() {
671            let Some((ident, value)) = cx.expect_name_value(item, item.span(), None) else {
672                continue;
673            };
674
675            let mut apply = |s: SanitizerSet| {
676                let is_on = match value.value_as_str() {
677                    Some(sym::on) => true,
678                    Some(sym::off) => false,
679                    Some(_) => {
680                        cx.adcx().expected_specific_argument_strings(
681                            value.value_span,
682                            &[sym::on, sym::off],
683                        );
684                        return;
685                    }
686                    None => {
687                        cx.adcx().expected_specific_argument_strings(
688                            value.value_span,
689                            &[sym::on, sym::off],
690                        );
691                        return;
692                    }
693                };
694
695                if is_on {
696                    on_set |= s;
697                } else {
698                    off_set |= s;
699                }
700            };
701
702            match ident.name {
703                sym::address | sym::kernel_address => {
704                    apply(SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS)
705                }
706                sym::cfi => apply(SanitizerSet::CFI),
707                sym::kcfi => apply(SanitizerSet::KCFI),
708                sym::memory => apply(SanitizerSet::MEMORY),
709                sym::memtag => apply(SanitizerSet::MEMTAG),
710                sym::shadow_call_stack => apply(SanitizerSet::SHADOWCALLSTACK),
711                sym::thread => apply(SanitizerSet::THREAD),
712                sym::hwaddress | sym::kernel_hwaddress => {
713                    apply(SanitizerSet::HWADDRESS | SanitizerSet::KERNELHWADDRESS)
714                }
715                sym::realtime => match value.value_as_str() {
716                    Some(sym::nonblocking) => rtsan = Some(RtsanSetting::Nonblocking),
717                    Some(sym::blocking) => rtsan = Some(RtsanSetting::Blocking),
718                    Some(sym::caller) => rtsan = Some(RtsanSetting::Caller),
719                    _ => {
720                        cx.adcx().expected_specific_argument_strings(
721                            value.value_span,
722                            &[sym::nonblocking, sym::blocking, sym::caller],
723                        );
724                    }
725                },
726                _ => {
727                    cx.adcx().expected_specific_argument_strings(
728                        ident.span,
729                        &[
730                            sym::address,
731                            sym::kernel_address,
732                            sym::cfi,
733                            sym::kcfi,
734                            sym::memory,
735                            sym::memtag,
736                            sym::shadow_call_stack,
737                            sym::thread,
738                            sym::hwaddress,
739                            sym::kernel_hwaddress,
740                            sym::realtime,
741                        ],
742                    );
743                    continue;
744                }
745            }
746        }
747
748        // The sanitizer attribute is only allowed on statics, if only address bits are set
749        let all_set_except_address =
750            (on_set | off_set) & !(SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS);
751        if cx.target == Target::Static
752            && let Some(set) = all_set_except_address.iter().next()
753        {
754            cx.emit_err(SanitizeInvalidStatic {
755                span: cx.attr_span,
756                field: set.as_str().expect("Since this `SanitizerSet` is returned from an iterator, exactly one field is set")
757            });
758        }
759
760        Some(AttributeKind::Sanitize { on_set, off_set, rtsan, span: cx.attr_span })
761    }
762}
763
764pub(crate) struct ThreadLocalParser;
765
766impl NoArgsAttributeParser for ThreadLocalParser {
767    const PATH: &[Symbol] = &[sym::thread_local];
768    const ALLOWED_TARGETS: AllowedTargets<'_> =
769        AllowedTargets::AllowList(&[Allow(Target::Static), Allow(Target::ForeignStatic)]);
770    const STABILITY: AttributeStability = AttributeStability::Unstable {
    gate_name: rustc_span::sym::thread_local,
    gate_check: rustc_feature::Features::thread_local,
    notes: &[],
}unstable!(thread_local);
771    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::ThreadLocal;
772}
773
774pub(crate) struct RustcPassIndirectlyInNonRusticAbisParser;
775
776impl NoArgsAttributeParser for RustcPassIndirectlyInNonRusticAbisParser {
777    const PATH: &[Symbol] = &[sym::rustc_pass_indirectly_in_non_rustic_abis];
778    const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Struct)]);
779    const STABILITY: AttributeStability = AttributeStability::Unstable {
    gate_name: rustc_span::sym::rustc_attrs,
    gate_check: rustc_feature::Features::rustc_attrs,
    notes: &[],
}unstable!(rustc_attrs);
780    const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcPassIndirectlyInNonRusticAbis;
781}
782
783pub(crate) struct RustcEiiForeignItemParser;
784
785impl NoArgsAttributeParser for RustcEiiForeignItemParser {
786    const PATH: &[Symbol] = &[sym::rustc_eii_foreign_item];
787    const ALLOWED_TARGETS: AllowedTargets<'_> =
788        AllowedTargets::AllowList(&[Allow(Target::ForeignFn), Allow(Target::ForeignStatic)]);
789    const STABILITY: AttributeStability = AttributeStability::Unstable {
    gate_name: rustc_span::sym::eii_internals,
    gate_check: rustc_feature::Features::eii_internals,
    notes: &[],
}unstable!(eii_internals);
790    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcEiiForeignItem;
791}
792
793pub(crate) struct PatchableFunctionEntryParser;
794
795impl SingleAttributeParser for PatchableFunctionEntryParser {
796    const PATH: &[Symbol] = &[sym::patchable_function_entry];
797    const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Fn)]);
798    const TEMPLATE: AttributeTemplate =
799        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\""]);
800    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);
801
802    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
803        let meta_item_list = cx.expect_list(args, cx.attr_span)?;
804
805        let mut prefix = None;
806        let mut entry = None;
807        let mut section = None;
808
809        if meta_item_list.len() == 0 {
810            cx.adcx().expected_at_least_one_argument(meta_item_list.span);
811            return None;
812        }
813
814        for item in meta_item_list.mixed() {
815            let Some((ident, value)) = cx.expect_name_value(item, item.span(), None) else {
816                return None;
817            };
818
819            let attrib_to_write = match ident.name {
820                sym::prefix_nops => {
821                    // Duplicate prefixes are not allowed
822                    if prefix.is_some() {
823                        cx.adcx().duplicate_key(ident.span, sym::prefix_nops);
824                        return None;
825                    }
826                    &mut prefix
827                }
828                sym::entry_nops => {
829                    // Duplicate entries are not allowed
830                    if entry.is_some() {
831                        cx.adcx().duplicate_key(ident.span, sym::entry_nops);
832                        return None;
833                    }
834                    &mut entry
835                }
836                sym::section => {
837                    // Duplicate entries are not allowed
838                    if section.is_some() {
839                        cx.adcx().duplicate_key(ident.span, sym::section);
840                        return None;
841                    }
842                    // Only a string type value is allowed.
843                    let Some(value_str) = value.value_as_str() else {
844                        cx.adcx().expect_string_literal(value);
845                        return None;
846                    };
847                    // The section name does not allow null characters.
848                    if value_str.as_str().contains('\0') {
849                        cx.emit_err(NullOnSection { span: value.value_span });
850                    }
851                    // The section name is not allowed to be empty, LLVM does
852                    // not allow them.
853                    if value_str.is_empty() {
854                        cx.emit_err(EmptySection { span: value.value_span });
855                    }
856                    section = Some(value_str);
857                    // Integer parsing is not needed, process next item.
858                    continue;
859                }
860                _ => {
861                    cx.adcx().expected_specific_argument(
862                        ident.span,
863                        &[sym::prefix_nops, sym::entry_nops],
864                    );
865                    return None;
866                }
867            };
868
869            let rustc_ast::LitKind::Int(val, _) = value.value_as_lit().kind else {
870                cx.adcx().expected_integer_literal(value.value_span);
871                return None;
872            };
873
874            let Ok(val) = val.get().try_into() else {
875                cx.adcx().expected_integer_literal_in_range(
876                    value.value_span,
877                    u8::MIN as isize,
878                    u8::MAX as isize,
879                );
880                return None;
881            };
882
883            *attrib_to_write = Some(val);
884        }
885
886        Some(AttributeKind::PatchableFunctionEntry { prefix, entry, section })
887    }
888}