1use std::mem;
2
3use rustc_ast::ptr::P;
4use rustc_ast::visit::{self, Visitor};
5use rustc_ast::{self as ast, NodeId, attr};
6use rustc_ast_pretty::pprust;
7use rustc_errors::DiagCtxtHandle;
8use rustc_expand::base::{ExtCtxt, ResolverExpand, parse_macro_name_and_helper_attrs};
9use rustc_expand::expand::{AstFragment, ExpansionConfig};
10use rustc_feature::Features;
11use rustc_session::Session;
12use rustc_span::hygiene::AstPass;
13use rustc_span::source_map::SourceMap;
14use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym};
15use smallvec::smallvec;
16use thin_vec::{ThinVec, thin_vec};
17
18use crate::errors;
19
20struct ProcMacroDerive {
21 id: NodeId,
22 trait_name: Symbol,
23 function_ident: Ident,
24 span: Span,
25 attrs: Vec<Symbol>,
26}
27
28struct ProcMacroDef {
29 id: NodeId,
30 function_ident: Ident,
31 span: Span,
32}
33
34enum ProcMacro {
35 Derive(ProcMacroDerive),
36 Attr(ProcMacroDef),
37 Bang(ProcMacroDef),
38}
39
40struct CollectProcMacros<'a> {
41 macros: Vec<ProcMacro>,
42 in_root: bool,
43 dcx: DiagCtxtHandle<'a>,
44 source_map: &'a SourceMap,
45 is_proc_macro_crate: bool,
46 is_test_crate: bool,
47}
48
49pub fn inject(
50 krate: &mut ast::Crate,
51 sess: &Session,
52 features: &Features,
53 resolver: &mut dyn ResolverExpand,
54 is_proc_macro_crate: bool,
55 has_proc_macro_decls: bool,
56 is_test_crate: bool,
57 dcx: DiagCtxtHandle<'_>,
58) {
59 let ecfg = ExpansionConfig::default("proc_macro".to_string(), features);
60 let mut cx = ExtCtxt::new(sess, ecfg, resolver, None);
61
62 let mut collect = CollectProcMacros {
63 macros: Vec::new(),
64 in_root: true,
65 dcx,
66 source_map: sess.source_map(),
67 is_proc_macro_crate,
68 is_test_crate,
69 };
70
71 if has_proc_macro_decls || is_proc_macro_crate {
72 visit::walk_crate(&mut collect, krate);
73 }
74 let macros = collect.macros;
75
76 if !is_proc_macro_crate {
77 return;
78 }
79
80 if is_test_crate {
81 return;
82 }
83
84 let decls = mk_decls(&mut cx, ¯os);
85 krate.items.push(decls);
86}
87
88impl<'a> CollectProcMacros<'a> {
89 fn check_not_pub_in_root(&self, vis: &ast::Visibility, sp: Span) {
90 if self.is_proc_macro_crate && self.in_root && vis.kind.is_pub() {
91 self.dcx.emit_err(errors::ProcMacro { span: sp });
92 }
93 }
94
95 fn collect_custom_derive(
96 &mut self,
97 item: &'a ast::Item,
98 function_ident: Ident,
99 attr: &'a ast::Attribute,
100 ) {
101 let Some((trait_name, proc_attrs)) =
102 parse_macro_name_and_helper_attrs(self.dcx, attr, "derive")
103 else {
104 return;
105 };
106
107 if self.in_root && item.vis.kind.is_pub() {
108 self.macros.push(ProcMacro::Derive(ProcMacroDerive {
109 id: item.id,
110 span: item.span,
111 trait_name,
112 function_ident,
113 attrs: proc_attrs,
114 }));
115 } else {
116 let msg = if !self.in_root {
117 "functions tagged with `#[proc_macro_derive]` must \
118 currently reside in the root of the crate"
119 } else {
120 "functions tagged with `#[proc_macro_derive]` must be `pub`"
121 };
122 self.dcx.span_err(self.source_map.guess_head_span(item.span), msg);
123 }
124 }
125
126 fn collect_attr_proc_macro(&mut self, item: &'a ast::Item, function_ident: Ident) {
127 if self.in_root && item.vis.kind.is_pub() {
128 self.macros.push(ProcMacro::Attr(ProcMacroDef {
129 id: item.id,
130 span: item.span,
131 function_ident,
132 }));
133 } else {
134 let msg = if !self.in_root {
135 "functions tagged with `#[proc_macro_attribute]` must \
136 currently reside in the root of the crate"
137 } else {
138 "functions tagged with `#[proc_macro_attribute]` must be `pub`"
139 };
140 self.dcx.span_err(self.source_map.guess_head_span(item.span), msg);
141 }
142 }
143
144 fn collect_bang_proc_macro(&mut self, item: &'a ast::Item, function_ident: Ident) {
145 if self.in_root && item.vis.kind.is_pub() {
146 self.macros.push(ProcMacro::Bang(ProcMacroDef {
147 id: item.id,
148 span: item.span,
149 function_ident,
150 }));
151 } else {
152 let msg = if !self.in_root {
153 "functions tagged with `#[proc_macro]` must \
154 currently reside in the root of the crate"
155 } else {
156 "functions tagged with `#[proc_macro]` must be `pub`"
157 };
158 self.dcx.span_err(self.source_map.guess_head_span(item.span), msg);
159 }
160 }
161}
162
163impl<'a> Visitor<'a> for CollectProcMacros<'a> {
164 fn visit_item(&mut self, item: &'a ast::Item) {
165 if let ast::ItemKind::MacroDef(..) = item.kind {
166 if self.is_proc_macro_crate && attr::contains_name(&item.attrs, sym::macro_export) {
167 self.dcx.emit_err(errors::ExportMacroRules {
168 span: self.source_map.guess_head_span(item.span),
169 });
170 }
171 }
172
173 let mut found_attr: Option<&'a ast::Attribute> = None;
174
175 for attr in &item.attrs {
176 if attr.is_proc_macro_attr() {
177 if let Some(prev_attr) = found_attr {
178 let prev_item = prev_attr.get_normal_item();
179 let item = attr.get_normal_item();
180 let path_str = pprust::path_to_string(&item.path);
181 let msg = if item.path.segments[0].ident.name
182 == prev_item.path.segments[0].ident.name
183 {
184 format!(
185 "only one `#[{path_str}]` attribute is allowed on any given function",
186 )
187 } else {
188 format!(
189 "`#[{}]` and `#[{}]` attributes cannot both be applied
190 to the same function",
191 path_str,
192 pprust::path_to_string(&prev_item.path),
193 )
194 };
195
196 self.dcx
197 .struct_span_err(attr.span, msg)
198 .with_span_label(prev_attr.span, "previous attribute here")
199 .emit();
200
201 return;
202 }
203
204 found_attr = Some(attr);
205 }
206 }
207
208 let Some(attr) = found_attr else {
209 self.check_not_pub_in_root(&item.vis, self.source_map.guess_head_span(item.span));
210 let prev_in_root = mem::replace(&mut self.in_root, false);
211 visit::walk_item(self, item);
212 self.in_root = prev_in_root;
213 return;
214 };
215
216 let fn_ident = if let ast::ItemKind::Fn(fn_) = &item.kind {
219 fn_.ident
220 } else {
221 self.dcx
222 .create_err(errors::AttributeOnlyBeUsedOnBareFunctions {
223 span: attr.span,
224 path: &pprust::path_to_string(&attr.get_normal_item().path),
225 })
226 .emit();
227 return;
228 };
229
230 if self.is_test_crate {
231 return;
232 }
233
234 if !self.is_proc_macro_crate {
235 self.dcx
236 .create_err(errors::AttributeOnlyUsableWithCrateType {
237 span: attr.span,
238 path: &pprust::path_to_string(&attr.get_normal_item().path),
239 })
240 .emit();
241 return;
242 }
243
244 if attr.has_name(sym::proc_macro_derive) {
246 self.collect_custom_derive(item, fn_ident, attr);
247 } else if attr.has_name(sym::proc_macro_attribute) {
248 self.collect_attr_proc_macro(item, fn_ident);
249 } else if attr.has_name(sym::proc_macro) {
250 self.collect_bang_proc_macro(item, fn_ident);
251 };
252
253 let prev_in_root = mem::replace(&mut self.in_root, false);
254 visit::walk_item(self, item);
255 self.in_root = prev_in_root;
256 }
257}
258
259fn mk_decls(cx: &mut ExtCtxt<'_>, macros: &[ProcMacro]) -> P<ast::Item> {
276 let expn_id = cx.resolver.expansion_for_ast_pass(
277 DUMMY_SP,
278 AstPass::ProcMacroHarness,
279 &[sym::rustc_attrs, sym::proc_macro_internals],
280 None,
281 );
282 let span = DUMMY_SP.with_def_site_ctxt(expn_id.to_expn_id());
283
284 let proc_macro = Ident::new(sym::proc_macro, span);
285 let krate = cx.item(span, ast::AttrVec::new(), ast::ItemKind::ExternCrate(None, proc_macro));
286
287 let bridge = Ident::new(sym::bridge, span);
288 let client = Ident::new(sym::client, span);
289 let proc_macro_ty = Ident::new(sym::ProcMacro, span);
290 let custom_derive = Ident::new(sym::custom_derive, span);
291 let attr = Ident::new(sym::attr, span);
292 let bang = Ident::new(sym::bang, span);
293
294 let decls = macros
299 .iter()
300 .map(|m| {
301 let harness_span = span;
302 let span = match m {
303 ProcMacro::Derive(m) => m.span,
304 ProcMacro::Attr(m) | ProcMacro::Bang(m) => m.span,
305 };
306 let local_path = |cx: &ExtCtxt<'_>, ident| cx.expr_path(cx.path(span, vec![ident]));
307 let proc_macro_ty_method_path = |cx: &ExtCtxt<'_>, method| {
308 cx.expr_path(cx.path(
309 span.with_ctxt(harness_span.ctxt()),
310 vec![proc_macro, bridge, client, proc_macro_ty, method],
311 ))
312 };
313 match m {
314 ProcMacro::Derive(cd) => {
315 cx.resolver.declare_proc_macro(cd.id);
316 cx.expr_call(
319 harness_span,
320 proc_macro_ty_method_path(cx, custom_derive),
321 thin_vec![
322 cx.expr_str(span, cd.trait_name),
323 cx.expr_array_ref(
324 span,
325 cd.attrs
326 .iter()
327 .map(|&s| cx.expr_str(span, s))
328 .collect::<ThinVec<_>>(),
329 ),
330 local_path(cx, cd.function_ident),
331 ],
332 )
333 }
334 ProcMacro::Attr(ca) | ProcMacro::Bang(ca) => {
335 cx.resolver.declare_proc_macro(ca.id);
336 let ident = match m {
337 ProcMacro::Attr(_) => attr,
338 ProcMacro::Bang(_) => bang,
339 ProcMacro::Derive(_) => unreachable!(),
340 };
341
342 cx.expr_call(
345 harness_span,
346 proc_macro_ty_method_path(cx, ident),
347 thin_vec![
348 cx.expr_str(span, ca.function_ident.name),
349 local_path(cx, ca.function_ident),
350 ],
351 )
352 }
353 }
354 })
355 .collect();
356
357 let decls_static = cx
358 .item_static(
359 span,
360 Ident::new(sym::_DECLS, span),
361 cx.ty_ref(
362 span,
363 cx.ty(
364 span,
365 ast::TyKind::Slice(
366 cx.ty_path(cx.path(span, vec![proc_macro, bridge, client, proc_macro_ty])),
367 ),
368 ),
369 None,
370 ast::Mutability::Not,
371 ),
372 ast::Mutability::Not,
373 cx.expr_array_ref(span, decls),
374 )
375 .map(|mut i| {
376 i.attrs.push(cx.attr_word(sym::rustc_proc_macro_decls, span));
377 i.attrs.push(cx.attr_word(sym::used, span));
378 i.attrs.push(cx.attr_nested_word(sym::allow, sym::deprecated, span));
379 i
380 });
381
382 let block = cx.expr_block(
383 cx.block(span, thin_vec![cx.stmt_item(span, krate), cx.stmt_item(span, decls_static)]),
384 );
385
386 let anon_constant = cx.item_const(
387 span,
388 Ident::new(kw::Underscore, span),
389 cx.ty(span, ast::TyKind::Tup(ThinVec::new())),
390 block,
391 );
392
393 let items = AstFragment::Items(smallvec![anon_constant]);
395 cx.monotonic_expander().fully_expand_fragment(items).make_items().pop().unwrap()
396}