]>
Commit | Line | Data |
---|---|---|
9e0c209e SL |
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; | |
c30ab7b3 | 14 | use proc_macro::{TokenStream, __internal}; |
476ff2be SL |
15 | use syntax::ast::{self, ItemKind, Attribute, Mac}; |
16 | use syntax::attr::{mark_used, mark_known}; | |
17 | use syntax::codemap::Span; | |
9e0c209e SL |
18 | use syntax::ext::base::*; |
19 | use syntax::fold::Folder; | |
476ff2be SL |
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) { | |
cc61c64b XL |
26 | if let Some(name) = attr.name() { |
27 | if self.0.contains(&name) { | |
28 | mark_used(attr); | |
29 | mark_known(attr); | |
30 | } | |
476ff2be SL |
31 | } |
32 | } | |
33 | ||
34 | fn visit_mac(&mut self, _mac: &Mac) {} | |
35 | } | |
9e0c209e | 36 | |
8bb4bdeb | 37 | pub struct ProcMacroDerive { |
9e0c209e | 38 | inner: fn(TokenStream) -> TokenStream, |
476ff2be | 39 | attrs: Vec<ast::Name>, |
9e0c209e SL |
40 | } |
41 | ||
8bb4bdeb XL |
42 | impl ProcMacroDerive { |
43 | pub fn new(inner: fn(TokenStream) -> TokenStream, attrs: Vec<ast::Name>) -> ProcMacroDerive { | |
44 | ProcMacroDerive { inner: inner, attrs: attrs } | |
9e0c209e SL |
45 | } |
46 | } | |
47 | ||
8bb4bdeb | 48 | impl MultiItemModifier for ProcMacroDerive { |
9e0c209e SL |
49 | fn expand(&self, |
50 | ecx: &mut ExtCtxt, | |
51 | span: Span, | |
476ff2be | 52 | _meta_item: &ast::MetaItem, |
9e0c209e SL |
53 | item: Annotatable) |
54 | -> Vec<Annotatable> { | |
55 | let item = match item { | |
56 | Annotatable::Item(item) => item, | |
57 | Annotatable::ImplItem(_) | | |
58 | Annotatable::TraitItem(_) => { | |
8bb4bdeb | 59 | ecx.span_err(span, "proc-macro derives may only be \ |
9e0c209e SL |
60 | applied to struct/enum items"); |
61 | return Vec::new() | |
62 | } | |
63 | }; | |
64 | match item.node { | |
65 | ItemKind::Struct(..) | | |
476ff2be | 66 | ItemKind::Enum(..) => {}, |
9e0c209e | 67 | _ => { |
8bb4bdeb | 68 | ecx.span_err(span, "proc-macro derives may only be \ |
9e0c209e SL |
69 | applied to struct/enum items"); |
70 | return Vec::new() | |
71 | } | |
72 | } | |
73 | ||
476ff2be SL |
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())); | |
9e0c209e SL |
78 | let res = __internal::set_parse_sess(&ecx.parse_sess, || { |
79 | let inner = self.inner; | |
80 | panic::catch_unwind(panic::AssertUnwindSafe(|| inner(input))) | |
81 | }); | |
32a655c1 SL |
82 | |
83 | let stream = match res { | |
84 | Ok(stream) => stream, | |
9e0c209e | 85 | Err(e) => { |
8bb4bdeb | 86 | let msg = "proc-macro derive panicked"; |
9e0c209e SL |
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 | ||
32a655c1 SL |
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 | |
8bb4bdeb | 105 | let msg = "proc-macro derive produced unparseable tokens"; |
32a655c1 SL |
106 | ecx.struct_span_fatal(span, msg).emit(); |
107 | panic!(FatalError); | |
108 | } | |
109 | } | |
110 | }); | |
111 | ||
476ff2be SL |
112 | // Reassign spans of all expanded items to the input `item` |
113 | // for better errors here. | |
8bb4bdeb XL |
114 | new_items.into_iter().map(|item| { |
115 | Annotatable::Item(ChangeSpan { span: span }.fold_item(item).expect_one("")) | |
116 | }).collect() | |
9e0c209e SL |
117 | } |
118 | } |