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