1use rustc_ast::token::{Delimiter, TokenKind};
2use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree};
3use rustc_ast::{
4 Attribute, DUMMY_NODE_ID, EiiDecl, EiiImpl, ItemKind, MetaItem, Mutability, Path, StmtKind,
5 Visibility, ast,
6};
7use rustc_ast_pretty::pprust::path_to_string;
8use rustc_expand::base::{Annotatable, ExtCtxt};
9use rustc_span::{ErrorGuaranteed, Ident, Span, kw, sym};
10use thin_vec::{ThinVec, thin_vec};
11
12use crate::diagnostics::{
13 EiiAttributeNotSupported, EiiBothDeclAndImpl, EiiExternTargetExpectedList,
14 EiiExternTargetExpectedMacro, EiiExternTargetExpectedUnsafe, EiiMacroExpectedMaxOneArgument,
15 EiiMultipleImplementations, EiiOnlyOnce, EiiSharedMacroInStatementPosition,
16 EiiSharedMacroTarget, EiiStaticArgumentRequired, EiiStaticDefaultApple, EiiStaticMutable,
17};
18
19pub(crate) fn eii(
39 ecx: &mut ExtCtxt<'_>,
40 span: Span,
41 meta_item: &ast::MetaItem,
42 item: Annotatable,
43) -> Vec<Annotatable> {
44 eii_(ecx, span, meta_item, item, false)
45}
46
47pub(crate) fn unsafe_eii(
48 ecx: &mut ExtCtxt<'_>,
49 span: Span,
50 meta_item: &ast::MetaItem,
51 item: Annotatable,
52) -> Vec<Annotatable> {
53 eii_(ecx, span, meta_item, item, true)
54}
55
56fn eii_(
57 ecx: &mut ExtCtxt<'_>,
58 eii_attr_span: Span,
59 meta_item: &ast::MetaItem,
60 orig_item: Annotatable,
61 impl_unsafe: bool,
62) -> Vec<Annotatable> {
63 let eii_attr_span = ecx.with_def_site_ctxt(eii_attr_span);
64
65 let item = if let Annotatable::Item(item) = orig_item {
66 item
67 } else if let Annotatable::Stmt(ref stmt) = orig_item
68 && let StmtKind::Item(ref item) = stmt.kind
69 && let ItemKind::Fn(ref f) = item.kind
70 {
71 ecx.dcx().emit_err(EiiSharedMacroInStatementPosition {
72 span: eii_attr_span.to(item.span),
73 name: path_to_string(&meta_item.path),
74 item_span: f.ident.span,
75 });
76 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[orig_item]))vec![orig_item];
77 } else {
78 ecx.dcx().emit_err(EiiSharedMacroTarget {
79 span: eii_attr_span,
80 name: path_to_string(&meta_item.path),
81 });
82 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[orig_item]))vec![orig_item];
83 };
84
85 let ast::Item { attrs, id: _, span: _, vis, kind, tokens: _ } = item.as_ref();
86 let (item_span, foreign_item_name) = match kind {
87 ItemKind::Fn(func) => (func.sig.span, func.ident),
88 ItemKind::Static(stat) => {
89 if let Some(expr) = &stat.expr
91 && ecx.sess.target.is_like_darwin
92 {
93 ecx.dcx().emit_err(EiiStaticDefaultApple {
94 span: expr.span,
95 name: path_to_string(&meta_item.path),
96 });
97 return ::alloc::vec::Vec::new()vec![];
98 }
99
100 if meta_item.is_word() {
102 ecx.dcx().emit_err(EiiStaticArgumentRequired {
103 span: eii_attr_span,
104 name: path_to_string(&meta_item.path),
105 });
106 return ::alloc::vec::Vec::new()vec![];
107 }
108
109 if stat.mutability == Mutability::Mut {
111 ecx.dcx().emit_err(EiiStaticMutable {
112 span: eii_attr_span,
113 name: path_to_string(&meta_item.path),
114 });
115 }
116
117 (item.span, stat.ident)
118 }
119 _ => {
120 ecx.dcx().emit_err(EiiSharedMacroTarget {
121 span: eii_attr_span,
122 name: path_to_string(&meta_item.path),
123 });
124 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[Annotatable::Item(item)]))vec![Annotatable::Item(item)];
125 }
126 };
127
128 match kind {
129 ItemKind::Fn(func) => {
130 if func.eii_impl.is_some() {
131 ecx.dcx().emit_err(EiiBothDeclAndImpl { span: eii_attr_span });
132 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[Annotatable::Item(item)]))vec![Annotatable::Item(item)];
133 }
134 }
135 ItemKind::Static(stat) => {
136 if stat.eii_impl.is_some() {
137 ecx.dcx().emit_err(EiiBothDeclAndImpl { span: eii_attr_span });
138 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[Annotatable::Item(item)]))vec![Annotatable::Item(item)];
139 }
140 }
141 _ => {
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("Target was checked earlier")));
}unreachable!("Target was checked earlier"),
142 };
143
144 let attrs = attrs.clone();
146 let vis = vis.clone();
147
148 let attrs_from_decl =
149 filter_attrs_for_multiple_eii_attr(ecx, attrs, eii_attr_span, &meta_item.path);
150 let (macro_attrs, foreign_item_attrs, default_func_attrs) =
151 split_attrs(ecx, item_span, attrs_from_decl);
152
153 let Ok(macro_name) = name_for_impl_macro(ecx, foreign_item_name, &meta_item) else {
154 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[Annotatable::Item(item)]))vec![Annotatable::Item(item)];
157 };
158
159 let mut module_items = Vec::new();
160
161 if let Some(default_impl) = generate_default_impl(
162 ecx,
163 kind,
164 impl_unsafe,
165 macro_name,
166 eii_attr_span,
167 item_span,
168 foreign_item_name,
169 default_func_attrs,
170 ) {
171 module_items.push(default_impl);
172 }
173
174 module_items.push(generate_foreign_item(
175 ecx,
176 eii_attr_span,
177 item_span,
178 kind,
179 vis,
180 foreign_item_attrs,
181 ));
182 module_items.push(generate_attribute_macro_to_implement(
183 ecx,
184 eii_attr_span,
185 macro_name,
186 foreign_item_name,
187 impl_unsafe,
188 macro_attrs,
189 ));
190
191 module_items.into_iter().map(Annotatable::Item).collect()
194}
195
196fn split_attrs(
197 ecx: &mut ExtCtxt<'_>,
198 span: Span,
199 attrs: ThinVec<Attribute>,
200) -> (ThinVec<Attribute>, ThinVec<Attribute>, ThinVec<Attribute>) {
201 let mut macro_attributes = ThinVec::new();
202 let mut foreign_item_attributes = ThinVec::new();
203 let mut default_attributes = ThinVec::new();
204
205 for attr in attrs {
206 match attr.name() {
207 Some(sym::inline) => default_attributes.push(attr),
209 Some(sym::lang) => foreign_item_attributes.push(attr),
212 Some(sym::deprecated) => {
214 foreign_item_attributes.push(attr.clone());
215 macro_attributes.push(attr);
216 }
217 Some(sym::stable) | Some(sym::unstable) => {
219 foreign_item_attributes.push(attr.clone());
220 macro_attributes.push(attr);
221 }
222 Some(sym::track_caller) => {
227 foreign_item_attributes.push(attr);
228 }
229 _ if attr.is_doc_comment() => {
233 foreign_item_attributes.push(attr.clone());
234 macro_attributes.push(attr);
235 }
236 Some(sym::eii) => {
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("should already be filtered out")));
}unreachable!("should already be filtered out"),
237 _ => {
238 ecx.dcx().emit_err(EiiAttributeNotSupported { span, attr_span: attr.span() });
239 }
240 }
241 }
242
243 (macro_attributes, foreign_item_attributes, default_attributes)
244}
245
246fn name_for_impl_macro(
250 ecx: &mut ExtCtxt<'_>,
251 item_ident: Ident,
252 meta_item: &MetaItem,
253) -> Result<Ident, ErrorGuaranteed> {
254 if meta_item.is_word() {
255 Ok(item_ident)
256 } else if let Some([first]) = meta_item.meta_item_list()
257 && let Some(m) = first.meta_item()
258 && m.path.segments.len() == 1
259 {
260 Ok(m.path.segments[0].ident)
261 } else {
262 Err(ecx.dcx().emit_err(EiiMacroExpectedMaxOneArgument {
263 span: meta_item.span,
264 name: path_to_string(&meta_item.path),
265 }))
266 }
267}
268
269fn filter_attrs_for_multiple_eii_attr(
271 ecx: &mut ExtCtxt<'_>,
272 attrs: ThinVec<Attribute>,
273 eii_attr_span: Span,
274 eii_attr_path: &Path,
275) -> ThinVec<Attribute> {
276 attrs
277 .into_iter()
278 .filter(|i| {
279 if i.has_name(sym::eii) {
280 ecx.dcx().emit_err(EiiOnlyOnce {
281 span: i.span,
282 first_span: eii_attr_span,
283 name: path_to_string(eii_attr_path),
284 });
285 false
286 } else {
287 true
288 }
289 })
290 .collect()
291}
292
293fn generate_default_impl(
294 ecx: &mut ExtCtxt<'_>,
295 item_kind: &ItemKind,
296 impl_unsafe: bool,
297 macro_name: Ident,
298 eii_attr_span: Span,
299 item_span: Span,
300 foreign_item_name: Ident,
301 attrs: ThinVec<Attribute>,
302) -> Option<Box<ast::Item>> {
303 match item_kind {
304 ItemKind::Fn(func) => {
305 if func.body.is_none() {
306 return None;
307 }
308 }
309 ItemKind::Static(stat) => {
310 if stat.expr.is_none() {
311 return None;
312 }
313 }
314 _ => {
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("Target was checked earlier")));
}unreachable!("Target was checked earlier"),
315 };
316
317 let eii_impl = Box::new(EiiImpl {
318 node_id: DUMMY_NODE_ID,
319 inner_span: macro_name.span,
320 eii_macro_path: ast::Path::from_ident(macro_name),
321 impl_safety: if impl_unsafe {
322 ast::Safety::Unsafe(eii_attr_span)
323 } else {
324 ast::Safety::Default
325 },
326 span: eii_attr_span,
327 is_default: true,
328 known_eii_macro_resolution: Some(ecx.path(
329 foreign_item_name.span,
330 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[Ident::from_str_and_span("self", foreign_item_name.span),
foreign_item_name]))vec![Ident::from_str_and_span("self", foreign_item_name.span), foreign_item_name],
333 )),
334 });
335
336 let mut item_kind = item_kind.clone();
337 match &mut item_kind {
338 ItemKind::Fn(func) => {
339 if !func.eii_impl.is_none() {
::core::panicking::panic("assertion failed: func.eii_impl.is_none()")
};assert!(func.eii_impl.is_none());
340 func.eii_impl = Some(eii_impl);
341 }
342 ItemKind::Static(stat) => {
343 if !stat.eii_impl.is_none() {
::core::panicking::panic("assertion failed: stat.eii_impl.is_none()")
};assert!(stat.eii_impl.is_none());
344 stat.eii_impl = Some(eii_impl);
345 }
346 _ => {
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("Target was checked earlier")));
}unreachable!("Target was checked earlier"),
347 };
348
349 let anon_mod = |span: Span, stmts: ThinVec<ast::Stmt>| {
350 let unit = ecx.ty(item_span, ast::TyKind::Tup(ThinVec::new()));
351 let underscore = Ident::new(kw::Underscore, item_span);
352 ecx.item_const(
353 span,
354 underscore,
355 unit,
356 Some(ecx.expr_block(ecx.block(span, stmts))),
357 ast::ConstItemKind::Body,
358 )
359 };
360
361 Some(anon_mod(
365 item_span,
366 {
let len = [()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(ecx.stmt_item(item_span, ecx.item(item_span, attrs, item_kind)));
vec
}thin_vec![ecx.stmt_item(item_span, ecx.item(item_span, attrs, item_kind))],
367 ))
368}
369
370fn generate_foreign_item(
376 ecx: &mut ExtCtxt<'_>,
377 eii_attr_span: Span,
378 item_span: Span,
379 item_kind: &ItemKind,
380 vis: Visibility,
381 attrs_from_decl: ThinVec<Attribute>,
382) -> Box<ast::Item> {
383 let mut foreign_item_attrs = attrs_from_decl;
384
385 foreign_item_attrs.push(ecx.attr_word(sym::rustc_eii_foreign_item, eii_attr_span));
388
389 let mut abi = Some(ast::StrLit {
392 symbol: sym::Rust,
393 suffix: None,
394 symbol_unescaped: sym::Rust,
395 style: ast::StrStyle::Cooked,
396 span: eii_attr_span,
397 });
398 let foreign_kind = match item_kind {
399 ItemKind::Fn(func) => generate_foreign_func(func.clone(), &mut abi),
400 ItemKind::Static(stat) => generate_foreign_static(stat.clone()),
401 _ => {
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("Target was checked earlier")));
}unreachable!("Target was checked earlier"),
402 };
403
404 ecx.item(
405 eii_attr_span,
406 ThinVec::new(),
407 ast::ItemKind::ForeignMod(ast::ForeignMod {
408 extern_span: eii_attr_span,
409 safety: ast::Safety::Unsafe(eii_attr_span),
410 abi,
411 items: From::from([Box::new(ast::ForeignItem {
412 attrs: foreign_item_attrs,
413 id: ast::DUMMY_NODE_ID,
414 span: item_span,
415 vis,
416 kind: foreign_kind,
417 tokens: None,
418 })]),
419 }),
420 )
421}
422
423fn generate_foreign_func(
424 mut func: Box<ast::Fn>,
425 abi: &mut Option<ast::StrLit>,
426) -> ast::ForeignItemKind {
427 match func.sig.header.ext {
428 ast::Extern::Explicit(lit, _) => *abi = Some(lit),
430 ast::Extern::Implicit(_) => *abi = None,
432 ast::Extern::None => {}
434 };
435
436 func.sig.header.ext = ast::Extern::None;
438 func.body = None;
439
440 if func.sig.header.safety == ast::Safety::Default {
442 func.sig.header.safety = ast::Safety::Safe(func.sig.span);
443 }
444
445 ast::ForeignItemKind::Fn(func)
446}
447
448fn generate_foreign_static(mut stat: Box<ast::StaticItem>) -> ast::ForeignItemKind {
449 if stat.safety == ast::Safety::Default {
450 stat.safety = ast::Safety::Safe(stat.ident.span);
451 }
452
453 stat.expr = None;
454
455 ast::ForeignItemKind::Static(stat)
456}
457
458fn generate_attribute_macro_to_implement(
469 ecx: &mut ExtCtxt<'_>,
470 span: Span,
471 macro_name: Ident,
472 foreign_item_name: Ident,
473 impl_unsafe: bool,
474 attrs_from_decl: ThinVec<Attribute>,
475) -> Box<ast::Item> {
476 let mut macro_attrs = attrs_from_decl;
477
478 macro_attrs.push(ecx.attr_name_value_str(sym::rustc_macro_transparency, sym::semiopaque, span));
480
481 macro_attrs.push(ecx.attr_nested_word(sym::rustc_builtin_macro, sym::eii_shared_macro, span));
483
484 Box::new(ast::Item {
486 attrs: macro_attrs,
487 id: ast::DUMMY_NODE_ID,
488 span,
489 vis: ast::Visibility { span, kind: ast::VisibilityKind::Public },
491 kind: ast::ItemKind::MacroDef(
492 macro_name,
494 ast::MacroDef {
495 body: Box::new(ast::DelimArgs {
497 dspan: DelimSpan::from_single(span),
498 delim: Delimiter::Brace,
499 tokens: TokenStream::from_iter([
500 TokenTree::Delimited(
501 DelimSpan::from_single(span),
502 DelimSpacing::new(Spacing::Alone, Spacing::Alone),
503 Delimiter::Parenthesis,
504 TokenStream::default(),
505 ),
506 TokenTree::token_alone(TokenKind::FatArrow, span),
507 TokenTree::Delimited(
508 DelimSpan::from_single(span),
509 DelimSpacing::new(Spacing::Alone, Spacing::Alone),
510 Delimiter::Brace,
511 TokenStream::default(),
512 ),
513 ]),
514 }),
515 macro_rules: false,
516 eii_declaration: Some(ast::EiiDecl {
518 foreign_item: ast::Path::from_ident(foreign_item_name),
519 impl_unsafe,
520 }),
521 },
522 ),
523 tokens: None,
524 })
525}
526
527pub(crate) fn eii_declaration(
528 ecx: &mut ExtCtxt<'_>,
529 span: Span,
530 meta_item: &ast::MetaItem,
531 mut item: Annotatable,
532) -> Vec<Annotatable> {
533 let i = if let Annotatable::Item(ref mut item) = item {
534 item
535 } else if let Annotatable::Stmt(ref mut stmt) = item
536 && let StmtKind::Item(ref mut item) = stmt.kind
537 {
538 item
539 } else {
540 ecx.dcx().emit_err(EiiExternTargetExpectedMacro { span });
541 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item];
542 };
543
544 let ItemKind::MacroDef(_, d) = &mut i.kind else {
545 ecx.dcx().emit_err(EiiExternTargetExpectedMacro { span });
546 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item];
547 };
548
549 let Some(list) = meta_item.meta_item_list() else {
550 ecx.dcx().emit_err(EiiExternTargetExpectedList { span: meta_item.span });
551 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item];
552 };
553
554 if list.len() > 2 {
555 ecx.dcx().emit_err(EiiExternTargetExpectedList { span: meta_item.span });
556 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item];
557 }
558
559 let Some(extern_item_path) = list.get(0).and_then(|i| i.meta_item()).map(|i| i.path.clone())
560 else {
561 ecx.dcx().emit_err(EiiExternTargetExpectedList { span: meta_item.span });
562 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item];
563 };
564
565 let impl_unsafe = if let Some(i) = list.get(1) {
566 if i.lit().and_then(|i| i.kind.str()).is_some_and(|i| i == kw::Unsafe) {
567 true
568 } else {
569 ecx.dcx().emit_err(EiiExternTargetExpectedUnsafe { span: i.span() });
570 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item];
571 }
572 } else {
573 false
574 };
575
576 d.eii_declaration = Some(EiiDecl { foreign_item: extern_item_path, impl_unsafe });
577
578 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item]
580}
581
582pub(crate) fn eii_shared_macro(
584 ecx: &mut ExtCtxt<'_>,
585 span: Span,
586 meta_item: &ast::MetaItem,
587 mut item: Annotatable,
588) -> Vec<Annotatable> {
589 let i = if let Annotatable::Item(ref mut item) = item {
590 item
591 } else if let Annotatable::Stmt(ref mut stmt) = item
592 && let StmtKind::Item(ref mut item) = stmt.kind
593 {
594 item
595 } else {
596 ecx.dcx().emit_err(EiiSharedMacroTarget { span, name: path_to_string(&meta_item.path) });
597 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item];
598 };
599
600 let eii_impl = match &mut i.kind {
601 ItemKind::Fn(func) => &mut func.eii_impl,
602 ItemKind::Static(stat) => &mut stat.eii_impl,
603 _ => {
604 ecx.dcx()
605 .emit_err(EiiSharedMacroTarget { span, name: path_to_string(&meta_item.path) });
606 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item];
607 }
608 };
609
610 let is_default = if meta_item.is_word() {
611 false
612 } else if let Some([first]) = meta_item.meta_item_list()
613 && let Some(m) = first.meta_item()
614 && m.path.segments.len() == 1
615 {
616 m.path.segments[0].ident.name == kw::Default
617 } else {
618 ecx.dcx().emit_err(EiiMacroExpectedMaxOneArgument {
619 span: meta_item.span,
620 name: path_to_string(&meta_item.path),
621 });
622 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item];
623 };
624
625 if eii_impl.is_some() {
626 ecx.dcx().emit_err(EiiMultipleImplementations { span });
627 }
628 *eii_impl = Some(Box::new(EiiImpl {
629 node_id: DUMMY_NODE_ID,
630 inner_span: meta_item.path.span,
631 eii_macro_path: meta_item.path.clone(),
632 impl_safety: meta_item.unsafety,
633 span,
634 is_default,
635 known_eii_macro_resolution: None,
636 }));
637
638 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item]
639}