]> git.lizzy.rs Git - rust.git/blob - src/libsyntax_ext/deriving/custom.rs
Auto merge of #43651 - petrochenkov:foreign-life, r=eddyb
[rust.git] / src / libsyntax_ext / deriving / custom.rs
1 // Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use std::panic;
12
13 use errors::FatalError;
14 use proc_macro::{TokenStream, __internal};
15 use syntax::ast::{self, ItemKind, Attribute, Mac};
16 use syntax::attr::{mark_used, mark_known};
17 use syntax::codemap::Span;
18 use syntax::ext::base::*;
19 use syntax::visit::Visitor;
20
21 struct MarkAttrs<'a>(&'a [ast::Name]);
22
23 impl<'a> Visitor<'a> for MarkAttrs<'a> {
24     fn visit_attribute(&mut self, attr: &Attribute) {
25         if let Some(name) = attr.name() {
26             if self.0.contains(&name) {
27                 mark_used(attr);
28                 mark_known(attr);
29             }
30         }
31     }
32
33     fn visit_mac(&mut self, _mac: &Mac) {}
34 }
35
36 pub struct ProcMacroDerive {
37     inner: fn(TokenStream) -> TokenStream,
38     attrs: Vec<ast::Name>,
39 }
40
41 impl ProcMacroDerive {
42     pub fn new(inner: fn(TokenStream) -> TokenStream, attrs: Vec<ast::Name>) -> ProcMacroDerive {
43         ProcMacroDerive { inner: inner, attrs: attrs }
44     }
45 }
46
47 impl MultiItemModifier for ProcMacroDerive {
48     fn expand(&self,
49               ecx: &mut ExtCtxt,
50               span: Span,
51               _meta_item: &ast::MetaItem,
52               item: Annotatable)
53               -> Vec<Annotatable> {
54         let item = match item {
55             Annotatable::Item(item) => item,
56             Annotatable::ImplItem(_) |
57             Annotatable::TraitItem(_) => {
58                 ecx.span_err(span, "proc-macro derives may only be \
59                                     applied to struct/enum items");
60                 return Vec::new()
61             }
62         };
63         match item.node {
64             ItemKind::Struct(..) |
65             ItemKind::Enum(..) => {},
66             _ => {
67                 ecx.span_err(span, "proc-macro derives may only be \
68                                     applied to struct/enum items");
69                 return Vec::new()
70             }
71         }
72
73         // Mark attributes as known, and used.
74         MarkAttrs(&self.attrs).visit_item(&item);
75
76         let input = __internal::new_token_stream(ecx.resolver.eliminate_crate_var(item.clone()));
77         let res = __internal::set_sess(ecx, || {
78             let inner = self.inner;
79             panic::catch_unwind(panic::AssertUnwindSafe(|| inner(input)))
80         });
81
82         let stream = match res {
83             Ok(stream) => stream,
84             Err(e) => {
85                 let msg = "proc-macro derive panicked";
86                 let mut err = ecx.struct_span_fatal(span, msg);
87                 if let Some(s) = e.downcast_ref::<String>() {
88                     err.help(&format!("message: {}", s));
89                 }
90                 if let Some(s) = e.downcast_ref::<&'static str>() {
91                     err.help(&format!("message: {}", s));
92                 }
93
94                 err.emit();
95                 panic!(FatalError);
96             }
97         };
98
99         __internal::set_sess(ecx, || {
100             match __internal::token_stream_parse_items(stream) {
101                 Ok(new_items) => new_items.into_iter().map(Annotatable::Item).collect(),
102                 Err(_) => {
103                     // FIXME: handle this better
104                     let msg = "proc-macro derive produced unparseable tokens";
105                     ecx.struct_span_fatal(span, msg).emit();
106                     panic!(FatalError);
107                 }
108             }
109         })
110     }
111 }