rustc_attr_parsing/attributes/
link_attrs.rs

1use rustc_feature::Features;
2use rustc_hir::attrs::AttributeKind::{LinkName, LinkOrdinal, LinkSection};
3use rustc_hir::attrs::*;
4use rustc_session::Session;
5use rustc_session::parse::feature_err;
6use rustc_span::kw;
7use rustc_target::spec::BinaryFormat;
8
9use super::prelude::*;
10use super::util::parse_single_integer;
11use crate::attributes::cfg::parse_cfg_entry;
12use crate::fluent_generated;
13use crate::session_diagnostics::{
14    AsNeededCompatibility, BundleNeedsStatic, EmptyLinkName, ImportNameTypeRaw, ImportNameTypeX86,
15    IncompatibleWasmLink, InvalidLinkModifier, LinkFrameworkApple, LinkOrdinalOutOfRange,
16    LinkRequiresName, MultipleModifiers, NullOnLinkSection, RawDylibNoNul, RawDylibOnlyWindows,
17    WholeArchiveNeedsStatic,
18};
19
20pub(crate) struct LinkNameParser;
21
22impl<S: Stage> SingleAttributeParser<S> for LinkNameParser {
23    const PATH: &[Symbol] = &[sym::link_name];
24    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
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 = 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 = 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<'c>(
65        cx: &'c mut AcceptContext<'_, '_, S>,
66        args: &'c ArgParser<'_>,
67    ) -> impl IntoIterator<Item = Self::Item> + 'c {
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                let suggestions = <Self as CombineAttributeParser<S>>::TEMPLATE
75                    .suggestions(cx.attr_style, "link");
76                let span = cx.attr_span;
77                cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span);
78                return None;
79            }
80            _ => {
81                cx.expected_list(cx.attr_span);
82                return None;
83            }
84        };
85
86        let sess = cx.sess();
87        let features = cx.features();
88
89        let mut name = None;
90        let mut kind = None;
91        let mut modifiers = None;
92        let mut cfg = None;
93        let mut wasm_import_module = None;
94        let mut import_name_type = None;
95        for item in items.mixed() {
96            let Some(item) = item.meta_item() else {
97                cx.unexpected_literal(item.span());
98                continue;
99            };
100
101            let cont = match item.path().word().map(|ident| ident.name) {
102                Some(sym::name) => Self::parse_link_name(item, &mut name, cx),
103                Some(sym::kind) => Self::parse_link_kind(item, &mut kind, cx, sess, features),
104                Some(sym::modifiers) => Self::parse_link_modifiers(item, &mut modifiers, cx),
105                Some(sym::cfg) => Self::parse_link_cfg(item, &mut cfg, cx, sess, features),
106                Some(sym::wasm_import_module) => {
107                    Self::parse_link_wasm_import_module(item, &mut wasm_import_module, cx)
108                }
109                Some(sym::import_name_type) => {
110                    Self::parse_link_import_name_type(item, &mut import_name_type, cx)
111                }
112                _ => {
113                    cx.expected_specific_argument_strings(
114                        item.span(),
115                        &[
116                            sym::name,
117                            sym::kind,
118                            sym::modifiers,
119                            sym::cfg,
120                            sym::wasm_import_module,
121                            sym::import_name_type,
122                        ],
123                    );
124                    true
125                }
126            };
127            if !cont {
128                return None;
129            }
130        }
131
132        // Do this outside the above loop so we don't depend on modifiers coming after kinds
133        let mut verbatim = None;
134        if let Some((modifiers, span)) = modifiers {
135            for modifier in modifiers.as_str().split(',') {
136                let (modifier, value): (Symbol, bool) = match modifier.strip_prefix(&['+', '-']) {
137                    Some(m) => (Symbol::intern(m), modifier.starts_with('+')),
138                    None => {
139                        cx.emit_err(InvalidLinkModifier { span });
140                        continue;
141                    }
142                };
143
144                macro report_unstable_modifier($feature: ident) {
145                    if !features.$feature() {
146                        // FIXME: make this translatable
147                        #[expect(rustc::untranslatable_diagnostic)]
148                        feature_err(
149                            sess,
150                            sym::$feature,
151                            span,
152                            format!("linking modifier `{modifier}` is unstable"),
153                        )
154                        .emit();
155                    }
156                }
157                let assign_modifier = |dst: &mut Option<bool>| {
158                    if dst.is_some() {
159                        cx.emit_err(MultipleModifiers { span, modifier });
160                    } else {
161                        *dst = Some(value);
162                    }
163                };
164                match (modifier, &mut kind) {
165                    (sym::bundle, Some(NativeLibKind::Static { bundle, .. })) => {
166                        assign_modifier(bundle)
167                    }
168                    (sym::bundle, _) => {
169                        cx.emit_err(BundleNeedsStatic { span });
170                    }
171
172                    (sym::verbatim, _) => assign_modifier(&mut verbatim),
173
174                    (
175                        sym::whole_dash_archive,
176                        Some(NativeLibKind::Static { whole_archive, .. }),
177                    ) => assign_modifier(whole_archive),
178                    (sym::whole_dash_archive, _) => {
179                        cx.emit_err(WholeArchiveNeedsStatic { span });
180                    }
181
182                    (sym::as_dash_needed, Some(NativeLibKind::Dylib { as_needed }))
183                    | (sym::as_dash_needed, Some(NativeLibKind::Framework { as_needed })) => {
184                        report_unstable_modifier!(native_link_modifiers_as_needed);
185                        assign_modifier(as_needed)
186                    }
187                    (sym::as_dash_needed, _) => {
188                        cx.emit_err(AsNeededCompatibility { span });
189                    }
190
191                    _ => {
192                        cx.expected_specific_argument_strings(
193                            span,
194                            &[
195                                sym::bundle,
196                                sym::verbatim,
197                                sym::whole_dash_archive,
198                                sym::as_dash_needed,
199                            ],
200                        );
201                    }
202                }
203            }
204        }
205
206        if let Some((_, span)) = wasm_import_module {
207            if name.is_some() || kind.is_some() || modifiers.is_some() || cfg.is_some() {
208                cx.emit_err(IncompatibleWasmLink { span });
209            }
210        }
211
212        if wasm_import_module.is_some() {
213            (name, kind) = (wasm_import_module, Some(NativeLibKind::WasmImportModule));
214        }
215        let Some((name, name_span)) = name else {
216            cx.emit_err(LinkRequiresName { span: cx.attr_span });
217            return None;
218        };
219
220        // Do this outside of the loop so that `import_name_type` can be specified before `kind`.
221        if let Some((_, span)) = import_name_type {
222            if kind != Some(NativeLibKind::RawDylib) {
223                cx.emit_err(ImportNameTypeRaw { span });
224            }
225        }
226
227        if let Some(NativeLibKind::RawDylib) = kind
228            && name.as_str().contains('\0')
229        {
230            cx.emit_err(RawDylibNoNul { span: name_span });
231        }
232
233        Some(LinkEntry {
234            span: cx.attr_span,
235            kind: kind.unwrap_or(NativeLibKind::Unspecified),
236            name,
237            cfg,
238            verbatim,
239            import_name_type,
240        })
241    }
242}
243
244impl LinkParser {
245    fn parse_link_name<S: Stage>(
246        item: &MetaItemParser<'_>,
247        name: &mut Option<(Symbol, Span)>,
248        cx: &mut AcceptContext<'_, '_, S>,
249    ) -> bool {
250        if name.is_some() {
251            cx.duplicate_key(item.span(), sym::name);
252            return true;
253        }
254        let Some(nv) = item.args().name_value() else {
255            cx.expected_name_value(item.span(), Some(sym::name));
256            return false;
257        };
258        let Some(link_name) = nv.value_as_str() else {
259            cx.expected_name_value(item.span(), Some(sym::name));
260            return false;
261        };
262
263        if link_name.is_empty() {
264            cx.emit_err(EmptyLinkName { span: nv.value_span });
265        }
266        *name = Some((link_name, nv.value_span));
267        true
268    }
269
270    fn parse_link_kind<S: Stage>(
271        item: &MetaItemParser<'_>,
272        kind: &mut Option<NativeLibKind>,
273        cx: &mut AcceptContext<'_, '_, S>,
274        sess: &Session,
275        features: &Features,
276    ) -> bool {
277        if kind.is_some() {
278            cx.duplicate_key(item.span(), sym::kind);
279            return true;
280        }
281        let Some(nv) = item.args().name_value() else {
282            cx.expected_name_value(item.span(), Some(sym::kind));
283            return true;
284        };
285        let Some(link_kind) = nv.value_as_str() else {
286            cx.expected_name_value(item.span(), Some(sym::kind));
287            return true;
288        };
289
290        let link_kind = match link_kind {
291            kw::Static => NativeLibKind::Static { bundle: None, whole_archive: None },
292            sym::dylib => NativeLibKind::Dylib { as_needed: None },
293            sym::framework => {
294                if !sess.target.is_like_darwin {
295                    cx.emit_err(LinkFrameworkApple { span: nv.value_span });
296                }
297                NativeLibKind::Framework { as_needed: None }
298            }
299            sym::raw_dash_dylib => {
300                if sess.target.is_like_windows {
301                    // raw-dylib is stable and working on Windows
302                } else if sess.target.binary_format == BinaryFormat::Elf && features.raw_dylib_elf()
303                {
304                    // raw-dylib is unstable on ELF, but the user opted in
305                } else if sess.target.binary_format == BinaryFormat::Elf && sess.is_nightly_build()
306                {
307                    feature_err(
308                        sess,
309                        sym::raw_dylib_elf,
310                        nv.value_span,
311                        fluent_generated::attr_parsing_raw_dylib_elf_unstable,
312                    )
313                    .emit();
314                } else {
315                    cx.emit_err(RawDylibOnlyWindows { span: nv.value_span });
316                }
317
318                NativeLibKind::RawDylib
319            }
320            sym::link_dash_arg => {
321                if !features.link_arg_attribute() {
322                    feature_err(
323                        sess,
324                        sym::link_arg_attribute,
325                        nv.value_span,
326                        fluent_generated::attr_parsing_link_arg_unstable,
327                    )
328                    .emit();
329                }
330                NativeLibKind::LinkArg
331            }
332            _kind => {
333                cx.expected_specific_argument_strings(
334                    nv.value_span,
335                    &[
336                        kw::Static,
337                        sym::dylib,
338                        sym::framework,
339                        sym::raw_dash_dylib,
340                        sym::link_dash_arg,
341                    ],
342                );
343                return true;
344            }
345        };
346        *kind = Some(link_kind);
347        true
348    }
349
350    fn parse_link_modifiers<S: Stage>(
351        item: &MetaItemParser<'_>,
352        modifiers: &mut Option<(Symbol, Span)>,
353        cx: &mut AcceptContext<'_, '_, S>,
354    ) -> bool {
355        if modifiers.is_some() {
356            cx.duplicate_key(item.span(), sym::modifiers);
357            return true;
358        }
359        let Some(nv) = item.args().name_value() else {
360            cx.expected_name_value(item.span(), Some(sym::modifiers));
361            return true;
362        };
363        let Some(link_modifiers) = nv.value_as_str() else {
364            cx.expected_name_value(item.span(), Some(sym::modifiers));
365            return true;
366        };
367        *modifiers = Some((link_modifiers, nv.value_span));
368        true
369    }
370
371    fn parse_link_cfg<S: Stage>(
372        item: &MetaItemParser<'_>,
373        cfg: &mut Option<CfgEntry>,
374        cx: &mut AcceptContext<'_, '_, S>,
375        sess: &Session,
376        features: &Features,
377    ) -> bool {
378        if cfg.is_some() {
379            cx.duplicate_key(item.span(), sym::cfg);
380            return true;
381        }
382        let Some(link_cfg) = item.args().list() else {
383            cx.expected_list(item.span());
384            return true;
385        };
386        let Some(link_cfg) = link_cfg.single() else {
387            cx.expected_single_argument(item.span());
388            return true;
389        };
390        if !features.link_cfg() {
391            feature_err(
392                sess,
393                sym::link_cfg,
394                item.span(),
395                fluent_generated::attr_parsing_link_cfg_unstable,
396            )
397            .emit();
398        }
399        *cfg = parse_cfg_entry(cx, link_cfg);
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.duplicate_key(item.span(), sym::wasm_import_module);
410            return true;
411        }
412        let Some(nv) = item.args().name_value() else {
413            cx.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.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.duplicate_key(item.span(), sym::import_name_type);
431            return true;
432        }
433        let Some(nv) = item.args().name_value() else {
434            cx.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.expected_name_value(item.span(), Some(sym::import_name_type));
439            return true;
440        };
441        if cx.sess().target.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.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 ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
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: false })),
474        Allow(Target::Method(MethodKind::Trait { body: true })),
475        Allow(Target::Method(MethodKind::TraitImpl)),
476    ]);
477    const TEMPLATE: AttributeTemplate = template!(
478        NameValueStr: "name",
479        "https://doc.rust-lang.org/reference/abi.html#the-link_section-attribute"
480    );
481
482    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
483        let Some(nv) = args.name_value() else {
484            cx.expected_name_value(cx.attr_span, None);
485            return None;
486        };
487        let Some(name) = nv.value_as_str() else {
488            cx.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 ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
506    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); //FIXME Still checked fully in `check_attr.rs`
507    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::ExportStable;
508}
509
510pub(crate) struct FfiConstParser;
511impl<S: Stage> NoArgsAttributeParser<S> for FfiConstParser {
512    const PATH: &[Symbol] = &[sym::ffi_const];
513    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
514    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::ForeignFn)]);
515    const CREATE: fn(Span) -> AttributeKind = AttributeKind::FfiConst;
516}
517
518pub(crate) struct FfiPureParser;
519impl<S: Stage> NoArgsAttributeParser<S> for FfiPureParser {
520    const PATH: &[Symbol] = &[sym::ffi_pure];
521    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
522    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::ForeignFn)]);
523    const CREATE: fn(Span) -> AttributeKind = AttributeKind::FfiPure;
524}
525
526pub(crate) struct StdInternalSymbolParser;
527impl<S: Stage> NoArgsAttributeParser<S> for StdInternalSymbolParser {
528    const PATH: &[Symbol] = &[sym::rustc_std_internal_symbol];
529    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
530    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
531        Allow(Target::Fn),
532        Allow(Target::ForeignFn),
533        Allow(Target::Static),
534        Allow(Target::ForeignStatic),
535    ]);
536    const CREATE: fn(Span) -> AttributeKind = AttributeKind::StdInternalSymbol;
537}
538
539pub(crate) struct LinkOrdinalParser;
540
541impl<S: Stage> SingleAttributeParser<S> for LinkOrdinalParser {
542    const PATH: &[Symbol] = &[sym::link_ordinal];
543    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
544    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
545    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
546        Allow(Target::ForeignFn),
547        Allow(Target::ForeignStatic),
548        Warn(Target::MacroCall),
549    ]);
550    const TEMPLATE: AttributeTemplate = template!(
551        List: &["ordinal"],
552        "https://doc.rust-lang.org/reference/items/external-blocks.html#the-link_ordinal-attribute"
553    );
554
555    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
556        let ordinal = parse_single_integer(cx, args)?;
557
558        // According to the table at
559        // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-header, the
560        // ordinal must fit into 16 bits. Similarly, the Ordinal field in COFFShortExport (defined
561        // in llvm/include/llvm/Object/COFFImportFile.h), which we use to communicate import
562        // information to LLVM for `#[link(kind = "raw-dylib"_])`, is also defined to be uint16_t.
563        //
564        // FIXME: should we allow an ordinal of 0?  The MSVC toolchain has inconsistent support for
565        // this: both LINK.EXE and LIB.EXE signal errors and abort when given a .DEF file that
566        // specifies a zero ordinal. However, llvm-dlltool is perfectly happy to generate an import
567        // library for such a .DEF file, and MSVC's LINK.EXE is also perfectly happy to consume an
568        // import library produced by LLVM with an ordinal of 0, and it generates an .EXE.  (I
569        // don't know yet if the resulting EXE runs, as I haven't yet built the necessary DLL --
570        // see earlier comment about LINK.EXE failing.)
571        let Ok(ordinal) = ordinal.try_into() else {
572            cx.emit_err(LinkOrdinalOutOfRange { span: cx.attr_span, ordinal });
573            return None;
574        };
575
576        Some(LinkOrdinal { ordinal, span: cx.attr_span })
577    }
578}
579
580pub(crate) struct LinkageParser;
581
582impl<S: Stage> SingleAttributeParser<S> for LinkageParser {
583    const PATH: &[Symbol] = &[sym::linkage];
584
585    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
586
587    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
588    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
589        Allow(Target::Fn),
590        Allow(Target::Method(MethodKind::Inherent)),
591        Allow(Target::Method(MethodKind::Trait { body: false })),
592        Allow(Target::Method(MethodKind::Trait { body: true })),
593        Allow(Target::Method(MethodKind::TraitImpl)),
594        Allow(Target::Static),
595        Allow(Target::ForeignStatic),
596        Allow(Target::ForeignFn),
597    ]);
598
599    const TEMPLATE: AttributeTemplate = template!(NameValueStr: [
600        "available_externally",
601        "common",
602        "extern_weak",
603        "external",
604        "internal",
605        "linkonce",
606        "linkonce_odr",
607        "weak",
608        "weak_odr",
609    ]);
610
611    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
612        let Some(name_value) = args.name_value() else {
613            cx.expected_name_value(cx.attr_span, Some(sym::linkage));
614            return None;
615        };
616
617        let Some(value) = name_value.value_as_str() else {
618            cx.expected_string_literal(name_value.value_span, Some(name_value.value_as_lit()));
619            return None;
620        };
621
622        // Use the names from src/llvm/docs/LangRef.rst here. Most types are only
623        // applicable to variable declarations and may not really make sense for
624        // Rust code in the first place but allow them anyway and trust that the
625        // user knows what they're doing. Who knows, unanticipated use cases may pop
626        // up in the future.
627        //
628        // ghost, dllimport, dllexport and linkonce_odr_autohide are not supported
629        // and don't have to be, LLVM treats them as no-ops.
630        let linkage = match value {
631            sym::available_externally => Linkage::AvailableExternally,
632            sym::common => Linkage::Common,
633            sym::extern_weak => Linkage::ExternalWeak,
634            sym::external => Linkage::External,
635            sym::internal => Linkage::Internal,
636            sym::linkonce => Linkage::LinkOnceAny,
637            sym::linkonce_odr => Linkage::LinkOnceODR,
638            sym::weak => Linkage::WeakAny,
639            sym::weak_odr => Linkage::WeakODR,
640
641            _ => {
642                cx.expected_specific_argument(
643                    name_value.value_span,
644                    &[
645                        sym::available_externally,
646                        sym::common,
647                        sym::extern_weak,
648                        sym::external,
649                        sym::internal,
650                        sym::linkonce,
651                        sym::linkonce_odr,
652                        sym::weak,
653                        sym::weak_odr,
654                    ],
655                );
656                return None;
657            }
658        };
659
660        Some(AttributeKind::Linkage(linkage, cx.attr_span))
661    }
662}