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, StmtKind, Visibility, ast,
5};
6use rustc_ast_pretty::pprust::path_to_string;
7use rustc_expand::base::{Annotatable, ExtCtxt};
8use rustc_span::{ErrorGuaranteed, Ident, Span, kw, sym};
9use thin_vec::{ThinVec, thin_vec};
10
11use crate::errors::{
12 EiiExternTargetExpectedList, EiiExternTargetExpectedMacro, EiiExternTargetExpectedUnsafe,
13 EiiMacroExpectedMaxOneArgument, EiiOnlyOnce, EiiSharedMacroExpectedFunction,
14 EiiSharedMacroInStatementPosition,
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 orig_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 = if let Annotatable::Item(item) = orig_item {
64 item
65 } else if let Annotatable::Stmt(ref stmt) = orig_item
66 && let StmtKind::Item(ref item) = stmt.kind
67 && let ItemKind::Fn(ref f) = item.kind
68 {
69 ecx.dcx().emit_err(EiiSharedMacroInStatementPosition {
70 span: eii_attr_span.to(item.span),
71 name: path_to_string(&meta_item.path),
72 item_span: f.ident.span,
73 });
74 return <[_]>::into_vec(::alloc::boxed::box_new([orig_item]))vec![orig_item];
75 } else {
76 ecx.dcx().emit_err(EiiSharedMacroExpectedFunction {
77 span: eii_attr_span,
78 name: path_to_string(&meta_item.path),
79 });
80 return <[_]>::into_vec(::alloc::boxed::box_new([orig_item]))vec![orig_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 <[_]>::into_vec(::alloc::boxed::box_new([Annotatable::Item(item)]))vec![Annotatable::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 <[_]>::into_vec(::alloc::boxed::box_new([Annotatable::Item(item)]))vec![Annotatable::Item(item)];
104 };
105
106 let item_span = func.sig.span;
108 let foreign_item_name = func.ident;
109
110 let mut module_items = Vec::new();
111
112 if func.body.is_some() {
113 module_items.push(generate_default_impl(
114 ecx,
115 &func,
116 impl_unsafe,
117 macro_name,
118 eii_attr_span,
119 item_span,
120 foreign_item_name,
121 ))
122 }
123
124 module_items.push(generate_foreign_item(
125 ecx,
126 eii_attr_span,
127 item_span,
128 func,
129 vis,
130 &attrs_from_decl,
131 ));
132 module_items.push(generate_attribute_macro_to_implement(
133 ecx,
134 eii_attr_span,
135 macro_name,
136 foreign_item_name,
137 impl_unsafe,
138 &attrs_from_decl,
139 ));
140
141 module_items.into_iter().map(Annotatable::Item).collect()
144}
145
146fn name_for_impl_macro(
150 ecx: &mut ExtCtxt<'_>,
151 func: &ast::Fn,
152 meta_item: &MetaItem,
153) -> Result<Ident, ErrorGuaranteed> {
154 if meta_item.is_word() {
155 Ok(func.ident)
156 } else if let Some([first]) = meta_item.meta_item_list()
157 && let Some(m) = first.meta_item()
158 && m.path.segments.len() == 1
159 {
160 Ok(m.path.segments[0].ident)
161 } else {
162 Err(ecx.dcx().emit_err(EiiMacroExpectedMaxOneArgument {
163 span: meta_item.span,
164 name: path_to_string(&meta_item.path),
165 }))
166 }
167}
168
169fn filter_attrs_for_multiple_eii_attr(
171 ecx: &mut ExtCtxt<'_>,
172 attrs: ThinVec<Attribute>,
173 eii_attr_span: Span,
174 eii_attr_path: &Path,
175) -> ThinVec<Attribute> {
176 attrs
177 .into_iter()
178 .filter(|i| {
179 if i.has_name(sym::eii) {
180 ecx.dcx().emit_err(EiiOnlyOnce {
181 span: i.span,
182 first_span: eii_attr_span,
183 name: path_to_string(eii_attr_path),
184 });
185 false
186 } else {
187 true
188 }
189 })
190 .collect()
191}
192
193fn generate_default_impl(
194 ecx: &mut ExtCtxt<'_>,
195 func: &ast::Fn,
196 impl_unsafe: bool,
197 macro_name: Ident,
198 eii_attr_span: Span,
199 item_span: Span,
200 foreign_item_name: Ident,
201) -> Box<ast::Item> {
202 let attrs = ThinVec::new();
204
205 let mut default_func = func.clone();
206 default_func.eii_impls.push(EiiImpl {
207 node_id: DUMMY_NODE_ID,
208 inner_span: macro_name.span,
209 eii_macro_path: ast::Path::from_ident(macro_name),
210 impl_safety: if impl_unsafe {
211 ast::Safety::Unsafe(eii_attr_span)
212 } else {
213 ast::Safety::Default
214 },
215 span: eii_attr_span,
216 is_default: true,
217 known_eii_macro_resolution: Some(ast::EiiDecl {
218 foreign_item: ecx.path(
219 foreign_item_name.span,
220 <[_]>::into_vec(::alloc::boxed::box_new([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],
223 ),
224 impl_unsafe,
225 }),
226 });
227
228 let anon_mod = |span: Span, stmts: ThinVec<ast::Stmt>| {
229 let unit = ecx.ty(item_span, ast::TyKind::Tup(ThinVec::new()));
230 let underscore = Ident::new(kw::Underscore, item_span);
231 ecx.item_const(
232 span,
233 underscore,
234 unit,
235 ast::ConstItemRhs::Body(ecx.expr_block(ecx.block(span, stmts))),
236 )
237 };
238
239 anon_mod(
243 item_span,
244 {
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(
245 item_span,
246 ecx.item(item_span, attrs, ItemKind::Fn(Box::new(default_func)))
247 ),],
248 )
249}
250
251fn generate_foreign_item(
257 ecx: &mut ExtCtxt<'_>,
258 eii_attr_span: Span,
259 item_span: Span,
260 mut func: ast::Fn,
261 vis: Visibility,
262 attrs_from_decl: &[Attribute],
263) -> Box<ast::Item> {
264 let mut foreign_item_attrs = ThinVec::new();
265 foreign_item_attrs.extend_from_slice(attrs_from_decl);
266
267 foreign_item_attrs.push(ecx.attr_word(sym::rustc_eii_foreign_item, eii_attr_span));
270
271 let abi = match func.sig.header.ext {
272 ast::Extern::Explicit(lit, _) => Some(lit),
274 ast::Extern::Implicit(_) => None,
276 ast::Extern::None => Some(ast::StrLit {
278 symbol: sym::Rust,
279 suffix: None,
280 symbol_unescaped: sym::Rust,
281 style: ast::StrStyle::Cooked,
282 span: eii_attr_span,
283 }),
284 };
285
286 func.sig.header.ext = ast::Extern::None;
288 func.body = None;
289
290 if func.sig.header.safety == ast::Safety::Default {
292 func.sig.header.safety = ast::Safety::Safe(func.sig.span);
293 }
294
295 ecx.item(
296 eii_attr_span,
297 ThinVec::new(),
298 ast::ItemKind::ForeignMod(ast::ForeignMod {
299 extern_span: eii_attr_span,
300 safety: ast::Safety::Unsafe(eii_attr_span),
301 abi,
302 items: From::from([Box::new(ast::ForeignItem {
303 attrs: foreign_item_attrs,
304 id: ast::DUMMY_NODE_ID,
305 span: item_span,
306 vis,
307 kind: ast::ForeignItemKind::Fn(Box::new(func.clone())),
308 tokens: None,
309 })]),
310 }),
311 )
312}
313
314fn generate_attribute_macro_to_implement(
325 ecx: &mut ExtCtxt<'_>,
326 span: Span,
327 macro_name: Ident,
328 foreign_item_name: Ident,
329 impl_unsafe: bool,
330 attrs_from_decl: &[Attribute],
331) -> Box<ast::Item> {
332 let mut macro_attrs = ThinVec::new();
333
334 macro_attrs.extend_from_slice(attrs_from_decl);
337
338 macro_attrs.push(ecx.attr_name_value_str(sym::rustc_macro_transparency, sym::semiopaque, span));
340
341 macro_attrs.push(ecx.attr_nested_word(sym::rustc_builtin_macro, sym::eii_shared_macro, span));
343
344 Box::new(ast::Item {
346 attrs: macro_attrs,
347 id: ast::DUMMY_NODE_ID,
348 span,
349 vis: ast::Visibility { span, kind: ast::VisibilityKind::Public, tokens: None },
351 kind: ast::ItemKind::MacroDef(
352 macro_name,
354 ast::MacroDef {
355 body: Box::new(ast::DelimArgs {
357 dspan: DelimSpan::from_single(span),
358 delim: Delimiter::Brace,
359 tokens: TokenStream::from_iter([
360 TokenTree::Delimited(
361 DelimSpan::from_single(span),
362 DelimSpacing::new(Spacing::Alone, Spacing::Alone),
363 Delimiter::Parenthesis,
364 TokenStream::default(),
365 ),
366 TokenTree::token_alone(TokenKind::FatArrow, span),
367 TokenTree::Delimited(
368 DelimSpan::from_single(span),
369 DelimSpacing::new(Spacing::Alone, Spacing::Alone),
370 Delimiter::Brace,
371 TokenStream::default(),
372 ),
373 ]),
374 }),
375 macro_rules: false,
376 eii_declaration: Some(ast::EiiDecl {
378 foreign_item: ast::Path::from_ident(foreign_item_name),
379 impl_unsafe,
380 }),
381 },
382 ),
383 tokens: None,
384 })
385}
386
387pub(crate) fn eii_declaration(
388 ecx: &mut ExtCtxt<'_>,
389 span: Span,
390 meta_item: &ast::MetaItem,
391 mut item: Annotatable,
392) -> Vec<Annotatable> {
393 let i = if let Annotatable::Item(ref mut item) = item {
394 item
395 } else if let Annotatable::Stmt(ref mut stmt) = item
396 && let StmtKind::Item(ref mut item) = stmt.kind
397 {
398 item
399 } else {
400 ecx.dcx().emit_err(EiiExternTargetExpectedMacro { span });
401 return <[_]>::into_vec(::alloc::boxed::box_new([item]))vec![item];
402 };
403
404 let ItemKind::MacroDef(_, d) = &mut i.kind else {
405 ecx.dcx().emit_err(EiiExternTargetExpectedMacro { span });
406 return <[_]>::into_vec(::alloc::boxed::box_new([item]))vec![item];
407 };
408
409 let Some(list) = meta_item.meta_item_list() else {
410 ecx.dcx().emit_err(EiiExternTargetExpectedList { span: meta_item.span });
411 return <[_]>::into_vec(::alloc::boxed::box_new([item]))vec![item];
412 };
413
414 if list.len() > 2 {
415 ecx.dcx().emit_err(EiiExternTargetExpectedList { span: meta_item.span });
416 return <[_]>::into_vec(::alloc::boxed::box_new([item]))vec![item];
417 }
418
419 let Some(extern_item_path) = list.get(0).and_then(|i| i.meta_item()).map(|i| i.path.clone())
420 else {
421 ecx.dcx().emit_err(EiiExternTargetExpectedList { span: meta_item.span });
422 return <[_]>::into_vec(::alloc::boxed::box_new([item]))vec![item];
423 };
424
425 let impl_unsafe = if let Some(i) = list.get(1) {
426 if i.lit().and_then(|i| i.kind.str()).is_some_and(|i| i == kw::Unsafe) {
427 true
428 } else {
429 ecx.dcx().emit_err(EiiExternTargetExpectedUnsafe { span: i.span() });
430 return <[_]>::into_vec(::alloc::boxed::box_new([item]))vec![item];
431 }
432 } else {
433 false
434 };
435
436 d.eii_declaration = Some(EiiDecl { foreign_item: extern_item_path, impl_unsafe });
437
438 <[_]>::into_vec(::alloc::boxed::box_new([item]))vec![item]
440}
441
442pub(crate) fn eii_shared_macro(
444 ecx: &mut ExtCtxt<'_>,
445 span: Span,
446 meta_item: &ast::MetaItem,
447 mut item: Annotatable,
448) -> Vec<Annotatable> {
449 let i = if let Annotatable::Item(ref mut item) = item {
450 item
451 } else if let Annotatable::Stmt(ref mut stmt) = item
452 && let StmtKind::Item(ref mut item) = stmt.kind
453 {
454 item
455 } else {
456 ecx.dcx().emit_err(EiiSharedMacroExpectedFunction {
457 span,
458 name: path_to_string(&meta_item.path),
459 });
460 return <[_]>::into_vec(::alloc::boxed::box_new([item]))vec![item];
461 };
462
463 let ItemKind::Fn(f) = &mut i.kind else {
464 ecx.dcx().emit_err(EiiSharedMacroExpectedFunction {
465 span,
466 name: path_to_string(&meta_item.path),
467 });
468 return <[_]>::into_vec(::alloc::boxed::box_new([item]))vec![item];
469 };
470
471 let is_default = if meta_item.is_word() {
472 false
473 } else if let Some([first]) = meta_item.meta_item_list()
474 && let Some(m) = first.meta_item()
475 && m.path.segments.len() == 1
476 {
477 m.path.segments[0].ident.name == kw::Default
478 } else {
479 ecx.dcx().emit_err(EiiMacroExpectedMaxOneArgument {
480 span: meta_item.span,
481 name: path_to_string(&meta_item.path),
482 });
483 return <[_]>::into_vec(::alloc::boxed::box_new([item]))vec![item];
484 };
485
486 f.eii_impls.push(EiiImpl {
487 node_id: DUMMY_NODE_ID,
488 inner_span: meta_item.path.span,
489 eii_macro_path: meta_item.path.clone(),
490 impl_safety: meta_item.unsafety,
491 span,
492 is_default,
493 known_eii_macro_resolution: None,
494 });
495
496 <[_]>::into_vec(::alloc::boxed::box_new([item]))vec![item]
497}