]>
Commit | Line | Data |
---|---|---|
85aaf69f | 1 | // Copyright 2013-2015 The Rust Project Developers. See the COPYRIGHT |
1a4d82fc JJ |
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 | // force-host | |
12 | ||
d9579d0f | 13 | #![feature(plugin_registrar, quote, rustc_private)] |
1a4d82fc JJ |
14 | |
15 | extern crate syntax; | |
16 | extern crate rustc; | |
92a42be0 | 17 | extern crate rustc_plugin; |
3157f602 | 18 | extern crate syntax_pos; |
1a4d82fc | 19 | |
9e0c209e SL |
20 | use syntax::ast::{self, Item, MetaItem, ItemKind}; |
21 | use syntax::codemap::DUMMY_SP; | |
1a4d82fc | 22 | use syntax::ext::base::*; |
9e0c209e | 23 | use syntax::ext::quote::rt::ToTokens; |
d9579d0f | 24 | use syntax::parse::{self, token}; |
1a4d82fc | 25 | use syntax::ptr::P; |
3157f602 XL |
26 | use syntax::tokenstream::TokenTree; |
27 | use syntax_pos::Span; | |
92a42be0 | 28 | use rustc_plugin::Registry; |
1a4d82fc JJ |
29 | |
30 | #[macro_export] | |
85aaf69f | 31 | macro_rules! exported_macro { () => (2) } |
85aaf69f | 32 | macro_rules! unexported_macro { () => (3) } |
1a4d82fc JJ |
33 | |
34 | #[plugin_registrar] | |
35 | pub fn plugin_registrar(reg: &mut Registry) { | |
36 | reg.register_macro("make_a_1", expand_make_a_1); | |
1a4d82fc | 37 | reg.register_macro("identity", expand_identity); |
85aaf69f SL |
38 | reg.register_syntax_extension( |
39 | token::intern("into_multi_foo"), | |
c34b1796 AL |
40 | // FIXME (#22405): Replace `Box::new` with `box` here when/if possible. |
41 | MultiModifier(Box::new(expand_into_foo_multi))); | |
d9579d0f AL |
42 | reg.register_syntax_extension( |
43 | token::intern("duplicate"), | |
44 | // FIXME (#22405): Replace `Box::new` with `box` here when/if possible. | |
45 | MultiDecorator(Box::new(expand_duplicate))); | |
9e0c209e SL |
46 | reg.register_syntax_extension( |
47 | token::intern("caller"), | |
48 | // FIXME (#22405): Replace `Box::new` with `box` here when/if possible. | |
49 | MultiDecorator(Box::new(expand_caller))); | |
1a4d82fc JJ |
50 | } |
51 | ||
9e0c209e | 52 | fn expand_make_a_1(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> Box<MacResult + 'static> { |
1a4d82fc JJ |
53 | if !tts.is_empty() { |
54 | cx.span_fatal(sp, "make_a_1 takes no arguments"); | |
55 | } | |
c34b1796 | 56 | MacEager::expr(quote_expr!(cx, 1)) |
1a4d82fc JJ |
57 | } |
58 | ||
59 | // See Issue #15750 | |
9e0c209e | 60 | fn expand_identity(cx: &mut ExtCtxt, _span: Span, tts: &[TokenTree]) -> Box<MacResult + 'static> { |
1a4d82fc | 61 | // Parse an expression and emit it unchanged. |
c30ab7b3 | 62 | let mut parser = parse::new_parser_from_tts(cx.parse_sess(), tts.to_vec()); |
92a42be0 | 63 | let expr = parser.parse_expr().unwrap(); |
c34b1796 | 64 | MacEager::expr(quote_expr!(&mut *cx, $expr)) |
1a4d82fc JJ |
65 | } |
66 | ||
85aaf69f | 67 | fn expand_into_foo_multi(cx: &mut ExtCtxt, |
9e0c209e SL |
68 | _sp: Span, |
69 | _attr: &MetaItem, | |
70 | it: Annotatable) | |
71 | -> Vec<Annotatable> { | |
85aaf69f | 72 | match it { |
3157f602 | 73 | Annotatable::Item(it) => vec![ |
85aaf69f SL |
74 | Annotatable::Item(P(Item { |
75 | attrs: it.attrs.clone(), | |
76 | ..(*quote_item!(cx, enum Foo2 { Bar2, Baz2 }).unwrap()).clone() | |
3157f602 XL |
77 | })), |
78 | Annotatable::Item(quote_item!(cx, enum Foo3 { Bar }).unwrap()), | |
5bcae85e | 79 | Annotatable::Item(quote_item!(cx, #[cfg(any())] fn foo2() {}).unwrap()), |
3157f602 | 80 | ], |
9e0c209e | 81 | Annotatable::ImplItem(_it) => vec![ |
c34b1796 AL |
82 | quote_item!(cx, impl X { fn foo(&self) -> i32 { 42 } }).unwrap().and_then(|i| { |
83 | match i.node { | |
9e0c209e | 84 | ItemKind::Impl(.., mut items) => { |
7453a54e | 85 | Annotatable::ImplItem(P(items.pop().expect("impl method not found"))) |
c34b1796 AL |
86 | } |
87 | _ => unreachable!("impl parsed to something other than impl") | |
88 | } | |
89 | }) | |
3157f602 | 90 | ], |
9e0c209e | 91 | Annotatable::TraitItem(_it) => vec![ |
c34b1796 AL |
92 | quote_item!(cx, trait X { fn foo(&self) -> i32 { 0 } }).unwrap().and_then(|i| { |
93 | match i.node { | |
9e0c209e | 94 | ItemKind::Trait(.., mut items) => { |
7453a54e | 95 | Annotatable::TraitItem(P(items.pop().expect("trait method not found"))) |
c34b1796 AL |
96 | } |
97 | _ => unreachable!("trait parsed to something other than trait") | |
98 | } | |
99 | }) | |
3157f602 | 100 | ], |
85aaf69f SL |
101 | } |
102 | } | |
103 | ||
d9579d0f AL |
104 | // Create a duplicate of the annotatable, based on the MetaItem |
105 | fn expand_duplicate(cx: &mut ExtCtxt, | |
9e0c209e | 106 | _sp: Span, |
d9579d0f | 107 | mi: &MetaItem, |
62682a34 | 108 | it: &Annotatable, |
9e0c209e | 109 | push: &mut FnMut(Annotatable)) { |
d9579d0f | 110 | let copy_name = match mi.node { |
7453a54e | 111 | ast::MetaItemKind::List(_, ref xs) => { |
9e0c209e SL |
112 | if let Some(word) = xs[0].word() { |
113 | token::str_to_ident(&word.name()) | |
d9579d0f AL |
114 | } else { |
115 | cx.span_err(mi.span, "Expected word"); | |
116 | return; | |
117 | } | |
118 | } | |
119 | _ => { | |
120 | cx.span_err(mi.span, "Expected list"); | |
121 | return; | |
122 | } | |
123 | }; | |
1a4d82fc | 124 | |
d9579d0f AL |
125 | // Duplicate the item but replace its ident by the MetaItem |
126 | match it.clone() { | |
127 | Annotatable::Item(it) => { | |
128 | let mut new_it = (*it).clone(); | |
129 | new_it.attrs.clear(); | |
130 | new_it.ident = copy_name; | |
131 | push(Annotatable::Item(P(new_it))); | |
132 | } | |
133 | Annotatable::ImplItem(it) => { | |
134 | let mut new_it = (*it).clone(); | |
135 | new_it.attrs.clear(); | |
136 | new_it.ident = copy_name; | |
137 | push(Annotatable::ImplItem(P(new_it))); | |
138 | } | |
139 | Annotatable::TraitItem(tt) => { | |
140 | let mut new_it = (*tt).clone(); | |
141 | new_it.attrs.clear(); | |
142 | new_it.ident = copy_name; | |
143 | push(Annotatable::TraitItem(P(new_it))); | |
144 | } | |
1a4d82fc | 145 | } |
1a4d82fc JJ |
146 | } |
147 | ||
9e0c209e SL |
148 | pub fn token_separate<T: ToTokens>(ecx: &ExtCtxt, things: &[T], |
149 | token: token::Token) -> Vec<TokenTree> { | |
150 | let mut output: Vec<TokenTree> = vec![]; | |
151 | for (i, thing) in things.iter().enumerate() { | |
152 | output.extend(thing.to_tokens(ecx)); | |
153 | if i < things.len() - 1 { | |
154 | output.push(TokenTree::Token(DUMMY_SP, token.clone())); | |
155 | } | |
156 | } | |
157 | ||
158 | output | |
159 | } | |
160 | ||
161 | fn expand_caller(cx: &mut ExtCtxt, | |
162 | sp: Span, | |
163 | mi: &MetaItem, | |
164 | it: &Annotatable, | |
165 | push: &mut FnMut(Annotatable)) { | |
166 | let (orig_fn_name, ret_type) = match *it { | |
167 | Annotatable::Item(ref item) => match item.node { | |
168 | ItemKind::Fn(ref decl, ..) => { | |
169 | (item.ident, &decl.output) | |
170 | } | |
171 | _ => cx.span_fatal(item.span, "Only functions with return types can be annotated.") | |
172 | }, | |
173 | _ => cx.span_fatal(sp, "Only functions can be annotated.") | |
174 | }; | |
175 | ||
176 | let (caller_name, arguments) = if let Some(list) = mi.meta_item_list() { | |
177 | if list.len() < 2 { | |
178 | cx.span_fatal(mi.span(), "Need a function name and at least one parameter."); | |
179 | } | |
180 | ||
181 | let fn_name = match list[0].name() { | |
182 | Some(name) => token::str_to_ident(&name), | |
183 | None => cx.span_fatal(list[0].span(), "First parameter must be an ident.") | |
184 | }; | |
185 | ||
186 | (fn_name, &list[1..]) | |
187 | } else { | |
188 | cx.span_fatal(mi.span, "Expected list."); | |
189 | }; | |
190 | ||
191 | let literals: Vec<ast::Lit> = arguments.iter().map(|arg| { | |
192 | if let Some(lit) = arg.literal() { | |
193 | lit.clone() | |
194 | } else { | |
195 | cx.span_fatal(arg.span(), "Expected literal."); | |
196 | } | |
197 | }).collect(); | |
198 | ||
199 | let arguments = token_separate(cx, literals.as_slice(), token::Comma); | |
200 | if let ast::FunctionRetTy::Ty(ref rt) = *ret_type { | |
201 | push(Annotatable::Item(quote_item!(cx, | |
202 | fn $caller_name() -> $rt { | |
203 | $orig_fn_name($arguments) | |
204 | }).unwrap())) | |
205 | } else { | |
206 | push(Annotatable::Item(quote_item!(cx, | |
207 | fn $caller_name() { | |
208 | $orig_fn_name($arguments) | |
209 | }).unwrap())) | |
210 | } | |
211 | } | |
212 | ||
1a4d82fc | 213 | pub fn foo() {} |