]>
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 | ||
9e0c209e | 11 | pub use self::SyntaxExtension::{MultiDecorator, MultiModifier, NormalTT, IdentTT}; |
1a4d82fc | 12 | |
9e0c209e | 13 | use ast::{self, Attribute, Name, PatKind}; |
3157f602 | 14 | use attr::HasAttrs; |
9e0c209e | 15 | use codemap::{self, CodeMap, ExpnInfo, Spanned, respan}; |
3157f602 | 16 | use syntax_pos::{Span, ExpnId, NO_EXPANSION}; |
9cc50fc6 | 17 | use errors::DiagnosticBuilder; |
c30ab7b3 | 18 | use ext::expand::{self, Expansion}; |
9e0c209e SL |
19 | use ext::hygiene::Mark; |
20 | use fold::{self, Folder}; | |
21 | use parse::{self, parser}; | |
223e47cc | 22 | use parse::token; |
9e0c209e | 23 | use parse::token::{InternedString, str_to_ident}; |
1a4d82fc JJ |
24 | use ptr::P; |
25 | use util::small_vector::SmallVector; | |
1a4d82fc | 26 | |
9e0c209e | 27 | use std::path::PathBuf; |
1a4d82fc | 28 | use std::rc::Rc; |
c34b1796 | 29 | use std::default::Default; |
9e0c209e | 30 | use tokenstream::{self, TokenStream}; |
1a4d82fc | 31 | |
223e47cc | 32 | |
85aaf69f SL |
33 | #[derive(Debug,Clone)] |
34 | pub enum Annotatable { | |
35 | Item(P<ast::Item>), | |
c34b1796 AL |
36 | TraitItem(P<ast::TraitItem>), |
37 | ImplItem(P<ast::ImplItem>), | |
85aaf69f SL |
38 | } |
39 | ||
3157f602 | 40 | impl HasAttrs for Annotatable { |
9e0c209e | 41 | fn attrs(&self) -> &[Attribute] { |
85aaf69f | 42 | match *self { |
3157f602 XL |
43 | Annotatable::Item(ref item) => &item.attrs, |
44 | Annotatable::TraitItem(ref trait_item) => &trait_item.attrs, | |
45 | Annotatable::ImplItem(ref impl_item) => &impl_item.attrs, | |
85aaf69f SL |
46 | } |
47 | } | |
48 | ||
9e0c209e | 49 | fn map_attrs<F: FnOnce(Vec<Attribute>) -> Vec<Attribute>>(self, f: F) -> Self { |
85aaf69f | 50 | match self { |
3157f602 XL |
51 | Annotatable::Item(item) => Annotatable::Item(item.map_attrs(f)), |
52 | Annotatable::TraitItem(trait_item) => Annotatable::TraitItem(trait_item.map_attrs(f)), | |
53 | Annotatable::ImplItem(impl_item) => Annotatable::ImplItem(impl_item.map_attrs(f)), | |
85aaf69f SL |
54 | } |
55 | } | |
3157f602 XL |
56 | } |
57 | ||
58 | impl Annotatable { | |
85aaf69f SL |
59 | pub fn expect_item(self) -> P<ast::Item> { |
60 | match self { | |
61 | Annotatable::Item(i) => i, | |
62 | _ => panic!("expected Item") | |
63 | } | |
64 | } | |
65 | ||
d9579d0f AL |
66 | pub fn map_item_or<F, G>(self, mut f: F, mut or: G) -> Annotatable |
67 | where F: FnMut(P<ast::Item>) -> P<ast::Item>, | |
68 | G: FnMut(Annotatable) -> Annotatable | |
69 | { | |
70 | match self { | |
71 | Annotatable::Item(i) => Annotatable::Item(f(i)), | |
72 | _ => or(self) | |
73 | } | |
74 | } | |
75 | ||
7453a54e | 76 | pub fn expect_trait_item(self) -> ast::TraitItem { |
85aaf69f | 77 | match self { |
7453a54e | 78 | Annotatable::TraitItem(i) => i.unwrap(), |
85aaf69f SL |
79 | _ => panic!("expected Item") |
80 | } | |
81 | } | |
82 | ||
7453a54e | 83 | pub fn expect_impl_item(self) -> ast::ImplItem { |
85aaf69f | 84 | match self { |
7453a54e | 85 | Annotatable::ImplItem(i) => i.unwrap(), |
85aaf69f SL |
86 | _ => panic!("expected Item") |
87 | } | |
88 | } | |
89 | } | |
90 | ||
d9579d0f AL |
91 | // A more flexible ItemDecorator. |
92 | pub trait MultiItemDecorator { | |
93 | fn expand(&self, | |
94 | ecx: &mut ExtCtxt, | |
95 | sp: Span, | |
96 | meta_item: &ast::MetaItem, | |
62682a34 | 97 | item: &Annotatable, |
d9579d0f AL |
98 | push: &mut FnMut(Annotatable)); |
99 | } | |
100 | ||
101 | impl<F> MultiItemDecorator for F | |
62682a34 | 102 | where F : Fn(&mut ExtCtxt, Span, &ast::MetaItem, &Annotatable, &mut FnMut(Annotatable)) |
d9579d0f AL |
103 | { |
104 | fn expand(&self, | |
105 | ecx: &mut ExtCtxt, | |
106 | sp: Span, | |
107 | meta_item: &ast::MetaItem, | |
62682a34 | 108 | item: &Annotatable, |
d9579d0f AL |
109 | push: &mut FnMut(Annotatable)) { |
110 | (*self)(ecx, sp, meta_item, item, push) | |
111 | } | |
112 | } | |
113 | ||
3157f602 | 114 | // `meta_item` is the annotation, and `item` is the item being modified. |
85aaf69f SL |
115 | // FIXME Decorators should follow the same pattern too. |
116 | pub trait MultiItemModifier { | |
117 | fn expand(&self, | |
118 | ecx: &mut ExtCtxt, | |
119 | span: Span, | |
120 | meta_item: &ast::MetaItem, | |
121 | item: Annotatable) | |
3157f602 | 122 | -> Vec<Annotatable>; |
85aaf69f SL |
123 | } |
124 | ||
3157f602 XL |
125 | impl<F, T> MultiItemModifier for F |
126 | where F: Fn(&mut ExtCtxt, Span, &ast::MetaItem, Annotatable) -> T, | |
127 | T: Into<Vec<Annotatable>>, | |
85aaf69f SL |
128 | { |
129 | fn expand(&self, | |
130 | ecx: &mut ExtCtxt, | |
131 | span: Span, | |
132 | meta_item: &ast::MetaItem, | |
133 | item: Annotatable) | |
3157f602 XL |
134 | -> Vec<Annotatable> { |
135 | (*self)(ecx, span, meta_item, item).into() | |
136 | } | |
137 | } | |
138 | ||
139 | impl Into<Vec<Annotatable>> for Annotatable { | |
140 | fn into(self) -> Vec<Annotatable> { | |
141 | vec![self] | |
85aaf69f SL |
142 | } |
143 | } | |
144 | ||
9e0c209e SL |
145 | pub trait ProcMacro { |
146 | fn expand<'cx>(&self, | |
147 | ecx: &'cx mut ExtCtxt, | |
148 | span: Span, | |
149 | ts: TokenStream) | |
150 | -> TokenStream; | |
151 | } | |
152 | ||
153 | impl<F> ProcMacro for F | |
154 | where F: Fn(TokenStream) -> TokenStream | |
155 | { | |
156 | fn expand<'cx>(&self, | |
157 | _ecx: &'cx mut ExtCtxt, | |
158 | _span: Span, | |
159 | ts: TokenStream) | |
160 | -> TokenStream { | |
161 | // FIXME setup implicit context in TLS before calling self. | |
162 | (*self)(ts) | |
163 | } | |
164 | } | |
165 | ||
166 | pub trait AttrProcMacro { | |
167 | fn expand<'cx>(&self, | |
168 | ecx: &'cx mut ExtCtxt, | |
169 | span: Span, | |
170 | annotation: TokenStream, | |
171 | annotated: TokenStream) | |
172 | -> TokenStream; | |
173 | } | |
174 | ||
175 | impl<F> AttrProcMacro for F | |
176 | where F: Fn(TokenStream, TokenStream) -> TokenStream | |
177 | { | |
178 | fn expand<'cx>(&self, | |
179 | _ecx: &'cx mut ExtCtxt, | |
180 | _span: Span, | |
181 | annotation: TokenStream, | |
182 | annotated: TokenStream) | |
183 | -> TokenStream { | |
184 | // FIXME setup implicit context in TLS before calling self. | |
185 | (*self)(annotation, annotated) | |
186 | } | |
187 | } | |
188 | ||
1a4d82fc JJ |
189 | /// Represents a thing that maps token trees to Macro Results |
190 | pub trait TTMacroExpander { | |
191 | fn expand<'cx>(&self, | |
192 | ecx: &'cx mut ExtCtxt, | |
193 | span: Span, | |
3157f602 | 194 | token_tree: &[tokenstream::TokenTree]) |
1a4d82fc | 195 | -> Box<MacResult+'cx>; |
223e47cc LB |
196 | } |
197 | ||
1a4d82fc | 198 | pub type MacroExpanderFn = |
3157f602 XL |
199 | for<'cx> fn(&'cx mut ExtCtxt, Span, &[tokenstream::TokenTree]) |
200 | -> Box<MacResult+'cx>; | |
223e47cc | 201 | |
1a4d82fc | 202 | impl<F> TTMacroExpander for F |
3157f602 XL |
203 | where F : for<'cx> Fn(&'cx mut ExtCtxt, Span, &[tokenstream::TokenTree]) |
204 | -> Box<MacResult+'cx> | |
1a4d82fc JJ |
205 | { |
206 | fn expand<'cx>(&self, | |
207 | ecx: &'cx mut ExtCtxt, | |
208 | span: Span, | |
3157f602 | 209 | token_tree: &[tokenstream::TokenTree]) |
1a4d82fc JJ |
210 | -> Box<MacResult+'cx> { |
211 | (*self)(ecx, span, token_tree) | |
212 | } | |
223e47cc LB |
213 | } |
214 | ||
1a4d82fc JJ |
215 | pub trait IdentMacroExpander { |
216 | fn expand<'cx>(&self, | |
217 | cx: &'cx mut ExtCtxt, | |
218 | sp: Span, | |
219 | ident: ast::Ident, | |
9e0c209e SL |
220 | token_tree: Vec<tokenstream::TokenTree>, |
221 | attrs: Vec<ast::Attribute>) | |
1a4d82fc JJ |
222 | -> Box<MacResult+'cx>; |
223 | } | |
223e47cc | 224 | |
1a4d82fc | 225 | pub type IdentMacroExpanderFn = |
3157f602 XL |
226 | for<'cx> fn(&'cx mut ExtCtxt, Span, ast::Ident, Vec<tokenstream::TokenTree>) |
227 | -> Box<MacResult+'cx>; | |
1a4d82fc JJ |
228 | |
229 | impl<F> IdentMacroExpander for F | |
230 | where F : for<'cx> Fn(&'cx mut ExtCtxt, Span, ast::Ident, | |
3157f602 | 231 | Vec<tokenstream::TokenTree>) -> Box<MacResult+'cx> |
1a4d82fc JJ |
232 | { |
233 | fn expand<'cx>(&self, | |
234 | cx: &'cx mut ExtCtxt, | |
235 | sp: Span, | |
236 | ident: ast::Ident, | |
9e0c209e SL |
237 | token_tree: Vec<tokenstream::TokenTree>, |
238 | _attrs: Vec<ast::Attribute>) | |
1a4d82fc JJ |
239 | -> Box<MacResult+'cx> |
240 | { | |
241 | (*self)(cx, sp, ident, token_tree) | |
242 | } | |
223e47cc LB |
243 | } |
244 | ||
c34b1796 | 245 | // Use a macro because forwarding to a simple function has type system issues |
9346a6ac | 246 | macro_rules! make_stmts_default { |
c34b1796 | 247 | ($me:expr) => { |
3157f602 XL |
248 | $me.make_expr().map(|e| SmallVector::one(ast::Stmt { |
249 | id: ast::DUMMY_NODE_ID, | |
250 | span: e.span, | |
251 | node: ast::StmtKind::Expr(e), | |
252 | })) | |
c34b1796 AL |
253 | } |
254 | } | |
255 | ||
1a4d82fc | 256 | /// The result of a macro expansion. The return values of the various |
c34b1796 | 257 | /// methods are spliced into the AST at the callsite of the macro. |
1a4d82fc JJ |
258 | pub trait MacResult { |
259 | /// Create an expression. | |
260 | fn make_expr(self: Box<Self>) -> Option<P<ast::Expr>> { | |
261 | None | |
262 | } | |
263 | /// Create zero or more items. | |
264 | fn make_items(self: Box<Self>) -> Option<SmallVector<P<ast::Item>>> { | |
265 | None | |
266 | } | |
223e47cc | 267 | |
c34b1796 | 268 | /// Create zero or more impl items. |
7453a54e | 269 | fn make_impl_items(self: Box<Self>) -> Option<SmallVector<ast::ImplItem>> { |
1a4d82fc JJ |
270 | None |
271 | } | |
223e47cc | 272 | |
3157f602 XL |
273 | /// Create zero or more trait items. |
274 | fn make_trait_items(self: Box<Self>) -> Option<SmallVector<ast::TraitItem>> { | |
275 | None | |
276 | } | |
277 | ||
1a4d82fc JJ |
278 | /// Create a pattern. |
279 | fn make_pat(self: Box<Self>) -> Option<P<ast::Pat>> { | |
280 | None | |
281 | } | |
223e47cc | 282 | |
9346a6ac | 283 | /// Create zero or more statements. |
1a4d82fc JJ |
284 | /// |
285 | /// By default this attempts to create an expression statement, | |
286 | /// returning None if that fails. | |
7453a54e | 287 | fn make_stmts(self: Box<Self>) -> Option<SmallVector<ast::Stmt>> { |
9346a6ac | 288 | make_stmts_default!(self) |
1a4d82fc | 289 | } |
e9174d1e SL |
290 | |
291 | fn make_ty(self: Box<Self>) -> Option<P<ast::Ty>> { | |
292 | None | |
293 | } | |
1a4d82fc | 294 | } |
223e47cc | 295 | |
c34b1796 AL |
296 | macro_rules! make_MacEager { |
297 | ( $( $fld:ident: $t:ty, )* ) => { | |
298 | /// `MacResult` implementation for the common case where you've already | |
299 | /// built each form of AST that you might return. | |
300 | #[derive(Default)] | |
301 | pub struct MacEager { | |
302 | $( | |
303 | pub $fld: Option<$t>, | |
304 | )* | |
305 | } | |
306 | ||
307 | impl MacEager { | |
308 | $( | |
309 | pub fn $fld(v: $t) -> Box<MacResult> { | |
d9579d0f | 310 | Box::new(MacEager { |
c34b1796 AL |
311 | $fld: Some(v), |
312 | ..Default::default() | |
d9579d0f | 313 | }) |
c34b1796 AL |
314 | } |
315 | )* | |
1a4d82fc JJ |
316 | } |
317 | } | |
318 | } | |
c34b1796 AL |
319 | |
320 | make_MacEager! { | |
321 | expr: P<ast::Expr>, | |
322 | pat: P<ast::Pat>, | |
323 | items: SmallVector<P<ast::Item>>, | |
7453a54e | 324 | impl_items: SmallVector<ast::ImplItem>, |
3157f602 | 325 | trait_items: SmallVector<ast::TraitItem>, |
7453a54e | 326 | stmts: SmallVector<ast::Stmt>, |
e9174d1e | 327 | ty: P<ast::Ty>, |
1a4d82fc | 328 | } |
c34b1796 AL |
329 | |
330 | impl MacResult for MacEager { | |
331 | fn make_expr(self: Box<Self>) -> Option<P<ast::Expr>> { | |
332 | self.expr | |
1a4d82fc | 333 | } |
c34b1796 AL |
334 | |
335 | fn make_items(self: Box<Self>) -> Option<SmallVector<P<ast::Item>>> { | |
336 | self.items | |
1a4d82fc | 337 | } |
223e47cc | 338 | |
7453a54e | 339 | fn make_impl_items(self: Box<Self>) -> Option<SmallVector<ast::ImplItem>> { |
c34b1796 | 340 | self.impl_items |
1a4d82fc | 341 | } |
223e47cc | 342 | |
3157f602 XL |
343 | fn make_trait_items(self: Box<Self>) -> Option<SmallVector<ast::TraitItem>> { |
344 | self.trait_items | |
345 | } | |
346 | ||
7453a54e | 347 | fn make_stmts(self: Box<Self>) -> Option<SmallVector<ast::Stmt>> { |
9346a6ac AL |
348 | match self.stmts.as_ref().map_or(0, |s| s.len()) { |
349 | 0 => make_stmts_default!(self), | |
350 | _ => self.stmts, | |
c34b1796 AL |
351 | } |
352 | } | |
353 | ||
354 | fn make_pat(self: Box<Self>) -> Option<P<ast::Pat>> { | |
355 | if let Some(p) = self.pat { | |
356 | return Some(p); | |
357 | } | |
358 | if let Some(e) = self.expr { | |
7453a54e | 359 | if let ast::ExprKind::Lit(_) = e.node { |
c34b1796 AL |
360 | return Some(P(ast::Pat { |
361 | id: ast::DUMMY_NODE_ID, | |
362 | span: e.span, | |
7453a54e | 363 | node: PatKind::Lit(e), |
c34b1796 AL |
364 | })); |
365 | } | |
366 | } | |
367 | None | |
1a4d82fc | 368 | } |
e9174d1e SL |
369 | |
370 | fn make_ty(self: Box<Self>) -> Option<P<ast::Ty>> { | |
371 | self.ty | |
372 | } | |
1a4d82fc | 373 | } |
223e47cc | 374 | |
1a4d82fc JJ |
375 | /// Fill-in macro expansion result, to allow compilation to continue |
376 | /// after hitting errors. | |
c34b1796 | 377 | #[derive(Copy, Clone)] |
1a4d82fc JJ |
378 | pub struct DummyResult { |
379 | expr_only: bool, | |
380 | span: Span | |
223e47cc LB |
381 | } |
382 | ||
1a4d82fc JJ |
383 | impl DummyResult { |
384 | /// Create a default MacResult that can be anything. | |
385 | /// | |
386 | /// Use this as a return value after hitting any errors and | |
387 | /// calling `span_err`. | |
388 | pub fn any(sp: Span) -> Box<MacResult+'static> { | |
d9579d0f | 389 | Box::new(DummyResult { expr_only: false, span: sp }) |
1a4d82fc JJ |
390 | } |
391 | ||
392 | /// Create a default MacResult that can only be an expression. | |
393 | /// | |
394 | /// Use this for macros that must expand to an expression, so even | |
395 | /// if an error is encountered internally, the user will receive | |
396 | /// an error that they also used it in the wrong place. | |
397 | pub fn expr(sp: Span) -> Box<MacResult+'static> { | |
d9579d0f | 398 | Box::new(DummyResult { expr_only: true, span: sp }) |
1a4d82fc JJ |
399 | } |
400 | ||
401 | /// A plain dummy expression. | |
402 | pub fn raw_expr(sp: Span) -> P<ast::Expr> { | |
403 | P(ast::Expr { | |
404 | id: ast::DUMMY_NODE_ID, | |
7453a54e | 405 | node: ast::ExprKind::Lit(P(codemap::respan(sp, ast::LitKind::Bool(false)))), |
1a4d82fc | 406 | span: sp, |
3157f602 | 407 | attrs: ast::ThinVec::new(), |
1a4d82fc JJ |
408 | }) |
409 | } | |
410 | ||
411 | /// A plain dummy pattern. | |
412 | pub fn raw_pat(sp: Span) -> ast::Pat { | |
413 | ast::Pat { | |
414 | id: ast::DUMMY_NODE_ID, | |
7453a54e | 415 | node: PatKind::Wild, |
1a4d82fc JJ |
416 | span: sp, |
417 | } | |
418 | } | |
419 | ||
e9174d1e SL |
420 | pub fn raw_ty(sp: Span) -> P<ast::Ty> { |
421 | P(ast::Ty { | |
422 | id: ast::DUMMY_NODE_ID, | |
7453a54e | 423 | node: ast::TyKind::Infer, |
e9174d1e SL |
424 | span: sp |
425 | }) | |
426 | } | |
1a4d82fc JJ |
427 | } |
428 | ||
429 | impl MacResult for DummyResult { | |
430 | fn make_expr(self: Box<DummyResult>) -> Option<P<ast::Expr>> { | |
431 | Some(DummyResult::raw_expr(self.span)) | |
432 | } | |
e9174d1e | 433 | |
1a4d82fc JJ |
434 | fn make_pat(self: Box<DummyResult>) -> Option<P<ast::Pat>> { |
435 | Some(P(DummyResult::raw_pat(self.span))) | |
436 | } | |
e9174d1e | 437 | |
1a4d82fc JJ |
438 | fn make_items(self: Box<DummyResult>) -> Option<SmallVector<P<ast::Item>>> { |
439 | // this code needs a comment... why not always just return the Some() ? | |
440 | if self.expr_only { | |
441 | None | |
442 | } else { | |
443 | Some(SmallVector::zero()) | |
444 | } | |
445 | } | |
e9174d1e | 446 | |
7453a54e | 447 | fn make_impl_items(self: Box<DummyResult>) -> Option<SmallVector<ast::ImplItem>> { |
1a4d82fc JJ |
448 | if self.expr_only { |
449 | None | |
450 | } else { | |
451 | Some(SmallVector::zero()) | |
452 | } | |
453 | } | |
e9174d1e | 454 | |
3157f602 XL |
455 | fn make_trait_items(self: Box<DummyResult>) -> Option<SmallVector<ast::TraitItem>> { |
456 | if self.expr_only { | |
457 | None | |
458 | } else { | |
459 | Some(SmallVector::zero()) | |
460 | } | |
461 | } | |
462 | ||
7453a54e | 463 | fn make_stmts(self: Box<DummyResult>) -> Option<SmallVector<ast::Stmt>> { |
3157f602 XL |
464 | Some(SmallVector::one(ast::Stmt { |
465 | id: ast::DUMMY_NODE_ID, | |
466 | node: ast::StmtKind::Expr(DummyResult::raw_expr(self.span)), | |
467 | span: self.span, | |
468 | })) | |
469 | } | |
470 | ||
471 | fn make_ty(self: Box<DummyResult>) -> Option<P<ast::Ty>> { | |
472 | Some(DummyResult::raw_ty(self.span)) | |
1a4d82fc JJ |
473 | } |
474 | } | |
475 | ||
476 | /// An enum representing the different kinds of syntax extensions. | |
477 | pub enum SyntaxExtension { | |
d9579d0f AL |
478 | /// A syntax extension that is attached to an item and creates new items |
479 | /// based upon it. | |
480 | /// | |
481 | /// `#[derive(...)]` is a `MultiItemDecorator`. | |
9e0c209e SL |
482 | /// |
483 | /// Prefer ProcMacro or MultiModifier since they are more flexible. | |
484 | MultiDecorator(Box<MultiItemDecorator>), | |
d9579d0f | 485 | |
85aaf69f | 486 | /// A syntax extension that is attached to an item and modifies it |
9e0c209e SL |
487 | /// in-place. Also allows decoration, i.e., creating new items. |
488 | MultiModifier(Box<MultiItemModifier>), | |
489 | ||
490 | /// A function-like procedural macro. TokenStream -> TokenStream. | |
491 | ProcMacro(Box<ProcMacro>), | |
492 | ||
493 | /// An attribute-like procedural macro. TokenStream, TokenStream -> TokenStream. | |
494 | /// The first TokenSteam is the attribute, the second is the annotated item. | |
495 | /// Allows modification of the input items and adding new items, similar to | |
496 | /// MultiModifier, but uses TokenStreams, rather than AST nodes. | |
497 | AttrProcMacro(Box<AttrProcMacro>), | |
85aaf69f | 498 | |
1a4d82fc JJ |
499 | /// A normal, function-like syntax extension. |
500 | /// | |
501 | /// `bytes!` is a `NormalTT`. | |
c34b1796 AL |
502 | /// |
503 | /// The `bool` dictates whether the contents of the macro can | |
504 | /// directly use `#[unstable]` things (true == yes). | |
9e0c209e | 505 | NormalTT(Box<TTMacroExpander>, Option<Span>, bool), |
1a4d82fc JJ |
506 | |
507 | /// A function-like syntax extension that has an extra ident before | |
508 | /// the block. | |
509 | /// | |
9e0c209e | 510 | IdentTT(Box<IdentMacroExpander>, Option<Span>, bool), |
c30ab7b3 SL |
511 | |
512 | CustomDerive(Box<MultiItemModifier>), | |
1a4d82fc JJ |
513 | } |
514 | ||
515 | pub type NamedSyntaxExtension = (Name, SyntaxExtension); | |
516 | ||
9e0c209e SL |
517 | pub trait Resolver { |
518 | fn next_node_id(&mut self) -> ast::NodeId; | |
c30ab7b3 | 519 | fn get_module_scope(&mut self, id: ast::NodeId) -> Mark; |
9e0c209e SL |
520 | |
521 | fn visit_expansion(&mut self, mark: Mark, expansion: &Expansion); | |
c30ab7b3 SL |
522 | fn add_macro(&mut self, scope: Mark, def: ast::MacroDef, export: bool); |
523 | fn add_ext(&mut self, ident: ast::Ident, ext: Rc<SyntaxExtension>); | |
9e0c209e SL |
524 | fn add_expansions_at_stmt(&mut self, id: ast::NodeId, macros: Vec<Mark>); |
525 | ||
526 | fn find_attr_invoc(&mut self, attrs: &mut Vec<Attribute>) -> Option<Attribute>; | |
c30ab7b3 SL |
527 | fn resolve_macro(&mut self, scope: Mark, path: &ast::Path, force: bool) |
528 | -> Result<Rc<SyntaxExtension>, Determinacy>; | |
529 | } | |
530 | ||
531 | #[derive(Copy, Clone, Debug)] | |
532 | pub enum Determinacy { | |
533 | Determined, | |
534 | Undetermined, | |
970d7e83 LB |
535 | } |
536 | ||
9e0c209e SL |
537 | pub struct DummyResolver; |
538 | ||
539 | impl Resolver for DummyResolver { | |
540 | fn next_node_id(&mut self) -> ast::NodeId { ast::DUMMY_NODE_ID } | |
c30ab7b3 | 541 | fn get_module_scope(&mut self, _id: ast::NodeId) -> Mark { Mark::root() } |
9e0c209e SL |
542 | |
543 | fn visit_expansion(&mut self, _invoc: Mark, _expansion: &Expansion) {} | |
c30ab7b3 SL |
544 | fn add_macro(&mut self, _scope: Mark, _def: ast::MacroDef, _export: bool) {} |
545 | fn add_ext(&mut self, _ident: ast::Ident, _ext: Rc<SyntaxExtension>) {} | |
9e0c209e SL |
546 | fn add_expansions_at_stmt(&mut self, _id: ast::NodeId, _macros: Vec<Mark>) {} |
547 | ||
548 | fn find_attr_invoc(&mut self, _attrs: &mut Vec<Attribute>) -> Option<Attribute> { None } | |
c30ab7b3 SL |
549 | fn resolve_macro(&mut self, _scope: Mark, _path: &ast::Path, _force: bool) |
550 | -> Result<Rc<SyntaxExtension>, Determinacy> { | |
551 | Err(Determinacy::Determined) | |
1a4d82fc JJ |
552 | } |
553 | } | |
970d7e83 | 554 | |
9e0c209e SL |
555 | #[derive(Clone)] |
556 | pub struct ModuleData { | |
557 | pub mod_path: Vec<ast::Ident>, | |
558 | pub directory: PathBuf, | |
559 | } | |
560 | ||
561 | #[derive(Clone)] | |
562 | pub struct ExpansionData { | |
563 | pub mark: Mark, | |
564 | pub depth: usize, | |
565 | pub backtrace: ExpnId, | |
566 | pub module: Rc<ModuleData>, | |
c30ab7b3 SL |
567 | |
568 | // True if non-inline modules without a `#[path]` are forbidden at the root of this expansion. | |
569 | pub no_noninline_mod: bool, | |
3157f602 XL |
570 | } |
571 | ||
1a4d82fc JJ |
572 | /// One of these is made during expansion and incrementally updated as we go; |
573 | /// when a macro expansion occurs, the resulting nodes have the backtrace() | |
574 | /// -> expn_info of their expansion context stored into their span. | |
575 | pub struct ExtCtxt<'a> { | |
576 | pub parse_sess: &'a parse::ParseSess, | |
85aaf69f | 577 | pub ecfg: expand::ExpansionConfig<'a>, |
e9174d1e | 578 | pub crate_root: Option<&'static str>, |
9e0c209e | 579 | pub resolver: &'a mut Resolver, |
c30ab7b3 | 580 | pub resolve_err_count: usize, |
9e0c209e | 581 | pub current_expansion: ExpansionData, |
1a4d82fc JJ |
582 | } |
583 | ||
584 | impl<'a> ExtCtxt<'a> { | |
c30ab7b3 | 585 | pub fn new(parse_sess: &'a parse::ParseSess, |
e9174d1e | 586 | ecfg: expand::ExpansionConfig<'a>, |
9e0c209e | 587 | resolver: &'a mut Resolver) |
3157f602 | 588 | -> ExtCtxt<'a> { |
1a4d82fc | 589 | ExtCtxt { |
970d7e83 | 590 | parse_sess: parse_sess, |
1a4d82fc | 591 | ecfg: ecfg, |
e9174d1e | 592 | crate_root: None, |
9e0c209e | 593 | resolver: resolver, |
c30ab7b3 | 594 | resolve_err_count: 0, |
9e0c209e SL |
595 | current_expansion: ExpansionData { |
596 | mark: Mark::root(), | |
597 | depth: 0, | |
598 | backtrace: NO_EXPANSION, | |
599 | module: Rc::new(ModuleData { mod_path: Vec::new(), directory: PathBuf::new() }), | |
c30ab7b3 | 600 | no_noninline_mod: false, |
9e0c209e | 601 | }, |
970d7e83 LB |
602 | } |
603 | } | |
604 | ||
b039eaaf | 605 | /// Returns a `Folder` for deeply expanding all macros in an AST node. |
1a4d82fc | 606 | pub fn expander<'b>(&'b mut self) -> expand::MacroExpander<'b, 'a> { |
9e0c209e SL |
607 | expand::MacroExpander::new(self, false) |
608 | } | |
609 | ||
610 | /// Returns a `Folder` that deeply expands all macros and assigns all node ids in an AST node. | |
611 | /// Once node ids are assigned, the node may not be expanded, removed, or otherwise modified. | |
612 | pub fn monotonic_expander<'b>(&'b mut self) -> expand::MacroExpander<'b, 'a> { | |
613 | expand::MacroExpander::new(self, true) | |
1a4d82fc JJ |
614 | } |
615 | ||
3157f602 | 616 | pub fn new_parser_from_tts(&self, tts: &[tokenstream::TokenTree]) |
1a4d82fc | 617 | -> parser::Parser<'a> { |
c30ab7b3 SL |
618 | let mut parser = parse::tts_to_parser(self.parse_sess, tts.to_vec()); |
619 | parser.allow_interpolated_tts = false; // FIXME(jseyfried) `quote!` can't handle these yet | |
620 | parser | |
1a4d82fc | 621 | } |
62682a34 | 622 | pub fn codemap(&self) -> &'a CodeMap { self.parse_sess.codemap() } |
1a4d82fc | 623 | pub fn parse_sess(&self) -> &'a parse::ParseSess { self.parse_sess } |
c30ab7b3 | 624 | pub fn cfg(&self) -> &ast::CrateConfig { &self.parse_sess.config } |
1a4d82fc | 625 | pub fn call_site(&self) -> Span { |
9e0c209e | 626 | self.codemap().with_expn_info(self.backtrace(), |ei| match ei { |
1a4d82fc | 627 | Some(expn_info) => expn_info.call_site, |
970d7e83 | 628 | None => self.bug("missing top span") |
1a4d82fc | 629 | }) |
970d7e83 | 630 | } |
9e0c209e | 631 | pub fn backtrace(&self) -> ExpnId { self.current_expansion.backtrace } |
d9579d0f | 632 | |
d9579d0f AL |
633 | /// Returns span for the macro which originally caused the current expansion to happen. |
634 | /// | |
635 | /// Stops backtracing at include! boundary. | |
636 | pub fn expansion_cause(&self) -> Span { | |
9e0c209e | 637 | let mut expn_id = self.backtrace(); |
d9579d0f | 638 | let mut last_macro = None; |
1a4d82fc | 639 | loop { |
d9579d0f AL |
640 | if self.codemap().with_expn_info(expn_id, |info| { |
641 | info.map_or(None, |i| { | |
b039eaaf | 642 | if i.callee.name().as_str() == "include" { |
d9579d0f AL |
643 | // Stop going up the backtrace once include! is encountered |
644 | return None; | |
1a4d82fc | 645 | } |
d9579d0f | 646 | expn_id = i.call_site.expn_id; |
b039eaaf | 647 | last_macro = Some(i.call_site); |
d9579d0f AL |
648 | return Some(()); |
649 | }) | |
650 | }).is_none() { | |
651 | break | |
223e47cc | 652 | } |
1a4d82fc | 653 | } |
d9579d0f | 654 | last_macro.expect("missing expansion backtrace") |
1a4d82fc JJ |
655 | } |
656 | ||
1a4d82fc | 657 | pub fn bt_push(&mut self, ei: ExpnInfo) { |
9e0c209e | 658 | if self.current_expansion.depth > self.ecfg.recursion_limit { |
92a42be0 | 659 | self.span_fatal(ei.call_site, |
1a4d82fc | 660 | &format!("recursion limit reached while expanding the macro `{}`", |
92a42be0 | 661 | ei.callee.name())); |
1a4d82fc JJ |
662 | } |
663 | ||
664 | let mut call_site = ei.call_site; | |
9e0c209e SL |
665 | call_site.expn_id = self.backtrace(); |
666 | self.current_expansion.backtrace = self.codemap().record_expansion(ExpnInfo { | |
1a4d82fc JJ |
667 | call_site: call_site, |
668 | callee: ei.callee | |
669 | }); | |
670 | } | |
9e0c209e | 671 | pub fn bt_pop(&mut self) {} |
1a4d82fc | 672 | |
9cc50fc6 SL |
673 | pub fn struct_span_warn(&self, |
674 | sp: Span, | |
675 | msg: &str) | |
676 | -> DiagnosticBuilder<'a> { | |
677 | self.parse_sess.span_diagnostic.struct_span_warn(sp, msg) | |
678 | } | |
679 | pub fn struct_span_err(&self, | |
680 | sp: Span, | |
681 | msg: &str) | |
682 | -> DiagnosticBuilder<'a> { | |
683 | self.parse_sess.span_diagnostic.struct_span_err(sp, msg) | |
684 | } | |
685 | pub fn struct_span_fatal(&self, | |
686 | sp: Span, | |
687 | msg: &str) | |
688 | -> DiagnosticBuilder<'a> { | |
689 | self.parse_sess.span_diagnostic.struct_span_fatal(sp, msg) | |
690 | } | |
691 | ||
1a4d82fc JJ |
692 | /// Emit `msg` attached to `sp`, and stop compilation immediately. |
693 | /// | |
694 | /// `span_err` should be strongly preferred where-ever possible: | |
695 | /// this should *only* be used when | |
696 | /// - continuing has a high risk of flow-on errors (e.g. errors in | |
697 | /// declaring a macro would cause all uses of that macro to | |
698 | /// complain about "undefined macro"), or | |
699 | /// - there is literally nothing else that can be done (however, | |
700 | /// in most cases one can construct a dummy expression/item to | |
701 | /// substitute; we never hit resolve/type-checking so the dummy | |
702 | /// value doesn't have to match anything) | |
703 | pub fn span_fatal(&self, sp: Span, msg: &str) -> ! { | |
9346a6ac | 704 | panic!(self.parse_sess.span_diagnostic.span_fatal(sp, msg)); |
970d7e83 | 705 | } |
1a4d82fc JJ |
706 | |
707 | /// Emit `msg` attached to `sp`, without immediately stopping | |
708 | /// compilation. | |
709 | /// | |
710 | /// Compilation will be stopped in the near future (at the end of | |
711 | /// the macro expansion phase). | |
712 | pub fn span_err(&self, sp: Span, msg: &str) { | |
970d7e83 LB |
713 | self.parse_sess.span_diagnostic.span_err(sp, msg); |
714 | } | |
1a4d82fc | 715 | pub fn span_warn(&self, sp: Span, msg: &str) { |
970d7e83 LB |
716 | self.parse_sess.span_diagnostic.span_warn(sp, msg); |
717 | } | |
1a4d82fc | 718 | pub fn span_unimpl(&self, sp: Span, msg: &str) -> ! { |
970d7e83 LB |
719 | self.parse_sess.span_diagnostic.span_unimpl(sp, msg); |
720 | } | |
1a4d82fc | 721 | pub fn span_bug(&self, sp: Span, msg: &str) -> ! { |
970d7e83 LB |
722 | self.parse_sess.span_diagnostic.span_bug(sp, msg); |
723 | } | |
724 | pub fn bug(&self, msg: &str) -> ! { | |
9cc50fc6 | 725 | self.parse_sess.span_diagnostic.bug(msg); |
970d7e83 | 726 | } |
970d7e83 | 727 | pub fn trace_macros(&self) -> bool { |
d9579d0f | 728 | self.ecfg.trace_mac |
970d7e83 | 729 | } |
1a4d82fc | 730 | pub fn set_trace_macros(&mut self, x: bool) { |
d9579d0f | 731 | self.ecfg.trace_mac = x |
970d7e83 | 732 | } |
1a4d82fc | 733 | pub fn ident_of(&self, st: &str) -> ast::Ident { |
970d7e83 LB |
734 | str_to_ident(st) |
735 | } | |
e9174d1e SL |
736 | pub fn std_path(&self, components: &[&str]) -> Vec<ast::Ident> { |
737 | let mut v = Vec::new(); | |
738 | if let Some(s) = self.crate_root { | |
739 | v.push(self.ident_of(s)); | |
740 | } | |
741 | v.extend(components.iter().map(|s| self.ident_of(s))); | |
742 | return v | |
85aaf69f | 743 | } |
1a4d82fc JJ |
744 | pub fn name_of(&self, st: &str) -> ast::Name { |
745 | token::intern(st) | |
223e47cc LB |
746 | } |
747 | } | |
748 | ||
1a4d82fc JJ |
749 | /// Extract a string literal from the macro expanded version of `expr`, |
750 | /// emitting `err_msg` if `expr` is not a string literal. This does not stop | |
751 | /// compilation on error, merely emits a non-fatal error and returns None. | |
9e0c209e SL |
752 | pub fn expr_to_spanned_string(cx: &mut ExtCtxt, expr: P<ast::Expr>, err_msg: &str) |
753 | -> Option<Spanned<(InternedString, ast::StrStyle)>> { | |
3157f602 XL |
754 | // Update `expr.span`'s expn_id now in case expr is an `include!` macro invocation. |
755 | let expr = expr.map(|mut expr| { | |
9e0c209e | 756 | expr.span.expn_id = cx.backtrace(); |
3157f602 XL |
757 | expr |
758 | }); | |
759 | ||
1a4d82fc JJ |
760 | // we want to be able to handle e.g. concat("foo", "bar") |
761 | let expr = cx.expander().fold_expr(expr); | |
223e47cc | 762 | match expr.node { |
7453a54e | 763 | ast::ExprKind::Lit(ref l) => match l.node { |
9e0c209e | 764 | ast::LitKind::Str(ref s, style) => return Some(respan(expr.span, (s.clone(), style))), |
1a4d82fc JJ |
765 | _ => cx.span_err(l.span, err_msg) |
766 | }, | |
767 | _ => cx.span_err(expr.span, err_msg) | |
223e47cc | 768 | } |
1a4d82fc | 769 | None |
223e47cc LB |
770 | } |
771 | ||
9e0c209e SL |
772 | pub fn expr_to_string(cx: &mut ExtCtxt, expr: P<ast::Expr>, err_msg: &str) |
773 | -> Option<(InternedString, ast::StrStyle)> { | |
774 | expr_to_spanned_string(cx, expr, err_msg).map(|s| s.node) | |
775 | } | |
776 | ||
1a4d82fc JJ |
777 | /// Non-fatally assert that `tts` is empty. Note that this function |
778 | /// returns even when `tts` is non-empty, macros that *need* to stop | |
779 | /// compilation should call | |
780 | /// `cx.parse_sess.span_diagnostic.abort_if_errors()` (this should be | |
781 | /// done as rarely as possible). | |
782 | pub fn check_zero_tts(cx: &ExtCtxt, | |
783 | sp: Span, | |
3157f602 | 784 | tts: &[tokenstream::TokenTree], |
223e47cc | 785 | name: &str) { |
9346a6ac | 786 | if !tts.is_empty() { |
c34b1796 | 787 | cx.span_err(sp, &format!("{} takes no arguments", name)); |
223e47cc LB |
788 | } |
789 | } | |
790 | ||
1a4d82fc JJ |
791 | /// Extract the string literal from the first token of `tts`. If this |
792 | /// is not a string literal, emit an error and return None. | |
793 | pub fn get_single_str_from_tts(cx: &mut ExtCtxt, | |
794 | sp: Span, | |
3157f602 | 795 | tts: &[tokenstream::TokenTree], |
1a4d82fc JJ |
796 | name: &str) |
797 | -> Option<String> { | |
798 | let mut p = cx.new_parser_from_tts(tts); | |
799 | if p.token == token::Eof { | |
c34b1796 | 800 | cx.span_err(sp, &format!("{} takes 1 argument", name)); |
1a4d82fc JJ |
801 | return None |
802 | } | |
9e0c209e | 803 | let ret = panictry!(p.parse_expr()); |
1a4d82fc | 804 | if p.token != token::Eof { |
c34b1796 | 805 | cx.span_err(sp, &format!("{} takes 1 argument", name)); |
1a4d82fc JJ |
806 | } |
807 | expr_to_string(cx, ret, "argument must be a string literal").map(|(s, _)| { | |
85aaf69f | 808 | s.to_string() |
1a4d82fc | 809 | }) |
223e47cc LB |
810 | } |
811 | ||
1a4d82fc JJ |
812 | /// Extract comma-separated expressions from `tts`. If there is a |
813 | /// parsing error, emit a non-fatal error and return None. | |
814 | pub fn get_exprs_from_tts(cx: &mut ExtCtxt, | |
815 | sp: Span, | |
3157f602 | 816 | tts: &[tokenstream::TokenTree]) -> Option<Vec<P<ast::Expr>>> { |
1a4d82fc JJ |
817 | let mut p = cx.new_parser_from_tts(tts); |
818 | let mut es = Vec::new(); | |
819 | while p.token != token::Eof { | |
92a42be0 | 820 | es.push(cx.expander().fold_expr(panictry!(p.parse_expr()))); |
9cc50fc6 | 821 | if p.eat(&token::Comma) { |
1a4d82fc JJ |
822 | continue; |
823 | } | |
824 | if p.token != token::Eof { | |
825 | cx.span_err(sp, "expected token: `,`"); | |
826 | return None; | |
223e47cc | 827 | } |
223e47cc | 828 | } |
1a4d82fc | 829 | Some(es) |
223e47cc LB |
830 | } |
831 | ||
9e0c209e SL |
832 | pub struct ChangeSpan { |
833 | pub span: Span | |
1a4d82fc | 834 | } |
223e47cc | 835 | |
9e0c209e SL |
836 | impl Folder for ChangeSpan { |
837 | fn new_span(&mut self, _sp: Span) -> Span { | |
838 | self.span | |
223e47cc | 839 | } |
3157f602 | 840 | |
9e0c209e SL |
841 | fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac { |
842 | fold::noop_fold_mac(mac, self) | |
3157f602 | 843 | } |
223e47cc | 844 | } |