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 items = match args {
69 ArgParser::List(list) => list,
70 ArgParser::NameValue(nv) if nv.value_as_str().is_some_and(|v| v == sym::dl) => {
74 let suggestions = <Self as CombineAttributeParser<S>>::TEMPLATE
75 .suggestions(cx.attr_style, "link");
76 let span = cx.attr_span;
77 cx.emit_lint(AttributeLintKind::IllFormedAttributeInput { suggestions }, span);
78 return None;
79 }
80 _ => {
81 cx.expected_list(cx.attr_span);
82 return None;
83 }
84 };
85
86 let sess = cx.sess();
87 let features = cx.features();
88
89 let mut name = None;
90 let mut kind = None;
91 let mut modifiers = None;
92 let mut cfg = None;
93 let mut wasm_import_module = None;
94 let mut import_name_type = None;
95 for item in items.mixed() {
96 let Some(item) = item.meta_item() else {
97 cx.unexpected_literal(item.span());
98 continue;
99 };
100
101 let cont = match item.path().word().map(|ident| ident.name) {
102 Some(sym::name) => Self::parse_link_name(item, &mut name, cx),
103 Some(sym::kind) => Self::parse_link_kind(item, &mut kind, cx, sess, features),
104 Some(sym::modifiers) => Self::parse_link_modifiers(item, &mut modifiers, cx),
105 Some(sym::cfg) => Self::parse_link_cfg(item, &mut cfg, cx, sess, features),
106 Some(sym::wasm_import_module) => {
107 Self::parse_link_wasm_import_module(item, &mut wasm_import_module, cx)
108 }
109 Some(sym::import_name_type) => {
110 Self::parse_link_import_name_type(item, &mut import_name_type, cx)
111 }
112 _ => {
113 cx.expected_specific_argument_strings(
114 item.span(),
115 &[
116 sym::name,
117 sym::kind,
118 sym::modifiers,
119 sym::cfg,
120 sym::wasm_import_module,
121 sym::import_name_type,
122 ],
123 );
124 true
125 }
126 };
127 if !cont {
128 return None;
129 }
130 }
131
132 let mut verbatim = None;
134 if let Some((modifiers, span)) = modifiers {
135 for modifier in modifiers.as_str().split(',') {
136 let (modifier, value): (Symbol, bool) = match modifier.strip_prefix(&['+', '-']) {
137 Some(m) => (Symbol::intern(m), modifier.starts_with('+')),
138 None => {
139 cx.emit_err(InvalidLinkModifier { span });
140 continue;
141 }
142 };
143
144 macro report_unstable_modifier($feature: ident) {
145 if !features.$feature() {
146 #[expect(rustc::untranslatable_diagnostic)]
148 feature_err(
149 sess,
150 sym::$feature,
151 span,
152 format!("linking modifier `{modifier}` is unstable"),
153 )
154 .emit();
155 }
156 }
157 let assign_modifier = |dst: &mut Option<bool>| {
158 if dst.is_some() {
159 cx.emit_err(MultipleModifiers { span, modifier });
160 } else {
161 *dst = Some(value);
162 }
163 };
164 match (modifier, &mut kind) {
165 (sym::bundle, Some(NativeLibKind::Static { bundle, .. })) => {
166 assign_modifier(bundle)
167 }
168 (sym::bundle, _) => {
169 cx.emit_err(BundleNeedsStatic { span });
170 }
171
172 (sym::verbatim, _) => assign_modifier(&mut verbatim),
173
174 (
175 sym::whole_dash_archive,
176 Some(NativeLibKind::Static { whole_archive, .. }),
177 ) => assign_modifier(whole_archive),
178 (sym::whole_dash_archive, _) => {
179 cx.emit_err(WholeArchiveNeedsStatic { span });
180 }
181
182 (sym::as_dash_needed, Some(NativeLibKind::Dylib { as_needed }))
183 | (sym::as_dash_needed, Some(NativeLibKind::Framework { as_needed }))
184 | (sym::as_dash_needed, Some(NativeLibKind::RawDylib { as_needed })) => {
185 report_unstable_modifier!(native_link_modifiers_as_needed);
186 assign_modifier(as_needed)
187 }
188 (sym::as_dash_needed, _) => {
189 cx.emit_err(AsNeededCompatibility { span });
190 }
191
192 _ => {
193 cx.expected_specific_argument_strings(
194 span,
195 &[
196 sym::bundle,
197 sym::verbatim,
198 sym::whole_dash_archive,
199 sym::as_dash_needed,
200 ],
201 );
202 }
203 }
204 }
205 }
206
207 if let Some((_, span)) = wasm_import_module {
208 if name.is_some() || kind.is_some() || modifiers.is_some() || cfg.is_some() {
209 cx.emit_err(IncompatibleWasmLink { span });
210 }
211 }
212
213 if wasm_import_module.is_some() {
214 (name, kind) = (wasm_import_module, Some(NativeLibKind::WasmImportModule));
215 }
216 let Some((name, name_span)) = name else {
217 cx.emit_err(LinkRequiresName { span: cx.attr_span });
218 return None;
219 };
220
221 if let Some((_, span)) = import_name_type {
223 if !matches!(kind, Some(NativeLibKind::RawDylib { .. })) {
224 cx.emit_err(ImportNameTypeRaw { span });
225 }
226 }
227
228 if let Some(NativeLibKind::RawDylib { .. }) = kind
229 && name.as_str().contains('\0')
230 {
231 cx.emit_err(RawDylibNoNul { span: name_span });
232 }
233
234 Some(LinkEntry {
235 span: cx.attr_span,
236 kind: kind.unwrap_or(NativeLibKind::Unspecified),
237 name,
238 cfg,
239 verbatim,
240 import_name_type,
241 })
242 }
243}
244
245impl LinkParser {
246 fn parse_link_name<S: Stage>(
247 item: &MetaItemParser<'_>,
248 name: &mut Option<(Symbol, Span)>,
249 cx: &mut AcceptContext<'_, '_, S>,
250 ) -> bool {
251 if name.is_some() {
252 cx.duplicate_key(item.span(), sym::name);
253 return true;
254 }
255 let Some(nv) = item.args().name_value() else {
256 cx.expected_name_value(item.span(), Some(sym::name));
257 return false;
258 };
259 let Some(link_name) = nv.value_as_str() else {
260 cx.expected_name_value(item.span(), Some(sym::name));
261 return false;
262 };
263
264 if link_name.is_empty() {
265 cx.emit_err(EmptyLinkName { span: nv.value_span });
266 }
267 *name = Some((link_name, nv.value_span));
268 true
269 }
270
271 fn parse_link_kind<S: Stage>(
272 item: &MetaItemParser<'_>,
273 kind: &mut Option<NativeLibKind>,
274 cx: &mut AcceptContext<'_, '_, S>,
275 sess: &Session,
276 features: &Features,
277 ) -> bool {
278 if kind.is_some() {
279 cx.duplicate_key(item.span(), sym::kind);
280 return true;
281 }
282 let Some(nv) = item.args().name_value() else {
283 cx.expected_name_value(item.span(), Some(sym::kind));
284 return true;
285 };
286 let Some(link_kind) = nv.value_as_str() else {
287 cx.expected_name_value(item.span(), Some(sym::kind));
288 return true;
289 };
290
291 let link_kind = match link_kind {
292 kw::Static => NativeLibKind::Static { bundle: None, whole_archive: None },
293 sym::dylib => NativeLibKind::Dylib { as_needed: None },
294 sym::framework => {
295 if !sess.target.is_like_darwin {
296 cx.emit_err(LinkFrameworkApple { span: nv.value_span });
297 }
298 NativeLibKind::Framework { as_needed: None }
299 }
300 sym::raw_dash_dylib => {
301 if sess.target.is_like_windows {
302 } else if sess.target.binary_format == BinaryFormat::Elf && features.raw_dylib_elf()
304 {
305 } else if sess.target.binary_format == BinaryFormat::Elf && sess.is_nightly_build()
307 {
308 feature_err(
309 sess,
310 sym::raw_dylib_elf,
311 nv.value_span,
312 fluent_generated::attr_parsing_raw_dylib_elf_unstable,
313 )
314 .emit();
315 } else {
316 cx.emit_err(RawDylibOnlyWindows { span: nv.value_span });
317 }
318
319 NativeLibKind::RawDylib { as_needed: None }
320 }
321 sym::link_dash_arg => {
322 if !features.link_arg_attribute() {
323 feature_err(
324 sess,
325 sym::link_arg_attribute,
326 nv.value_span,
327 fluent_generated::attr_parsing_link_arg_unstable,
328 )
329 .emit();
330 }
331 NativeLibKind::LinkArg
332 }
333 _kind => {
334 cx.expected_specific_argument_strings(
335 nv.value_span,
336 &[
337 kw::Static,
338 sym::dylib,
339 sym::framework,
340 sym::raw_dash_dylib,
341 sym::link_dash_arg,
342 ],
343 );
344 return true;
345 }
346 };
347 *kind = Some(link_kind);
348 true
349 }
350
351 fn parse_link_modifiers<S: Stage>(
352 item: &MetaItemParser<'_>,
353 modifiers: &mut Option<(Symbol, Span)>,
354 cx: &mut AcceptContext<'_, '_, S>,
355 ) -> bool {
356 if modifiers.is_some() {
357 cx.duplicate_key(item.span(), sym::modifiers);
358 return true;
359 }
360 let Some(nv) = item.args().name_value() else {
361 cx.expected_name_value(item.span(), Some(sym::modifiers));
362 return true;
363 };
364 let Some(link_modifiers) = nv.value_as_str() else {
365 cx.expected_name_value(item.span(), Some(sym::modifiers));
366 return true;
367 };
368 *modifiers = Some((link_modifiers, nv.value_span));
369 true
370 }
371
372 fn parse_link_cfg<S: Stage>(
373 item: &MetaItemParser<'_>,
374 cfg: &mut Option<CfgEntry>,
375 cx: &mut AcceptContext<'_, '_, S>,
376 sess: &Session,
377 features: &Features,
378 ) -> bool {
379 if cfg.is_some() {
380 cx.duplicate_key(item.span(), sym::cfg);
381 return true;
382 }
383 let Some(link_cfg) = item.args().list() else {
384 cx.expected_list(item.span());
385 return true;
386 };
387 let Some(link_cfg) = link_cfg.single() else {
388 cx.expected_single_argument(item.span());
389 return true;
390 };
391 if !features.link_cfg() {
392 feature_err(
393 sess,
394 sym::link_cfg,
395 item.span(),
396 fluent_generated::attr_parsing_link_cfg_unstable,
397 )
398 .emit();
399 }
400 *cfg = parse_cfg_entry(cx, link_cfg);
401 true
402 }
403
404 fn parse_link_wasm_import_module<S: Stage>(
405 item: &MetaItemParser<'_>,
406 wasm_import_module: &mut Option<(Symbol, Span)>,
407 cx: &mut AcceptContext<'_, '_, S>,
408 ) -> bool {
409 if wasm_import_module.is_some() {
410 cx.duplicate_key(item.span(), sym::wasm_import_module);
411 return true;
412 }
413 let Some(nv) = item.args().name_value() else {
414 cx.expected_name_value(item.span(), Some(sym::wasm_import_module));
415 return true;
416 };
417 let Some(link_wasm_import_module) = nv.value_as_str() else {
418 cx.expected_name_value(item.span(), Some(sym::wasm_import_module));
419 return true;
420 };
421 *wasm_import_module = Some((link_wasm_import_module, item.span()));
422 true
423 }
424
425 fn parse_link_import_name_type<S: Stage>(
426 item: &MetaItemParser<'_>,
427 import_name_type: &mut Option<(PeImportNameType, Span)>,
428 cx: &mut AcceptContext<'_, '_, S>,
429 ) -> bool {
430 if import_name_type.is_some() {
431 cx.duplicate_key(item.span(), sym::import_name_type);
432 return true;
433 }
434 let Some(nv) = item.args().name_value() else {
435 cx.expected_name_value(item.span(), Some(sym::import_name_type));
436 return true;
437 };
438 let Some(link_import_name_type) = nv.value_as_str() else {
439 cx.expected_name_value(item.span(), Some(sym::import_name_type));
440 return true;
441 };
442 if cx.sess().target.arch != "x86" {
443 cx.emit_err(ImportNameTypeX86 { span: item.span() });
444 return true;
445 }
446
447 let link_import_name_type = match link_import_name_type {
448 sym::decorated => PeImportNameType::Decorated,
449 sym::noprefix => PeImportNameType::NoPrefix,
450 sym::undecorated => PeImportNameType::Undecorated,
451 _ => {
452 cx.expected_specific_argument_strings(
453 item.span(),
454 &[sym::decorated, sym::noprefix, sym::undecorated],
455 );
456 return true;
457 }
458 };
459 *import_name_type = Some((link_import_name_type, item.span()));
460 true
461 }
462}
463
464pub(crate) struct LinkSectionParser;
465
466impl<S: Stage> SingleAttributeParser<S> for LinkSectionParser {
467 const PATH: &[Symbol] = &[sym::link_section];
468 const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
469 const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
470 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[
471 Allow(Target::Static),
472 Allow(Target::Fn),
473 Allow(Target::Method(MethodKind::Inherent)),
474 Allow(Target::Method(MethodKind::Trait { body: false })),
475 Allow(Target::Method(MethodKind::Trait { body: true })),
476 Allow(Target::Method(MethodKind::TraitImpl)),
477 ]);
478 const TEMPLATE: AttributeTemplate = template!(
479 NameValueStr: "name",
480 "https://doc.rust-lang.org/reference/abi.html#the-link_section-attribute"
481 );
482
483 fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
484 let Some(nv) = args.name_value() else {
485 cx.expected_name_value(cx.attr_span, None);
486 return None;
487 };
488 let Some(name) = nv.value_as_str() else {
489 cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
490 return None;
491 };
492 if name.as_str().contains('\0') {
493 cx.emit_err(NullOnLinkSection { span: cx.attr_span });
496 return None;
497 }
498
499 Some(LinkSection { name, span: cx.attr_span })
500 }
501}
502
503pub(crate) struct ExportStableParser;
504impl<S: Stage> NoArgsAttributeParser<S> for ExportStableParser {
505 const PATH: &[Symbol] = &[sym::export_stable];
506 const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
507 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::ExportStable;
509}
510
511pub(crate) struct FfiConstParser;
512impl<S: Stage> NoArgsAttributeParser<S> for FfiConstParser {
513 const PATH: &[Symbol] = &[sym::ffi_const];
514 const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
515 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::ForeignFn)]);
516 const CREATE: fn(Span) -> AttributeKind = AttributeKind::FfiConst;
517}
518
519pub(crate) struct FfiPureParser;
520impl<S: Stage> NoArgsAttributeParser<S> for FfiPureParser {
521 const PATH: &[Symbol] = &[sym::ffi_pure];
522 const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
523 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::ForeignFn)]);
524 const CREATE: fn(Span) -> AttributeKind = AttributeKind::FfiPure;
525}
526
527pub(crate) struct StdInternalSymbolParser;
528impl<S: Stage> NoArgsAttributeParser<S> for StdInternalSymbolParser {
529 const PATH: &[Symbol] = &[sym::rustc_std_internal_symbol];
530 const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
531 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
532 Allow(Target::Fn),
533 Allow(Target::ForeignFn),
534 Allow(Target::Static),
535 Allow(Target::ForeignStatic),
536 ]);
537 const CREATE: fn(Span) -> AttributeKind = AttributeKind::StdInternalSymbol;
538}
539
540pub(crate) struct LinkOrdinalParser;
541
542impl<S: Stage> SingleAttributeParser<S> for LinkOrdinalParser {
543 const PATH: &[Symbol] = &[sym::link_ordinal];
544 const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
545 const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
546 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
547 Allow(Target::ForeignFn),
548 Allow(Target::ForeignStatic),
549 Warn(Target::MacroCall),
550 ]);
551 const TEMPLATE: AttributeTemplate = template!(
552 List: &["ordinal"],
553 "https://doc.rust-lang.org/reference/items/external-blocks.html#the-link_ordinal-attribute"
554 );
555
556 fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
557 let ordinal = parse_single_integer(cx, args)?;
558
559 let Ok(ordinal) = ordinal.try_into() else {
573 cx.emit_err(LinkOrdinalOutOfRange { span: cx.attr_span, ordinal });
574 return None;
575 };
576
577 Some(LinkOrdinal { ordinal, span: cx.attr_span })
578 }
579}
580
581pub(crate) struct LinkageParser;
582
583impl<S: Stage> SingleAttributeParser<S> for LinkageParser {
584 const PATH: &[Symbol] = &[sym::linkage];
585
586 const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
587
588 const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
589 const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
590 Allow(Target::Fn),
591 Allow(Target::Method(MethodKind::Inherent)),
592 Allow(Target::Method(MethodKind::Trait { body: false })),
593 Allow(Target::Method(MethodKind::Trait { body: true })),
594 Allow(Target::Method(MethodKind::TraitImpl)),
595 Allow(Target::Static),
596 Allow(Target::ForeignStatic),
597 Allow(Target::ForeignFn),
598 ]);
599
600 const TEMPLATE: AttributeTemplate = template!(NameValueStr: [
601 "available_externally",
602 "common",
603 "extern_weak",
604 "external",
605 "internal",
606 "linkonce",
607 "linkonce_odr",
608 "weak",
609 "weak_odr",
610 ]);
611
612 fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
613 let Some(name_value) = args.name_value() else {
614 cx.expected_name_value(cx.attr_span, Some(sym::linkage));
615 return None;
616 };
617
618 let Some(value) = name_value.value_as_str() else {
619 cx.expected_string_literal(name_value.value_span, Some(name_value.value_as_lit()));
620 return None;
621 };
622
623 let linkage = match value {
632 sym::available_externally => Linkage::AvailableExternally,
633 sym::common => Linkage::Common,
634 sym::extern_weak => Linkage::ExternalWeak,
635 sym::external => Linkage::External,
636 sym::internal => Linkage::Internal,
637 sym::linkonce => Linkage::LinkOnceAny,
638 sym::linkonce_odr => Linkage::LinkOnceODR,
639 sym::weak => Linkage::WeakAny,
640 sym::weak_odr => Linkage::WeakODR,
641
642 _ => {
643 cx.expected_specific_argument(
644 name_value.value_span,
645 &[
646 sym::available_externally,
647 sym::common,
648 sym::extern_weak,
649 sym::external,
650 sym::internal,
651 sym::linkonce,
652 sym::linkonce_odr,
653 sym::weak,
654 sym::weak_odr,
655 ],
656 );
657 return None;
658 }
659 };
660
661 Some(AttributeKind::Linkage(linkage, cx.attr_span))
662 }
663}