]> git.lizzy.rs Git - rust.git/blob - src/libsyntax_ext/deriving/custom.rs
Rollup merge of #40521 - TimNN:panic-free-shift, r=alexcrichton
[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::fold::Folder;
20 use syntax::visit::Visitor;
21
22 struct MarkAttrs<'a>(&'a [ast::Name]);
23
24 impl<'a> Visitor<'a> for MarkAttrs<'a> {
25     fn visit_attribute(&mut self, attr: &Attribute) {
26         if let Some(name) = attr.name() {
27             if self.0.contains(&name) {
28                 mark_used(attr);
29                 mark_known(attr);
30             }
31         }
32     }
33
34     fn visit_mac(&mut self, _mac: &Mac) {}
35 }
36
37 pub struct ProcMacroDerive {
38     inner: fn(TokenStream) -> TokenStream,
39     attrs: Vec<ast::Name>,
40 }
41
42 impl ProcMacroDerive {
43     pub fn new(inner: fn(TokenStream) -> TokenStream, attrs: Vec<ast::Name>) -> ProcMacroDerive {
44         ProcMacroDerive { inner: inner, attrs: attrs }
45     }
46 }
47
48 impl MultiItemModifier for ProcMacroDerive {
49     fn expand(&self,
50               ecx: &mut ExtCtxt,
51               span: Span,
52               _meta_item: &ast::MetaItem,
53               item: Annotatable)
54               -> Vec<Annotatable> {
55         let item = match item {
56             Annotatable::Item(item) => item,
57             Annotatable::ImplItem(_) |
58             Annotatable::TraitItem(_) => {
59                 ecx.span_err(span, "proc-macro derives may only be \
60                                     applied to struct/enum items");
61                 return Vec::new()
62             }
63         };
64         match item.node {
65             ItemKind::Struct(..) |
66             ItemKind::Enum(..) => {},
67             _ => {
68                 ecx.span_err(span, "proc-macro derives may only be \
69                                     applied to struct/enum items");
70                 return Vec::new()
71             }
72         }
73
74         // Mark attributes as known, and used.
75         MarkAttrs(&self.attrs).visit_item(&item);
76
77         let input = __internal::new_token_stream(ecx.resolver.eliminate_crate_var(item.clone()));
78         let res = __internal::set_parse_sess(&ecx.parse_sess, || {
79             let inner = self.inner;
80             panic::catch_unwind(panic::AssertUnwindSafe(|| inner(input)))
81         });
82
83         let stream = match res {
84             Ok(stream) => stream,
85             Err(e) => {
86                 let msg = "proc-macro derive panicked";
87                 let mut err = ecx.struct_span_fatal(span, msg);
88                 if let Some(s) = e.downcast_ref::<String>() {
89                     err.help(&format!("message: {}", s));
90                 }
91                 if let Some(s) = e.downcast_ref::<&'static str>() {
92                     err.help(&format!("message: {}", s));
93                 }
94
95                 err.emit();
96                 panic!(FatalError);
97             }
98         };
99
100         let new_items = __internal::set_parse_sess(&ecx.parse_sess, || {
101             match __internal::token_stream_parse_items(stream) {
102                 Ok(new_items) => new_items,
103                 Err(_) => {
104                     // FIXME: handle this better
105                     let msg = "proc-macro derive produced unparseable tokens";
106                     ecx.struct_span_fatal(span, msg).emit();
107                     panic!(FatalError);
108                 }
109             }
110         });
111
112         // Reassign spans of all expanded items to the input `item`
113         // for better errors here.
114         new_items.into_iter().map(|item| {
115             Annotatable::Item(ChangeSpan { span: span }.fold_item(item).expect_one(""))
116         }).collect()
117     }
118 }