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, EiiExternTargetExpectedList, EiiExternTargetExpectedMacro,
14 EiiExternTargetExpectedUnsafe, EiiMacroExpectedMaxOneArgument, EiiOnlyOnce,
15 EiiSharedMacroInStatementPosition, EiiSharedMacroTarget, EiiStaticArgumentRequired,
16 EiiStaticDefault, 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 let (macro_attrs, foreign_item_attrs, default_func_attrs) =
132 split_attrs(ecx, item_span, attrs_from_decl);
133
134 let Ok(macro_name) = name_for_impl_macro(ecx, foreign_item_name, &meta_item) else {
135 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)];
138 };
139
140 let mut module_items = Vec::new();
141
142 if let ItemKind::Fn(func) = kind
143 && func.body.is_some()
144 {
145 module_items.push(generate_default_func_impl(
146 ecx,
147 &func,
148 impl_unsafe,
149 macro_name,
150 eii_attr_span,
151 item_span,
152 foreign_item_name,
153 default_func_attrs,
154 ))
155 }
156
157 module_items.push(generate_foreign_item(
158 ecx,
159 eii_attr_span,
160 item_span,
161 kind,
162 vis,
163 foreign_item_attrs,
164 ));
165 module_items.push(generate_attribute_macro_to_implement(
166 ecx,
167 eii_attr_span,
168 macro_name,
169 foreign_item_name,
170 impl_unsafe,
171 macro_attrs,
172 ));
173
174 module_items.into_iter().map(Annotatable::Item).collect()
177}
178
179fn split_attrs(
180 ecx: &mut ExtCtxt<'_>,
181 span: Span,
182 attrs: ThinVec<Attribute>,
183) -> (ThinVec<Attribute>, ThinVec<Attribute>, ThinVec<Attribute>) {
184 let mut macro_attributes = ThinVec::new();
185 let mut foreign_item_attributes = ThinVec::new();
186 let mut default_attributes = ThinVec::new();
187
188 for attr in attrs {
189 match attr.name() {
190 Some(sym::inline) => default_attributes.push(attr),
192 Some(sym::lang) => foreign_item_attributes.push(attr),
195 Some(sym::deprecated) => {
197 foreign_item_attributes.push(attr.clone());
198 macro_attributes.push(attr);
199 }
200 Some(sym::stable) | Some(sym::unstable) => {
202 foreign_item_attributes.push(attr.clone());
203 macro_attributes.push(attr);
204 }
205 _ if attr.is_doc_comment() => {
209 foreign_item_attributes.push(attr.clone());
210 macro_attributes.push(attr);
211 }
212 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"),
213 _ => {
214 ecx.dcx().emit_err(EiiAttributeNotSupported { span, attr_span: attr.span() });
215 }
216 }
217 }
218
219 (macro_attributes, foreign_item_attributes, default_attributes)
220}
221
222fn name_for_impl_macro(
226 ecx: &mut ExtCtxt<'_>,
227 item_ident: Ident,
228 meta_item: &MetaItem,
229) -> Result<Ident, ErrorGuaranteed> {
230 if meta_item.is_word() {
231 Ok(item_ident)
232 } else if let Some([first]) = meta_item.meta_item_list()
233 && let Some(m) = first.meta_item()
234 && m.path.segments.len() == 1
235 {
236 Ok(m.path.segments[0].ident)
237 } else {
238 Err(ecx.dcx().emit_err(EiiMacroExpectedMaxOneArgument {
239 span: meta_item.span,
240 name: path_to_string(&meta_item.path),
241 }))
242 }
243}
244
245fn filter_attrs_for_multiple_eii_attr(
247 ecx: &mut ExtCtxt<'_>,
248 attrs: ThinVec<Attribute>,
249 eii_attr_span: Span,
250 eii_attr_path: &Path,
251) -> ThinVec<Attribute> {
252 attrs
253 .into_iter()
254 .filter(|i| {
255 if i.has_name(sym::eii) {
256 ecx.dcx().emit_err(EiiOnlyOnce {
257 span: i.span,
258 first_span: eii_attr_span,
259 name: path_to_string(eii_attr_path),
260 });
261 false
262 } else {
263 true
264 }
265 })
266 .collect()
267}
268
269fn generate_default_func_impl(
270 ecx: &mut ExtCtxt<'_>,
271 func: &ast::Fn,
272 impl_unsafe: bool,
273 macro_name: Ident,
274 eii_attr_span: Span,
275 item_span: Span,
276 foreign_item_name: Ident,
277 attrs: ThinVec<Attribute>,
278) -> Box<ast::Item> {
279 let mut default_func = func.clone();
280 default_func.eii_impls.push(EiiImpl {
281 node_id: DUMMY_NODE_ID,
282 inner_span: macro_name.span,
283 eii_macro_path: ast::Path::from_ident(macro_name),
284 impl_safety: if impl_unsafe {
285 ast::Safety::Unsafe(eii_attr_span)
286 } else {
287 ast::Safety::Default
288 },
289 span: eii_attr_span,
290 is_default: true,
291 known_eii_macro_resolution: Some(ast::EiiDecl {
292 foreign_item: ecx.path(
293 foreign_item_name.span,
294 ::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],
297 ),
298 impl_unsafe,
299 }),
300 });
301
302 let anon_mod = |span: Span, stmts: ThinVec<ast::Stmt>| {
303 let unit = ecx.ty(item_span, ast::TyKind::Tup(ThinVec::new()));
304 let underscore = Ident::new(kw::Underscore, item_span);
305 ecx.item_const(
306 span,
307 underscore,
308 unit,
309 ast::ConstItemRhsKind::new_body(ecx.expr_block(ecx.block(span, stmts))),
310 )
311 };
312
313 anon_mod(
317 item_span,
318 {
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(
319 item_span,
320 ecx.item(item_span, attrs, ItemKind::Fn(Box::new(default_func)))
321 ),],
322 )
323}
324
325fn generate_foreign_item(
331 ecx: &mut ExtCtxt<'_>,
332 eii_attr_span: Span,
333 item_span: Span,
334 item_kind: &ItemKind,
335 vis: Visibility,
336 attrs_from_decl: ThinVec<Attribute>,
337) -> Box<ast::Item> {
338 let mut foreign_item_attrs = attrs_from_decl;
339
340 foreign_item_attrs.push(ecx.attr_word(sym::rustc_eii_foreign_item, eii_attr_span));
343
344 let mut abi = Some(ast::StrLit {
347 symbol: sym::Rust,
348 suffix: None,
349 symbol_unescaped: sym::Rust,
350 style: ast::StrStyle::Cooked,
351 span: eii_attr_span,
352 });
353 let foreign_kind = match item_kind {
354 ItemKind::Fn(func) => generate_foreign_func(func.clone(), &mut abi),
355 ItemKind::Static(stat) => generate_foreign_static(stat.clone()),
356 _ => {
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("Target was checked earlier")));
}unreachable!("Target was checked earlier"),
357 };
358
359 ecx.item(
360 eii_attr_span,
361 ThinVec::new(),
362 ast::ItemKind::ForeignMod(ast::ForeignMod {
363 extern_span: eii_attr_span,
364 safety: ast::Safety::Unsafe(eii_attr_span),
365 abi,
366 items: From::from([Box::new(ast::ForeignItem {
367 attrs: foreign_item_attrs,
368 id: ast::DUMMY_NODE_ID,
369 span: item_span,
370 vis,
371 kind: foreign_kind,
372 tokens: None,
373 })]),
374 }),
375 )
376}
377
378fn generate_foreign_func(
379 mut func: Box<ast::Fn>,
380 abi: &mut Option<ast::StrLit>,
381) -> ast::ForeignItemKind {
382 match func.sig.header.ext {
383 ast::Extern::Explicit(lit, _) => *abi = Some(lit),
385 ast::Extern::Implicit(_) => *abi = None,
387 ast::Extern::None => {}
389 };
390
391 func.sig.header.ext = ast::Extern::None;
393 func.body = None;
394
395 if func.sig.header.safety == ast::Safety::Default {
397 func.sig.header.safety = ast::Safety::Safe(func.sig.span);
398 }
399
400 ast::ForeignItemKind::Fn(func)
401}
402
403fn generate_foreign_static(mut stat: Box<ast::StaticItem>) -> ast::ForeignItemKind {
404 if stat.safety == ast::Safety::Default {
405 stat.safety = ast::Safety::Safe(stat.ident.span);
406 }
407
408 ast::ForeignItemKind::Static(stat)
409}
410
411fn generate_attribute_macro_to_implement(
422 ecx: &mut ExtCtxt<'_>,
423 span: Span,
424 macro_name: Ident,
425 foreign_item_name: Ident,
426 impl_unsafe: bool,
427 attrs_from_decl: ThinVec<Attribute>,
428) -> Box<ast::Item> {
429 let mut macro_attrs = attrs_from_decl;
430
431 macro_attrs.push(ecx.attr_name_value_str(sym::rustc_macro_transparency, sym::semiopaque, span));
433
434 macro_attrs.push(ecx.attr_nested_word(sym::rustc_builtin_macro, sym::eii_shared_macro, span));
436
437 Box::new(ast::Item {
439 attrs: macro_attrs,
440 id: ast::DUMMY_NODE_ID,
441 span,
442 vis: ast::Visibility { span, kind: ast::VisibilityKind::Public, tokens: None },
444 kind: ast::ItemKind::MacroDef(
445 macro_name,
447 ast::MacroDef {
448 body: Box::new(ast::DelimArgs {
450 dspan: DelimSpan::from_single(span),
451 delim: Delimiter::Brace,
452 tokens: TokenStream::from_iter([
453 TokenTree::Delimited(
454 DelimSpan::from_single(span),
455 DelimSpacing::new(Spacing::Alone, Spacing::Alone),
456 Delimiter::Parenthesis,
457 TokenStream::default(),
458 ),
459 TokenTree::token_alone(TokenKind::FatArrow, span),
460 TokenTree::Delimited(
461 DelimSpan::from_single(span),
462 DelimSpacing::new(Spacing::Alone, Spacing::Alone),
463 Delimiter::Brace,
464 TokenStream::default(),
465 ),
466 ]),
467 }),
468 macro_rules: false,
469 eii_declaration: Some(ast::EiiDecl {
471 foreign_item: ast::Path::from_ident(foreign_item_name),
472 impl_unsafe,
473 }),
474 },
475 ),
476 tokens: None,
477 })
478}
479
480pub(crate) fn eii_declaration(
481 ecx: &mut ExtCtxt<'_>,
482 span: Span,
483 meta_item: &ast::MetaItem,
484 mut item: Annotatable,
485) -> Vec<Annotatable> {
486 let i = if let Annotatable::Item(ref mut item) = item {
487 item
488 } else if let Annotatable::Stmt(ref mut stmt) = item
489 && let StmtKind::Item(ref mut item) = stmt.kind
490 {
491 item
492 } else {
493 ecx.dcx().emit_err(EiiExternTargetExpectedMacro { span });
494 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item];
495 };
496
497 let ItemKind::MacroDef(_, d) = &mut i.kind else {
498 ecx.dcx().emit_err(EiiExternTargetExpectedMacro { span });
499 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item];
500 };
501
502 let Some(list) = meta_item.meta_item_list() else {
503 ecx.dcx().emit_err(EiiExternTargetExpectedList { span: meta_item.span });
504 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item];
505 };
506
507 if list.len() > 2 {
508 ecx.dcx().emit_err(EiiExternTargetExpectedList { span: meta_item.span });
509 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item];
510 }
511
512 let Some(extern_item_path) = list.get(0).and_then(|i| i.meta_item()).map(|i| i.path.clone())
513 else {
514 ecx.dcx().emit_err(EiiExternTargetExpectedList { span: meta_item.span });
515 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item];
516 };
517
518 let impl_unsafe = if let Some(i) = list.get(1) {
519 if i.lit().and_then(|i| i.kind.str()).is_some_and(|i| i == kw::Unsafe) {
520 true
521 } else {
522 ecx.dcx().emit_err(EiiExternTargetExpectedUnsafe { span: i.span() });
523 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item];
524 }
525 } else {
526 false
527 };
528
529 d.eii_declaration = Some(EiiDecl { foreign_item: extern_item_path, impl_unsafe });
530
531 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item]
533}
534
535pub(crate) fn eii_shared_macro(
537 ecx: &mut ExtCtxt<'_>,
538 span: Span,
539 meta_item: &ast::MetaItem,
540 mut item: Annotatable,
541) -> Vec<Annotatable> {
542 let i = if let Annotatable::Item(ref mut item) = item {
543 item
544 } else if let Annotatable::Stmt(ref mut stmt) = item
545 && let StmtKind::Item(ref mut item) = stmt.kind
546 {
547 item
548 } else {
549 ecx.dcx().emit_err(EiiSharedMacroTarget { span, name: path_to_string(&meta_item.path) });
550 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item];
551 };
552
553 let eii_impls = match &mut i.kind {
554 ItemKind::Fn(func) => &mut func.eii_impls,
555 ItemKind::Static(stat) => {
556 if !stat.eii_impls.is_empty() {
557 ecx.dcx().emit_err(EiiStaticMultipleImplementations { span });
560 }
561 &mut stat.eii_impls
562 }
563 _ => {
564 ecx.dcx()
565 .emit_err(EiiSharedMacroTarget { span, name: path_to_string(&meta_item.path) });
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 };
569
570 let is_default = if meta_item.is_word() {
571 false
572 } else if let Some([first]) = meta_item.meta_item_list()
573 && let Some(m) = first.meta_item()
574 && m.path.segments.len() == 1
575 {
576 m.path.segments[0].ident.name == kw::Default
577 } else {
578 ecx.dcx().emit_err(EiiMacroExpectedMaxOneArgument {
579 span: meta_item.span,
580 name: path_to_string(&meta_item.path),
581 });
582 return ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item];
583 };
584
585 eii_impls.push(EiiImpl {
586 node_id: DUMMY_NODE_ID,
587 inner_span: meta_item.path.span,
588 eii_macro_path: meta_item.path.clone(),
589 impl_safety: meta_item.unsafety,
590 span,
591 is_default,
592 known_eii_macro_resolution: None,
593 });
594
595 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[item]))vec![item]
596}