Skip to main content

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