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<'_> =
562        AllowedTargets::AllowList(&[Allow(Target::ForeignFn)]);
563    const STABILITY: AttributeStability = AttributeStability::Unstable {
    gate_name: rustc_span::sym::ffi_const,
    gate_check: rustc_feature::Features::ffi_const,
    notes: &[],
}unstable!(ffi_const);
564    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::FfiConst;
565}
566
567pub(crate) struct FfiPureParser;
568impl NoArgsAttributeParser for FfiPureParser {
569    const PATH: &[Symbol] = &[sym::ffi_pure];
570    const SAFETY: AttributeSafety = AttributeSafety::Unsafe {
571        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.",
572        unsafe_since: None,
573    };
574    const ALLOWED_TARGETS: AllowedTargets<'_> =
575        AllowedTargets::AllowList(&[Allow(Target::ForeignFn)]);
576    const STABILITY: AttributeStability = AttributeStability::Unstable {
    gate_name: rustc_span::sym::ffi_pure,
    gate_check: rustc_feature::Features::ffi_pure,
    notes: &[],
}unstable!(ffi_pure);
577    const CREATE: fn(Span) -> AttributeKind = AttributeKind::FfiPure;
578}
579
580pub(crate) struct RustcStdInternalSymbolParser;
581impl NoArgsAttributeParser for RustcStdInternalSymbolParser {
582    const PATH: &[Symbol] = &[sym::rustc_std_internal_symbol];
583    const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[
584        Allow(Target::Fn),
585        Allow(Target::ForeignFn),
586        Allow(Target::Static),
587        Allow(Target::ForeignStatic),
588    ]);
589    const STABILITY: AttributeStability = AttributeStability::Unstable {
    gate_name: rustc_span::sym::rustc_attrs,
    gate_check: rustc_feature::Features::rustc_attrs,
    notes: &[],
}unstable!(rustc_attrs);
590    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcStdInternalSymbol;
591}
592
593pub(crate) struct LinkOrdinalParser;
594
595impl SingleAttributeParser for LinkOrdinalParser {
596    const PATH: &[Symbol] = &[sym::link_ordinal];
597    const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[
598        Allow(Target::ForeignFn),
599        Allow(Target::ForeignStatic),
600        Warn(Target::MacroCall),
601    ]);
602    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!(
603        List: &["ordinal"],
604        "https://doc.rust-lang.org/reference/items/external-blocks.html#the-link_ordinal-attribute"
605    );
606    const STABILITY: AttributeStability = AttributeStability::Stable;
607
608    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
609        let ordinal = parse_single_integer(cx, args)?;
610
611        // According to the table at
612        // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-header, the
613        // ordinal must fit into 16 bits. Similarly, the Ordinal field in COFFShortExport (defined
614        // in llvm/include/llvm/Object/COFFImportFile.h), which we use to communicate import
615        // information to LLVM for `#[link(kind = "raw-dylib"_])`, is also defined to be uint16_t.
616        //
617        // FIXME: should we allow an ordinal of 0?  The MSVC toolchain has inconsistent support for
618        // this: both LINK.EXE and LIB.EXE signal errors and abort when given a .DEF file that
619        // specifies a zero ordinal. However, llvm-dlltool is perfectly happy to generate an import
620        // library for such a .DEF file, and MSVC's LINK.EXE is also perfectly happy to consume an
621        // import library produced by LLVM with an ordinal of 0, and it generates an .EXE.  (I
622        // don't know yet if the resulting EXE runs, as I haven't yet built the necessary DLL --
623        // see earlier comment about LINK.EXE failing.)
624        let Ok(ordinal) = ordinal.try_into() else {
625            cx.emit_err(LinkOrdinalOutOfRange { span: cx.attr_span, ordinal });
626            return None;
627        };
628
629        Some(LinkOrdinal { ordinal, span: cx.attr_span })
630    }
631}
632
633pub(crate) struct LinkageParser;
634
635impl SingleAttributeParser for LinkageParser {
636    const PATH: &[Symbol] = &[sym::linkage];
637    const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[
638        Allow(Target::Fn),
639        Allow(Target::Method(MethodKind::Inherent)),
640        Allow(Target::Method(MethodKind::Trait { body: true })),
641        Allow(Target::Method(MethodKind::TraitImpl)),
642        Allow(Target::Static),
643        Allow(Target::ForeignStatic),
644        Allow(Target::ForeignFn),
645        Warn(Target::Method(MethodKind::Trait { body: false })), // Not inherited
646    ]);
647    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: [
648        "available_externally",
649        "common",
650        "extern_weak",
651        "external",
652        "internal",
653        "linkonce",
654        "linkonce_odr",
655        "weak",
656        "weak_odr",
657    ]);
658    const STABILITY: AttributeStability = AttributeStability::Unstable {
    gate_name: rustc_span::sym::linkage,
    gate_check: rustc_feature::Features::linkage,
    notes: &[],
}unstable!(linkage);
659
660    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
661        let name_value = cx.expect_name_value(args, cx.attr_span, Some(sym::linkage))?;
662
663        let Some(value) = cx.expect_string_literal(name_value) else {
664            return None;
665        };
666
667        // Use the names from src/llvm/docs/LangRef.rst here. Most types are only
668        // applicable to variable declarations and may not really make sense for
669        // Rust code in the first place but allow them anyway and trust that the
670        // user knows what they're doing. Who knows, unanticipated use cases may pop
671        // up in the future.
672        //
673        // ghost, dllimport, dllexport and linkonce_odr_autohide are not supported
674        // and don't have to be, LLVM treats them as no-ops.
675        let linkage = match value {
676            sym::available_externally => Linkage::AvailableExternally,
677            sym::common => Linkage::Common,
678            sym::extern_weak => Linkage::ExternalWeak,
679            sym::external => Linkage::External,
680            sym::internal => Linkage::Internal,
681            sym::linkonce => Linkage::LinkOnceAny,
682            sym::linkonce_odr => Linkage::LinkOnceODR,
683            sym::weak => Linkage::WeakAny,
684            sym::weak_odr => Linkage::WeakODR,
685
686            _ => {
687                cx.adcx().expected_specific_argument(
688                    name_value.value_span,
689                    &[
690                        sym::available_externally,
691                        sym::common,
692                        sym::extern_weak,
693                        sym::external,
694                        sym::internal,
695                        sym::linkonce,
696                        sym::linkonce_odr,
697                        sym::weak,
698                        sym::weak_odr,
699                    ],
700                );
701                return None;
702            }
703        };
704
705        Some(AttributeKind::Linkage(linkage, cx.attr_span))
706    }
707}
708
709pub(crate) struct NeedsAllocatorParser;
710
711impl NoArgsAttributeParser for NeedsAllocatorParser {
712    const PATH: &[Symbol] = &[sym::needs_allocator];
713    const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
714    const STABILITY: AttributeStability = AttributeStability::Unstable {
    gate_name: rustc_span::sym::allocator_internals,
    gate_check: rustc_feature::Features::allocator_internals,
    notes: &[],
}unstable!(allocator_internals);
715    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::NeedsAllocator;
716}
717
718pub(crate) struct CompilerBuiltinsParser;
719
720impl NoArgsAttributeParser for CompilerBuiltinsParser {
721    const PATH: &[Symbol] = &[sym::compiler_builtins];
722    const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
723    const STABILITY: AttributeStability = AttributeStability::Unstable {
    gate_name: rustc_span::sym::compiler_builtins,
    gate_check: rustc_feature::Features::compiler_builtins,
    notes: &[],
}unstable!(compiler_builtins);
724    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::CompilerBuiltins;
725}