rustc_attr_parsing/attributes/
link_attrs.rs1use 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); 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 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 #[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 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 } else if sess.target.binary_format == BinaryFormat::Elf && features.raw_dylib_elf()
292 {
293 } 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 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); 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 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 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}