Skip to main content

rustc_attr_parsing/attributes/
link_attrs.rs

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