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