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