Skip to main content

rustc_attr_parsing/attributes/
link_attrs.rs

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