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