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 =
114        AllowedTargets::AllowList(&[Allow(Target::ForeignFn), Allow(Target::ForeignStatic)]);
115    const TEMPLATE: AttributeTemplate = template!(
116        List: &["ordinal"],
117        "https://doc.rust-lang.org/reference/items/external-blocks.html#the-link_ordinal-attribute"
118    );
119
120    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
121        let ordinal = parse_single_integer(cx, args)?;
122
123        // According to the table at
124        // https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-header, the
125        // ordinal must fit into 16 bits. Similarly, the Ordinal field in COFFShortExport (defined
126        // in llvm/include/llvm/Object/COFFImportFile.h), which we use to communicate import
127        // information to LLVM for `#[link(kind = "raw-dylib"_])`, is also defined to be uint16_t.
128        //
129        // FIXME: should we allow an ordinal of 0?  The MSVC toolchain has inconsistent support for
130        // this: both LINK.EXE and LIB.EXE signal errors and abort when given a .DEF file that
131        // specifies a zero ordinal. However, llvm-dlltool is perfectly happy to generate an import
132        // library for such a .DEF file, and MSVC's LINK.EXE is also perfectly happy to consume an
133        // import library produced by LLVM with an ordinal of 0, and it generates an .EXE.  (I
134        // don't know yet if the resulting EXE runs, as I haven't yet built the necessary DLL --
135        // see earlier comment about LINK.EXE failing.)
136        let Ok(ordinal) = ordinal.try_into() else {
137            cx.emit_err(LinkOrdinalOutOfRange { span: cx.attr_span, ordinal });
138            return None;
139        };
140
141        Some(LinkOrdinal { ordinal, span: cx.attr_span })
142    }
143}
144
145pub(crate) struct LinkageParser;
146
147impl<S: Stage> SingleAttributeParser<S> for LinkageParser {
148    const PATH: &[Symbol] = &[sym::linkage];
149
150    const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
151
152    const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
153    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
154        Allow(Target::Fn),
155        Allow(Target::Method(MethodKind::Inherent)),
156        Allow(Target::Method(MethodKind::Trait { body: false })),
157        Allow(Target::Method(MethodKind::Trait { body: true })),
158        Allow(Target::Method(MethodKind::TraitImpl)),
159        Allow(Target::Static),
160        Allow(Target::ForeignStatic),
161        Allow(Target::ForeignFn),
162    ]);
163
164    const TEMPLATE: AttributeTemplate = template!(NameValueStr: [
165        "available_externally",
166        "common",
167        "extern_weak",
168        "external",
169        "internal",
170        "linkonce",
171        "linkonce_odr",
172        "weak",
173        "weak_odr",
174    ]);
175
176    fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
177        let Some(name_value) = args.name_value() else {
178            cx.expected_name_value(cx.attr_span, Some(sym::linkage));
179            return None;
180        };
181
182        let Some(value) = name_value.value_as_str() else {
183            cx.expected_string_literal(name_value.value_span, Some(name_value.value_as_lit()));
184            return None;
185        };
186
187        // Use the names from src/llvm/docs/LangRef.rst here. Most types are only
188        // applicable to variable declarations and may not really make sense for
189        // Rust code in the first place but allow them anyway and trust that the
190        // user knows what they're doing. Who knows, unanticipated use cases may pop
191        // up in the future.
192        //
193        // ghost, dllimport, dllexport and linkonce_odr_autohide are not supported
194        // and don't have to be, LLVM treats them as no-ops.
195        let linkage = match value {
196            sym::available_externally => Linkage::AvailableExternally,
197            sym::common => Linkage::Common,
198            sym::extern_weak => Linkage::ExternalWeak,
199            sym::external => Linkage::External,
200            sym::internal => Linkage::Internal,
201            sym::linkonce => Linkage::LinkOnceAny,
202            sym::linkonce_odr => Linkage::LinkOnceODR,
203            sym::weak => Linkage::WeakAny,
204            sym::weak_odr => Linkage::WeakODR,
205
206            _ => {
207                cx.expected_specific_argument(
208                    name_value.value_span,
209                    &[
210                        sym::available_externally,
211                        sym::common,
212                        sym::extern_weak,
213                        sym::external,
214                        sym::internal,
215                        sym::linkonce,
216                        sym::linkonce_odr,
217                        sym::weak,
218                        sym::weak_odr,
219                    ],
220                );
221                return None;
222            }
223        };
224
225        Some(AttributeKind::Linkage(linkage, cx.attr_span))
226    }
227}