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::diagnostics::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, BothFfiConstAndPure, BundleNeedsStatic, EmptyLinkName,
18    ExportSymbolsNeedsStatic, ImportNameTypeRaw, ImportNameTypeX86, IncompatibleWasmLink,
19    InvalidLinkModifier, InvalidMachoSection, InvalidMachoSectionReason, LinkFrameworkApple,
20    LinkOrdinalOutOfRange, LinkRequiresName, MultipleModifiers, NullOnLinkName, NullOnLinkSection,
21    RawDylibOnlyWindows, 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) =
148                    if let Some(m) = modifier.strip_prefix(['+', '-']) {
149                        (Symbol::intern(m), modifier.starts_with('+'))
150                    } else {
151                        cx.emit_err(InvalidLinkModifier { span });
152                        continue;
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                    (
200                        sym::as_dash_needed,
201                        Some(
202                            NativeLibKind::Dylib { as_needed }
203                            | NativeLibKind::Framework { as_needed }
204                            | NativeLibKind::RawDylib { as_needed },
205                        ),
206                    ) => {
207                        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);
208                        assign_modifier(as_needed)
209                    }
210                    (sym::as_dash_needed, _) => {
211                        cx.emit_err(AsNeededCompatibility { span });
212                    }
213
214                    _ => {
215                        cx.adcx().expected_specific_argument_strings(
216                            span,
217                            &[
218                                sym::bundle,
219                                sym::export_symbols,
220                                sym::verbatim,
221                                sym::whole_dash_archive,
222                                sym::as_dash_needed,
223                            ],
224                        );
225                    }
226                }
227            }
228        }
229
230        if let Some((_, span)) = wasm_import_module {
231            if name.is_some() || kind.is_some() || modifiers.is_some() || cfg.is_some() {
232                cx.emit_err(IncompatibleWasmLink { span });
233            }
234        }
235
236        if wasm_import_module.is_some() {
237            (name, kind) = (wasm_import_module, Some(NativeLibKind::WasmImportModule));
238        }
239        let Some((name, _name_span)) = name else {
240            cx.emit_err(LinkRequiresName { span: cx.attr_span });
241            return None;
242        };
243
244        // Do this outside of the loop so that `import_name_type` can be specified before `kind`.
245        if let Some((_, span)) = import_name_type {
246            if !#[allow(non_exhaustive_omitted_patterns)] match kind {
    Some(NativeLibKind::RawDylib { .. }) => true,
    _ => false,
}matches!(kind, Some(NativeLibKind::RawDylib { .. })) {
247                cx.emit_err(ImportNameTypeRaw { span });
248            }
249        }
250
251        Some(LinkEntry {
252            span: cx.attr_span,
253            kind: kind.unwrap_or(NativeLibKind::Unspecified),
254            name,
255            cfg,
256            verbatim,
257            import_name_type,
258        })
259    }
260}
261
262impl LinkParser {
263    fn parse_link_name(
264        item: &MetaItemParser,
265        name: &mut Option<(Symbol, Span)>,
266        cx: &mut AcceptContext<'_, '_>,
267    ) -> bool {
268        if name.is_some() {
269            cx.adcx().duplicate_key(item.span(), sym::name);
270            return true;
271        }
272        let Some(nv) = cx.expect_name_value(item.args(), item.span(), Some(sym::name)) else {
273            return false;
274        };
275        let Some(link_name) = cx.expect_string_literal(nv) else {
276            return false;
277        };
278
279        if link_name.as_str().contains('\0') {
280            cx.emit_err(NullOnLinkName { span: nv.value_span });
281        }
282        if link_name.is_empty() {
283            cx.emit_err(EmptyLinkName { span: nv.value_span });
284        }
285
286        *name = Some((link_name, nv.value_span));
287        true
288    }
289
290    fn parse_link_kind(
291        item: &MetaItemParser,
292        kind: &mut Option<NativeLibKind>,
293        cx: &mut AcceptContext<'_, '_>,
294        sess: &Session,
295        features: &Features,
296    ) -> bool {
297        if kind.is_some() {
298            cx.adcx().duplicate_key(item.span(), sym::kind);
299            return true;
300        }
301        let Some(nv) = cx.expect_name_value(item.args(), item.span(), Some(sym::kind)) else {
302            return true;
303        };
304        let Some(link_kind) = cx.expect_string_literal(nv) else {
305            return true;
306        };
307
308        let link_kind = match link_kind {
309            kw::Static => {
310                NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None }
311            }
312            sym::dylib => NativeLibKind::Dylib { as_needed: None },
313            sym::framework => {
314                if !sess.target.is_like_darwin {
315                    cx.emit_err(LinkFrameworkApple { span: nv.value_span });
316                }
317                NativeLibKind::Framework { as_needed: None }
318            }
319            sym::raw_dash_dylib => {
320                if sess.target.is_like_windows {
321                    // raw-dylib is stable and working on Windows
322                } else if sess.target.binary_format == BinaryFormat::Elf && features.raw_dylib_elf()
323                {
324                    // raw-dylib is unstable on ELF, but the user opted in
325                } else if sess.target.binary_format == BinaryFormat::Elf && sess.is_nightly_build()
326                {
327                    feature_err(
328                        sess,
329                        sym::raw_dylib_elf,
330                        nv.value_span,
331                        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"),
332                    )
333                    .emit();
334                } else {
335                    cx.emit_err(RawDylibOnlyWindows { span: nv.value_span });
336                }
337
338                NativeLibKind::RawDylib { as_needed: None }
339            }
340            sym::link_dash_arg => {
341                if !features.link_arg_attribute() {
342                    feature_err(
343                        sess,
344                        sym::link_arg_attribute,
345                        nv.value_span,
346                        rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("link kind `link-arg` is unstable"))msg!("link kind `link-arg` is unstable"),
347                    )
348                    .emit();
349                }
350                NativeLibKind::LinkArg
351            }
352            _kind => {
353                cx.adcx().expected_specific_argument_strings(
354                    nv.value_span,
355                    &[
356                        kw::Static,
357                        sym::dylib,
358                        sym::framework,
359                        sym::raw_dash_dylib,
360                        sym::link_dash_arg,
361                    ],
362                );
363                return true;
364            }
365        };
366        *kind = Some(link_kind);
367        true
368    }
369
370    fn parse_link_modifiers(
371        item: &MetaItemParser,
372        modifiers: &mut Option<(Symbol, Span)>,
373        cx: &mut AcceptContext<'_, '_>,
374    ) -> bool {
375        if modifiers.is_some() {
376            cx.adcx().duplicate_key(item.span(), sym::modifiers);
377            return true;
378        }
379        let Some(nv) = cx.expect_name_value(item.args(), item.span(), Some(sym::modifiers)) else {
380            return true;
381        };
382        let Some(link_modifiers) = cx.expect_string_literal(nv) else {
383            return true;
384        };
385        *modifiers = Some((link_modifiers, nv.value_span));
386        true
387    }
388
389    fn parse_link_cfg(
390        item: &MetaItemParser,
391        cfg: &mut Option<CfgEntry>,
392        cx: &mut AcceptContext<'_, '_>,
393        sess: &Session,
394        features: &Features,
395    ) -> bool {
396        if cfg.is_some() {
397            cx.adcx().duplicate_key(item.span(), sym::cfg);
398            return true;
399        }
400        let Some(link_cfg) = cx.expect_single_element_list(item.args(), item.span()) else {
401            return true;
402        };
403        if !features.link_cfg() {
404            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();
405        }
406        *cfg = parse_cfg_entry(cx, link_cfg).ok();
407        true
408    }
409
410    fn parse_link_wasm_import_module(
411        item: &MetaItemParser,
412        wasm_import_module: &mut Option<(Symbol, Span)>,
413        cx: &mut AcceptContext<'_, '_>,
414    ) -> bool {
415        if wasm_import_module.is_some() {
416            cx.adcx().duplicate_key(item.span(), sym::wasm_import_module);
417            return true;
418        }
419        let Some(nv) =
420            cx.expect_name_value(item.args(), item.span(), Some(sym::wasm_import_module))
421        else {
422            return true;
423        };
424        let Some(link_wasm_import_module) = cx.expect_string_literal(nv) else {
425            return true;
426        };
427        *wasm_import_module = Some((link_wasm_import_module, item.span()));
428        true
429    }
430
431    fn parse_link_import_name_type(
432        item: &MetaItemParser,
433        import_name_type: &mut Option<(PeImportNameType, Span)>,
434        cx: &mut AcceptContext<'_, '_>,
435    ) -> bool {
436        if import_name_type.is_some() {
437            cx.adcx().duplicate_key(item.span(), sym::import_name_type);
438            return true;
439        }
440        let Some(nv) = cx.expect_name_value(item.args(), item.span(), Some(sym::import_name_type))
441        else {
442            return true;
443        };
444        let Some(link_import_name_type) = cx.expect_string_literal(nv) else {
445            return true;
446        };
447        if cx.sess().target.arch != Arch::X86 {
448            cx.emit_err(ImportNameTypeX86 { span: item.span() });
449            return true;
450        }
451
452        let link_import_name_type = match link_import_name_type {
453            sym::decorated => PeImportNameType::Decorated,
454            sym::noprefix => PeImportNameType::NoPrefix,
455            sym::undecorated => PeImportNameType::Undecorated,
456            _ => {
457                cx.adcx().expected_specific_argument_strings(
458                    item.span(),
459                    &[sym::decorated, sym::noprefix, sym::undecorated],
460                );
461                return true;
462            }
463        };
464        *import_name_type = Some((link_import_name_type, item.span()));
465        true
466    }
467}
468
469pub(crate) struct LinkSectionParser;
470
471fn check_link_section_macho(name: Symbol) -> Result<(), InvalidMachoSectionReason> {
472    let mut parts = name.as_str().split(',').map(|s| s.trim());
473
474    // The segment can be empty.
475    let _segment = parts.next();
476
477    // But the section is required.
478    let section = match parts.next() {
479        None | Some("") => return Err(InvalidMachoSectionReason::MissingSection),
480        Some(section) => section,
481    };
482
483    if section.len() > 16 {
484        return Err(InvalidMachoSectionReason::SectionTooLong { section: section.to_string() });
485    }
486
487    // LLVM also checks the other components of the section specifier, but that logic is hard to
488    // keep in sync. We skip it here for now, assuming that if you got that far you'll be able
489    // to interpret the LLVM errors.
490
491    Ok(())
492}
493
494impl SingleAttributeParser for LinkSectionParser {
495    const PATH: &[Symbol] = &[sym::link_section];
496    const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError;
497    const SAFETY: AttributeSafety = AttributeSafety::Unsafe {
498        note: "the program's behavior with overridden link sections on items is unpredictable and Rust cannot provide guarantees when you manually override them",
499        unsafe_since: Some(Edition2024),
500    };
501    const STABILITY: AttributeStability = AttributeStability::Stable;
502    const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowListWarnRest(&[
503        Allow(Target::Static),
504        Allow(Target::Fn),
505        Allow(Target::Method(MethodKind::Inherent)),
506        Allow(Target::Method(MethodKind::Trait { body: true })),
507        Allow(Target::Method(MethodKind::TraitImpl)),
508    ]);
509    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!(
510        NameValueStr: "name",
511        "https://doc.rust-lang.org/reference/abi.html#the-link_section-attribute"
512    );
513
514    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
515        let nv = cx.expect_name_value(args, cx.attr_span, None)?;
516        let name = cx.expect_string_literal(nv)?;
517        if name.as_str().contains('\0') {
518            // `#[link_section = ...]` will be converted to a null-terminated string,
519            // so it may not contain any null characters.
520            cx.emit_err(NullOnLinkSection { span: cx.attr_span });
521            return None;
522        }
523
524        // We (currently) only validate macho section specifiers.
525        match cx.sess.target.binary_format {
526            BinaryFormat::MachO => match check_link_section_macho(name) {
527                Ok(()) => {}
528                Err(reason) => {
529                    cx.emit_err(InvalidMachoSection { name_span: nv.value_span, reason });
530                    return None;
531                }
532            },
533            BinaryFormat::Coff | BinaryFormat::Elf | BinaryFormat::Wasm | BinaryFormat::Xcoff => {}
534        }
535
536        Some(LinkSection { name })
537    }
538}
539
540pub(crate) struct ExportStableParser;
541impl NoArgsAttributeParser for ExportStableParser {
542    const PATH: &[Symbol] = &[sym::export_stable];
543    const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[
544        Allow(Target::Fn),
545        Allow(Target::Method(MethodKind::Inherent)),
546        Allow(Target::Struct),
547        Allow(Target::Enum),
548        Allow(Target::Union),
549        Allow(Target::TyAlias),
550        Allow(Target::AssocTy),
551        Allow(Target::Use),
552        Allow(Target::Mod),
553        Allow(Target::Impl { of_trait: false }),
554    ]);
555    const STABILITY: AttributeStability = AttributeStability::Unstable {
    gate_name: rustc_span::sym::export_stable,
    gate_check: rustc_feature::Features::export_stable,
    notes: &[],
}unstable!(export_stable);
556    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::ExportStable;
557}
558
559pub(crate) struct FfiConstParser;
560impl NoArgsAttributeParser for FfiConstParser {
561    const PATH: &[Symbol] = &[sym::ffi_const];
562    const SAFETY: AttributeSafety = AttributeSafety::Unsafe {
563        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.",
564        unsafe_since: None,
565    };
566    const ALLOWED_TARGETS: AllowedTargets<'_> =
567        AllowedTargets::AllowList(&[Allow(Target::ForeignFn)]);
568    const STABILITY: AttributeStability = AttributeStability::Unstable {
    gate_name: rustc_span::sym::ffi_const,
    gate_check: rustc_feature::Features::ffi_const,
    notes: &[],
}unstable!(ffi_const);
569    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::FfiConst;
570}
571
572pub(crate) struct FfiPureParser;
573impl NoArgsAttributeParser for FfiPureParser {
574    const PATH: &[Symbol] = &[sym::ffi_pure];
575    const SAFETY: AttributeSafety = AttributeSafety::Unsafe {
576        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.",
577        unsafe_since: None,
578    };
579    const ALLOWED_TARGETS: AllowedTargets<'_> =
580        AllowedTargets::AllowList(&[Allow(Target::ForeignFn)]);
581    const STABILITY: AttributeStability = AttributeStability::Unstable {
    gate_name: rustc_span::sym::ffi_pure,
    gate_check: rustc_feature::Features::ffi_pure,
    notes: &[],
}unstable!(ffi_pure);
582    const CREATE: fn(Span) -> AttributeKind = AttributeKind::FfiPure;
583
584    fn finalize_check(cx: &FinalizeCheckContext<'_, '_>, attr_span: Span) {
585        // `#[ffi_const]` functions cannot be `#[ffi_pure]`.
586        if cx.all_attrs.iter().any(|a| a.word_is(sym::ffi_const)) {
587            cx.emit_err(BothFfiConstAndPure { attr_span });
588        }
589    }
590}
591
592pub(crate) struct RustcStdInternalSymbolParser;
593impl NoArgsAttributeParser for RustcStdInternalSymbolParser {
594    const PATH: &[Symbol] = &[sym::rustc_std_internal_symbol];
595    const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[
596        Allow(Target::Fn),
597        Allow(Target::ForeignFn),
598        Allow(Target::Static),
599        Allow(Target::ForeignStatic),
600    ]);
601    const STABILITY: AttributeStability = AttributeStability::Unstable {
    gate_name: rustc_span::sym::rustc_attrs,
    gate_check: rustc_feature::Features::rustc_attrs,
    notes: &[],
}unstable!(rustc_attrs);
602    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcStdInternalSymbol;
603}
604
605pub(crate) struct LinkOrdinalParser;
606
607impl SingleAttributeParser for LinkOrdinalParser {
608    const PATH: &[Symbol] = &[sym::link_ordinal];
609    const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[
610        Allow(Target::ForeignFn),
611        Allow(Target::ForeignStatic),
612        Warn(Target::MacroCall),
613    ]);
614    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!(
615        List: &["ordinal"],
616        "https://doc.rust-lang.org/reference/items/external-blocks.html#the-link_ordinal-attribute"
617    );
618    const STABILITY: AttributeStability = AttributeStability::Stable;
619
620    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
621        let ordinal = parse_single_integer(cx, args)?;
622
623        // According to the table at
624        // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-header, the
625        // ordinal must fit into 16 bits. Similarly, the Ordinal field in COFFShortExport (defined
626        // in llvm/include/llvm/Object/COFFImportFile.h), which we use to communicate import
627        // information to LLVM for `#[link(kind = "raw-dylib"_])`, is also defined to be uint16_t.
628        //
629        // FIXME: should we allow an ordinal of 0?  The MSVC toolchain has inconsistent support for
630        // this: both LINK.EXE and LIB.EXE signal errors and abort when given a .DEF file that
631        // specifies a zero ordinal. However, llvm-dlltool is perfectly happy to generate an import
632        // library for such a .DEF file, and MSVC's LINK.EXE is also perfectly happy to consume an
633        // import library produced by LLVM with an ordinal of 0, and it generates an .EXE.  (I
634        // don't know yet if the resulting EXE runs, as I haven't yet built the necessary DLL --
635        // see earlier comment about LINK.EXE failing.)
636        let Ok(ordinal) = ordinal.try_into() else {
637            cx.emit_err(LinkOrdinalOutOfRange { span: cx.attr_span, ordinal });
638            return None;
639        };
640
641        Some(LinkOrdinal { ordinal, span: cx.attr_span })
642    }
643}
644
645pub(crate) struct LinkageParser;
646
647impl SingleAttributeParser for LinkageParser {
648    const PATH: &[Symbol] = &[sym::linkage];
649    const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[
650        Allow(Target::Fn), // const fn denied in check_attr
651        Allow(Target::Method(MethodKind::Inherent)),
652        Allow(Target::Method(MethodKind::Trait { body: true })),
653        Allow(Target::Method(MethodKind::TraitImpl)),
654        Allow(Target::Static),
655        Allow(Target::ForeignStatic), // extern static mut denied in check_attr
656        Allow(Target::ForeignFn),
657    ]);
658    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: [
659        "available_externally",
660        "common",
661        "extern_weak",
662        "external",
663        "internal",
664        "linkonce",
665        "linkonce_odr",
666        "weak",
667        "weak_odr",
668    ]);
669    const STABILITY: AttributeStability = AttributeStability::Unstable {
    gate_name: rustc_span::sym::linkage,
    gate_check: rustc_feature::Features::linkage,
    notes: &[],
}unstable!(linkage);
670
671    fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
672        let name_value = cx.expect_name_value(args, cx.attr_span, Some(sym::linkage))?;
673
674        let value = cx.expect_string_literal(name_value)?;
675
676        // Use the names from src/llvm/docs/LangRef.rst here. Most types are only
677        // applicable to variable declarations and may not really make sense for
678        // Rust code in the first place but allow them anyway and trust that the
679        // user knows what they're doing. Who knows, unanticipated use cases may pop
680        // up in the future.
681        //
682        // ghost, dllimport, dllexport and linkonce_odr_autohide are not supported
683        // and don't have to be, LLVM treats them as no-ops.
684        let linkage = match value {
685            sym::available_externally => Linkage::AvailableExternally,
686            sym::common => Linkage::Common,
687            sym::extern_weak => Linkage::ExternalWeak,
688            sym::external => Linkage::External,
689            sym::internal => Linkage::Internal,
690            sym::linkonce => Linkage::LinkOnceAny,
691            sym::linkonce_odr => Linkage::LinkOnceODR,
692            sym::weak => Linkage::WeakAny,
693            sym::weak_odr => Linkage::WeakODR,
694
695            _ => {
696                cx.adcx().expected_specific_argument(
697                    name_value.value_span,
698                    &[
699                        sym::available_externally,
700                        sym::common,
701                        sym::extern_weak,
702                        sym::external,
703                        sym::internal,
704                        sym::linkonce,
705                        sym::linkonce_odr,
706                        sym::weak,
707                        sym::weak_odr,
708                    ],
709                );
710                return None;
711            }
712        };
713
714        Some(AttributeKind::Linkage(linkage, cx.attr_span))
715    }
716}
717
718pub(crate) struct NeedsAllocatorParser;
719
720impl NoArgsAttributeParser for NeedsAllocatorParser {
721    const PATH: &[Symbol] = &[sym::needs_allocator];
722    const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
723    const STABILITY: AttributeStability = AttributeStability::Unstable {
    gate_name: rustc_span::sym::allocator_internals,
    gate_check: rustc_feature::Features::allocator_internals,
    notes: &[],
}unstable!(allocator_internals);
724    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::NeedsAllocator;
725}
726
727pub(crate) struct CompilerBuiltinsParser;
728
729impl NoArgsAttributeParser for CompilerBuiltinsParser {
730    const PATH: &[Symbol] = &[sym::compiler_builtins];
731    const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
732    const STABILITY: AttributeStability = AttributeStability::Unstable {
    gate_name: rustc_span::sym::compiler_builtins,
    gate_check: rustc_feature::Features::compiler_builtins,
    notes: &[],
}unstable!(compiler_builtins);
733    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::CompilerBuiltins;
734}