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