]> git.lizzy.rs Git - rust.git/blob - src/libsyntax_ext/deriving/custom.rs
Simplify SaveHandler trait
[rust.git] / src / libsyntax_ext / deriving / custom.rs
1 use crate::proc_macro_impl::EXEC_STRATEGY;
2 use crate::proc_macro_server;
3
4 use errors::FatalError;
5 use rustc_data_structures::sync::Lrc;
6 use syntax::ast::{self, ItemKind, Attribute, Mac};
7 use syntax::attr::{mark_used, mark_known};
8 use syntax::source_map::Span;
9 use syntax::ext::base::*;
10 use syntax::parse;
11 use syntax::parse::token;
12 use syntax::tokenstream;
13 use syntax::visit::Visitor;
14 use syntax_pos::DUMMY_SP;
15
16 struct MarkAttrs<'a>(&'a [ast::Name]);
17
18 impl<'a> Visitor<'a> for MarkAttrs<'a> {
19     fn visit_attribute(&mut self, attr: &Attribute) {
20         if let Some(ident) = attr.ident() {
21             if self.0.contains(&ident.name) {
22                 mark_used(attr);
23                 mark_known(attr);
24             }
25         }
26     }
27
28     fn visit_mac(&mut self, _mac: &Mac) {}
29 }
30
31 pub struct ProcMacroDerive {
32     pub client: proc_macro::bridge::client::Client<
33         fn(proc_macro::TokenStream) -> proc_macro::TokenStream,
34     >,
35     pub attrs: Vec<ast::Name>,
36 }
37
38 impl MultiItemModifier for ProcMacroDerive {
39     fn expand(&self,
40               ecx: &mut ExtCtxt<'_>,
41               span: Span,
42               _meta_item: &ast::MetaItem,
43               item: Annotatable)
44               -> Vec<Annotatable> {
45         let item = match item {
46             Annotatable::Item(item) => item,
47             Annotatable::ImplItem(_) |
48             Annotatable::TraitItem(_) |
49             Annotatable::ForeignItem(_) |
50             Annotatable::Stmt(_) |
51             Annotatable::Expr(_) => {
52                 ecx.span_err(span, "proc-macro derives may only be \
53                                     applied to a struct, enum, or union");
54                 return Vec::new()
55             }
56         };
57         match item.node {
58             ItemKind::Struct(..) |
59             ItemKind::Enum(..) |
60             ItemKind::Union(..) => {},
61             _ => {
62                 ecx.span_err(span, "proc-macro derives may only be \
63                                     applied to a struct, enum, or union");
64                 return Vec::new()
65             }
66         }
67
68         // Mark attributes as known, and used.
69         MarkAttrs(&self.attrs).visit_item(&item);
70
71         let token = token::Interpolated(Lrc::new(token::NtItem(item)));
72         let input = tokenstream::TokenTree::token(token, DUMMY_SP).into();
73
74         let server = proc_macro_server::Rustc::new(ecx);
75         let stream = match self.client.run(&EXEC_STRATEGY, server, input) {
76             Ok(stream) => stream,
77             Err(e) => {
78                 let msg = "proc-macro derive panicked";
79                 let mut err = ecx.struct_span_fatal(span, msg);
80                 if let Some(s) = e.as_str() {
81                     err.help(&format!("message: {}", s));
82                 }
83
84                 err.emit();
85                 FatalError.raise();
86             }
87         };
88
89         let error_count_before = ecx.parse_sess.span_diagnostic.err_count();
90         let msg = "proc-macro derive produced unparseable tokens";
91
92         let mut parser = parse::stream_to_parser(ecx.parse_sess, stream, Some("proc-macro derive"));
93         let mut items = vec![];
94
95         loop {
96             match parser.parse_item() {
97                 Ok(None) => break,
98                 Ok(Some(item)) => {
99                     items.push(Annotatable::Item(item))
100                 }
101                 Err(mut err) => {
102                     // FIXME: handle this better
103                     err.cancel();
104                     ecx.struct_span_fatal(span, msg).emit();
105                     FatalError.raise();
106                 }
107             }
108         }
109
110
111         // fail if there have been errors emitted
112         if ecx.parse_sess.span_diagnostic.err_count() > error_count_before {
113             ecx.struct_span_fatal(span, msg).emit();
114             FatalError.raise();
115         }
116
117         items
118     }
119 }