Skip to main content

rustc_attr_parsing/attributes/
link_attrs.rs

1use rustc_abi::ExternAbi;
2use rustc_attr_ir::AttributeKind::{LinkName, LinkOrdinal, LinkSection};
3use rustc_attr_ir::target::AstTarget;
4use rustc_attr_ir::*;
5use rustc_errors::msg;
6use rustc_feature::{AttributeStability, Features};
7use rustc_lint_defs::builtin::{ILL_FORMED_ATTRIBUTE_INPUT, UNUSED_ATTRIBUTES};
8use rustc_session::Session;
9use rustc_session::diagnostics::feature_err;
10use rustc_span::edition::Edition::Edition2024;
11use rustc_span::kw;
12use rustc_structures::NativeLibKind;
13use rustc_target::spec::{Arch, BinaryFormat};
14
15use super::prelude::*;
16use super::util::parse_single_integer;
17use crate::attributes::AttributeSafety;
18use crate::attributes::cfg::parse_cfg_entry;
19use crate::diagnostics::{
20    AsNeededCompatibility, BothFfiConstAndPure, BundleNeedsStatic, EmptyLinkName,
21    ExportSymbolsNeedsStatic, ImportNameTypeRaw, ImportNameTypeX86, IncompatibleWasmLink,
22    InvalidLinkModifier, InvalidMachoSection, InvalidMachoSectionReason, Link, LinkFrameworkApple,
23    LinkOrdinalOutOfRange, LinkRequiresName, MultipleModifiers, NullOnLinkName, NullOnLinkSection,
24    RawDylibOnlyWindows, WholeArchiveNeedsStatic,
25};
26
27pub(crate) struct LinkNameParser;
28
29impl SingleAttributeParser for LinkNameParser {
30    const PATH: &[Symbol] = &[sym::link_name];
31    const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError;
32    const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowListWarnRest(&[
33        Allow(Target::ForeignFn),
34        Allow(Target::ForeignStatic),
35        Error(Target::Param),
36    ]);
37    const TEMPLATE: AttributeTemplate = crate::AttributeTemplate {
    word: false,
    list: None,
    one_of: &[],
    name_value_str: Some(&["name"]),
    docs: Some("https://doc.rust-lang.org/reference/items/external-blocks.html#the-link_name-attribute"),
}template!(
38        NameValueStr: "name",
39        "https://doc.rust-lang.org/reference/items/external-blocks.html#the-link_name-attribute"
40    );
41    const STABILITY: AttributeStability = AttributeStability::Stable;
42
43    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
44        let nv = cx.expect_name_value(args, cx.attr_span, None)?;
45        let name = cx.expect_string_literal(nv)?;
46
47        if name.as_str().contains('\0') {
48            // `#[link_name = ...]` will be converted to a null-terminated string,
49            // so it may not contain any null characters.
50            cx.emit_err(NullOnLinkName { span: nv.value_span });
51            return None;
52        }
53        if name.is_empty() {
54            // Otherwise LLVM will just make up a name and the linker will fail
55            // to find an empty symbol name.
56            cx.emit_err(EmptyLinkName { span: nv.value_span });
57            return None;
58        }
59
60        Some(LinkName { name, span: cx.attr_span })
61    }
62}
63
64pub(crate) struct LinkParser;
65
66impl CombineAttributeParser for LinkParser {
67    type Item = LinkEntry;
68    const PATH: &[Symbol] = &[sym::link];
69    const CONVERT: ConvertFn<Self::Item> = AttributeKind::Link;
70    const TEMPLATE: AttributeTemplate = crate::AttributeTemplate {
    word: false,
    list: Some(&[r#"name = "...""#,
                    r#"name = "...", kind = "dylib|static|...""#,
                    r#"name = "...", wasm_import_module = "...""#,
                    r#"name = "...", import_name_type = "decorated|noprefix|undecorated""#,
                    r#"name = "...", kind = "dylib|static|...", wasm_import_module = "...", import_name_type = "decorated|noprefix|undecorated""#]),
    one_of: &[],
    name_value_str: None,
    docs: Some("https://doc.rust-lang.org/reference/items/external-blocks.html#the-link-attribute"),
}template!(List: &[
71            r#"name = "...""#,
72            r#"name = "...", kind = "dylib|static|...""#,
73            r#"name = "...", wasm_import_module = "...""#,
74            r#"name = "...", import_name_type = "decorated|noprefix|undecorated""#,
75            r#"name = "...", kind = "dylib|static|...", wasm_import_module = "...", import_name_type = "decorated|noprefix|undecorated""#,
76        ], "https://doc.rust-lang.org/reference/items/external-blocks.html#the-link-attribute");
77    const ALLOWED_TARGETS: AllowedTargets<'_> =
78        AllowedTargets::AllowListWarnRest(&[Allow(Target::ForeignMod), Error(Target::Param)]);
79    const STABILITY: AttributeStability = AttributeStability::Stable;
80
81    fn extend(
82        cx: &mut AcceptContext<'_, '_>,
83        args: &ArgParser,
84    ) -> impl IntoIterator<Item = Self::Item> {
85        let items = match args {
86            ArgParser::List(list) => list,
87            // This is an edgecase added because making this a hard error would break too many crates
88            // Specifically `#[link = "dl"]` is accepted with a FCW
89            // For more information, see https://github.com/rust-lang/rust/pull/143193
90            ArgParser::NameValue(nv) if nv.value_as_str().is_some_and(|v| v == sym::dl) => {
91                cx.adcx().warn_ill_formed_attribute_input(ILL_FORMED_ATTRIBUTE_INPUT);
92                return None;
93            }
94            _ => {
95                let attr_span = cx.attr_span;
96                cx.adcx().expected_list(attr_span, args);
97                return None;
98            }
99        };
100
101        let sess = cx.sess();
102        let features = cx.features();
103
104        let mut name = None;
105        let mut kind = None;
106        let mut modifiers = None;
107        let mut cfg = None;
108        let mut wasm_import_module = None;
109        let mut import_name_type = None;
110        for item in items.mixed() {
111            let Some(item) = item.meta_item() else {
112                cx.adcx().expected_not_literal(item.span());
113                continue;
114            };
115
116            let cont = match item.path().word().map(|ident| ident.name) {
117                Some(sym::name) => Self::parse_link_name(item, &mut name, cx),
118                Some(sym::kind) => Self::parse_link_kind(item, &mut kind, cx, sess, features),
119                Some(sym::modifiers) => Self::parse_link_modifiers(item, &mut modifiers, cx),
120                Some(sym::cfg) => Self::parse_link_cfg(item, &mut cfg, cx, sess, features),
121                Some(sym::wasm_import_module) => {
122                    Self::parse_link_wasm_import_module(item, &mut wasm_import_module, cx)
123                }
124                Some(sym::import_name_type) => {
125                    Self::parse_link_import_name_type(item, &mut import_name_type, cx)
126                }
127                _ => {
128                    cx.adcx().expected_specific_argument_strings(
129                        item.span(),
130                        &[
131                            sym::name,
132                            sym::kind,
133                            sym::modifiers,
134                            sym::cfg,
135                            sym::wasm_import_module,
136                            sym::import_name_type,
137                        ],
138                    );
139                    true
140                }
141            };
142            if !cont {
143                return None;
144            }
145        }
146
147        // Do this outside the above loop so we don't depend on modifiers coming after kinds
148        let mut verbatim = None;
149        if let Some((modifiers, span)) = modifiers {
150            for modifier in modifiers.as_str().split(',') {
151                let (modifier, value): (Symbol, bool) =
152                    if let Some(m) = modifier.strip_prefix(['+', '-']) {
153                        (Symbol::intern(m), modifier.starts_with('+'))
154                    } else {
155                        cx.emit_err(InvalidLinkModifier { span });
156                        continue;
157                    };
158
159                macro report_unstable_modifier($feature: ident) {
160                    if !features.$feature() {
161                        feature_err(
162                            sess,
163                            sym::$feature,
164                            span,
165                            format!("linking modifier `{modifier}` is unstable"),
166                        )
167                        .emit();
168                    }
169                }
170                let assign_modifier = |dst: &mut Option<bool>| {
171                    if dst.is_some() {
172                        cx.emit_err(MultipleModifiers { span, modifier });
173                    } else {
174                        *dst = Some(value);
175                    }
176                };
177                match (modifier, &mut kind) {
178                    (sym::bundle, Some(NativeLibKind::Static { bundle, .. })) => {
179                        assign_modifier(bundle)
180                    }
181                    (sym::bundle, _) => {
182                        cx.emit_err(BundleNeedsStatic { span });
183                    }
184
185                    (sym::export_symbols, Some(NativeLibKind::Static { export_symbols, .. })) => {
186                        assign_modifier(export_symbols)
187                    }
188
189                    (sym::export_symbols, _) => {
190                        cx.emit_err(ExportSymbolsNeedsStatic { span });
191                    }
192
193                    (sym::verbatim, _) => assign_modifier(&mut verbatim),
194
195                    (
196                        sym::whole_dash_archive,
197                        Some(NativeLibKind::Static { whole_archive, .. }),
198                    ) => assign_modifier(whole_archive),
199                    (sym::whole_dash_archive, _) => {
200                        cx.emit_err(WholeArchiveNeedsStatic { span });
201                    }
202
203                    (
204                        sym::as_dash_needed,
205                        Some(
206                            NativeLibKind::Dylib { as_needed }
207                            | NativeLibKind::Framework { as_needed }
208                            | NativeLibKind::RawDylib { as_needed },
209                        ),
210                    ) => {
211                        if !features.native_link_modifiers_as_needed() {
    feature_err(sess, sym::native_link_modifiers_as_needed, span,
            ::alloc::__export::must_use({
                    ::alloc::fmt::format(format_args!("linking modifier `{0}` is unstable",
                            modifier))
                })).emit();
};report_unstable_modifier!(native_link_modifiers_as_needed);
212                        assign_modifier(as_needed)
213                    }
214                    (sym::as_dash_needed, _) => {
215                        cx.emit_err(AsNeededCompatibility { span });
216                    }
217
218                    _ => {
219                        cx.adcx().expected_specific_argument_strings(
220                            span,
221                            &[
222                                sym::bundle,
223                                sym::export_symbols,
224                                sym::verbatim,
225                                sym::whole_dash_archive,
226                                sym::as_dash_needed,
227                            ],
228                        );
229                    }
230                }
231            }
232        }
233
234        if let Some((_, span)) = wasm_import_module {
235            if name.is_some() || kind.is_some() || modifiers.is_some() || cfg.is_some() {
236                cx.emit_err(IncompatibleWasmLink { span });
237            }
238        }
239
240        if wasm_import_module.is_some() {
241            (name, kind) = (wasm_import_module, Some(NativeLibKind::WasmImportModule));
242        }
243        let Some((name, _name_span)) = name else {
244            cx.emit_err(LinkRequiresName { span: cx.attr_span });
245            return None;
246        };
247
248        // Do this outside of the loop so that `import_name_type` can be specified before `kind`.
249        if let Some((_, span)) = import_name_type {
250            if !#[allow(non_exhaustive_omitted_patterns)] match kind {
    Some(NativeLibKind::RawDylib { .. }) => true,
    _ => false,
}matches!(kind, Some(NativeLibKind::RawDylib { .. })) {
251                cx.emit_err(ImportNameTypeRaw { span });
252            }
253        }
254
255        Some(LinkEntry {
256            span: cx.attr_span,
257            kind: kind.unwrap_or(NativeLibKind::Unspecified),
258            name,
259            cfg,
260            verbatim,
261            import_name_type,
262        })
263    }
264
265    fn finalize_check(cx: &mut FinalizeCheckContext<'_, '_>, attr_span: Span) {
266        let AstTarget::ForeignMod(fm) = cx.ast_target else {
267            return;
268        };
269
270        let abi = fm.abi.map_or(ExternAbi::FALLBACK, |abi| {
271            abi.symbol_unescaped.as_str().parse().unwrap_or_else(|_| {
272                cx.dcx().span_delayed_bug(
273                    abi.span,
274                    "LinkParser::finalize_check was unable to pre-detect the ABI, so it continues to use the recovery value solely to check for unused_attributes in the lint; a user error E0703 will be reported later in lower_abi",
275                );
276                ExternAbi::Rust
277            })
278        });
279        if #[allow(non_exhaustive_omitted_patterns)] match abi {
    ExternAbi::Rust => true,
    _ => false,
}matches!(abi, ExternAbi::Rust) {
280            cx.emit_lint(UNUSED_ATTRIBUTES, Link, attr_span);
281        }
282    }
283}
284
285impl LinkParser {
286    fn parse_link_name(
287        item: &MetaItemParser,
288        name: &mut Option<(Symbol, Span)>,
289        cx: &mut AcceptContext<'_, '_>,
290    ) -> bool {
291        if name.is_some() {
292            cx.adcx().duplicate_key(item.span(), sym::name);
293            return true;
294        }
295        let Some(nv) = cx.expect_name_value(item.args(), item.span(), Some(sym::name)) else {
296            return false;
297        };
298        let Some(link_name) = cx.expect_string_literal(nv) else {
299            return false;
300        };
301
302        if link_name.as_str().contains('\0') {
303            cx.emit_err(NullOnLinkName { span: nv.value_span });
304        }
305        if link_name.is_empty() {
306            cx.emit_err(EmptyLinkName { span: nv.value_span });
307        }
308
309        *name = Some((link_name, nv.value_span));
310        true
311    }
312
313    fn parse_link_kind(
314        item: &MetaItemParser,
315        kind: &mut Option<NativeLibKind>,
316        cx: &mut AcceptContext<'_, '_>,
317        sess: &Session,
318        features: &Features,
319    ) -> bool {
320        if kind.is_some() {
321            cx.adcx().duplicate_key(item.span(), sym::kind);
322            return true;
323        }
324        let Some(nv) = cx.expect_name_value(item.args(), item.span(), Some(sym::kind)) else {
325            return true;
326        };
327        let Some(link_kind) = cx.expect_string_literal(nv) else {
328            return true;
329        };
330
331        let link_kind = match link_kind {
332            kw::Static => {
333                NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None }
334            }
335            sym::dylib => NativeLibKind::Dylib { as_needed: None },
336            sym::framework => {
337                if !sess.target.is_like_darwin {
338                    cx.emit_err(LinkFrameworkApple { span: nv.value_span });
339                }
340                NativeLibKind::Framework { as_needed: None }
341            }
342            sym::raw_dash_dylib => {
343                if sess.target.is_like_windows {
344                    // raw-dylib is stable and working on Windows
345                } else if sess.target.binary_format == BinaryFormat::Elf && features.raw_dylib_elf()
346                {
347                    // raw-dylib is unstable on ELF, but the user opted in
348                } else if sess.target.binary_format == BinaryFormat::Elf && sess.is_nightly_build()
349                {
350                    feature_err(
351                        sess,
352                        sym::raw_dylib_elf,
353                        nv.value_span,
354                        rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("link kind `raw-dylib` is unstable on ELF platforms"))msg!("link kind `raw-dylib` is unstable on ELF platforms"),
355                    )
356                    .emit();
357                } else {
358                    cx.emit_err(RawDylibOnlyWindows { span: nv.value_span });
359                }
360
361                NativeLibKind::RawDylib { as_needed: None }
362            }
363            sym::link_dash_arg => {
364                if !features.link_arg_attribute() {
365                    feature_err(
366                        sess,
367                        sym::link_arg_attribute,
368                        nv.value_span,
369                        rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("link kind `link-arg` is unstable"))msg!("link kind `link-arg` is unstable"),
370                    )
371                    .emit();
372                }
373                NativeLibKind::LinkArg
374            }
375            _kind => {
376                cx.adcx().expected_specific_argument_strings(
377                    nv.value_span,
378                    &[
379                        kw::Static,
380                        sym::dylib,
381                        sym::framework,
382                        sym::raw_dash_dylib,
383                        sym::link_dash_arg,
384                    ],
385                );
386                return true;
387            }
388        };
389        *kind = Some(link_kind);
390        true
391    }
392
393    fn parse_link_modifiers(
394        item: &MetaItemParser,
395        modifiers: &mut Option<(Symbol, Span)>,
396        cx: &mut AcceptContext<'_, '_>,
397    ) -> bool {
398        if modifiers.is_some() {
399            cx.adcx().duplicate_key(item.span(), sym::modifiers);
400            return true;
401        }
402        let Some(nv) = cx.expect_name_value(item.args(), item.span(), Some(sym::modifiers)) else {
403            return true;
404        };
405        let Some(link_modifiers) = cx.expect_string_literal(nv) else {
406            return true;
407        };
408        *modifiers = Some((link_modifiers, nv.value_span));
409        true
410    }
411
412    fn parse_link_cfg(
413        item: &MetaItemParser,
414        cfg: &mut Option<CfgEntry>,
415        cx: &mut AcceptContext<'_, '_>,
416        sess: &Session,
417        features: &Features,
418    ) -> bool {
419        if cfg.is_some() {
420            cx.adcx().duplicate_key(item.span(), sym::cfg);
421            return true;
422        }
423        let Some(link_cfg) = cx.expect_single_element_list(item.args(), item.span()) else {
424            return true;
425        };
426        if !features.link_cfg() {
427            feature_err(sess, sym::link_cfg, item.span(), rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("link cfg is unstable"))msg!("link cfg is unstable")).emit();
428        }
429        *cfg = parse_cfg_entry(cx, link_cfg).ok();
430        true
431    }
432
433    fn parse_link_wasm_import_module(
434        item: &MetaItemParser,
435        wasm_import_module: &mut Option<(Symbol, Span)>,
436        cx: &mut AcceptContext<'_, '_>,
437    ) -> bool {
438        if wasm_import_module.is_some() {
439            cx.adcx().duplicate_key(item.span(), sym::wasm_import_module);
440            return true;
441        }
442        let Some(nv) =
443            cx.expect_name_value(item.args(), item.span(), Some(sym::wasm_import_module))
444        else {
445            return true;
446        };
447        let Some(link_wasm_import_module) = cx.expect_string_literal(nv) else {
448            return true;
449        };
450        *wasm_import_module = Some((link_wasm_import_module, item.span()));
451        true
452    }
453
454    fn parse_link_import_name_type(
455        item: &MetaItemParser,
456        import_name_type: &mut Option<(PeImportNameType, Span)>,
457        cx: &mut AcceptContext<'_, '_>,
458    ) -> bool {
459        if import_name_type.is_some() {
460            cx.adcx().duplicate_key(item.span(), sym::import_name_type);
461            return true;
462        }
463        let Some(nv) = cx.expect_name_value(item.args(), item.span(), Some(sym::import_name_type))
464        else {
465            return true;
466        };
467        let Some(link_import_name_type) = cx.expect_string_literal(nv) else {
468            return true;
469        };
470        if cx.sess().target.arch != Arch::X86 {
471            cx.emit_err(ImportNameTypeX86 { span: item.span() });
472            return true;
473        }
474
475        let link_import_name_type = match link_import_name_type {
476            sym::decorated => PeImportNameType::Decorated,
477            sym::noprefix => PeImportNameType::NoPrefix,
478            sym::undecorated => PeImportNameType::Undecorated,
479            _ => {
480                cx.adcx().expected_specific_argument_strings(
481                    item.span(),
482                    &[sym::decorated, sym::noprefix, sym::undecorated],
483                );
484                return true;
485            }
486        };
487        *import_name_type = Some((link_import_name_type, item.span()));
488        true
489    }
490}
491
492pub(crate) struct LinkSectionParser;
493
494fn check_link_section_macho(name: Symbol) -> Result<(), InvalidMachoSectionReason> {
495    let mut parts = name.as_str().split(',').map(|s| s.trim());
496
497    // The segment can be empty.
498    let _segment = parts.next();
499
500    // But the section is required.
501    let section = match parts.next() {
502        None | Some("") => return Err(InvalidMachoSectionReason::MissingSection),
503        Some(section) => section,
504    };
505
506    if section.len() > 16 {
507        return Err(InvalidMachoSectionReason::SectionTooLong { section: section.to_string() });
508    }
509
510    // LLVM also checks the other components of the section specifier, but that logic is hard to
511    // keep in sync. We skip it here for now, assuming that if you got that far you'll be able
512    // to interpret the LLVM errors.
513
514    Ok(())
515}
516
517impl SingleAttributeParser for LinkSectionParser {
518    const PATH: &[Symbol] = &[sym::link_section];
519    const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError;
520    const SAFETY: AttributeSafety = AttributeSafety::Unsafe {
521        note: "the program's behavior with overridden link sections on items is unpredictable and Rust cannot provide guarantees when you manually override them",
522        unsafe_since: Some(Edition2024),
523    };
524    const STABILITY: AttributeStability = AttributeStability::Stable;
525    const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowListWarnRest(&[
526        Allow(Target::Static),
527        Allow(Target::Fn),
528        Allow(Target::Method(MethodKind::Inherent)),
529        Allow(Target::Method(MethodKind::Trait { body: true })),
530        Allow(Target::Method(MethodKind::TraitImpl)),
531        Error(Target::Param),
532    ]);
533    const TEMPLATE: AttributeTemplate = crate::AttributeTemplate {
    word: false,
    list: None,
    one_of: &[],
    name_value_str: Some(&["name"]),
    docs: Some("https://doc.rust-lang.org/reference/abi.html#the-link_section-attribute"),
}template!(
534        NameValueStr: "name",
535        "https://doc.rust-lang.org/reference/abi.html#the-link_section-attribute"
536    );
537
538    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
539        let nv = cx.expect_name_value(args, cx.attr_span, None)?;
540        let name = cx.expect_string_literal(nv)?;
541        if name.as_str().contains('\0') {
542            // `#[link_section = ...]` will be converted to a null-terminated string,
543            // so it may not contain any null characters.
544            cx.emit_err(NullOnLinkSection { span: cx.attr_span });
545            return None;
546        }
547
548        // We (currently) only validate macho section specifiers.
549        match cx.sess.target.binary_format {
550            BinaryFormat::MachO => match check_link_section_macho(name) {
551                Ok(()) => {}
552                Err(reason) => {
553                    cx.emit_err(InvalidMachoSection { name_span: nv.value_span, reason });
554                    return None;
555                }
556            },
557            BinaryFormat::Coff | BinaryFormat::Elf | BinaryFormat::Wasm | BinaryFormat::Xcoff => {}
558        }
559
560        Some(LinkSection { name })
561    }
562}
563
564pub(crate) struct ExportStableParser;
565impl NoArgsAttributeParser for ExportStableParser {
566    const PATH: &[Symbol] = &[sym::export_stable];
567    const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[
568        Allow(Target::Fn),
569        Allow(Target::Method(MethodKind::Inherent)),
570        Allow(Target::Struct),
571        Allow(Target::Enum),
572        Allow(Target::Union),
573        Allow(Target::TyAlias),
574        Allow(Target::AssocTy(AssocCtxt::Impl { of_trait: false })),
575        Allow(Target::AssocTy(AssocCtxt::Trait)),
576        Allow(Target::AssocTy(AssocCtxt::Impl { of_trait: true })),
577        Allow(Target::Use),
578        Allow(Target::Mod),
579        Allow(Target::Impl { of_trait: false }),
580    ]);
581    const STABILITY: AttributeStability = {
    _ = rustc_feature::Features::export_stable;
    AttributeStability::Unstable {
        gate_name: rustc_span::sym::export_stable,
        notes: &[],
    }
}unstable!(export_stable);
582    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::ExportStable;
583}
584
585pub(crate) struct FfiConstParser;
586impl NoArgsAttributeParser for FfiConstParser {
587    const PATH: &[Symbol] = &[sym::ffi_const];
588    const SAFETY: AttributeSafety = AttributeSafety::Unsafe {
589        note: "`#[ffi_const]` functions shall have no effects except for its return value, which can only depend on the values of the function parameters, and is not affected by changes to the observable state of the program.",
590        unsafe_since: None,
591    };
592    const ALLOWED_TARGETS: AllowedTargets<'_> =
593        AllowedTargets::AllowList(&[Allow(Target::ForeignFn)]);
594    const STABILITY: AttributeStability = {
    _ = rustc_feature::Features::ffi_const;
    AttributeStability::Unstable {
        gate_name: rustc_span::sym::ffi_const,
        notes: &[],
    }
}unstable!(ffi_const);
595    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::FfiConst;
596}
597
598pub(crate) struct FfiPureParser;
599impl NoArgsAttributeParser for FfiPureParser {
600    const PATH: &[Symbol] = &[sym::ffi_pure];
601    const SAFETY: AttributeSafety = AttributeSafety::Unsafe {
602        note: "`#[ffi_pure]` functions shall have no effects except for its return value, which shall not change across two consecutive function calls with the same parameters.",
603        unsafe_since: None,
604    };
605    const ALLOWED_TARGETS: AllowedTargets<'_> =
606        AllowedTargets::AllowList(&[Allow(Target::ForeignFn)]);
607    const STABILITY: AttributeStability = {
    _ = rustc_feature::Features::ffi_pure;
    AttributeStability::Unstable {
        gate_name: rustc_span::sym::ffi_pure,
        notes: &[],
    }
}unstable!(ffi_pure);
608    const CREATE: fn(Span) -> AttributeKind = AttributeKind::FfiPure;
609
610    fn finalize_check(cx: &mut FinalizeCheckContext<'_, '_>, attr_span: Span) {
611        // `#[ffi_const]` functions cannot be `#[ffi_pure]`.
612        if cx.all_attrs.iter().any(|a| a.word_is(sym::ffi_const)) {
613            cx.emit_err(BothFfiConstAndPure { attr_span });
614        }
615    }
616}
617
618pub(crate) struct RustcStdInternalSymbolParser;
619impl NoArgsAttributeParser for RustcStdInternalSymbolParser {
620    const PATH: &[Symbol] = &[sym::rustc_std_internal_symbol];
621    const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[
622        Allow(Target::Fn),
623        Allow(Target::ForeignFn),
624        Allow(Target::Static),
625        Allow(Target::ForeignStatic),
626    ]);
627    const STABILITY: AttributeStability = {
    _ = rustc_feature::Features::rustc_attrs;
    AttributeStability::Unstable {
        gate_name: rustc_span::sym::rustc_attrs,
        notes: &[],
    }
}unstable!(rustc_attrs);
628    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcStdInternalSymbol;
629}
630
631pub(crate) struct LinkOrdinalParser;
632
633impl SingleAttributeParser for LinkOrdinalParser {
634    const PATH: &[Symbol] = &[sym::link_ordinal];
635    const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[
636        Allow(Target::ForeignFn),
637        Allow(Target::ForeignStatic),
638        Warn(Target::MacroCall),
639    ]);
640    const TEMPLATE: AttributeTemplate = crate::AttributeTemplate {
    word: false,
    list: Some(&["ordinal"]),
    one_of: &[],
    name_value_str: None,
    docs: Some("https://doc.rust-lang.org/reference/items/external-blocks.html#the-link_ordinal-attribute"),
}template!(
641        List: &["ordinal"],
642        "https://doc.rust-lang.org/reference/items/external-blocks.html#the-link_ordinal-attribute"
643    );
644    const STABILITY: AttributeStability = AttributeStability::Stable;
645
646    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
647        let ordinal = parse_single_integer(cx, args)?;
648
649        // According to the table at
650        // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-header, the
651        // ordinal must fit into 16 bits. Similarly, the Ordinal field in COFFShortExport (defined
652        // in llvm/include/llvm/Object/COFFImportFile.h), which we use to communicate import
653        // information to LLVM for `#[link(kind = "raw-dylib"_])`, is also defined to be uint16_t.
654        //
655        // FIXME: should we allow an ordinal of 0?  The MSVC toolchain has inconsistent support for
656        // this: both LINK.EXE and LIB.EXE signal errors and abort when given a .DEF file that
657        // specifies a zero ordinal. However, llvm-dlltool is perfectly happy to generate an import
658        // library for such a .DEF file, and MSVC's LINK.EXE is also perfectly happy to consume an
659        // import library produced by LLVM with an ordinal of 0, and it generates an .EXE.  (I
660        // don't know yet if the resulting EXE runs, as I haven't yet built the necessary DLL --
661        // see earlier comment about LINK.EXE failing.)
662        let Ok(ordinal) = ordinal.try_into() else {
663            cx.emit_err(LinkOrdinalOutOfRange { span: cx.attr_span, ordinal });
664            return None;
665        };
666
667        Some(LinkOrdinal { ordinal, span: cx.attr_span })
668    }
669}
670
671pub(crate) struct LinkageParser;
672
673impl SingleAttributeParser for LinkageParser {
674    const PATH: &[Symbol] = &[sym::linkage];
675    const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[
676        Allow(Target::Fn), // const fn denied in check_attr
677        Allow(Target::Method(MethodKind::Inherent)),
678        Allow(Target::Method(MethodKind::Trait { body: true })),
679        Allow(Target::Method(MethodKind::TraitImpl)),
680        Allow(Target::Static),
681        Allow(Target::ForeignStatic), // extern static mut denied in check_attr
682        Allow(Target::ForeignFn),
683    ]);
684    const TEMPLATE: AttributeTemplate = crate::AttributeTemplate {
    word: false,
    list: None,
    one_of: &[],
    name_value_str: Some(&["available_externally", "extern_weak", "linkonce",
                    "linkonce_odr", "weak", "weak_odr"]),
    docs: None,
}template!(NameValueStr: [
685        "available_externally",
686        "extern_weak",
687        "linkonce",
688        "linkonce_odr",
689        "weak",
690        "weak_odr",
691    ]);
692    const STABILITY: AttributeStability = {
    _ = rustc_feature::Features::linkage;
    AttributeStability::Unstable {
        gate_name: rustc_span::sym::linkage,
        notes: &[],
    }
}unstable!(linkage);
693
694    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
695        let name_value = cx.expect_name_value(args, cx.attr_span, Some(sym::linkage))?;
696
697        let value = cx.expect_string_literal(name_value)?;
698
699        // Use the names from src/llvm/docs/LangRef.rst here. Most types are only
700        // applicable to variable declarations and may not really make sense for
701        // Rust code in the first place but allow them anyway and trust that the
702        // user knows what they're doing. Who knows, unanticipated use cases may pop
703        // up in the future.
704        //
705        // ghost, dllimport, dllexport and linkonce_odr_autohide are not supported
706        // and don't have to be, LLVM treats them as no-ops.
707        let linkage = match value {
708            sym::available_externally => Linkage::AvailableExternally,
709            sym::extern_weak => Linkage::ExternalWeak,
710            sym::linkonce => Linkage::LinkOnceAny,
711            sym::linkonce_odr => Linkage::LinkOnceODR,
712            sym::weak => Linkage::WeakAny,
713            sym::weak_odr => Linkage::WeakODR,
714
715            _ => {
716                cx.adcx().expected_specific_argument(
717                    name_value.value_span,
718                    &[
719                        sym::available_externally,
720                        sym::extern_weak,
721                        sym::linkonce,
722                        sym::linkonce_odr,
723                        sym::weak,
724                        sym::weak_odr,
725                    ],
726                );
727                return None;
728            }
729        };
730
731        Some(AttributeKind::Linkage(linkage, cx.attr_span))
732    }
733}
734
735pub(crate) struct NeedsAllocatorParser;
736
737impl NoArgsAttributeParser for NeedsAllocatorParser {
738    const PATH: &[Symbol] = &[sym::needs_allocator];
739    const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
740    const STABILITY: AttributeStability = {
    _ = rustc_feature::Features::allocator_internals;
    AttributeStability::Unstable {
        gate_name: rustc_span::sym::allocator_internals,
        notes: &[],
    }
}unstable!(allocator_internals);
741    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::NeedsAllocator;
742}
743
744pub(crate) struct CompilerBuiltinsParser;
745
746impl NoArgsAttributeParser for CompilerBuiltinsParser {
747    const PATH: &[Symbol] = &[sym::compiler_builtins];
748    const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
749    const STABILITY: AttributeStability = {
    _ = rustc_feature::Features::compiler_builtins;
    AttributeStability::Unstable {
        gate_name: rustc_span::sym::compiler_builtins,
        notes: &[],
    }
}unstable!(compiler_builtins);
750    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::CompilerBuiltins;
751}