]> git.lizzy.rs Git - rust.git/blob - src/libsyntax_ext/deriving/custom.rs
Point macros 1.1 errors to the input item
[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 rustc_macro::{TokenStream, __internal};
14 use syntax::ast::{self, ItemKind};
15 use syntax::codemap::Span;
16 use syntax::ext::base::*;
17 use syntax::fold::{self, Folder};
18 use errors::FatalError;
19
20 pub struct CustomDerive {
21     inner: fn(TokenStream) -> TokenStream,
22 }
23
24 impl CustomDerive {
25     pub fn new(inner: fn(TokenStream) -> TokenStream) -> CustomDerive {
26         CustomDerive { inner: inner }
27     }
28 }
29
30 impl MultiItemModifier for CustomDerive {
31     fn expand(&self,
32               ecx: &mut ExtCtxt,
33               span: Span,
34               _meta_item: &ast::MetaItem,
35               item: Annotatable)
36               -> Vec<Annotatable> {
37         let item = match item {
38             Annotatable::Item(item) => item,
39             Annotatable::ImplItem(_) |
40             Annotatable::TraitItem(_) => {
41                 ecx.span_err(span, "custom derive attributes may only be \
42                                     applied to struct/enum items");
43                 return Vec::new()
44             }
45         };
46         match item.node {
47             ItemKind::Struct(..) |
48             ItemKind::Enum(..) => {}
49             _ => {
50                 ecx.span_err(span, "custom derive attributes may only be \
51                                     applied to struct/enum items");
52                 return Vec::new()
53             }
54         }
55
56         let input_span = item.span;
57         let input = __internal::new_token_stream(item);
58         let res = __internal::set_parse_sess(&ecx.parse_sess, || {
59             let inner = self.inner;
60             panic::catch_unwind(panic::AssertUnwindSafe(|| inner(input)))
61         });
62         let item = match res {
63             Ok(stream) => __internal::token_stream_items(stream),
64             Err(e) => {
65                 let msg = "custom derive attribute panicked";
66                 let mut err = ecx.struct_span_fatal(span, msg);
67                 if let Some(s) = e.downcast_ref::<String>() {
68                     err.help(&format!("message: {}", s));
69                 }
70                 if let Some(s) = e.downcast_ref::<&'static str>() {
71                     err.help(&format!("message: {}", s));
72                 }
73
74                 err.emit();
75                 panic!(FatalError);
76             }
77         };
78
79         // Right now we have no knowledge of spans at all in custom derive
80         // macros, everything is just parsed as a string. Reassign all spans to
81         // the input `item` for better errors here.
82         item.into_iter().flat_map(|item| {
83             ChangeSpan { span: input_span }.fold_item(item)
84         }).map(Annotatable::Item).collect()
85     }
86 }
87
88 struct ChangeSpan { span: Span }
89
90 impl Folder for ChangeSpan {
91     fn new_span(&mut self, _sp: Span) -> Span {
92         self.span
93     }
94
95     fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
96         fold::noop_fold_mac(mac, self)
97     }
98 }