rustc_attr_parsing/attributes/
link_attrs.rs

1use rustc_hir::attrs::AttributeKind::{LinkName, LinkOrdinal, LinkSection};
2use rustc_hir::attrs::Linkage;
3
4use super::prelude::*;
5use super::util::parse_single_integer;
6use crate::session_diagnostics::{LinkOrdinalOutOfRange, NullOnLinkSection};
7
8pub(crate) struct LinkNameParser;
9
10impl<S: Stage> SingleAttributeParser<S> for LinkNameParser {
11    const PATH: &[Symbol] = &[sym::link_name];
12    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
13    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
14    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[
15        Allow(Target::ForeignFn),
16        Allow(Target::ForeignStatic),
17    ]);
18    const TEMPLATE: AttributeTemplate = template!(
19        NameValueStr: "name",
20        "https://doc.rust-lang.org/reference/items/external-blocks.html#the-link_name-attribute"
21    );
22
23    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
24        let Some(nv) = args.name_value() else {
25            cx.expected_name_value(cx.attr_span, None);
26            return None;
27        };
28        let Some(name) = nv.value_as_str() else {
29            cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
30            return None;
31        };
32
33        Some(LinkName { name, span: cx.attr_span })
34    }
35}
36
37pub(crate) struct LinkSectionParser;
38
39impl<S: Stage> SingleAttributeParser<S> for LinkSectionParser {
40    const PATH: &[Symbol] = &[sym::link_section];
41    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
42    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
43    const ALLOWED_TARGETS: AllowedTargets =
44        AllowedTargets::AllowListWarnRest(&[Allow(Target::Static), Allow(Target::Fn)]);
45    const TEMPLATE: AttributeTemplate = template!(
46        NameValueStr: "name",
47        "https://doc.rust-lang.org/reference/abi.html#the-link_section-attribute"
48    );
49
50    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
51        let Some(nv) = args.name_value() else {
52            cx.expected_name_value(cx.attr_span, None);
53            return None;
54        };
55        let Some(name) = nv.value_as_str() else {
56            cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
57            return None;
58        };
59        if name.as_str().contains('\0') {
60            // `#[link_section = ...]` will be converted to a null-terminated string,
61            // so it may not contain any null characters.
62            cx.emit_err(NullOnLinkSection { span: cx.attr_span });
63            return None;
64        }
65
66        Some(LinkSection { name, span: cx.attr_span })
67    }
68}
69
70pub(crate) struct ExportStableParser;
71impl<S: Stage> NoArgsAttributeParser<S> for ExportStableParser {
72    const PATH: &[Symbol] = &[sym::export_stable];
73    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
74    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); //FIXME Still checked fully in `check_attr.rs`
75    const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::ExportStable;
76}
77
78pub(crate) struct FfiConstParser;
79impl<S: Stage> NoArgsAttributeParser<S> for FfiConstParser {
80    const PATH: &[Symbol] = &[sym::ffi_const];
81    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
82    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::ForeignFn)]);
83    const CREATE: fn(Span) -> AttributeKind = AttributeKind::FfiConst;
84}
85
86pub(crate) struct FfiPureParser;
87impl<S: Stage> NoArgsAttributeParser<S> for FfiPureParser {
88    const PATH: &[Symbol] = &[sym::ffi_pure];
89    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
90    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::ForeignFn)]);
91    const CREATE: fn(Span) -> AttributeKind = AttributeKind::FfiPure;
92}
93
94pub(crate) struct StdInternalSymbolParser;
95impl<S: Stage> NoArgsAttributeParser<S> for StdInternalSymbolParser {
96    const PATH: &[Symbol] = &[sym::rustc_std_internal_symbol];
97    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
98    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
99        Allow(Target::Fn),
100        Allow(Target::ForeignFn),
101        Allow(Target::Static),
102        Allow(Target::ForeignStatic),
103    ]);
104    const CREATE: fn(Span) -> AttributeKind = AttributeKind::StdInternalSymbol;
105}
106
107pub(crate) struct LinkOrdinalParser;
108
109impl<S: Stage> SingleAttributeParser<S> for LinkOrdinalParser {
110    const PATH: &[Symbol] = &[sym::link_ordinal];
111    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
112    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
113    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
114        Allow(Target::ForeignFn),
115        Allow(Target::ForeignStatic),
116        Warn(Target::MacroCall),
117    ]);
118    const TEMPLATE: AttributeTemplate = template!(
119        List: &["ordinal"],
120        "https://doc.rust-lang.org/reference/items/external-blocks.html#the-link_ordinal-attribute"
121    );
122
123    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
124        let ordinal = parse_single_integer(cx, args)?;
125
126        // According to the table at
127        // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-header, the
128        // ordinal must fit into 16 bits. Similarly, the Ordinal field in COFFShortExport (defined
129        // in llvm/include/llvm/Object/COFFImportFile.h), which we use to communicate import
130        // information to LLVM for `#[link(kind = "raw-dylib"_])`, is also defined to be uint16_t.
131        //
132        // FIXME: should we allow an ordinal of 0?  The MSVC toolchain has inconsistent support for
133        // this: both LINK.EXE and LIB.EXE signal errors and abort when given a .DEF file that
134        // specifies a zero ordinal. However, llvm-dlltool is perfectly happy to generate an import
135        // library for such a .DEF file, and MSVC's LINK.EXE is also perfectly happy to consume an
136        // import library produced by LLVM with an ordinal of 0, and it generates an .EXE.  (I
137        // don't know yet if the resulting EXE runs, as I haven't yet built the necessary DLL --
138        // see earlier comment about LINK.EXE failing.)
139        let Ok(ordinal) = ordinal.try_into() else {
140            cx.emit_err(LinkOrdinalOutOfRange { span: cx.attr_span, ordinal });
141            return None;
142        };
143
144        Some(LinkOrdinal { ordinal, span: cx.attr_span })
145    }
146}
147
148pub(crate) struct LinkageParser;
149
150impl<S: Stage> SingleAttributeParser<S> for LinkageParser {
151    const PATH: &[Symbol] = &[sym::linkage];
152
153    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
154
155    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
156    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
157        Allow(Target::Fn),
158        Allow(Target::Method(MethodKind::Inherent)),
159        Allow(Target::Method(MethodKind::Trait { body: false })),
160        Allow(Target::Method(MethodKind::Trait { body: true })),
161        Allow(Target::Method(MethodKind::TraitImpl)),
162        Allow(Target::Static),
163        Allow(Target::ForeignStatic),
164        Allow(Target::ForeignFn),
165    ]);
166
167    const TEMPLATE: AttributeTemplate = template!(NameValueStr: [
168        "available_externally",
169        "common",
170        "extern_weak",
171        "external",
172        "internal",
173        "linkonce",
174        "linkonce_odr",
175        "weak",
176        "weak_odr",
177    ]);
178
179    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
180        let Some(name_value) = args.name_value() else {
181            cx.expected_name_value(cx.attr_span, Some(sym::linkage));
182            return None;
183        };
184
185        let Some(value) = name_value.value_as_str() else {
186            cx.expected_string_literal(name_value.value_span, Some(name_value.value_as_lit()));
187            return None;
188        };
189
190        // Use the names from src/llvm/docs/LangRef.rst here. Most types are only
191        // applicable to variable declarations and may not really make sense for
192        // Rust code in the first place but allow them anyway and trust that the
193        // user knows what they're doing. Who knows, unanticipated use cases may pop
194        // up in the future.
195        //
196        // ghost, dllimport, dllexport and linkonce_odr_autohide are not supported
197        // and don't have to be, LLVM treats them as no-ops.
198        let linkage = match value {
199            sym::available_externally => Linkage::AvailableExternally,
200            sym::common => Linkage::Common,
201            sym::extern_weak => Linkage::ExternalWeak,
202            sym::external => Linkage::External,
203            sym::internal => Linkage::Internal,
204            sym::linkonce => Linkage::LinkOnceAny,
205            sym::linkonce_odr => Linkage::LinkOnceODR,
206            sym::weak => Linkage::WeakAny,
207            sym::weak_odr => Linkage::WeakODR,
208
209            _ => {
210                cx.expected_specific_argument(
211                    name_value.value_span,
212                    &[
213                        sym::available_externally,
214                        sym::common,
215                        sym::extern_weak,
216                        sym::external,
217                        sym::internal,
218                        sym::linkonce,
219                        sym::linkonce_odr,
220                        sym::weak,
221                        sym::weak_odr,
222                    ],
223                );
224                return None;
225            }
226        };
227
228        Some(AttributeKind::Linkage(linkage, cx.attr_span))
229    }
230}