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