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