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