]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/ext/proc_macro.rs
Replace AstBuilder with inherent methods
[rust.git] / src / libsyntax / ext / proc_macro.rs
1 use crate::ast::{self, ItemKind, Attribute, Mac};
2 use crate::attr::{mark_used, mark_known, HasAttrs};
3 use crate::errors::{Applicability, FatalError};
4 use crate::ext::base::{self, *};
5 use crate::ext::proc_macro_server;
6 use crate::parse::{self, token};
7 use crate::parse::parser::PathStyle;
8 use crate::symbol::{sym, Symbol};
9 use crate::tokenstream::{self, TokenStream};
10 use crate::visit::Visitor;
11
12 use rustc_data_structures::fx::FxHashSet;
13 use rustc_data_structures::sync::Lrc;
14 use syntax_pos::hygiene::{ExpnInfo, ExpnKind};
15 use syntax_pos::{Span, DUMMY_SP};
16
17 const EXEC_STRATEGY: proc_macro::bridge::server::SameThread =
18     proc_macro::bridge::server::SameThread;
19
20 pub struct BangProcMacro {
21     pub client: proc_macro::bridge::client::Client<
22         fn(proc_macro::TokenStream) -> proc_macro::TokenStream,
23     >,
24 }
25
26 impl base::ProcMacro for BangProcMacro {
27     fn expand<'cx>(&self,
28                    ecx: &'cx mut ExtCtxt<'_>,
29                    span: Span,
30                    input: TokenStream)
31                    -> TokenStream {
32         let server = proc_macro_server::Rustc::new(ecx);
33         match self.client.run(&EXEC_STRATEGY, server, input) {
34             Ok(stream) => stream,
35             Err(e) => {
36                 let msg = "proc macro panicked";
37                 let mut err = ecx.struct_span_fatal(span, msg);
38                 if let Some(s) = e.as_str() {
39                     err.help(&format!("message: {}", s));
40                 }
41
42                 err.emit();
43                 FatalError.raise();
44             }
45         }
46     }
47 }
48
49 pub struct AttrProcMacro {
50     pub client: proc_macro::bridge::client::Client<
51         fn(proc_macro::TokenStream, proc_macro::TokenStream) -> proc_macro::TokenStream,
52     >,
53 }
54
55 impl base::AttrProcMacro for AttrProcMacro {
56     fn expand<'cx>(&self,
57                    ecx: &'cx mut ExtCtxt<'_>,
58                    span: Span,
59                    annotation: TokenStream,
60                    annotated: TokenStream)
61                    -> TokenStream {
62         let server = proc_macro_server::Rustc::new(ecx);
63         match self.client.run(&EXEC_STRATEGY, server, annotation, annotated) {
64             Ok(stream) => stream,
65             Err(e) => {
66                 let msg = "custom attribute panicked";
67                 let mut err = ecx.struct_span_fatal(span, msg);
68                 if let Some(s) = e.as_str() {
69                     err.help(&format!("message: {}", s));
70                 }
71
72                 err.emit();
73                 FatalError.raise();
74             }
75         }
76     }
77 }
78
79 pub struct ProcMacroDerive {
80     pub client: proc_macro::bridge::client::Client<
81         fn(proc_macro::TokenStream) -> proc_macro::TokenStream,
82     >,
83     pub attrs: Vec<ast::Name>,
84 }
85
86 impl MultiItemModifier for ProcMacroDerive {
87     fn expand(&self,
88               ecx: &mut ExtCtxt<'_>,
89               span: Span,
90               _meta_item: &ast::MetaItem,
91               item: Annotatable)
92               -> Vec<Annotatable> {
93         let item = match item {
94             Annotatable::Item(item) => item,
95             Annotatable::ImplItem(_) |
96             Annotatable::TraitItem(_) |
97             Annotatable::ForeignItem(_) |
98             Annotatable::Stmt(_) |
99             Annotatable::Expr(_) => {
100                 ecx.span_err(span, "proc-macro derives may only be \
101                                     applied to a struct, enum, or union");
102                 return Vec::new()
103             }
104         };
105         match item.node {
106             ItemKind::Struct(..) |
107             ItemKind::Enum(..) |
108             ItemKind::Union(..) => {},
109             _ => {
110                 ecx.span_err(span, "proc-macro derives may only be \
111                                     applied to a struct, enum, or union");
112                 return Vec::new()
113             }
114         }
115
116         // Mark attributes as known, and used.
117         MarkAttrs(&self.attrs).visit_item(&item);
118
119         let token = token::Interpolated(Lrc::new(token::NtItem(item)));
120         let input = tokenstream::TokenTree::token(token, DUMMY_SP).into();
121
122         let server = proc_macro_server::Rustc::new(ecx);
123         let stream = match self.client.run(&EXEC_STRATEGY, server, input) {
124             Ok(stream) => stream,
125             Err(e) => {
126                 let msg = "proc-macro derive panicked";
127                 let mut err = ecx.struct_span_fatal(span, msg);
128                 if let Some(s) = e.as_str() {
129                     err.help(&format!("message: {}", s));
130                 }
131
132                 err.emit();
133                 FatalError.raise();
134             }
135         };
136
137         let error_count_before = ecx.parse_sess.span_diagnostic.err_count();
138         let msg = "proc-macro derive produced unparseable tokens";
139
140         let mut parser = parse::stream_to_parser(ecx.parse_sess, stream, Some("proc-macro derive"));
141         let mut items = vec![];
142
143         loop {
144             match parser.parse_item() {
145                 Ok(None) => break,
146                 Ok(Some(item)) => {
147                     items.push(Annotatable::Item(item))
148                 }
149                 Err(mut err) => {
150                     // FIXME: handle this better
151                     err.cancel();
152                     ecx.struct_span_fatal(span, msg).emit();
153                     FatalError.raise();
154                 }
155             }
156         }
157
158
159         // fail if there have been errors emitted
160         if ecx.parse_sess.span_diagnostic.err_count() > error_count_before {
161             ecx.struct_span_fatal(span, msg).emit();
162             FatalError.raise();
163         }
164
165         items
166     }
167 }
168
169 struct MarkAttrs<'a>(&'a [ast::Name]);
170
171 impl<'a> Visitor<'a> for MarkAttrs<'a> {
172     fn visit_attribute(&mut self, attr: &Attribute) {
173         if let Some(ident) = attr.ident() {
174             if self.0.contains(&ident.name) {
175                 mark_used(attr);
176                 mark_known(attr);
177             }
178         }
179     }
180
181     fn visit_mac(&mut self, _mac: &Mac) {}
182 }
183
184 pub fn is_proc_macro_attr(attr: &Attribute) -> bool {
185     [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive]
186         .iter().any(|kind| attr.check_name(*kind))
187 }
188
189 crate fn collect_derives(cx: &mut ExtCtxt<'_>, attrs: &mut Vec<ast::Attribute>) -> Vec<ast::Path> {
190     let mut result = Vec::new();
191     attrs.retain(|attr| {
192         if attr.path != sym::derive {
193             return true;
194         }
195         if !attr.is_meta_item_list() {
196             cx.struct_span_err(attr.span, "malformed `derive` attribute input")
197                 .span_suggestion(
198                     attr.span,
199                     "missing traits to be derived",
200                     "#[derive(Trait1, Trait2, ...)]".to_owned(),
201                     Applicability::HasPlaceholders,
202                 ).emit();
203             return false;
204         }
205
206         match attr.parse_list(cx.parse_sess,
207                               |parser| parser.parse_path_allowing_meta(PathStyle::Mod)) {
208             Ok(traits) => {
209                 result.extend(traits);
210                 true
211             }
212             Err(mut e) => {
213                 e.emit();
214                 false
215             }
216         }
217     });
218     result
219 }
220
221 crate fn add_derived_markers<T: HasAttrs>(
222     cx: &mut ExtCtxt<'_>, span: Span, traits: &[ast::Path], item: &mut T
223 ) {
224     let (mut names, mut pretty_name) = (FxHashSet::default(), String::new());
225     for (i, path) in traits.iter().enumerate() {
226         if i > 0 {
227             pretty_name.push_str(", ");
228         }
229         pretty_name.push_str(&path.to_string());
230         names.insert(unwrap_or!(path.segments.get(0), continue).ident.name);
231     }
232
233     let span = span.fresh_expansion(cx.current_expansion.id, ExpnInfo::allow_unstable(
234         ExpnKind::Macro(MacroKind::Derive, Symbol::intern(&pretty_name)), span,
235         cx.parse_sess.edition, cx.allow_derive_markers.clone(),
236     ));
237
238     item.visit_attrs(|attrs| {
239         if names.contains(&sym::Eq) && names.contains(&sym::PartialEq) {
240             let meta = cx.meta_word(span, sym::structural_match);
241             attrs.push(cx.attribute(meta));
242         }
243         if names.contains(&sym::Copy) {
244             let meta = cx.meta_word(span, sym::rustc_copy_clone_marker);
245             attrs.push(cx.attribute(meta));
246         }
247     });
248 }