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::errors::{
13 EiiExternTargetExpectedList, EiiExternTargetExpectedMacro, EiiExternTargetExpectedUnsafe,
14 EiiMacroExpectedMaxOneArgument, EiiOnlyOnce, EiiSharedMacroInStatementPosition,
15 EiiSharedMacroTarget, EiiStaticArgumentRequired, EiiStaticDefault,
16 EiiStaticMultipleImplementations, 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(stat_body) = &stat.expr {
91 ecx.dcx().emit_err(EiiStaticDefault {
92 span: stat_body.span,
93 name: path_to_string(&meta_item.path),
94 });
95 return ::alloc::vec::Vec::new()vec![];
96 }
97 if meta_item.is_word() {
99 ecx.dcx().emit_err(EiiStaticArgumentRequired {
100 span: eii_attr_span,
101 name: path_to_string(&meta_item.path),
102 });
103 return ::alloc::vec::Vec::new()vec![];
104 }
105
106 if stat.mutability == Mutability::Mut {
108 ecx.dcx().emit_err(EiiStaticMutable {
109 span: eii_attr_span,
110 name: path_to_string(&meta_item.path),
111 });
112 }
113
114 (item.span, stat.ident)
115 }
116 _ => {
117 ecx.dcx().emit_err(EiiSharedMacroTarget {
118 span: eii_attr_span,
119 name: path_to_string(&meta_item.path),
120 });
121 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)];
122 }
123 };
124
125 let attrs = attrs.clone();
127 let vis = vis.clone();
128
129 let attrs_from_decl =
130 filter_attrs_for_multiple_eii_attr(ecx, attrs, eii_attr_span, &meta_item.path);
131
132 let Ok(macro_name) = name_for_impl_macro(ecx, foreign_item_name, &meta_item) else {
133 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)];
136 };
137
138 let mut module_items = Vec::new();
139
140 if let ItemKind::Fn(func) = kind
141 && func.body.is_some()
142 {
143 module_items.push(generate_default_func_impl(
144 ecx,
145 &func,
146 impl_unsafe,
147 macro_name,
148 eii_attr_span,
149 item_span,
150 foreign_item_name,
151 ))
152 }
153
154 module_items.push(generate_foreign_item(
155 ecx,
156 eii_attr_span,
157 item_span,
158 kind,
159 vis,
160 &attrs_from_decl,
161 ));
162 module_items.push(generate_attribute_macro_to_implement(
163 ecx,
164 eii_attr_span,
165 macro_name,
166 foreign_item_name,
167 impl_unsafe,
168 &attrs_from_decl,
169 ));
170
171 module_items.into_iter().map(Annotatable::Item).collect()
174}
175
176fn name_for_impl_macro(
180 ecx: &mut ExtCtxt<'_>,
181 item_ident: Ident,
182 meta_item: &MetaItem,
183) -> Result<Ident, ErrorGuaranteed> {
184 if meta_item.is_word() {
185 Ok(item_ident)
186 } else if let Some([first]) = meta_item.meta_item_list()
187 && let Some(m) = first.meta_item()
188 && m.path.segments.len() == 1
189 {
190 Ok(m.path.segments[0].ident)
191 } else {
192 Err(ecx.dcx().emit_err(EiiMacroExpectedMaxOneArgument {
193 span: meta_item.span,
194 name: path_to_string(&meta_item.path),
195 }))
196 }
197}
198
199fn filter_attrs_for_multiple_eii_attr(
201 ecx: &mut ExtCtxt<'_>,
202 attrs: ThinVec<Attribute>,
203 eii_attr_span: Span,
204 eii_attr_path: &Path,
205) -> ThinVec<Attribute> {
206 attrs
207 .into_iter()
208 .filter(|i| {
209 if i.has_name(sym::eii) {
210 ecx.dcx().emit_err(EiiOnlyOnce {
211 span: i.span,
212 first_span: eii_attr_span,
213 name: path_to_string(eii_attr_path),
214 });
215 false
216 } else {
217 true
218 }
219 })
220 .collect()
221}
222
223fn generate_default_func_impl(
224 ecx: &mut ExtCtxt<'_>,
225 func: &ast::Fn,
226 impl_unsafe: bool,
227 macro_name: Ident,
228 eii_attr_span: Span,
229 item_span: Span,
230 foreign_item_name: Ident,
231) -> Box<ast::Item> {
232 let attrs = ThinVec::new();
234
235 let mut default_func = func.clone();
236 default_func.eii_impls.push(EiiImpl {
237 node_id: DUMMY_NODE_ID,
238 inner_span: macro_name.span,
239 eii_macro_path: ast::Path::from_ident(macro_name),
240 impl_safety: if impl_unsafe {
241 ast::Safety::Unsafe(eii_attr_span)
242 } else {
243 ast::Safety::Default
244 },
245 span: eii_attr_span,
246 is_default: true,
247 known_eii_macro_resolution: Some(ast::EiiDecl {
248 foreign_item: ecx.path(
249 foreign_item_name.span,
250 ::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],
253 ),
254 impl_unsafe,
255 }),
256 });
257
258 let anon_mod = |span: Span, stmts: ThinVec<ast::Stmt>| {
259 let unit = ecx.ty(item_span, ast::TyKind::Tup(ThinVec::new()));
260 let underscore = Ident::new(kw::Underscore, item_span);
261 ecx.item_const(
262 span,
263 underscore,
264 unit,
265 ast::ConstItemRhsKind::new_body(ecx.expr_block(ecx.block(span, stmts))),
266 )
267 };
268
269 anon_mod(
273 item_span,
274 {
let len = [()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(ecx.stmt_item(item_span,
ecx.item(item_span, attrs,
ItemKind::Fn(Box::new(default_func)))));
vec
}thin_vec![ecx.stmt_item(
275 item_span,
276 ecx.item(item_span, attrs, ItemKind::Fn(Box::new(default_func)))
277 ),],
278 )
279}
280
281fn generate_foreign_item(
287 ecx: &mut ExtCtxt<'_>,
288 eii_attr_span: Span,
289 item_span: Span,
290 item_kind: &ItemKind,
291 vis: Visibility,
292 attrs_from_decl: &[Attribute],
293) -> Box<ast::Item> {
294 let mut foreign_item_attrs = ThinVec::new();
295 foreign_item_attrs.extend_from_slice(attrs_from_decl);
296
297 foreign_item_attrs.push(ecx.attr_word(sym::rustc_eii_foreign_item, eii_attr_span));
300
301 let mut abi = Some(ast::StrLit {
304 symbol: sym::Rust,
305 suffix: None,
306 symbol_unescaped: sym::Rust,
307 style: ast::StrStyle::Cooked,
308 span: eii_attr_span,
309 });
310 let foreign_kind = match item_kind {
311 ItemKind::Fn(func) => generate_foreign_func(func.clone(), &mut abi),
312 ItemKind::Static(stat) => generate_foreign_static(stat.clone()),
313 _ => {
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("Target was checked earlier")));
}unreachable!("Target was checked earlier"),
314 };
315
316 ecx.item(
317 eii_attr_span,
318 ThinVec::new(),
319 ast::ItemKind::ForeignMod(ast::ForeignMod {
320 extern_span: eii_attr_span,
321 safety: ast::Safety::Unsafe(eii_attr_span),
322 abi,
323 items: From::from([Box::new(ast::ForeignItem {
324 attrs: foreign_item_attrs,
325 id: ast::DUMMY_NODE_ID,
326 span: item_span,
327 vis,
328 kind: foreign_kind,
329 tokens: None,
330 })]),
331 }),
332 )
333}
334
335fn generate_foreign_func(
336 mut func: Box<ast::Fn>,
337 abi: &mut Option<ast::StrLit>,
338) -> ast::ForeignItemKind {
339 match func.sig.header.ext {
340 ast::Extern::Explicit(lit, _) => *abi = Some(lit),
342 ast::Extern::Implicit(_) => *abi = None,
344 ast::Extern::None => {}
346 };
347
348 func.sig.header.ext = ast::Extern::None;
350 func.body = None;
351
352 if func.sig.header.safety == ast::Safety::Default {
354 func.sig.header.safety = ast::Safety::Safe(func.sig.span);
355 }
356
357 ast::ForeignItemKind::Fn(func)
358}
359
360fn generate_foreign_static(mut stat: Box<ast::StaticItem>) -> ast::ForeignItemKind {
361 if stat.safety == ast::Safety::Default {
362 stat.safety = ast::Safety::Safe(stat.ident.span);
363 }
364
365 ast::ForeignItemKind::Static(stat)
366}
367
368fn generate_attribute_macro_to_implement(
379 ecx: &mut ExtCtxt<'_>,
380 span: Span,
381 macro_name: Ident,
382 foreign_item_name: Ident,
383 impl_unsafe: bool,
384 attrs_from_decl: &[Attribute],
385) -> Box<ast::Item> {
386 let mut macro_attrs = ThinVec::new();
387
388 macro_attrs.extend_from_slice(attrs_from_decl);
391
392 macro_attrs.push(ecx.attr_name_value_str(sym::rustc_macro_transparency, sym::semiopaque, span));
394
395 macro_attrs.push(ecx.attr_nested_word(sym::rustc_builtin_macro, sym::eii_shared_macro, span));
397
398 Box::new(ast::Item {
400 attrs: macro_attrs,
401 id: ast::DUMMY_NODE_ID,
402 span,
403 vis: ast::Visibility { span, kind: ast::VisibilityKind::Public, tokens: None },
405 kind: ast::ItemKind::MacroDef(
406 macro_name,
408 ast::MacroDef {
409 body: Box::new(ast::DelimArgs {
411 dspan: DelimSpan::from_single(span),
412 delim: Delimiter::Brace,
413 tokens: TokenStream::from_iter([
414 TokenTree::Delimited(
415 DelimSpan::from_single(span),
416 DelimSpacing::new(Spacing::Alone, Spacing::Alone),
417 Delimiter::Parenthesis,
418 TokenStream::default(),
419 ),
420 TokenTree::token_alone(TokenKind::FatArrow, span),
421 TokenTree::Delimited(
422 DelimSpan::from_single(span),
423 DelimSpacing::new(Spacing::Alone, Spacing::Alone),
424 Delimiter::Brace,
425 TokenStream::default(),
426 ),
427 ]),
428 }),
429 macro_rules: false,
430 eii_declaration: Some(ast::EiiDecl {
432 foreign_item: ast::Path::from_ident(foreign_item_name),
433 impl_unsafe,
434 }),
435 },
436 ),
437 tokens: None,
438 })
439}
440
441pub(crate) fn eii_declaration(
442 ecx: &mut ExtCtxt<'_>,
443 span: Span,
444 meta_item: &ast::MetaItem,
445 mut item: Annotatable,
446) -> Vec<Annotatable> {
447 let i = if let Annotatable::Item(ref mut item) = item {
448 item
449 } else if let Annotatable::Stmt(ref mut stmt) = item
450 && let StmtKind::Item(ref mut item) = stmt.kind
451 {
452 item
453 } else {
454 ecx.dcx().emit_err(EiiExternTargetExpectedMacro { span });
455 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item];
456 };
457
458 let ItemKind::MacroDef(_, d) = &mut i.kind else {
459 ecx.dcx().emit_err(EiiExternTargetExpectedMacro { span });
460 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item];
461 };
462
463 let Some(list) = meta_item.meta_item_list() else {
464 ecx.dcx().emit_err(EiiExternTargetExpectedList { span: meta_item.span });
465 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item];
466 };
467
468 if list.len() > 2 {
469 ecx.dcx().emit_err(EiiExternTargetExpectedList { span: meta_item.span });
470 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item];
471 }
472
473 let Some(extern_item_path) = list.get(0).and_then(|i| i.meta_item()).map(|i| i.path.clone())
474 else {
475 ecx.dcx().emit_err(EiiExternTargetExpectedList { span: meta_item.span });
476 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item];
477 };
478
479 let impl_unsafe = if let Some(i) = list.get(1) {
480 if i.lit().and_then(|i| i.kind.str()).is_some_and(|i| i == kw::Unsafe) {
481 true
482 } else {
483 ecx.dcx().emit_err(EiiExternTargetExpectedUnsafe { span: i.span() });
484 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item];
485 }
486 } else {
487 false
488 };
489
490 d.eii_declaration = Some(EiiDecl { foreign_item: extern_item_path, impl_unsafe });
491
492 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item]
494}
495
496pub(crate) fn eii_shared_macro(
498 ecx: &mut ExtCtxt<'_>,
499 span: Span,
500 meta_item: &ast::MetaItem,
501 mut item: Annotatable,
502) -> Vec<Annotatable> {
503 let i = if let Annotatable::Item(ref mut item) = item {
504 item
505 } else if let Annotatable::Stmt(ref mut stmt) = item
506 && let StmtKind::Item(ref mut item) = stmt.kind
507 {
508 item
509 } else {
510 ecx.dcx().emit_err(EiiSharedMacroTarget { span, name: path_to_string(&meta_item.path) });
511 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item];
512 };
513
514 let eii_impls = match &mut i.kind {
515 ItemKind::Fn(func) => &mut func.eii_impls,
516 ItemKind::Static(stat) => {
517 if !stat.eii_impls.is_empty() {
518 ecx.dcx().emit_err(EiiStaticMultipleImplementations { span });
521 }
522 &mut stat.eii_impls
523 }
524 _ => {
525 ecx.dcx()
526 .emit_err(EiiSharedMacroTarget { span, name: path_to_string(&meta_item.path) });
527 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item];
528 }
529 };
530
531 let is_default = if meta_item.is_word() {
532 false
533 } else if let Some([first]) = meta_item.meta_item_list()
534 && let Some(m) = first.meta_item()
535 && m.path.segments.len() == 1
536 {
537 m.path.segments[0].ident.name == kw::Default
538 } else {
539 ecx.dcx().emit_err(EiiMacroExpectedMaxOneArgument {
540 span: meta_item.span,
541 name: path_to_string(&meta_item.path),
542 });
543 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item];
544 };
545
546 eii_impls.push(EiiImpl {
547 node_id: DUMMY_NODE_ID,
548 inner_span: meta_item.path.span,
549 eii_macro_path: meta_item.path.clone(),
550 impl_safety: meta_item.unsafety,
551 span,
552 is_default,
553 known_eii_macro_resolution: None,
554 });
555
556 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item]
557}