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