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