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