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, Path, Stmt, 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, EiiSharedMacroExpectedFunction,
15};
16
17pub(crate) fn eii(
37 ecx: &mut ExtCtxt<'_>,
38 span: Span,
39 meta_item: &ast::MetaItem,
40 item: Annotatable,
41) -> Vec<Annotatable> {
42 eii_(ecx, span, meta_item, item, false)
43}
44
45pub(crate) fn unsafe_eii(
46 ecx: &mut ExtCtxt<'_>,
47 span: Span,
48 meta_item: &ast::MetaItem,
49 item: Annotatable,
50) -> Vec<Annotatable> {
51 eii_(ecx, span, meta_item, item, true)
52}
53
54fn eii_(
55 ecx: &mut ExtCtxt<'_>,
56 eii_attr_span: Span,
57 meta_item: &ast::MetaItem,
58 item: Annotatable,
59 impl_unsafe: bool,
60) -> Vec<Annotatable> {
61 let eii_attr_span = ecx.with_def_site_ctxt(eii_attr_span);
62
63 let (item, wrap_item): (_, &dyn Fn(_) -> _) = if let Annotatable::Item(item) = item {
64 (item, &Annotatable::Item)
65 } else if let Annotatable::Stmt(ref stmt) = item
66 && let StmtKind::Item(ref item) = stmt.kind
67 {
68 (item.clone(), &|item| {
69 Annotatable::Stmt(Box::new(Stmt {
70 id: DUMMY_NODE_ID,
71 kind: StmtKind::Item(item),
72 span: eii_attr_span,
73 }))
74 })
75 } else {
76 ecx.dcx().emit_err(EiiSharedMacroExpectedFunction {
77 span: eii_attr_span,
78 name: path_to_string(&meta_item.path),
79 });
80 return vec![item];
81 };
82
83 let ast::Item { attrs, id: _, span: _, vis, kind: ItemKind::Fn(func), tokens: _ } =
84 item.as_ref()
85 else {
86 ecx.dcx().emit_err(EiiSharedMacroExpectedFunction {
87 span: eii_attr_span,
88 name: path_to_string(&meta_item.path),
89 });
90 return vec![wrap_item(item)];
91 };
92 let attrs = attrs.clone();
94 let func = (**func).clone();
95 let vis = vis.clone();
96
97 let attrs_from_decl =
98 filter_attrs_for_multiple_eii_attr(ecx, attrs, eii_attr_span, &meta_item.path);
99
100 let Ok(macro_name) = name_for_impl_macro(ecx, &func, &meta_item) else {
101 return vec![wrap_item(item)];
102 };
103
104 let item_span = func.sig.span;
106 let foreign_item_name = func.ident;
107
108 let mut return_items = Vec::new();
109
110 if func.body.is_some() {
111 return_items.push(generate_default_impl(
112 ecx,
113 &func,
114 impl_unsafe,
115 macro_name,
116 eii_attr_span,
117 item_span,
118 foreign_item_name,
119 ))
120 }
121
122 return_items.push(generate_foreign_item(
123 ecx,
124 eii_attr_span,
125 item_span,
126 func,
127 vis,
128 &attrs_from_decl,
129 ));
130 return_items.push(generate_attribute_macro_to_implement(
131 ecx,
132 eii_attr_span,
133 macro_name,
134 foreign_item_name,
135 impl_unsafe,
136 &attrs_from_decl,
137 ));
138
139 return_items.into_iter().map(wrap_item).collect()
140}
141
142fn name_for_impl_macro(
146 ecx: &mut ExtCtxt<'_>,
147 func: &ast::Fn,
148 meta_item: &MetaItem,
149) -> Result<Ident, ErrorGuaranteed> {
150 if meta_item.is_word() {
151 Ok(func.ident)
152 } else if let Some([first]) = meta_item.meta_item_list()
153 && let Some(m) = first.meta_item()
154 && m.path.segments.len() == 1
155 {
156 Ok(m.path.segments[0].ident)
157 } else {
158 Err(ecx.dcx().emit_err(EiiMacroExpectedMaxOneArgument {
159 span: meta_item.span,
160 name: path_to_string(&meta_item.path),
161 }))
162 }
163}
164
165fn filter_attrs_for_multiple_eii_attr(
167 ecx: &mut ExtCtxt<'_>,
168 attrs: ThinVec<Attribute>,
169 eii_attr_span: Span,
170 eii_attr_path: &Path,
171) -> ThinVec<Attribute> {
172 attrs
173 .into_iter()
174 .filter(|i| {
175 if i.has_name(sym::eii) {
176 ecx.dcx().emit_err(EiiOnlyOnce {
177 span: i.span,
178 first_span: eii_attr_span,
179 name: path_to_string(eii_attr_path),
180 });
181 false
182 } else {
183 true
184 }
185 })
186 .collect()
187}
188
189fn generate_default_impl(
190 ecx: &mut ExtCtxt<'_>,
191 func: &ast::Fn,
192 impl_unsafe: bool,
193 macro_name: Ident,
194 eii_attr_span: Span,
195 item_span: Span,
196 foreign_item_name: Ident,
197) -> Box<ast::Item> {
198 let attrs = ThinVec::new();
200
201 let mut default_func = func.clone();
202 default_func.eii_impls.push(EiiImpl {
203 node_id: DUMMY_NODE_ID,
204 inner_span: macro_name.span,
205 eii_macro_path: ast::Path::from_ident(macro_name),
206 impl_safety: if impl_unsafe {
207 ast::Safety::Unsafe(eii_attr_span)
208 } else {
209 ast::Safety::Default
210 },
211 span: eii_attr_span,
212 is_default: true,
213 known_eii_macro_resolution: Some(ast::EiiDecl {
214 foreign_item: ecx.path(
215 foreign_item_name.span,
216 vec![Ident::from_str_and_span("super", foreign_item_name.span), foreign_item_name],
218 ),
219 impl_unsafe,
220 }),
221 });
222
223 let item_mod = |span: Span, name: Ident, items: ThinVec<Box<ast::Item>>| {
224 ecx.item(
225 item_span,
226 ThinVec::new(),
227 ItemKind::Mod(
228 ast::Safety::Default,
229 name,
230 ast::ModKind::Loaded(
231 items,
232 ast::Inline::Yes,
233 ast::ModSpans { inner_span: span, inject_use_span: span },
234 ),
235 ),
236 )
237 };
238
239 let anon_mod = |span: Span, stmts: ThinVec<ast::Stmt>| {
240 let unit = ecx.ty(item_span, ast::TyKind::Tup(ThinVec::new()));
241 let underscore = Ident::new(kw::Underscore, item_span);
242 ecx.item_const(
243 span,
244 underscore,
245 unit,
246 ast::ConstItemRhs::Body(ecx.expr_block(ecx.block(span, stmts))),
247 )
248 };
249
250 anon_mod(
257 item_span,
258 thin_vec![ecx.stmt_item(
259 item_span,
260 item_mod(
261 item_span,
262 Ident::from_str_and_span("dflt", item_span),
263 thin_vec![
264 ecx.item(
265 item_span,
266 thin_vec![ecx.attr_nested_word(sym::allow, sym::unused_imports, item_span)],
267 ItemKind::Use(ast::UseTree {
268 prefix: ast::Path::from_ident(Ident::from_str_and_span(
269 "super", item_span,
270 )),
271 kind: ast::UseTreeKind::Glob,
272 span: item_span,
273 })
274 ),
275 ecx.item(item_span, attrs, ItemKind::Fn(Box::new(default_func)))
276 ]
277 )
278 ),],
279 )
280}
281
282fn generate_foreign_item(
288 ecx: &mut ExtCtxt<'_>,
289 eii_attr_span: Span,
290 item_span: Span,
291 mut func: ast::Fn,
292 vis: Visibility,
293 attrs_from_decl: &[Attribute],
294) -> Box<ast::Item> {
295 let mut foreign_item_attrs = ThinVec::new();
296 foreign_item_attrs.extend_from_slice(attrs_from_decl);
297
298 foreign_item_attrs.push(ecx.attr_word(sym::rustc_eii_foreign_item, eii_attr_span));
301
302 let abi = match func.sig.header.ext {
303 ast::Extern::Explicit(lit, _) => Some(lit),
305 ast::Extern::Implicit(_) => None,
307 ast::Extern::None => Some(ast::StrLit {
309 symbol: sym::Rust,
310 suffix: None,
311 symbol_unescaped: sym::Rust,
312 style: ast::StrStyle::Cooked,
313 span: eii_attr_span,
314 }),
315 };
316
317 func.sig.header.ext = ast::Extern::None;
319 func.body = None;
320
321 if func.sig.header.safety == ast::Safety::Default {
323 func.sig.header.safety = ast::Safety::Safe(func.sig.span);
324 }
325
326 ecx.item(
327 eii_attr_span,
328 ThinVec::new(),
329 ast::ItemKind::ForeignMod(ast::ForeignMod {
330 extern_span: eii_attr_span,
331 safety: ast::Safety::Unsafe(eii_attr_span),
332 abi,
333 items: From::from([Box::new(ast::ForeignItem {
334 attrs: foreign_item_attrs,
335 id: ast::DUMMY_NODE_ID,
336 span: item_span,
337 vis,
338 kind: ast::ForeignItemKind::Fn(Box::new(func.clone())),
339 tokens: None,
340 })]),
341 }),
342 )
343}
344
345fn generate_attribute_macro_to_implement(
356 ecx: &mut ExtCtxt<'_>,
357 span: Span,
358 macro_name: Ident,
359 foreign_item_name: Ident,
360 impl_unsafe: bool,
361 attrs_from_decl: &[Attribute],
362) -> Box<ast::Item> {
363 let mut macro_attrs = ThinVec::new();
364
365 macro_attrs.extend_from_slice(attrs_from_decl);
368
369 macro_attrs.push(ecx.attr_nested_word(sym::rustc_builtin_macro, sym::eii_shared_macro, span));
371
372 Box::new(ast::Item {
374 attrs: macro_attrs,
375 id: ast::DUMMY_NODE_ID,
376 span,
377 vis: ast::Visibility { span, kind: ast::VisibilityKind::Public, tokens: None },
379 kind: ast::ItemKind::MacroDef(
380 macro_name,
382 ast::MacroDef {
383 body: Box::new(ast::DelimArgs {
385 dspan: DelimSpan::from_single(span),
386 delim: Delimiter::Brace,
387 tokens: TokenStream::from_iter([
388 TokenTree::Delimited(
389 DelimSpan::from_single(span),
390 DelimSpacing::new(Spacing::Alone, Spacing::Alone),
391 Delimiter::Parenthesis,
392 TokenStream::default(),
393 ),
394 TokenTree::token_alone(TokenKind::FatArrow, span),
395 TokenTree::Delimited(
396 DelimSpan::from_single(span),
397 DelimSpacing::new(Spacing::Alone, Spacing::Alone),
398 Delimiter::Brace,
399 TokenStream::default(),
400 ),
401 ]),
402 }),
403 macro_rules: false,
404 eii_declaration: Some(ast::EiiDecl {
406 foreign_item: ast::Path::from_ident(foreign_item_name),
407 impl_unsafe,
408 }),
409 },
410 ),
411 tokens: None,
412 })
413}
414
415pub(crate) fn eii_declaration(
416 ecx: &mut ExtCtxt<'_>,
417 span: Span,
418 meta_item: &ast::MetaItem,
419 mut item: Annotatable,
420) -> Vec<Annotatable> {
421 let i = if let Annotatable::Item(ref mut item) = item {
422 item
423 } else if let Annotatable::Stmt(ref mut stmt) = item
424 && let StmtKind::Item(ref mut item) = stmt.kind
425 {
426 item
427 } else {
428 ecx.dcx().emit_err(EiiExternTargetExpectedMacro { span });
429 return vec![item];
430 };
431
432 let ItemKind::MacroDef(_, d) = &mut i.kind else {
433 ecx.dcx().emit_err(EiiExternTargetExpectedMacro { span });
434 return vec![item];
435 };
436
437 let Some(list) = meta_item.meta_item_list() else {
438 ecx.dcx().emit_err(EiiExternTargetExpectedList { span: meta_item.span });
439 return vec![item];
440 };
441
442 if list.len() > 2 {
443 ecx.dcx().emit_err(EiiExternTargetExpectedList { span: meta_item.span });
444 return vec![item];
445 }
446
447 let Some(extern_item_path) = list.get(0).and_then(|i| i.meta_item()).map(|i| i.path.clone())
448 else {
449 ecx.dcx().emit_err(EiiExternTargetExpectedList { span: meta_item.span });
450 return vec![item];
451 };
452
453 let impl_unsafe = if let Some(i) = list.get(1) {
454 if i.lit().and_then(|i| i.kind.str()).is_some_and(|i| i == kw::Unsafe) {
455 true
456 } else {
457 ecx.dcx().emit_err(EiiExternTargetExpectedUnsafe { span: i.span() });
458 return vec![item];
459 }
460 } else {
461 false
462 };
463
464 d.eii_declaration = Some(EiiDecl { foreign_item: extern_item_path, impl_unsafe });
465
466 vec![item]
468}
469
470pub(crate) fn eii_shared_macro(
472 ecx: &mut ExtCtxt<'_>,
473 span: Span,
474 meta_item: &ast::MetaItem,
475 mut item: Annotatable,
476) -> Vec<Annotatable> {
477 let i = if let Annotatable::Item(ref mut item) = item {
478 item
479 } else if let Annotatable::Stmt(ref mut stmt) = item
480 && let StmtKind::Item(ref mut item) = stmt.kind
481 {
482 item
483 } else {
484 ecx.dcx().emit_err(EiiSharedMacroExpectedFunction {
485 span,
486 name: path_to_string(&meta_item.path),
487 });
488 return vec![item];
489 };
490
491 let ItemKind::Fn(f) = &mut i.kind else {
492 ecx.dcx().emit_err(EiiSharedMacroExpectedFunction {
493 span,
494 name: path_to_string(&meta_item.path),
495 });
496 return vec![item];
497 };
498
499 let is_default = if meta_item.is_word() {
500 false
501 } else if let Some([first]) = meta_item.meta_item_list()
502 && let Some(m) = first.meta_item()
503 && m.path.segments.len() == 1
504 {
505 m.path.segments[0].ident.name == kw::Default
506 } else {
507 ecx.dcx().emit_err(EiiMacroExpectedMaxOneArgument {
508 span: meta_item.span,
509 name: path_to_string(&meta_item.path),
510 });
511 return vec![item];
512 };
513
514 f.eii_impls.push(EiiImpl {
515 node_id: DUMMY_NODE_ID,
516 inner_span: meta_item.path.span,
517 eii_macro_path: meta_item.path.clone(),
518 impl_safety: meta_item.unsafety,
519 span,
520 is_default,
521 known_eii_macro_resolution: None,
522 });
523
524 vec![item]
525}