]>
Commit | Line | Data |
---|---|---|
c34b1796 | 1 | // Copyright 2015 The Rust Project Developers. See the COPYRIGHT |
223e47cc LB |
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 | ||
1a4d82fc JJ |
11 | pub use self::SyntaxExtension::*; |
12 | ||
223e47cc | 13 | use ast; |
970d7e83 | 14 | use ast::Name; |
223e47cc | 15 | use codemap; |
d9579d0f | 16 | use codemap::{CodeMap, Span, ExpnId, ExpnInfo, NO_EXPANSION, CompilerExpansion}; |
223e47cc | 17 | use ext; |
1a4d82fc JJ |
18 | use ext::expand; |
19 | use ext::tt::macro_rules; | |
223e47cc | 20 | use parse; |
1a4d82fc | 21 | use parse::parser; |
223e47cc | 22 | use parse::token; |
1a4d82fc JJ |
23 | use parse::token::{InternedString, intern, str_to_ident}; |
24 | use ptr::P; | |
25 | use util::small_vector::SmallVector; | |
26 | use ext::mtwt; | |
27 | use fold::Folder; | |
28 | ||
29 | use std::collections::HashMap; | |
30 | use std::rc::Rc; | |
c34b1796 | 31 | use std::default::Default; |
1a4d82fc | 32 | |
d9579d0f AL |
33 | #[unstable(feature = "rustc_private")] |
34 | #[deprecated(since = "1.0.0", reason = "replaced by MultiItemDecorator")] | |
1a4d82fc JJ |
35 | pub trait ItemDecorator { |
36 | fn expand(&self, | |
37 | ecx: &mut ExtCtxt, | |
38 | sp: Span, | |
39 | meta_item: &ast::MetaItem, | |
40 | item: &ast::Item, | |
85aaf69f | 41 | push: &mut FnMut(P<ast::Item>)); |
1a4d82fc | 42 | } |
223e47cc | 43 | |
d9579d0f AL |
44 | #[allow(deprecated)] |
45 | #[unstable(feature = "rustc_private")] | |
46 | #[deprecated(since = "1.0.0", reason = "replaced by MultiItemDecorator")] | |
1a4d82fc | 47 | impl<F> ItemDecorator for F |
85aaf69f | 48 | where F : Fn(&mut ExtCtxt, Span, &ast::MetaItem, &ast::Item, &mut FnMut(P<ast::Item>)) |
1a4d82fc JJ |
49 | { |
50 | fn expand(&self, | |
51 | ecx: &mut ExtCtxt, | |
52 | sp: Span, | |
53 | meta_item: &ast::MetaItem, | |
54 | item: &ast::Item, | |
85aaf69f | 55 | push: &mut FnMut(P<ast::Item>)) { |
1a4d82fc JJ |
56 | (*self)(ecx, sp, meta_item, item, push) |
57 | } | |
58 | } | |
223e47cc | 59 | |
d9579d0f AL |
60 | #[unstable(feature = "rustc_private")] |
61 | #[deprecated(since = "1.0.0", reason = "replaced by MultiItemModifier")] | |
1a4d82fc JJ |
62 | pub trait ItemModifier { |
63 | fn expand(&self, | |
64 | ecx: &mut ExtCtxt, | |
65 | span: Span, | |
66 | meta_item: &ast::MetaItem, | |
67 | item: P<ast::Item>) | |
68 | -> P<ast::Item>; | |
223e47cc LB |
69 | } |
70 | ||
d9579d0f AL |
71 | #[allow(deprecated)] |
72 | #[unstable(feature = "rustc_private")] | |
73 | #[deprecated(since = "1.0.0", reason = "replaced by MultiItemModifier")] | |
1a4d82fc JJ |
74 | impl<F> ItemModifier for F |
75 | where F : Fn(&mut ExtCtxt, Span, &ast::MetaItem, P<ast::Item>) -> P<ast::Item> | |
76 | { | |
d9579d0f | 77 | |
1a4d82fc JJ |
78 | fn expand(&self, |
79 | ecx: &mut ExtCtxt, | |
80 | span: Span, | |
81 | meta_item: &ast::MetaItem, | |
82 | item: P<ast::Item>) | |
83 | -> P<ast::Item> { | |
84 | (*self)(ecx, span, meta_item, item) | |
85 | } | |
86 | } | |
223e47cc | 87 | |
85aaf69f SL |
88 | #[derive(Debug,Clone)] |
89 | pub enum Annotatable { | |
90 | Item(P<ast::Item>), | |
c34b1796 AL |
91 | TraitItem(P<ast::TraitItem>), |
92 | ImplItem(P<ast::ImplItem>), | |
85aaf69f SL |
93 | } |
94 | ||
95 | impl Annotatable { | |
96 | pub fn attrs(&self) -> &[ast::Attribute] { | |
97 | match *self { | |
c34b1796 AL |
98 | Annotatable::Item(ref i) => &i.attrs, |
99 | Annotatable::TraitItem(ref ti) => &ti.attrs, | |
100 | Annotatable::ImplItem(ref ii) => &ii.attrs, | |
85aaf69f SL |
101 | } |
102 | } | |
103 | ||
104 | pub fn fold_attrs(self, attrs: Vec<ast::Attribute>) -> Annotatable { | |
105 | match self { | |
c34b1796 | 106 | Annotatable::Item(i) => Annotatable::Item(i.map(|i| ast::Item { |
85aaf69f | 107 | attrs: attrs, |
c34b1796 AL |
108 | ..i |
109 | })), | |
110 | Annotatable::TraitItem(i) => Annotatable::TraitItem(i.map(|ti| { | |
111 | ast::TraitItem { attrs: attrs, ..ti } | |
112 | })), | |
113 | Annotatable::ImplItem(i) => Annotatable::ImplItem(i.map(|ii| { | |
114 | ast::ImplItem { attrs: attrs, ..ii } | |
85aaf69f | 115 | })), |
85aaf69f SL |
116 | } |
117 | } | |
118 | ||
119 | pub fn expect_item(self) -> P<ast::Item> { | |
120 | match self { | |
121 | Annotatable::Item(i) => i, | |
122 | _ => panic!("expected Item") | |
123 | } | |
124 | } | |
125 | ||
d9579d0f AL |
126 | pub fn map_item_or<F, G>(self, mut f: F, mut or: G) -> Annotatable |
127 | where F: FnMut(P<ast::Item>) -> P<ast::Item>, | |
128 | G: FnMut(Annotatable) -> Annotatable | |
129 | { | |
130 | match self { | |
131 | Annotatable::Item(i) => Annotatable::Item(f(i)), | |
132 | _ => or(self) | |
133 | } | |
134 | } | |
135 | ||
c34b1796 | 136 | pub fn expect_trait_item(self) -> P<ast::TraitItem> { |
85aaf69f SL |
137 | match self { |
138 | Annotatable::TraitItem(i) => i, | |
139 | _ => panic!("expected Item") | |
140 | } | |
141 | } | |
142 | ||
c34b1796 | 143 | pub fn expect_impl_item(self) -> P<ast::ImplItem> { |
85aaf69f SL |
144 | match self { |
145 | Annotatable::ImplItem(i) => i, | |
146 | _ => panic!("expected Item") | |
147 | } | |
148 | } | |
149 | } | |
150 | ||
d9579d0f AL |
151 | // A more flexible ItemDecorator. |
152 | pub trait MultiItemDecorator { | |
153 | fn expand(&self, | |
154 | ecx: &mut ExtCtxt, | |
155 | sp: Span, | |
156 | meta_item: &ast::MetaItem, | |
157 | item: Annotatable, | |
158 | push: &mut FnMut(Annotatable)); | |
159 | } | |
160 | ||
161 | impl<F> MultiItemDecorator for F | |
162 | where F : Fn(&mut ExtCtxt, Span, &ast::MetaItem, Annotatable, &mut FnMut(Annotatable)) | |
163 | { | |
164 | fn expand(&self, | |
165 | ecx: &mut ExtCtxt, | |
166 | sp: Span, | |
167 | meta_item: &ast::MetaItem, | |
168 | item: Annotatable, | |
169 | push: &mut FnMut(Annotatable)) { | |
170 | (*self)(ecx, sp, meta_item, item, push) | |
171 | } | |
172 | } | |
173 | ||
85aaf69f SL |
174 | // A more flexible ItemModifier (ItemModifier should go away, eventually, FIXME). |
175 | // meta_item is the annotation, item is the item being modified, parent_item | |
176 | // is the impl or trait item is declared in if item is part of such a thing. | |
177 | // FIXME Decorators should follow the same pattern too. | |
178 | pub trait MultiItemModifier { | |
179 | fn expand(&self, | |
180 | ecx: &mut ExtCtxt, | |
181 | span: Span, | |
182 | meta_item: &ast::MetaItem, | |
183 | item: Annotatable) | |
184 | -> Annotatable; | |
185 | } | |
186 | ||
187 | impl<F> MultiItemModifier for F | |
188 | where F: Fn(&mut ExtCtxt, | |
189 | Span, | |
190 | &ast::MetaItem, | |
191 | Annotatable) -> Annotatable | |
192 | { | |
193 | fn expand(&self, | |
194 | ecx: &mut ExtCtxt, | |
195 | span: Span, | |
196 | meta_item: &ast::MetaItem, | |
197 | item: Annotatable) | |
198 | -> Annotatable { | |
199 | (*self)(ecx, span, meta_item, item) | |
200 | } | |
201 | } | |
202 | ||
1a4d82fc JJ |
203 | /// Represents a thing that maps token trees to Macro Results |
204 | pub trait TTMacroExpander { | |
205 | fn expand<'cx>(&self, | |
206 | ecx: &'cx mut ExtCtxt, | |
207 | span: Span, | |
208 | token_tree: &[ast::TokenTree]) | |
209 | -> Box<MacResult+'cx>; | |
223e47cc LB |
210 | } |
211 | ||
1a4d82fc JJ |
212 | pub type MacroExpanderFn = |
213 | for<'cx> fn(&'cx mut ExtCtxt, Span, &[ast::TokenTree]) -> Box<MacResult+'cx>; | |
223e47cc | 214 | |
1a4d82fc JJ |
215 | impl<F> TTMacroExpander for F |
216 | where F : for<'cx> Fn(&'cx mut ExtCtxt, Span, &[ast::TokenTree]) -> Box<MacResult+'cx> | |
217 | { | |
218 | fn expand<'cx>(&self, | |
219 | ecx: &'cx mut ExtCtxt, | |
220 | span: Span, | |
221 | token_tree: &[ast::TokenTree]) | |
222 | -> Box<MacResult+'cx> { | |
223 | (*self)(ecx, span, token_tree) | |
224 | } | |
223e47cc LB |
225 | } |
226 | ||
1a4d82fc JJ |
227 | pub trait IdentMacroExpander { |
228 | fn expand<'cx>(&self, | |
229 | cx: &'cx mut ExtCtxt, | |
230 | sp: Span, | |
231 | ident: ast::Ident, | |
232 | token_tree: Vec<ast::TokenTree> ) | |
233 | -> Box<MacResult+'cx>; | |
234 | } | |
223e47cc | 235 | |
1a4d82fc JJ |
236 | pub type IdentMacroExpanderFn = |
237 | for<'cx> fn(&'cx mut ExtCtxt, Span, ast::Ident, Vec<ast::TokenTree>) -> Box<MacResult+'cx>; | |
238 | ||
239 | impl<F> IdentMacroExpander for F | |
240 | where F : for<'cx> Fn(&'cx mut ExtCtxt, Span, ast::Ident, | |
241 | Vec<ast::TokenTree>) -> Box<MacResult+'cx> | |
242 | { | |
243 | fn expand<'cx>(&self, | |
244 | cx: &'cx mut ExtCtxt, | |
245 | sp: Span, | |
246 | ident: ast::Ident, | |
247 | token_tree: Vec<ast::TokenTree> ) | |
248 | -> Box<MacResult+'cx> | |
249 | { | |
250 | (*self)(cx, sp, ident, token_tree) | |
251 | } | |
223e47cc LB |
252 | } |
253 | ||
c34b1796 | 254 | // Use a macro because forwarding to a simple function has type system issues |
9346a6ac | 255 | macro_rules! make_stmts_default { |
c34b1796 AL |
256 | ($me:expr) => { |
257 | $me.make_expr().map(|e| { | |
9346a6ac AL |
258 | SmallVector::one(P(codemap::respan( |
259 | e.span, ast::StmtExpr(e, ast::DUMMY_NODE_ID)))) | |
c34b1796 AL |
260 | }) |
261 | } | |
262 | } | |
263 | ||
1a4d82fc | 264 | /// The result of a macro expansion. The return values of the various |
c34b1796 | 265 | /// methods are spliced into the AST at the callsite of the macro. |
1a4d82fc JJ |
266 | pub trait MacResult { |
267 | /// Create an expression. | |
268 | fn make_expr(self: Box<Self>) -> Option<P<ast::Expr>> { | |
269 | None | |
270 | } | |
271 | /// Create zero or more items. | |
272 | fn make_items(self: Box<Self>) -> Option<SmallVector<P<ast::Item>>> { | |
273 | None | |
274 | } | |
223e47cc | 275 | |
c34b1796 AL |
276 | /// Create zero or more impl items. |
277 | fn make_impl_items(self: Box<Self>) -> Option<SmallVector<P<ast::ImplItem>>> { | |
1a4d82fc JJ |
278 | None |
279 | } | |
223e47cc | 280 | |
1a4d82fc JJ |
281 | /// Create a pattern. |
282 | fn make_pat(self: Box<Self>) -> Option<P<ast::Pat>> { | |
283 | None | |
284 | } | |
223e47cc | 285 | |
9346a6ac | 286 | /// Create zero or more statements. |
1a4d82fc JJ |
287 | /// |
288 | /// By default this attempts to create an expression statement, | |
289 | /// returning None if that fails. | |
9346a6ac AL |
290 | fn make_stmts(self: Box<Self>) -> Option<SmallVector<P<ast::Stmt>>> { |
291 | make_stmts_default!(self) | |
1a4d82fc JJ |
292 | } |
293 | } | |
223e47cc | 294 | |
c34b1796 AL |
295 | macro_rules! make_MacEager { |
296 | ( $( $fld:ident: $t:ty, )* ) => { | |
297 | /// `MacResult` implementation for the common case where you've already | |
298 | /// built each form of AST that you might return. | |
299 | #[derive(Default)] | |
300 | pub struct MacEager { | |
301 | $( | |
302 | pub $fld: Option<$t>, | |
303 | )* | |
304 | } | |
305 | ||
306 | impl MacEager { | |
307 | $( | |
308 | pub fn $fld(v: $t) -> Box<MacResult> { | |
d9579d0f | 309 | Box::new(MacEager { |
c34b1796 AL |
310 | $fld: Some(v), |
311 | ..Default::default() | |
d9579d0f | 312 | }) |
c34b1796 AL |
313 | } |
314 | )* | |
1a4d82fc JJ |
315 | } |
316 | } | |
317 | } | |
c34b1796 AL |
318 | |
319 | make_MacEager! { | |
320 | expr: P<ast::Expr>, | |
321 | pat: P<ast::Pat>, | |
322 | items: SmallVector<P<ast::Item>>, | |
323 | impl_items: SmallVector<P<ast::ImplItem>>, | |
9346a6ac | 324 | stmts: SmallVector<P<ast::Stmt>>, |
1a4d82fc | 325 | } |
c34b1796 AL |
326 | |
327 | impl MacResult for MacEager { | |
328 | fn make_expr(self: Box<Self>) -> Option<P<ast::Expr>> { | |
329 | self.expr | |
1a4d82fc | 330 | } |
c34b1796 AL |
331 | |
332 | fn make_items(self: Box<Self>) -> Option<SmallVector<P<ast::Item>>> { | |
333 | self.items | |
1a4d82fc | 334 | } |
223e47cc | 335 | |
c34b1796 AL |
336 | fn make_impl_items(self: Box<Self>) -> Option<SmallVector<P<ast::ImplItem>>> { |
337 | self.impl_items | |
1a4d82fc | 338 | } |
223e47cc | 339 | |
9346a6ac AL |
340 | fn make_stmts(self: Box<Self>) -> Option<SmallVector<P<ast::Stmt>>> { |
341 | match self.stmts.as_ref().map_or(0, |s| s.len()) { | |
342 | 0 => make_stmts_default!(self), | |
343 | _ => self.stmts, | |
c34b1796 AL |
344 | } |
345 | } | |
346 | ||
347 | fn make_pat(self: Box<Self>) -> Option<P<ast::Pat>> { | |
348 | if let Some(p) = self.pat { | |
349 | return Some(p); | |
350 | } | |
351 | if let Some(e) = self.expr { | |
352 | if let ast::ExprLit(_) = e.node { | |
353 | return Some(P(ast::Pat { | |
354 | id: ast::DUMMY_NODE_ID, | |
355 | span: e.span, | |
356 | node: ast::PatLit(e), | |
357 | })); | |
358 | } | |
359 | } | |
360 | None | |
1a4d82fc JJ |
361 | } |
362 | } | |
223e47cc | 363 | |
1a4d82fc JJ |
364 | /// Fill-in macro expansion result, to allow compilation to continue |
365 | /// after hitting errors. | |
c34b1796 | 366 | #[derive(Copy, Clone)] |
1a4d82fc JJ |
367 | pub struct DummyResult { |
368 | expr_only: bool, | |
369 | span: Span | |
223e47cc LB |
370 | } |
371 | ||
1a4d82fc JJ |
372 | impl DummyResult { |
373 | /// Create a default MacResult that can be anything. | |
374 | /// | |
375 | /// Use this as a return value after hitting any errors and | |
376 | /// calling `span_err`. | |
377 | pub fn any(sp: Span) -> Box<MacResult+'static> { | |
d9579d0f | 378 | Box::new(DummyResult { expr_only: false, span: sp }) |
1a4d82fc JJ |
379 | } |
380 | ||
381 | /// Create a default MacResult that can only be an expression. | |
382 | /// | |
383 | /// Use this for macros that must expand to an expression, so even | |
384 | /// if an error is encountered internally, the user will receive | |
385 | /// an error that they also used it in the wrong place. | |
386 | pub fn expr(sp: Span) -> Box<MacResult+'static> { | |
d9579d0f | 387 | Box::new(DummyResult { expr_only: true, span: sp }) |
1a4d82fc JJ |
388 | } |
389 | ||
390 | /// A plain dummy expression. | |
391 | pub fn raw_expr(sp: Span) -> P<ast::Expr> { | |
392 | P(ast::Expr { | |
393 | id: ast::DUMMY_NODE_ID, | |
394 | node: ast::ExprLit(P(codemap::respan(sp, ast::LitBool(false)))), | |
395 | span: sp, | |
396 | }) | |
397 | } | |
398 | ||
399 | /// A plain dummy pattern. | |
400 | pub fn raw_pat(sp: Span) -> ast::Pat { | |
401 | ast::Pat { | |
402 | id: ast::DUMMY_NODE_ID, | |
403 | node: ast::PatWild(ast::PatWildSingle), | |
404 | span: sp, | |
405 | } | |
406 | } | |
407 | ||
408 | } | |
409 | ||
410 | impl MacResult for DummyResult { | |
411 | fn make_expr(self: Box<DummyResult>) -> Option<P<ast::Expr>> { | |
412 | Some(DummyResult::raw_expr(self.span)) | |
413 | } | |
414 | fn make_pat(self: Box<DummyResult>) -> Option<P<ast::Pat>> { | |
415 | Some(P(DummyResult::raw_pat(self.span))) | |
416 | } | |
417 | fn make_items(self: Box<DummyResult>) -> Option<SmallVector<P<ast::Item>>> { | |
418 | // this code needs a comment... why not always just return the Some() ? | |
419 | if self.expr_only { | |
420 | None | |
421 | } else { | |
422 | Some(SmallVector::zero()) | |
423 | } | |
424 | } | |
c34b1796 | 425 | fn make_impl_items(self: Box<DummyResult>) -> Option<SmallVector<P<ast::ImplItem>>> { |
1a4d82fc JJ |
426 | if self.expr_only { |
427 | None | |
428 | } else { | |
429 | Some(SmallVector::zero()) | |
430 | } | |
431 | } | |
9346a6ac AL |
432 | fn make_stmts(self: Box<DummyResult>) -> Option<SmallVector<P<ast::Stmt>>> { |
433 | Some(SmallVector::one(P( | |
434 | codemap::respan(self.span, | |
435 | ast::StmtExpr(DummyResult::raw_expr(self.span), | |
436 | ast::DUMMY_NODE_ID))))) | |
1a4d82fc JJ |
437 | } |
438 | } | |
439 | ||
440 | /// An enum representing the different kinds of syntax extensions. | |
441 | pub enum SyntaxExtension { | |
442 | /// A syntax extension that is attached to an item and creates new items | |
443 | /// based upon it. | |
d9579d0f AL |
444 | #[unstable(feature = "rustc_private")] |
445 | #[deprecated(since = "1.0.0", reason = "replaced by MultiDecorator")] | |
446 | #[allow(deprecated)] | |
1a4d82fc JJ |
447 | Decorator(Box<ItemDecorator + 'static>), |
448 | ||
d9579d0f AL |
449 | /// A syntax extension that is attached to an item and creates new items |
450 | /// based upon it. | |
451 | /// | |
452 | /// `#[derive(...)]` is a `MultiItemDecorator`. | |
453 | MultiDecorator(Box<MultiItemDecorator + 'static>), | |
454 | ||
1a4d82fc JJ |
455 | /// A syntax extension that is attached to an item and modifies it |
456 | /// in-place. | |
d9579d0f AL |
457 | #[unstable(feature = "rustc_private")] |
458 | #[deprecated(since = "1.0.0", reason = "replaced by MultiModifier")] | |
459 | #[allow(deprecated)] | |
1a4d82fc JJ |
460 | Modifier(Box<ItemModifier + 'static>), |
461 | ||
85aaf69f SL |
462 | /// A syntax extension that is attached to an item and modifies it |
463 | /// in-place. More flexible version than Modifier. | |
464 | MultiModifier(Box<MultiItemModifier + 'static>), | |
465 | ||
1a4d82fc JJ |
466 | /// A normal, function-like syntax extension. |
467 | /// | |
468 | /// `bytes!` is a `NormalTT`. | |
c34b1796 AL |
469 | /// |
470 | /// The `bool` dictates whether the contents of the macro can | |
471 | /// directly use `#[unstable]` things (true == yes). | |
472 | NormalTT(Box<TTMacroExpander + 'static>, Option<Span>, bool), | |
1a4d82fc JJ |
473 | |
474 | /// A function-like syntax extension that has an extra ident before | |
475 | /// the block. | |
476 | /// | |
c34b1796 | 477 | IdentTT(Box<IdentMacroExpander + 'static>, Option<Span>, bool), |
1a4d82fc JJ |
478 | |
479 | /// Represents `macro_rules!` itself. | |
480 | MacroRulesTT, | |
481 | } | |
482 | ||
483 | pub type NamedSyntaxExtension = (Name, SyntaxExtension); | |
484 | ||
970d7e83 | 485 | pub struct BlockInfo { |
1a4d82fc JJ |
486 | /// Should macros escape from this scope? |
487 | pub macros_escape: bool, | |
488 | /// What are the pending renames? | |
489 | pub pending_renames: mtwt::RenameList, | |
970d7e83 LB |
490 | } |
491 | ||
1a4d82fc JJ |
492 | impl BlockInfo { |
493 | pub fn new() -> BlockInfo { | |
494 | BlockInfo { | |
495 | macros_escape: false, | |
496 | pending_renames: Vec::new(), | |
497 | } | |
498 | } | |
499 | } | |
970d7e83 | 500 | |
1a4d82fc JJ |
501 | /// The base map of methods for expanding syntax extension |
502 | /// AST nodes into full ASTs | |
85aaf69f SL |
503 | fn initial_syntax_expander_table<'feat>(ecfg: &expand::ExpansionConfig<'feat>) |
504 | -> SyntaxEnv { | |
223e47cc | 505 | // utility function to simplify creating NormalTT syntax extensions |
1a4d82fc | 506 | fn builtin_normal_expander(f: MacroExpanderFn) -> SyntaxExtension { |
c34b1796 | 507 | NormalTT(Box::new(f), None, false) |
1a4d82fc JJ |
508 | } |
509 | ||
510 | let mut syntax_expanders = SyntaxEnv::new(); | |
511 | syntax_expanders.insert(intern("macro_rules"), MacroRulesTT); | |
512 | syntax_expanders.insert(intern("format_args"), | |
c34b1796 AL |
513 | // format_args uses `unstable` things internally. |
514 | NormalTT(Box::new(ext::format::expand_format_args), None, true)); | |
1a4d82fc JJ |
515 | syntax_expanders.insert(intern("env"), |
516 | builtin_normal_expander( | |
517 | ext::env::expand_env)); | |
518 | syntax_expanders.insert(intern("option_env"), | |
519 | builtin_normal_expander( | |
520 | ext::env::expand_option_env)); | |
970d7e83 | 521 | syntax_expanders.insert(intern("concat_idents"), |
1a4d82fc JJ |
522 | builtin_normal_expander( |
523 | ext::concat_idents::expand_syntax_ext)); | |
524 | syntax_expanders.insert(intern("concat"), | |
525 | builtin_normal_expander( | |
526 | ext::concat::expand_syntax_ext)); | |
527 | syntax_expanders.insert(intern("log_syntax"), | |
528 | builtin_normal_expander( | |
529 | ext::log_syntax::expand_syntax_ext)); | |
c34b1796 AL |
530 | |
531 | ext::deriving::register_all(&mut syntax_expanders); | |
1a4d82fc | 532 | |
85aaf69f | 533 | if ecfg.enable_quotes() { |
1a4d82fc JJ |
534 | // Quasi-quoting expanders |
535 | syntax_expanders.insert(intern("quote_tokens"), | |
536 | builtin_normal_expander( | |
537 | ext::quote::expand_quote_tokens)); | |
538 | syntax_expanders.insert(intern("quote_expr"), | |
539 | builtin_normal_expander( | |
540 | ext::quote::expand_quote_expr)); | |
541 | syntax_expanders.insert(intern("quote_ty"), | |
542 | builtin_normal_expander( | |
543 | ext::quote::expand_quote_ty)); | |
1a4d82fc JJ |
544 | syntax_expanders.insert(intern("quote_item"), |
545 | builtin_normal_expander( | |
546 | ext::quote::expand_quote_item)); | |
547 | syntax_expanders.insert(intern("quote_pat"), | |
548 | builtin_normal_expander( | |
549 | ext::quote::expand_quote_pat)); | |
550 | syntax_expanders.insert(intern("quote_arm"), | |
551 | builtin_normal_expander( | |
552 | ext::quote::expand_quote_arm)); | |
553 | syntax_expanders.insert(intern("quote_stmt"), | |
554 | builtin_normal_expander( | |
555 | ext::quote::expand_quote_stmt)); | |
c34b1796 AL |
556 | syntax_expanders.insert(intern("quote_matcher"), |
557 | builtin_normal_expander( | |
558 | ext::quote::expand_quote_matcher)); | |
559 | syntax_expanders.insert(intern("quote_attr"), | |
560 | builtin_normal_expander( | |
561 | ext::quote::expand_quote_attr)); | |
1a4d82fc JJ |
562 | } |
563 | ||
564 | syntax_expanders.insert(intern("line"), | |
565 | builtin_normal_expander( | |
566 | ext::source_util::expand_line)); | |
567 | syntax_expanders.insert(intern("column"), | |
568 | builtin_normal_expander( | |
569 | ext::source_util::expand_column)); | |
570 | syntax_expanders.insert(intern("file"), | |
571 | builtin_normal_expander( | |
572 | ext::source_util::expand_file)); | |
573 | syntax_expanders.insert(intern("stringify"), | |
574 | builtin_normal_expander( | |
575 | ext::source_util::expand_stringify)); | |
576 | syntax_expanders.insert(intern("include"), | |
577 | builtin_normal_expander( | |
578 | ext::source_util::expand_include)); | |
579 | syntax_expanders.insert(intern("include_str"), | |
580 | builtin_normal_expander( | |
581 | ext::source_util::expand_include_str)); | |
582 | syntax_expanders.insert(intern("include_bytes"), | |
583 | builtin_normal_expander( | |
584 | ext::source_util::expand_include_bytes)); | |
585 | syntax_expanders.insert(intern("module_path"), | |
586 | builtin_normal_expander( | |
587 | ext::source_util::expand_mod)); | |
588 | syntax_expanders.insert(intern("asm"), | |
589 | builtin_normal_expander( | |
590 | ext::asm::expand_asm)); | |
591 | syntax_expanders.insert(intern("cfg"), | |
592 | builtin_normal_expander( | |
593 | ext::cfg::expand_cfg)); | |
1a4d82fc JJ |
594 | syntax_expanders.insert(intern("trace_macros"), |
595 | builtin_normal_expander( | |
596 | ext::trace_macros::expand_trace_macros)); | |
597 | syntax_expanders | |
598 | } | |
599 | ||
600 | /// One of these is made during expansion and incrementally updated as we go; | |
601 | /// when a macro expansion occurs, the resulting nodes have the backtrace() | |
602 | /// -> expn_info of their expansion context stored into their span. | |
603 | pub struct ExtCtxt<'a> { | |
604 | pub parse_sess: &'a parse::ParseSess, | |
605 | pub cfg: ast::CrateConfig, | |
606 | pub backtrace: ExpnId, | |
85aaf69f SL |
607 | pub ecfg: expand::ExpansionConfig<'a>, |
608 | pub use_std: bool, | |
1a4d82fc JJ |
609 | |
610 | pub mod_path: Vec<ast::Ident> , | |
1a4d82fc JJ |
611 | pub exported_macros: Vec<ast::MacroDef>, |
612 | ||
613 | pub syntax_env: SyntaxEnv, | |
85aaf69f | 614 | pub recursion_count: usize, |
1a4d82fc JJ |
615 | } |
616 | ||
617 | impl<'a> ExtCtxt<'a> { | |
618 | pub fn new(parse_sess: &'a parse::ParseSess, cfg: ast::CrateConfig, | |
85aaf69f | 619 | ecfg: expand::ExpansionConfig<'a>) -> ExtCtxt<'a> { |
1a4d82fc JJ |
620 | let env = initial_syntax_expander_table(&ecfg); |
621 | ExtCtxt { | |
970d7e83 LB |
622 | parse_sess: parse_sess, |
623 | cfg: cfg, | |
1a4d82fc JJ |
624 | backtrace: NO_EXPANSION, |
625 | mod_path: Vec::new(), | |
626 | ecfg: ecfg, | |
85aaf69f | 627 | use_std: true, |
1a4d82fc JJ |
628 | exported_macros: Vec::new(), |
629 | syntax_env: env, | |
630 | recursion_count: 0, | |
970d7e83 LB |
631 | } |
632 | } | |
633 | ||
85aaf69f SL |
634 | #[unstable(feature = "rustc_private")] |
635 | #[deprecated(since = "1.0.0", | |
636 | reason = "Replaced with `expander().fold_expr()`")] | |
1a4d82fc JJ |
637 | pub fn expand_expr(&mut self, e: P<ast::Expr>) -> P<ast::Expr> { |
638 | self.expander().fold_expr(e) | |
639 | } | |
640 | ||
641 | /// Returns a `Folder` for deeply expanding all macros in a AST node. | |
642 | pub fn expander<'b>(&'b mut self) -> expand::MacroExpander<'b, 'a> { | |
643 | expand::MacroExpander::new(self) | |
644 | } | |
645 | ||
646 | pub fn new_parser_from_tts(&self, tts: &[ast::TokenTree]) | |
647 | -> parser::Parser<'a> { | |
648 | parse::tts_to_parser(self.parse_sess, tts.to_vec(), self.cfg()) | |
649 | } | |
650 | ||
651 | pub fn codemap(&self) -> &'a CodeMap { &self.parse_sess.span_diagnostic.cm } | |
652 | pub fn parse_sess(&self) -> &'a parse::ParseSess { self.parse_sess } | |
653 | pub fn cfg(&self) -> ast::CrateConfig { self.cfg.clone() } | |
654 | pub fn call_site(&self) -> Span { | |
655 | self.codemap().with_expn_info(self.backtrace, |ei| match ei { | |
656 | Some(expn_info) => expn_info.call_site, | |
970d7e83 | 657 | None => self.bug("missing top span") |
1a4d82fc | 658 | }) |
970d7e83 | 659 | } |
1a4d82fc | 660 | pub fn backtrace(&self) -> ExpnId { self.backtrace } |
d9579d0f AL |
661 | |
662 | /// Original span that caused the current exapnsion to happen. | |
1a4d82fc JJ |
663 | pub fn original_span(&self) -> Span { |
664 | let mut expn_id = self.backtrace; | |
665 | let mut call_site = None; | |
666 | loop { | |
667 | match self.codemap().with_expn_info(expn_id, |ei| ei.map(|ei| ei.call_site)) { | |
668 | None => break, | |
669 | Some(cs) => { | |
670 | call_site = Some(cs); | |
671 | expn_id = cs.expn_id; | |
672 | } | |
223e47cc LB |
673 | } |
674 | } | |
1a4d82fc JJ |
675 | call_site.expect("missing expansion backtrace") |
676 | } | |
d9579d0f AL |
677 | |
678 | /// Returns span for the macro which originally caused the current expansion to happen. | |
679 | /// | |
680 | /// Stops backtracing at include! boundary. | |
681 | pub fn expansion_cause(&self) -> Span { | |
1a4d82fc | 682 | let mut expn_id = self.backtrace; |
d9579d0f | 683 | let mut last_macro = None; |
1a4d82fc | 684 | loop { |
d9579d0f AL |
685 | if self.codemap().with_expn_info(expn_id, |info| { |
686 | info.map_or(None, |i| { | |
687 | if i.callee.name == "include" { | |
688 | // Stop going up the backtrace once include! is encountered | |
689 | return None; | |
1a4d82fc | 690 | } |
d9579d0f AL |
691 | expn_id = i.call_site.expn_id; |
692 | if i.callee.format != CompilerExpansion { | |
693 | last_macro = Some(i.call_site) | |
694 | } | |
695 | return Some(()); | |
696 | }) | |
697 | }).is_none() { | |
698 | break | |
223e47cc | 699 | } |
1a4d82fc | 700 | } |
d9579d0f | 701 | last_macro.expect("missing expansion backtrace") |
1a4d82fc JJ |
702 | } |
703 | ||
704 | pub fn mod_push(&mut self, i: ast::Ident) { self.mod_path.push(i); } | |
705 | pub fn mod_pop(&mut self) { self.mod_path.pop().unwrap(); } | |
706 | pub fn mod_path(&self) -> Vec<ast::Ident> { | |
707 | let mut v = Vec::new(); | |
c34b1796 | 708 | v.push(token::str_to_ident(&self.ecfg.crate_name)); |
85aaf69f | 709 | v.extend(self.mod_path.iter().cloned()); |
1a4d82fc JJ |
710 | return v; |
711 | } | |
712 | pub fn bt_push(&mut self, ei: ExpnInfo) { | |
713 | self.recursion_count += 1; | |
714 | if self.recursion_count > self.ecfg.recursion_limit { | |
9346a6ac | 715 | panic!(self.span_fatal(ei.call_site, |
1a4d82fc | 716 | &format!("recursion limit reached while expanding the macro `{}`", |
9346a6ac | 717 | ei.callee.name))); |
1a4d82fc JJ |
718 | } |
719 | ||
720 | let mut call_site = ei.call_site; | |
721 | call_site.expn_id = self.backtrace; | |
722 | self.backtrace = self.codemap().record_expansion(ExpnInfo { | |
723 | call_site: call_site, | |
724 | callee: ei.callee | |
725 | }); | |
726 | } | |
727 | pub fn bt_pop(&mut self) { | |
728 | match self.backtrace { | |
729 | NO_EXPANSION => self.bug("tried to pop without a push"), | |
730 | expn_id => { | |
731 | self.recursion_count -= 1; | |
732 | self.backtrace = self.codemap().with_expn_info(expn_id, |expn_info| { | |
733 | expn_info.map_or(NO_EXPANSION, |ei| ei.call_site.expn_id) | |
734 | }); | |
735 | } | |
736 | } | |
737 | } | |
738 | ||
739 | pub fn insert_macro(&mut self, def: ast::MacroDef) { | |
740 | if def.export { | |
741 | self.exported_macros.push(def.clone()); | |
742 | } | |
743 | if def.use_locally { | |
744 | let ext = macro_rules::compile(self, &def); | |
745 | self.syntax_env.insert(def.ident.name, ext); | |
223e47cc | 746 | } |
223e47cc | 747 | } |
1a4d82fc JJ |
748 | |
749 | /// Emit `msg` attached to `sp`, and stop compilation immediately. | |
750 | /// | |
751 | /// `span_err` should be strongly preferred where-ever possible: | |
752 | /// this should *only* be used when | |
753 | /// - continuing has a high risk of flow-on errors (e.g. errors in | |
754 | /// declaring a macro would cause all uses of that macro to | |
755 | /// complain about "undefined macro"), or | |
756 | /// - there is literally nothing else that can be done (however, | |
757 | /// in most cases one can construct a dummy expression/item to | |
758 | /// substitute; we never hit resolve/type-checking so the dummy | |
759 | /// value doesn't have to match anything) | |
760 | pub fn span_fatal(&self, sp: Span, msg: &str) -> ! { | |
9346a6ac | 761 | panic!(self.parse_sess.span_diagnostic.span_fatal(sp, msg)); |
970d7e83 | 762 | } |
1a4d82fc JJ |
763 | |
764 | /// Emit `msg` attached to `sp`, without immediately stopping | |
765 | /// compilation. | |
766 | /// | |
767 | /// Compilation will be stopped in the near future (at the end of | |
768 | /// the macro expansion phase). | |
769 | pub fn span_err(&self, sp: Span, msg: &str) { | |
970d7e83 LB |
770 | self.parse_sess.span_diagnostic.span_err(sp, msg); |
771 | } | |
1a4d82fc | 772 | pub fn span_warn(&self, sp: Span, msg: &str) { |
970d7e83 LB |
773 | self.parse_sess.span_diagnostic.span_warn(sp, msg); |
774 | } | |
1a4d82fc | 775 | pub fn span_unimpl(&self, sp: Span, msg: &str) -> ! { |
970d7e83 LB |
776 | self.parse_sess.span_diagnostic.span_unimpl(sp, msg); |
777 | } | |
1a4d82fc | 778 | pub fn span_bug(&self, sp: Span, msg: &str) -> ! { |
970d7e83 LB |
779 | self.parse_sess.span_diagnostic.span_bug(sp, msg); |
780 | } | |
1a4d82fc | 781 | pub fn span_note(&self, sp: Span, msg: &str) { |
1a4d82fc JJ |
782 | self.parse_sess.span_diagnostic.span_note(sp, msg); |
783 | } | |
784 | pub fn span_help(&self, sp: Span, msg: &str) { | |
1a4d82fc JJ |
785 | self.parse_sess.span_diagnostic.span_help(sp, msg); |
786 | } | |
c34b1796 | 787 | pub fn fileline_help(&self, sp: Span, msg: &str) { |
c34b1796 AL |
788 | self.parse_sess.span_diagnostic.fileline_help(sp, msg); |
789 | } | |
970d7e83 | 790 | pub fn bug(&self, msg: &str) -> ! { |
970d7e83 LB |
791 | self.parse_sess.span_diagnostic.handler().bug(msg); |
792 | } | |
970d7e83 | 793 | pub fn trace_macros(&self) -> bool { |
d9579d0f | 794 | self.ecfg.trace_mac |
970d7e83 | 795 | } |
1a4d82fc | 796 | pub fn set_trace_macros(&mut self, x: bool) { |
d9579d0f | 797 | self.ecfg.trace_mac = x |
970d7e83 | 798 | } |
1a4d82fc | 799 | pub fn ident_of(&self, st: &str) -> ast::Ident { |
970d7e83 LB |
800 | str_to_ident(st) |
801 | } | |
85aaf69f SL |
802 | pub fn ident_of_std(&self, st: &str) -> ast::Ident { |
803 | self.ident_of(if self.use_std { "std" } else { st }) | |
804 | } | |
1a4d82fc JJ |
805 | pub fn name_of(&self, st: &str) -> ast::Name { |
806 | token::intern(st) | |
223e47cc LB |
807 | } |
808 | } | |
809 | ||
1a4d82fc JJ |
810 | /// Extract a string literal from the macro expanded version of `expr`, |
811 | /// emitting `err_msg` if `expr` is not a string literal. This does not stop | |
812 | /// compilation on error, merely emits a non-fatal error and returns None. | |
813 | pub fn expr_to_string(cx: &mut ExtCtxt, expr: P<ast::Expr>, err_msg: &str) | |
814 | -> Option<(InternedString, ast::StrStyle)> { | |
815 | // we want to be able to handle e.g. concat("foo", "bar") | |
816 | let expr = cx.expander().fold_expr(expr); | |
223e47cc | 817 | match expr.node { |
1a4d82fc JJ |
818 | ast::ExprLit(ref l) => match l.node { |
819 | ast::LitStr(ref s, style) => return Some(((*s).clone(), style)), | |
820 | _ => cx.span_err(l.span, err_msg) | |
821 | }, | |
822 | _ => cx.span_err(expr.span, err_msg) | |
223e47cc | 823 | } |
1a4d82fc | 824 | None |
223e47cc LB |
825 | } |
826 | ||
1a4d82fc JJ |
827 | /// Non-fatally assert that `tts` is empty. Note that this function |
828 | /// returns even when `tts` is non-empty, macros that *need* to stop | |
829 | /// compilation should call | |
830 | /// `cx.parse_sess.span_diagnostic.abort_if_errors()` (this should be | |
831 | /// done as rarely as possible). | |
832 | pub fn check_zero_tts(cx: &ExtCtxt, | |
833 | sp: Span, | |
834 | tts: &[ast::TokenTree], | |
223e47cc | 835 | name: &str) { |
9346a6ac | 836 | if !tts.is_empty() { |
c34b1796 | 837 | cx.span_err(sp, &format!("{} takes no arguments", name)); |
223e47cc LB |
838 | } |
839 | } | |
840 | ||
1a4d82fc JJ |
841 | /// Extract the string literal from the first token of `tts`. If this |
842 | /// is not a string literal, emit an error and return None. | |
843 | pub fn get_single_str_from_tts(cx: &mut ExtCtxt, | |
844 | sp: Span, | |
845 | tts: &[ast::TokenTree], | |
846 | name: &str) | |
847 | -> Option<String> { | |
848 | let mut p = cx.new_parser_from_tts(tts); | |
849 | if p.token == token::Eof { | |
c34b1796 | 850 | cx.span_err(sp, &format!("{} takes 1 argument", name)); |
1a4d82fc JJ |
851 | return None |
852 | } | |
853 | let ret = cx.expander().fold_expr(p.parse_expr()); | |
854 | if p.token != token::Eof { | |
c34b1796 | 855 | cx.span_err(sp, &format!("{} takes 1 argument", name)); |
1a4d82fc JJ |
856 | } |
857 | expr_to_string(cx, ret, "argument must be a string literal").map(|(s, _)| { | |
85aaf69f | 858 | s.to_string() |
1a4d82fc | 859 | }) |
223e47cc LB |
860 | } |
861 | ||
1a4d82fc JJ |
862 | /// Extract comma-separated expressions from `tts`. If there is a |
863 | /// parsing error, emit a non-fatal error and return None. | |
864 | pub fn get_exprs_from_tts(cx: &mut ExtCtxt, | |
865 | sp: Span, | |
866 | tts: &[ast::TokenTree]) -> Option<Vec<P<ast::Expr>>> { | |
867 | let mut p = cx.new_parser_from_tts(tts); | |
868 | let mut es = Vec::new(); | |
869 | while p.token != token::Eof { | |
870 | es.push(cx.expander().fold_expr(p.parse_expr())); | |
9346a6ac | 871 | if panictry!(p.eat(&token::Comma)){ |
1a4d82fc JJ |
872 | continue; |
873 | } | |
874 | if p.token != token::Eof { | |
875 | cx.span_err(sp, "expected token: `,`"); | |
876 | return None; | |
223e47cc | 877 | } |
223e47cc | 878 | } |
1a4d82fc | 879 | Some(es) |
223e47cc LB |
880 | } |
881 | ||
1a4d82fc JJ |
882 | /// In order to have some notion of scoping for macros, |
883 | /// we want to implement the notion of a transformation | |
884 | /// environment. | |
885 | /// | |
886 | /// This environment maps Names to SyntaxExtensions. | |
887 | pub struct SyntaxEnv { | |
888 | chain: Vec<MapChainFrame> , | |
889 | } | |
223e47cc | 890 | |
1a4d82fc | 891 | // impl question: how to implement it? Initially, the |
223e47cc LB |
892 | // env will contain only macros, so it might be painful |
893 | // to add an empty frame for every context. Let's just | |
894 | // get it working, first.... | |
895 | ||
896 | // NB! the mutability of the underlying maps means that | |
897 | // if expansion is out-of-order, a deeper scope may be | |
898 | // able to refer to a macro that was added to an enclosing | |
899 | // scope lexically later than the deeper scope. | |
900 | ||
1a4d82fc JJ |
901 | struct MapChainFrame { |
902 | info: BlockInfo, | |
903 | map: HashMap<Name, Rc<SyntaxExtension>>, | |
223e47cc LB |
904 | } |
905 | ||
1a4d82fc JJ |
906 | impl SyntaxEnv { |
907 | fn new() -> SyntaxEnv { | |
908 | let mut map = SyntaxEnv { chain: Vec::new() }; | |
909 | map.push_frame(); | |
910 | map | |
223e47cc LB |
911 | } |
912 | ||
1a4d82fc JJ |
913 | pub fn push_frame(&mut self) { |
914 | self.chain.push(MapChainFrame { | |
915 | info: BlockInfo::new(), | |
916 | map: HashMap::new(), | |
917 | }); | |
223e47cc LB |
918 | } |
919 | ||
1a4d82fc JJ |
920 | pub fn pop_frame(&mut self) { |
921 | assert!(self.chain.len() > 1, "too many pops on MapChain!"); | |
922 | self.chain.pop(); | |
223e47cc LB |
923 | } |
924 | ||
1a4d82fc JJ |
925 | fn find_escape_frame<'a>(&'a mut self) -> &'a mut MapChainFrame { |
926 | for (i, frame) in self.chain.iter_mut().enumerate().rev() { | |
927 | if !frame.info.macros_escape || i == 0 { | |
928 | return frame | |
929 | } | |
223e47cc | 930 | } |
1a4d82fc | 931 | unreachable!() |
223e47cc LB |
932 | } |
933 | ||
1a4d82fc JJ |
934 | pub fn find(&self, k: &Name) -> Option<Rc<SyntaxExtension>> { |
935 | for frame in self.chain.iter().rev() { | |
936 | match frame.map.get(k) { | |
937 | Some(v) => return Some(v.clone()), | |
938 | None => {} | |
223e47cc LB |
939 | } |
940 | } | |
1a4d82fc | 941 | None |
223e47cc LB |
942 | } |
943 | ||
1a4d82fc JJ |
944 | pub fn insert(&mut self, k: Name, v: SyntaxExtension) { |
945 | self.find_escape_frame().map.insert(k, Rc::new(v)); | |
223e47cc LB |
946 | } |
947 | ||
1a4d82fc JJ |
948 | pub fn info<'a>(&'a mut self) -> &'a mut BlockInfo { |
949 | let last_chain_index = self.chain.len() - 1; | |
950 | &mut self.chain[last_chain_index].info | |
223e47cc LB |
951 | } |
952 | } |