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