1use rustc_ast::token::{Delimiter, TokenKind};
2use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree};
3use rustc_ast::{
4 DUMMY_NODE_ID, EiiExternTarget, EiiImpl, ItemKind, Stmt, StmtKind, ast, token, tokenstream,
5};
6use rustc_ast_pretty::pprust::path_to_string;
7use rustc_expand::base::{Annotatable, ExtCtxt};
8use rustc_span::{Ident, Span, kw, sym};
9use thin_vec::{ThinVec, thin_vec};
10
11use crate::errors::{
12 EiiExternTargetExpectedList, EiiExternTargetExpectedMacro, EiiExternTargetExpectedUnsafe,
13 EiiMacroExpectedMaxOneArgument, EiiOnlyOnce, EiiSharedMacroExpectedFunction,
14};
15
16pub(crate) fn eii(
36 ecx: &mut ExtCtxt<'_>,
37 span: Span,
38 meta_item: &ast::MetaItem,
39 item: Annotatable,
40) -> Vec<Annotatable> {
41 eii_(ecx, span, meta_item, item, false)
42}
43
44pub(crate) fn unsafe_eii(
45 ecx: &mut ExtCtxt<'_>,
46 span: Span,
47 meta_item: &ast::MetaItem,
48 item: Annotatable,
49) -> Vec<Annotatable> {
50 eii_(ecx, span, meta_item, item, true)
51}
52
53fn eii_(
54 ecx: &mut ExtCtxt<'_>,
55 span: Span,
56 meta_item: &ast::MetaItem,
57 item: Annotatable,
58 impl_unsafe: bool,
59) -> Vec<Annotatable> {
60 let span = ecx.with_def_site_ctxt(span);
61
62 let (item, stmt) = if let Annotatable::Item(item) = item {
63 (item, false)
64 } else if let Annotatable::Stmt(ref stmt) = item
65 && let StmtKind::Item(ref item) = stmt.kind
66 {
67 (item.clone(), true)
68 } else {
69 ecx.dcx().emit_err(EiiSharedMacroExpectedFunction {
70 span,
71 name: path_to_string(&meta_item.path),
72 });
73 return vec![item];
74 };
75
76 let orig_item = item.clone();
77
78 let item = *item;
79
80 let ast::Item { attrs, id: _, span: item_span, vis, kind: ItemKind::Fn(mut func), tokens: _ } =
81 item
82 else {
83 ecx.dcx().emit_err(EiiSharedMacroExpectedFunction {
84 span,
85 name: path_to_string(&meta_item.path),
86 });
87 return vec![Annotatable::Item(Box::new(item))];
88 };
89
90 let mut new_attrs = ThinVec::new();
92 for i in attrs {
93 if i.has_name(sym::eii) {
94 ecx.dcx().emit_err(EiiOnlyOnce {
95 span: i.span,
96 first_span: span,
97 name: path_to_string(&meta_item.path),
98 });
99 } else {
100 new_attrs.push(i);
101 }
102 }
103 let attrs = new_attrs;
104
105 let macro_name = if meta_item.is_word() {
106 func.ident
107 } else if let Some([first]) = meta_item.meta_item_list()
108 && let Some(m) = first.meta_item()
109 && m.path.segments.len() == 1
110 {
111 m.path.segments[0].ident
112 } else {
113 ecx.dcx().emit_err(EiiMacroExpectedMaxOneArgument {
114 span: meta_item.span,
115 name: path_to_string(&meta_item.path),
116 });
117 return vec![Annotatable::Item(orig_item)];
118 };
119
120 let mut return_items = Vec::new();
121
122 if func.body.is_some() {
123 let mut default_func = func.clone();
124 func.body = None;
125 default_func.eii_impls.push(ast::EiiImpl {
126 node_id: DUMMY_NODE_ID,
127 eii_macro_path: ast::Path::from_ident(macro_name),
128 impl_safety: if impl_unsafe { ast::Safety::Unsafe(span) } else { ast::Safety::Default },
129 span,
130 inner_span: macro_name.span,
131 is_default: true, });
133
134 return_items.push(Box::new(ast::Item {
135 attrs: ThinVec::new(),
136 id: ast::DUMMY_NODE_ID,
137 span,
138 vis: ast::Visibility { span, kind: ast::VisibilityKind::Inherited, tokens: None },
139 kind: ast::ItemKind::Const(Box::new(ast::ConstItem {
140 ident: Ident { name: kw::Underscore, span },
141 defaultness: ast::Defaultness::Final,
142 generics: ast::Generics::default(),
143 ty: Box::new(ast::Ty {
144 id: DUMMY_NODE_ID,
145 kind: ast::TyKind::Tup(ThinVec::new()),
146 span,
147 tokens: None,
148 }),
149 rhs: Some(ast::ConstItemRhs::Body(Box::new(ast::Expr {
150 id: DUMMY_NODE_ID,
151 kind: ast::ExprKind::Block(
152 Box::new(ast::Block {
153 stmts: thin_vec![ast::Stmt {
154 id: DUMMY_NODE_ID,
155 kind: ast::StmtKind::Item(Box::new(ast::Item {
156 attrs: thin_vec![], id: DUMMY_NODE_ID,
158 span: item_span,
159 vis: ast::Visibility {
160 span,
161 kind: ast::VisibilityKind::Inherited,
162 tokens: None
163 },
164 kind: ItemKind::Fn(default_func),
165 tokens: None,
166 })),
167 span
168 }],
169 id: DUMMY_NODE_ID,
170 rules: ast::BlockCheckMode::Default,
171 span,
172 tokens: None,
173 }),
174 None,
175 ),
176 span,
177 attrs: ThinVec::new(),
178 tokens: None,
179 }))),
180 define_opaque: None,
181 })),
182 tokens: None,
183 }))
184 }
185
186 let decl_span = span.to(func.sig.span);
187
188 let abi = match func.sig.header.ext {
189 ast::Extern::Explicit(lit, _) => Some(lit),
191 ast::Extern::Implicit(_) => None,
193 ast::Extern::None => Some(ast::StrLit {
195 symbol: sym::Rust,
196 suffix: None,
197 symbol_unescaped: sym::Rust,
198 style: ast::StrStyle::Cooked,
199 span,
200 }),
201 };
202
203 func.sig.header.ext = ast::Extern::None;
205
206 if func.sig.header.safety == ast::Safety::Default {
208 func.sig.header.safety = ast::Safety::Safe(func.sig.span);
209 }
210
211 let mut extern_item_attrs = attrs.clone();
213 extern_item_attrs.push(ast::Attribute {
214 kind: ast::AttrKind::Normal(Box::new(ast::NormalAttr {
215 item: ast::AttrItem {
216 unsafety: ast::Safety::Default,
217 path: ast::Path::from_ident(Ident::new(sym::rustc_eii_extern_item, span)),
220 args: ast::AttrArgs::Empty,
221 tokens: None,
222 },
223 tokens: None,
224 })),
225 id: ecx.sess.psess.attr_id_generator.mk_attr_id(),
226 style: ast::AttrStyle::Outer,
227 span,
228 });
229
230 let extern_block = Box::new(ast::Item {
231 attrs: ast::AttrVec::default(),
232 id: ast::DUMMY_NODE_ID,
233 span,
234 vis: ast::Visibility { span, kind: ast::VisibilityKind::Inherited, tokens: None },
235 kind: ast::ItemKind::ForeignMod(ast::ForeignMod {
236 extern_span: span,
237 safety: ast::Safety::Unsafe(span),
238 abi,
239 items: From::from([Box::new(ast::ForeignItem {
240 attrs: extern_item_attrs,
241 id: ast::DUMMY_NODE_ID,
242 span: item_span,
243 vis,
244 kind: ast::ForeignItemKind::Fn(func.clone()),
245 tokens: None,
246 })]),
247 }),
248 tokens: None,
249 });
250
251 let mut macro_attrs = attrs.clone();
252 macro_attrs.push(
253 ast::Attribute {
255 kind: ast::AttrKind::Normal(Box::new(ast::NormalAttr {
256 item: ast::AttrItem {
257 unsafety: ast::Safety::Default,
258 path: ast::Path::from_ident(Ident::new(sym::rustc_builtin_macro, span)),
259 args: ast::AttrArgs::Delimited(ast::DelimArgs {
260 dspan: DelimSpan::from_single(span),
261 delim: Delimiter::Parenthesis,
262 tokens: TokenStream::new(vec![tokenstream::TokenTree::token_alone(
263 token::TokenKind::Ident(sym::eii_shared_macro, token::IdentIsRaw::No),
264 span,
265 )]),
266 }),
267 tokens: None,
268 },
269 tokens: None,
270 })),
271 id: ecx.sess.psess.attr_id_generator.mk_attr_id(),
272 style: ast::AttrStyle::Outer,
273 span,
274 },
275 );
276
277 let macro_def = Box::new(ast::Item {
278 attrs: macro_attrs,
279 id: ast::DUMMY_NODE_ID,
280 span,
281 vis: ast::Visibility { span, kind: ast::VisibilityKind::Public, tokens: None },
283 kind: ast::ItemKind::MacroDef(
284 macro_name,
286 ast::MacroDef {
287 body: Box::new(ast::DelimArgs {
289 dspan: DelimSpan::from_single(span),
290 delim: Delimiter::Brace,
291 tokens: TokenStream::from_iter([
292 TokenTree::Delimited(
293 DelimSpan::from_single(span),
294 DelimSpacing::new(Spacing::Alone, Spacing::Alone),
295 Delimiter::Parenthesis,
296 TokenStream::default(),
297 ),
298 TokenTree::token_alone(TokenKind::FatArrow, span),
299 TokenTree::Delimited(
300 DelimSpan::from_single(span),
301 DelimSpacing::new(Spacing::Alone, Spacing::Alone),
302 Delimiter::Brace,
303 TokenStream::default(),
304 ),
305 ]),
306 }),
307 macro_rules: false,
308 eii_extern_target: Some(ast::EiiExternTarget {
310 extern_item_path: ast::Path::from_ident(func.ident),
311 impl_unsafe,
312 span: decl_span,
313 }),
314 },
315 ),
316 tokens: None,
317 });
318
319 return_items.push(extern_block);
320 return_items.push(macro_def);
321
322 if stmt {
323 return_items
324 .into_iter()
325 .map(|i| {
326 Annotatable::Stmt(Box::new(Stmt {
327 id: DUMMY_NODE_ID,
328 kind: StmtKind::Item(i),
329 span,
330 }))
331 })
332 .collect()
333 } else {
334 return_items.into_iter().map(|i| Annotatable::Item(i)).collect()
335 }
336}
337
338pub(crate) fn eii_extern_target(
339 ecx: &mut ExtCtxt<'_>,
340 span: Span,
341 meta_item: &ast::MetaItem,
342 mut item: Annotatable,
343) -> Vec<Annotatable> {
344 let i = if let Annotatable::Item(ref mut item) = item {
345 item
346 } else if let Annotatable::Stmt(ref mut stmt) = item
347 && let StmtKind::Item(ref mut item) = stmt.kind
348 {
349 item
350 } else {
351 ecx.dcx().emit_err(EiiExternTargetExpectedMacro { span });
352 return vec![item];
353 };
354
355 let ItemKind::MacroDef(_, d) = &mut i.kind else {
356 ecx.dcx().emit_err(EiiExternTargetExpectedMacro { span });
357 return vec![item];
358 };
359
360 let Some(list) = meta_item.meta_item_list() else {
361 ecx.dcx().emit_err(EiiExternTargetExpectedList { span: meta_item.span });
362 return vec![item];
363 };
364
365 if list.len() > 2 {
366 ecx.dcx().emit_err(EiiExternTargetExpectedList { span: meta_item.span });
367 return vec![item];
368 }
369
370 let Some(extern_item_path) = list.get(0).and_then(|i| i.meta_item()).map(|i| i.path.clone())
371 else {
372 ecx.dcx().emit_err(EiiExternTargetExpectedList { span: meta_item.span });
373 return vec![item];
374 };
375
376 let impl_unsafe = if let Some(i) = list.get(1) {
377 if i.lit().and_then(|i| i.kind.str()).is_some_and(|i| i == kw::Unsafe) {
378 true
379 } else {
380 ecx.dcx().emit_err(EiiExternTargetExpectedUnsafe { span: i.span() });
381 return vec![item];
382 }
383 } else {
384 false
385 };
386
387 d.eii_extern_target = Some(EiiExternTarget { extern_item_path, impl_unsafe, span });
388
389 vec![item]
391}
392
393pub(crate) fn eii_shared_macro(
395 ecx: &mut ExtCtxt<'_>,
396 span: Span,
397 meta_item: &ast::MetaItem,
398 mut item: Annotatable,
399) -> Vec<Annotatable> {
400 let i = if let Annotatable::Item(ref mut item) = item {
401 item
402 } else if let Annotatable::Stmt(ref mut stmt) = item
403 && let StmtKind::Item(ref mut item) = stmt.kind
404 {
405 item
406 } else {
407 ecx.dcx().emit_err(EiiSharedMacroExpectedFunction {
408 span,
409 name: path_to_string(&meta_item.path),
410 });
411 return vec![item];
412 };
413
414 let ItemKind::Fn(f) = &mut i.kind else {
415 ecx.dcx().emit_err(EiiSharedMacroExpectedFunction {
416 span,
417 name: path_to_string(&meta_item.path),
418 });
419 return vec![item];
420 };
421
422 let is_default = if meta_item.is_word() {
423 false
424 } else if let Some([first]) = meta_item.meta_item_list()
425 && let Some(m) = first.meta_item()
426 && m.path.segments.len() == 1
427 {
428 m.path.segments[0].ident.name == kw::Default
429 } else {
430 ecx.dcx().emit_err(EiiMacroExpectedMaxOneArgument {
431 span: meta_item.span,
432 name: path_to_string(&meta_item.path),
433 });
434 return vec![item];
435 };
436
437 f.eii_impls.push(EiiImpl {
438 node_id: DUMMY_NODE_ID,
439 eii_macro_path: meta_item.path.clone(),
440 impl_safety: meta_item.unsafety,
441 span,
442 inner_span: meta_item.path.span,
443 is_default,
444 });
445
446 vec![item]
447}