Skip to main content

rustc_attr_parsing/attributes/
link_attrs.rs

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