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