]> git.proxmox.com Git - rustc.git/blame_incremental - compiler/rustc_builtin_macros/src/proc_macro_harness.rs
New upstream version 1.55.0+dfsg1
[rustc.git] / compiler / rustc_builtin_macros / src / proc_macro_harness.rs
... / ...
CommitLineData
1use std::mem;
2
3use rustc_ast::attr;
4use rustc_ast::ptr::P;
5use rustc_ast::visit::{self, Visitor};
6use rustc_ast::{self as ast, NodeId};
7use rustc_ast_pretty::pprust;
8use rustc_expand::base::{parse_macro_name_and_helper_attrs, ExtCtxt, ResolverExpand};
9use rustc_expand::expand::{AstFragment, ExpansionConfig};
10use rustc_session::Session;
11use rustc_span::hygiene::AstPass;
12use rustc_span::source_map::SourceMap;
13use rustc_span::symbol::{kw, sym, Ident, Symbol};
14use rustc_span::{Span, DUMMY_SP};
15use smallvec::smallvec;
16use std::cell::RefCell;
17
18struct ProcMacroDerive {
19 id: NodeId,
20 trait_name: Symbol,
21 function_name: Ident,
22 span: Span,
23 attrs: Vec<Symbol>,
24}
25
26enum ProcMacroDefType {
27 Attr,
28 Bang,
29}
30
31struct ProcMacroDef {
32 id: NodeId,
33 function_name: Ident,
34 span: Span,
35 def_type: ProcMacroDefType,
36}
37
38enum ProcMacro {
39 Derive(ProcMacroDerive),
40 Def(ProcMacroDef),
41}
42
43struct CollectProcMacros<'a> {
44 sess: &'a Session,
45 macros: Vec<ProcMacro>,
46 in_root: bool,
47 handler: &'a rustc_errors::Handler,
48 source_map: &'a SourceMap,
49 is_proc_macro_crate: bool,
50 is_test_crate: bool,
51}
52
53pub fn inject(
54 sess: &Session,
55 resolver: &mut dyn ResolverExpand,
56 mut krate: ast::Crate,
57 is_proc_macro_crate: bool,
58 has_proc_macro_decls: bool,
59 is_test_crate: bool,
60 num_crate_types: usize,
61 handler: &rustc_errors::Handler,
62) -> ast::Crate {
63 let ecfg = ExpansionConfig::default("proc_macro".to_string());
64 let mut cx = ExtCtxt::new(sess, ecfg, resolver, None);
65
66 let mut collect = CollectProcMacros {
67 sess,
68 macros: Vec::new(),
69 in_root: true,
70 handler,
71 source_map: sess.source_map(),
72 is_proc_macro_crate,
73 is_test_crate,
74 };
75
76 if has_proc_macro_decls || is_proc_macro_crate {
77 visit::walk_crate(&mut collect, &krate);
78 }
79 let macros = collect.macros;
80
81 if !is_proc_macro_crate {
82 return krate;
83 }
84
85 if num_crate_types > 1 {
86 handler.err("cannot mix `proc-macro` crate type with others");
87 }
88
89 if is_test_crate {
90 return krate;
91 }
92
93 let decls = mk_decls(&mut krate, &mut cx, &macros);
94 krate.items.push(decls);
95
96 krate
97}
98
99impl<'a> CollectProcMacros<'a> {
100 fn check_not_pub_in_root(&self, vis: &ast::Visibility, sp: Span) {
101 if self.is_proc_macro_crate && self.in_root && vis.kind.is_pub() {
102 self.handler.span_err(
103 sp,
104 "`proc-macro` crate types currently cannot export any items other \
105 than functions tagged with `#[proc_macro]`, `#[proc_macro_derive]`, \
106 or `#[proc_macro_attribute]`",
107 );
108 }
109 }
110
111 fn collect_custom_derive(&mut self, item: &'a ast::Item, attr: &'a ast::Attribute) {
112 let (trait_name, proc_attrs) =
113 match parse_macro_name_and_helper_attrs(self.handler, attr, "derive") {
114 Some(name_and_attrs) => name_and_attrs,
115 None => return,
116 };
117
118 if self.in_root && item.vis.kind.is_pub() {
119 self.macros.push(ProcMacro::Derive(ProcMacroDerive {
120 id: item.id,
121 span: item.span,
122 trait_name,
123 function_name: item.ident,
124 attrs: proc_attrs,
125 }));
126 } else {
127 let msg = if !self.in_root {
128 "functions tagged with `#[proc_macro_derive]` must \
129 currently reside in the root of the crate"
130 } else {
131 "functions tagged with `#[proc_macro_derive]` must be `pub`"
132 };
133 self.handler.span_err(self.source_map.guess_head_span(item.span), msg);
134 }
135 }
136
137 fn collect_attr_proc_macro(&mut self, item: &'a ast::Item) {
138 if self.in_root && item.vis.kind.is_pub() {
139 self.macros.push(ProcMacro::Def(ProcMacroDef {
140 id: item.id,
141 span: item.span,
142 function_name: item.ident,
143 def_type: ProcMacroDefType::Attr,
144 }));
145 } else {
146 let msg = if !self.in_root {
147 "functions tagged with `#[proc_macro_attribute]` must \
148 currently reside in the root of the crate"
149 } else {
150 "functions tagged with `#[proc_macro_attribute]` must be `pub`"
151 };
152 self.handler.span_err(self.source_map.guess_head_span(item.span), msg);
153 }
154 }
155
156 fn collect_bang_proc_macro(&mut self, item: &'a ast::Item) {
157 if self.in_root && item.vis.kind.is_pub() {
158 self.macros.push(ProcMacro::Def(ProcMacroDef {
159 id: item.id,
160 span: item.span,
161 function_name: item.ident,
162 def_type: ProcMacroDefType::Bang,
163 }));
164 } else {
165 let msg = if !self.in_root {
166 "functions tagged with `#[proc_macro]` must \
167 currently reside in the root of the crate"
168 } else {
169 "functions tagged with `#[proc_macro]` must be `pub`"
170 };
171 self.handler.span_err(self.source_map.guess_head_span(item.span), msg);
172 }
173 }
174}
175
176impl<'a> Visitor<'a> for CollectProcMacros<'a> {
177 fn visit_item(&mut self, item: &'a ast::Item) {
178 if let ast::ItemKind::MacroDef(..) = item.kind {
179 if self.is_proc_macro_crate && self.sess.contains_name(&item.attrs, sym::macro_export) {
180 let msg =
181 "cannot export macro_rules! macros from a `proc-macro` crate type currently";
182 self.handler.span_err(self.source_map.guess_head_span(item.span), msg);
183 }
184 }
185
186 // First up, make sure we're checking a bare function. If we're not then
187 // we're just not interested in this item.
188 //
189 // If we find one, try to locate a `#[proc_macro_derive]` attribute on it.
190 let is_fn = matches!(item.kind, ast::ItemKind::Fn(..));
191
192 let mut found_attr: Option<&'a ast::Attribute> = None;
193
194 for attr in &item.attrs {
195 if self.sess.is_proc_macro_attr(&attr) {
196 if let Some(prev_attr) = found_attr {
197 let prev_item = prev_attr.get_normal_item();
198 let item = attr.get_normal_item();
199 let path_str = pprust::path_to_string(&item.path);
200 let msg = if item.path.segments[0].ident.name
201 == prev_item.path.segments[0].ident.name
202 {
203 format!(
204 "only one `#[{}]` attribute is allowed on any given function",
205 path_str,
206 )
207 } else {
208 format!(
209 "`#[{}]` and `#[{}]` attributes cannot both be applied
210 to the same function",
211 path_str,
212 pprust::path_to_string(&prev_item.path),
213 )
214 };
215
216 self.handler
217 .struct_span_err(attr.span, &msg)
218 .span_label(prev_attr.span, "previous attribute here")
219 .emit();
220
221 return;
222 }
223
224 found_attr = Some(attr);
225 }
226 }
227
228 let attr = match found_attr {
229 None => {
230 self.check_not_pub_in_root(&item.vis, self.source_map.guess_head_span(item.span));
231 let prev_in_root = mem::replace(&mut self.in_root, false);
232 visit::walk_item(self, item);
233 self.in_root = prev_in_root;
234 return;
235 }
236 Some(attr) => attr,
237 };
238
239 if !is_fn {
240 let msg = format!(
241 "the `#[{}]` attribute may only be used on bare functions",
242 pprust::path_to_string(&attr.get_normal_item().path),
243 );
244
245 self.handler.span_err(attr.span, &msg);
246 return;
247 }
248
249 if self.is_test_crate {
250 return;
251 }
252
253 if !self.is_proc_macro_crate {
254 let msg = format!(
255 "the `#[{}]` attribute is only usable with crates of the `proc-macro` crate type",
256 pprust::path_to_string(&attr.get_normal_item().path),
257 );
258
259 self.handler.span_err(attr.span, &msg);
260 return;
261 }
262
263 if self.sess.check_name(attr, sym::proc_macro_derive) {
264 self.collect_custom_derive(item, attr);
265 } else if self.sess.check_name(attr, sym::proc_macro_attribute) {
266 self.collect_attr_proc_macro(item);
267 } else if self.sess.check_name(attr, sym::proc_macro) {
268 self.collect_bang_proc_macro(item);
269 };
270
271 let prev_in_root = mem::replace(&mut self.in_root, false);
272 visit::walk_item(self, item);
273 self.in_root = prev_in_root;
274 }
275}
276
277// Creates a new module which looks like:
278//
279// const _: () = {
280// extern crate proc_macro;
281//
282// use proc_macro::bridge::client::ProcMacro;
283//
284// #[rustc_proc_macro_decls]
285// #[allow(deprecated)]
286// static DECLS: &[ProcMacro] = &[
287// ProcMacro::custom_derive($name_trait1, &[], ::$name1);
288// ProcMacro::custom_derive($name_trait2, &["attribute_name"], ::$name2);
289// // ...
290// ];
291// }
292fn mk_decls(
293 ast_krate: &mut ast::Crate,
294 cx: &mut ExtCtxt<'_>,
295 macros: &[ProcMacro],
296) -> P<ast::Item> {
297 // We're the ones filling in this Vec,
298 // so it should be empty to start with
299 assert!(ast_krate.proc_macros.is_empty());
300
301 let expn_id = cx.resolver.expansion_for_ast_pass(
302 DUMMY_SP,
303 AstPass::ProcMacroHarness,
304 &[sym::rustc_attrs, sym::proc_macro_internals],
305 None,
306 );
307 let span = DUMMY_SP.with_def_site_ctxt(expn_id.to_expn_id());
308
309 let proc_macro = Ident::new(sym::proc_macro, span);
310 let krate = cx.item(span, proc_macro, Vec::new(), ast::ItemKind::ExternCrate(None));
311
312 let bridge = Ident::new(sym::bridge, span);
313 let client = Ident::new(sym::client, span);
314 let proc_macro_ty = Ident::new(sym::ProcMacro, span);
315 let custom_derive = Ident::new(sym::custom_derive, span);
316 let attr = Ident::new(sym::attr, span);
317 let bang = Ident::new(sym::bang, span);
318
319 let krate_ref = RefCell::new(ast_krate);
320
321 // We add NodeIds to 'krate.proc_macros' in the order
322 // that we generate expressions. The position of each NodeId
323 // in the 'proc_macros' Vec corresponds to its position
324 // in the static array that will be generated
325 let decls = {
326 let local_path =
327 |sp: Span, name| cx.expr_path(cx.path(sp.with_ctxt(span.ctxt()), vec![name]));
328 let proc_macro_ty_method_path = |method| {
329 cx.expr_path(cx.path(span, vec![proc_macro, bridge, client, proc_macro_ty, method]))
330 };
331 macros
332 .iter()
333 .map(|m| match m {
334 ProcMacro::Derive(cd) => {
335 krate_ref.borrow_mut().proc_macros.push(cd.id);
336 cx.expr_call(
337 span,
338 proc_macro_ty_method_path(custom_derive),
339 vec![
340 cx.expr_str(cd.span, cd.trait_name),
341 cx.expr_vec_slice(
342 span,
343 cd.attrs
344 .iter()
345 .map(|&s| cx.expr_str(cd.span, s))
346 .collect::<Vec<_>>(),
347 ),
348 local_path(cd.span, cd.function_name),
349 ],
350 )
351 }
352 ProcMacro::Def(ca) => {
353 krate_ref.borrow_mut().proc_macros.push(ca.id);
354 let ident = match ca.def_type {
355 ProcMacroDefType::Attr => attr,
356 ProcMacroDefType::Bang => bang,
357 };
358
359 cx.expr_call(
360 span,
361 proc_macro_ty_method_path(ident),
362 vec![
363 cx.expr_str(ca.span, ca.function_name.name),
364 local_path(ca.span, ca.function_name),
365 ],
366 )
367 }
368 })
369 .collect()
370 };
371
372 let decls_static = cx
373 .item_static(
374 span,
375 Ident::new(sym::_DECLS, span),
376 cx.ty_rptr(
377 span,
378 cx.ty(
379 span,
380 ast::TyKind::Slice(
381 cx.ty_path(cx.path(span, vec![proc_macro, bridge, client, proc_macro_ty])),
382 ),
383 ),
384 None,
385 ast::Mutability::Not,
386 ),
387 ast::Mutability::Not,
388 cx.expr_vec_slice(span, decls),
389 )
390 .map(|mut i| {
391 let attr = cx.meta_word(span, sym::rustc_proc_macro_decls);
392 i.attrs.push(cx.attribute(attr));
393
394 let deprecated_attr = attr::mk_nested_word_item(Ident::new(sym::deprecated, span));
395 let allow_deprecated_attr =
396 attr::mk_list_item(Ident::new(sym::allow, span), vec![deprecated_attr]);
397 i.attrs.push(cx.attribute(allow_deprecated_attr));
398
399 i
400 });
401
402 let block = cx.expr_block(
403 cx.block(span, vec![cx.stmt_item(span, krate), cx.stmt_item(span, decls_static)]),
404 );
405
406 let anon_constant = cx.item_const(
407 span,
408 Ident::new(kw::Underscore, span),
409 cx.ty(span, ast::TyKind::Tup(Vec::new())),
410 block,
411 );
412
413 // Integrate the new item into existing module structures.
414 let items = AstFragment::Items(smallvec![anon_constant]);
415 cx.monotonic_expander().fully_expand_fragment(items).make_items().pop().unwrap()
416}