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