rustc_attr_parsing/attributes/
link_attrs.rs

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