Skip to main content

rustc_attr_parsing/attributes/
codegen_attrs.rs

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