1use rustc_ast::token::{Delimiter, TokenKind};
2use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree};
3use rustc_ast::{
4 AttrKind, Attribute, DUMMY_NODE_ID, EiiDecl, EiiImpl, ItemKind, MetaItem, Mutability, Path,
5 StmtKind, SyntheticAttr, 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) = split_attrs(ecx, item_span, attrs_from_decl);
151
152 let Ok(macro_name) = name_for_impl_macro(ecx, foreign_item_name, meta_item) else {
153 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)];
156 };
157
158 let mut module_items = Vec::new();
159
160 if let Some(default_impl) = generate_default_impl(
161 ecx,
162 kind,
163 impl_unsafe,
164 macro_name,
165 eii_attr_span,
166 item_span,
167 foreign_item_name,
168 ) {
169 module_items.push(default_impl);
170 }
171
172 module_items.push(generate_foreign_item(
173 ecx,
174 eii_attr_span,
175 item_span,
176 kind,
177 vis,
178 foreign_item_attrs,
179 ));
180 module_items.push(generate_attribute_macro_to_implement(
181 ecx,
182 eii_attr_span,
183 macro_name,
184 foreign_item_name,
185 impl_unsafe,
186 macro_attrs,
187 ));
188
189 module_items.into_iter().map(Annotatable::Item).collect()
192}
193
194fn split_attrs(
195 ecx: &mut ExtCtxt<'_>,
196 span: Span,
197 attrs: ThinVec<Attribute>,
198) -> (ThinVec<Attribute>, ThinVec<Attribute>) {
199 let mut macro_attributes = ThinVec::new();
200 let mut foreign_item_attributes = ThinVec::new();
201
202 for attr in attrs {
203 match &attr.kind {
204 AttrKind::Synthetic(SyntheticAttr::CfgTrace(_) | SyntheticAttr::CfgAttrTrace(_)) => {
206 foreign_item_attributes.push(attr.clone());
207 macro_attributes.push(attr);
208 }
209 AttrKind::DocComment(_, _) => {
213 foreign_item_attributes.push(attr.clone());
214 macro_attributes.push(attr);
215 }
216 AttrKind::Normal(normal) => {
217 match normal.item.name() {
218 Some(sym::lang) => foreign_item_attributes.push(attr),
221 Some(sym::deprecated) => {
223 foreign_item_attributes.push(attr.clone());
224 macro_attributes.push(attr);
225 }
226 Some(sym::stable) | Some(sym::unstable) => {
228 foreign_item_attributes.push(attr.clone());
229 macro_attributes.push(attr);
230 }
231 Some(sym::track_caller) => {
236 foreign_item_attributes.push(attr);
237 }
238 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"),
239 _ => {
240 ecx.dcx()
241 .emit_err(EiiAttributeNotSupported { span, attr_span: attr.span() });
242 }
243 }
244 }
245 }
246 }
247
248 (macro_attributes, foreign_item_attributes)
249}
250
251fn name_for_impl_macro(
255 ecx: &mut ExtCtxt<'_>,
256 item_ident: Ident,
257 meta_item: &MetaItem,
258) -> Result<Ident, ErrorGuaranteed> {
259 if meta_item.is_word() {
260 Ok(item_ident)
261 } else if let Some([first]) = meta_item.meta_item_list()
262 && let Some(m) = first.meta_item()
263 && m.path.segments.len() == 1
264 {
265 Ok(m.path.segments[0].ident)
266 } else {
267 Err(ecx.dcx().emit_err(EiiMacroExpectedMaxOneArgument {
268 span: meta_item.span,
269 name: path_to_string(&meta_item.path),
270 }))
271 }
272}
273
274fn filter_attrs_for_multiple_eii_attr(
276 ecx: &mut ExtCtxt<'_>,
277 attrs: ThinVec<Attribute>,
278 eii_attr_span: Span,
279 eii_attr_path: &Path,
280) -> ThinVec<Attribute> {
281 attrs
282 .into_iter()
283 .filter(|i| {
284 if i.has_name(sym::eii) {
285 ecx.dcx().emit_err(EiiOnlyOnce {
286 span: i.span,
287 first_span: eii_attr_span,
288 name: path_to_string(eii_attr_path),
289 });
290 false
291 } else {
292 true
293 }
294 })
295 .collect()
296}
297
298fn generate_default_impl(
299 ecx: &mut ExtCtxt<'_>,
300 item_kind: &ItemKind,
301 impl_unsafe: bool,
302 macro_name: Ident,
303 eii_attr_span: Span,
304 item_span: Span,
305 foreign_item_name: Ident,
306) -> Option<Box<ast::Item>> {
307 let attrs = ThinVec::new();
308
309 match item_kind {
310 ItemKind::Fn(func) => {
311 func.body.as_ref()?;
312 }
313 ItemKind::Static(stat) => {
314 stat.expr.as_ref()?;
315 }
316 _ => {
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("Target was checked earlier")));
}unreachable!("Target was checked earlier"),
317 };
318
319 let eii_impl = Box::new(EiiImpl {
320 node_id: DUMMY_NODE_ID,
321 inner_span: macro_name.span,
322 eii_macro_path: ast::Path::from_ident(macro_name),
323 impl_safety: if impl_unsafe {
324 ast::Safety::Unsafe(eii_attr_span)
325 } else {
326 ast::Safety::Default
327 },
328 span: eii_attr_span,
329 is_default: true,
330 known_eii_macro_resolution: Some(ecx.path(
331 foreign_item_name.span,
332 ::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],
335 )),
336 });
337
338 let mut item_kind = item_kind.clone();
339 match &mut item_kind {
340 ItemKind::Fn(func) => {
341 if !func.eii_impl.is_none() {
::core::panicking::panic("assertion failed: func.eii_impl.is_none()")
};assert!(func.eii_impl.is_none());
342 func.eii_impl = Some(eii_impl);
343 }
344 ItemKind::Static(stat) => {
345 if !stat.eii_impl.is_none() {
::core::panicking::panic("assertion failed: stat.eii_impl.is_none()")
};assert!(stat.eii_impl.is_none());
346 stat.eii_impl = Some(eii_impl);
347 }
348 _ => {
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("Target was checked earlier")));
}unreachable!("Target was checked earlier"),
349 };
350
351 let anon_mod = |span: Span, stmts: ThinVec<ast::Stmt>| {
352 let unit = ecx.ty(item_span, ast::TyKind::Tup(ThinVec::new()));
353 let underscore = Ident::new(kw::Underscore, item_span);
354 ecx.item_const(span, underscore, unit, Some(ecx.expr_block(ecx.block(span, stmts))))
355 };
356
357 Some(anon_mod(
361 item_span,
362 {
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))],
363 ))
364}
365
366fn generate_foreign_item(
372 ecx: &mut ExtCtxt<'_>,
373 eii_attr_span: Span,
374 item_span: Span,
375 item_kind: &ItemKind,
376 vis: Visibility,
377 attrs_from_decl: ThinVec<Attribute>,
378) -> Box<ast::Item> {
379 let mut foreign_item_attrs = attrs_from_decl;
380
381 foreign_item_attrs.push(ecx.attr_word(sym::rustc_eii_foreign_item, eii_attr_span));
384
385 let mut abi = Some(ast::StrLit {
388 symbol: sym::Rust,
389 suffix: None,
390 symbol_unescaped: sym::Rust,
391 style: ast::StrStyle::Cooked,
392 span: eii_attr_span,
393 });
394 let foreign_kind = match item_kind {
395 ItemKind::Fn(func) => generate_foreign_func(func.clone(), &mut abi),
396 ItemKind::Static(stat) => generate_foreign_static(stat.clone()),
397 _ => {
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("Target was checked earlier")));
}unreachable!("Target was checked earlier"),
398 };
399
400 ecx.item(
401 eii_attr_span,
402 ThinVec::new(),
403 ast::ItemKind::ForeignMod(ast::ForeignMod {
404 extern_span: eii_attr_span,
405 safety: ast::Safety::Unsafe(eii_attr_span),
406 abi,
407 items: From::from([Box::new(ast::ForeignItem {
408 attrs: foreign_item_attrs,
409 id: ast::DUMMY_NODE_ID,
410 span: item_span,
411 vis,
412 kind: foreign_kind,
413 tokens: None,
414 })]),
415 }),
416 )
417}
418
419fn generate_foreign_func(
420 mut func: Box<ast::Fn>,
421 abi: &mut Option<ast::StrLit>,
422) -> ast::ForeignItemKind {
423 match func.sig.header.ext {
424 ast::Extern::Explicit(lit, _) => *abi = Some(lit),
426 ast::Extern::Implicit(_) => *abi = None,
428 ast::Extern::None => {}
430 };
431
432 func.sig.header.ext = ast::Extern::None;
434 func.body = None;
435
436 if func.sig.header.safety == ast::Safety::Default {
438 func.sig.header.safety = ast::Safety::Safe(func.sig.span);
439 }
440
441 ast::ForeignItemKind::Fn(func)
442}
443
444fn generate_foreign_static(mut stat: Box<ast::StaticItem>) -> ast::ForeignItemKind {
445 if stat.safety == ast::Safety::Default {
446 stat.safety = ast::Safety::Safe(stat.ident.span);
447 }
448
449 stat.expr = None;
450
451 ast::ForeignItemKind::Static(stat)
452}
453
454fn generate_attribute_macro_to_implement(
465 ecx: &mut ExtCtxt<'_>,
466 span: Span,
467 macro_name: Ident,
468 foreign_item_name: Ident,
469 impl_unsafe: bool,
470 attrs_from_decl: ThinVec<Attribute>,
471) -> Box<ast::Item> {
472 let mut macro_attrs = attrs_from_decl;
473
474 macro_attrs.push(ecx.attr_name_value_str(sym::rustc_macro_transparency, sym::semiopaque, span));
476
477 macro_attrs.push(ecx.attr_nested_word(sym::rustc_builtin_macro, sym::eii_shared_macro, span));
479
480 Box::new(ast::Item {
482 attrs: macro_attrs,
483 id: ast::DUMMY_NODE_ID,
484 span,
485 vis: ast::Visibility { span, kind: ast::VisibilityKind::Public },
487 kind: ast::ItemKind::MacroDef(
488 macro_name,
490 ast::MacroDef {
491 body: Box::new(ast::DelimArgs {
493 dspan: DelimSpan::from_single(span),
494 delim: Delimiter::Brace,
495 tokens: TokenStream::from_iter([
496 TokenTree::Delimited(
497 DelimSpan::from_single(span),
498 DelimSpacing::new(Spacing::Alone, Spacing::Alone),
499 Delimiter::Parenthesis,
500 TokenStream::default(),
501 ),
502 TokenTree::token_alone(TokenKind::FatArrow, span),
503 TokenTree::Delimited(
504 DelimSpan::from_single(span),
505 DelimSpacing::new(Spacing::Alone, Spacing::Alone),
506 Delimiter::Brace,
507 TokenStream::default(),
508 ),
509 ]),
510 }),
511 macro_rules: false,
512 eii_declaration: Some(ast::EiiDecl {
514 foreign_item: ast::Path::from_ident(foreign_item_name),
515 impl_unsafe,
516 }),
517 },
518 ),
519 tokens: None,
520 })
521}
522
523pub(crate) fn eii_declaration(
524 ecx: &mut ExtCtxt<'_>,
525 span: Span,
526 meta_item: &ast::MetaItem,
527 mut item: Annotatable,
528) -> Vec<Annotatable> {
529 let i = if let Annotatable::Item(ref mut item) = item {
530 item
531 } else if let Annotatable::Stmt(ref mut stmt) = item
532 && let StmtKind::Item(ref mut item) = stmt.kind
533 {
534 item
535 } else {
536 ecx.dcx().emit_err(EiiExternTargetExpectedMacro { span });
537 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item];
538 };
539
540 let ItemKind::MacroDef(_, d) = &mut i.kind else {
541 ecx.dcx().emit_err(EiiExternTargetExpectedMacro { span });
542 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item];
543 };
544
545 let Some(list) = meta_item.meta_item_list() else {
546 ecx.dcx().emit_err(EiiExternTargetExpectedList { span: meta_item.span });
547 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item];
548 };
549
550 if list.len() > 2 {
551 ecx.dcx().emit_err(EiiExternTargetExpectedList { span: meta_item.span });
552 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item];
553 }
554
555 let Some(extern_item_path) = list.get(0).and_then(|i| i.meta_item()).map(|i| i.path.clone())
556 else {
557 ecx.dcx().emit_err(EiiExternTargetExpectedList { span: meta_item.span });
558 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item];
559 };
560
561 let impl_unsafe = if let Some(i) = list.get(1) {
562 if i.lit().and_then(|i| i.kind.str()).is_some_and(|i| i == kw::Unsafe) {
563 true
564 } else {
565 ecx.dcx().emit_err(EiiExternTargetExpectedUnsafe { span: i.span() });
566 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item];
567 }
568 } else {
569 false
570 };
571
572 d.eii_declaration = Some(EiiDecl { foreign_item: extern_item_path, impl_unsafe });
573
574 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item]
576}
577
578pub(crate) fn eii_shared_macro(
580 ecx: &mut ExtCtxt<'_>,
581 span: Span,
582 meta_item: &ast::MetaItem,
583 mut item: Annotatable,
584) -> Vec<Annotatable> {
585 let i = if let Annotatable::Item(ref mut item) = item {
586 item
587 } else if let Annotatable::Stmt(ref mut stmt) = item
588 && let StmtKind::Item(ref mut item) = stmt.kind
589 {
590 item
591 } else {
592 ecx.dcx().emit_err(EiiSharedMacroTarget { span, name: path_to_string(&meta_item.path) });
593 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item];
594 };
595
596 let eii_impl = match &mut i.kind {
597 ItemKind::Fn(func) => &mut func.eii_impl,
598 ItemKind::Static(stat) => &mut stat.eii_impl,
599 _ => {
600 ecx.dcx()
601 .emit_err(EiiSharedMacroTarget { span, name: path_to_string(&meta_item.path) });
602 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item];
603 }
604 };
605
606 let is_default = if meta_item.is_word() {
607 false
608 } else if let Some([first]) = meta_item.meta_item_list()
609 && let Some(m) = first.meta_item()
610 && m.path.segments.len() == 1
611 {
612 m.path.segments[0].ident.name == kw::Default
613 } else {
614 ecx.dcx().emit_err(EiiMacroExpectedMaxOneArgument {
615 span: meta_item.span,
616 name: path_to_string(&meta_item.path),
617 });
618 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item];
619 };
620
621 if eii_impl.is_some() {
622 ecx.dcx().emit_err(EiiMultipleImplementations { span });
623 }
624 *eii_impl = Some(Box::new(EiiImpl {
625 node_id: DUMMY_NODE_ID,
626 inner_span: meta_item.path.span,
627 eii_macro_path: meta_item.path.clone(),
628 impl_safety: meta_item.unsafety,
629 span,
630 is_default,
631 known_eii_macro_resolution: None,
632 }));
633
634 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item]
635}