]> git.lizzy.rs Git - rust.git/blob - src/libsyntax_expand/proc_macro.rs
099cf0a4be9049bcb41d04f479eb5c8dd3bdba68
[rust.git] / src / libsyntax_expand / proc_macro.rs
1 use crate::base::{self, *};
2 use crate::proc_macro_server;
3
4 use syntax::ast::{self, ItemKind};
5 use syntax::errors::{Applicability, FatalError};
6 use syntax::symbol::sym;
7 use syntax::token;
8 use syntax::tokenstream::{self, TokenStream};
9
10 use rustc_data_structures::sync::Lrc;
11 use syntax_pos::{Span, DUMMY_SP};
12
13 const EXEC_STRATEGY: pm::bridge::server::SameThread = pm::bridge::server::SameThread;
14
15 pub struct BangProcMacro {
16     pub client: pm::bridge::client::Client<
17         fn(pm::TokenStream) -> pm::TokenStream,
18     >,
19 }
20
21 impl base::ProcMacro for BangProcMacro {
22     fn expand<'cx>(&self,
23                    ecx: &'cx mut ExtCtxt<'_>,
24                    span: Span,
25                    input: TokenStream)
26                    -> TokenStream {
27         let server = proc_macro_server::Rustc::new(ecx);
28         match self.client.run(&EXEC_STRATEGY, server, input) {
29             Ok(stream) => stream,
30             Err(e) => {
31                 let msg = "proc macro panicked";
32                 let mut err = ecx.struct_span_fatal(span, msg);
33                 if let Some(s) = e.as_str() {
34                     err.help(&format!("message: {}", s));
35                 }
36
37                 err.emit();
38                 FatalError.raise();
39             }
40         }
41     }
42 }
43
44 pub struct AttrProcMacro {
45     pub client: pm::bridge::client::Client<fn(pm::TokenStream, pm::TokenStream) -> pm::TokenStream>,
46 }
47
48 impl base::AttrProcMacro for AttrProcMacro {
49     fn expand<'cx>(&self,
50                    ecx: &'cx mut ExtCtxt<'_>,
51                    span: Span,
52                    annotation: TokenStream,
53                    annotated: TokenStream)
54                    -> TokenStream {
55         let server = proc_macro_server::Rustc::new(ecx);
56         match self.client.run(&EXEC_STRATEGY, server, annotation, annotated) {
57             Ok(stream) => stream,
58             Err(e) => {
59                 let msg = "custom attribute panicked";
60                 let mut err = ecx.struct_span_fatal(span, msg);
61                 if let Some(s) = e.as_str() {
62                     err.help(&format!("message: {}", s));
63                 }
64
65                 err.emit();
66                 FatalError.raise();
67             }
68         }
69     }
70 }
71
72 pub struct ProcMacroDerive {
73     pub client: pm::bridge::client::Client<fn(pm::TokenStream) -> pm::TokenStream>,
74 }
75
76 impl MultiItemModifier for ProcMacroDerive {
77     fn expand(&self,
78               ecx: &mut ExtCtxt<'_>,
79               span: Span,
80               _meta_item: &ast::MetaItem,
81               item: Annotatable)
82               -> Vec<Annotatable> {
83         let item = match item {
84             Annotatable::Arm(..) |
85             Annotatable::Field(..) |
86             Annotatable::FieldPat(..) |
87             Annotatable::GenericParam(..) |
88             Annotatable::Param(..) |
89             Annotatable::StructField(..) |
90             Annotatable::Variant(..)
91                 => panic!("unexpected annotatable"),
92             Annotatable::Item(item) => item,
93             Annotatable::ImplItem(_) |
94             Annotatable::TraitItem(_) |
95             Annotatable::ForeignItem(_) |
96             Annotatable::Stmt(_) |
97             Annotatable::Expr(_) => {
98                 ecx.span_err(span, "proc-macro derives may only be \
99                                     applied to a struct, enum, or union");
100                 return Vec::new()
101             }
102         };
103         match item.kind {
104             ItemKind::Struct(..) |
105             ItemKind::Enum(..) |
106             ItemKind::Union(..) => {},
107             _ => {
108                 ecx.span_err(span, "proc-macro derives may only be \
109                                     applied to a struct, enum, or union");
110                 return Vec::new()
111             }
112         }
113
114         let token = token::Interpolated(Lrc::new(token::NtItem(item)));
115         let input = tokenstream::TokenTree::token(token, DUMMY_SP).into();
116
117         let server = proc_macro_server::Rustc::new(ecx);
118         let stream = match self.client.run(&EXEC_STRATEGY, server, input) {
119             Ok(stream) => stream,
120             Err(e) => {
121                 let msg = "proc-macro derive panicked";
122                 let mut err = ecx.struct_span_fatal(span, msg);
123                 if let Some(s) = e.as_str() {
124                     err.help(&format!("message: {}", s));
125                 }
126
127                 err.emit();
128                 FatalError.raise();
129             }
130         };
131
132         let error_count_before = ecx.parse_sess.span_diagnostic.err_count();
133         let msg = "proc-macro derive produced unparseable tokens";
134
135         let mut parser = rustc_parse::stream_to_parser(
136             ecx.parse_sess,
137             stream,
138             Some("proc-macro derive"),
139         );
140         let mut items = vec![];
141
142         loop {
143             match parser.parse_item() {
144                 Ok(None) => break,
145                 Ok(Some(item)) => {
146                     items.push(Annotatable::Item(item))
147                 }
148                 Err(mut err) => {
149                     // FIXME: handle this better
150                     err.cancel();
151                     ecx.struct_span_fatal(span, msg).emit();
152                     FatalError.raise();
153                 }
154             }
155         }
156
157
158         // fail if there have been errors emitted
159         if ecx.parse_sess.span_diagnostic.err_count() > error_count_before {
160             ecx.struct_span_fatal(span, msg).emit();
161             FatalError.raise();
162         }
163
164         items
165     }
166 }
167
168 crate fn collect_derives(cx: &mut ExtCtxt<'_>, attrs: &mut Vec<ast::Attribute>) -> Vec<ast::Path> {
169     let mut result = Vec::new();
170     attrs.retain(|attr| {
171         if !attr.has_name(sym::derive) {
172             return true;
173         }
174         if !attr.is_meta_item_list() {
175             cx.struct_span_err(attr.span, "malformed `derive` attribute input")
176                 .span_suggestion(
177                     attr.span,
178                     "missing traits to be derived",
179                     "#[derive(Trait1, Trait2, ...)]".to_owned(),
180                     Applicability::HasPlaceholders,
181                 ).emit();
182             return false;
183         }
184
185         let parse_derive_paths = |attr: &ast::Attribute| {
186             if attr.get_normal_item().tokens.is_empty() {
187                 return Ok(Vec::new());
188             }
189             rustc_parse::parse_in_attr(cx.parse_sess, attr, |p| p.parse_derive_paths())
190         };
191
192         match parse_derive_paths(attr) {
193             Ok(traits) => {
194                 result.extend(traits);
195                 true
196             }
197             Err(mut e) => {
198                 e.emit();
199                 false
200             }
201         }
202     });
203     result
204 }