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