Skip to main content

rustc_attr_parsing/attributes/
link_attrs.rs

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