Skip to main content

rustc_attr_parsing/attributes/
codegen_attrs.rs

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