Skip to main content

rustc_attr_parsing/attributes/
link_attrs.rs

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