1 // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
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.
11 use ast
::{Block, Crate, Ident, Mac_, PatKind}
;
12 use ast
::{MacStmtStyle, Stmt, StmtKind, ItemKind}
;
14 use ext
::hygiene
::Mark
;
15 use attr
::{self, HasAttrs}
;
16 use attr
::AttrMetaMethods
;
17 use codemap
::{dummy_spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute}
;
18 use syntax_pos
::{self, Span, ExpnId}
;
19 use config
::StripUnconfigured
;
21 use feature_gate
::{self, Features}
;
24 use parse
::token
::{intern, keywords}
;
26 use tokenstream
::TokenTree
;
27 use util
::small_vector
::SmallVector
;
32 // A trait for AST nodes and AST node lists into which macro invocations may expand.
33 trait MacroGenerable
: Sized
{
34 // Expand the given MacResult using its appropriate `make_*` method.
35 fn make_with
<'a
>(result
: Box
<MacResult
+ 'a
>) -> Option
<Self>;
37 // Fold this node or list of nodes using the given folder.
38 fn fold_with
<F
: Folder
>(self, folder
: &mut F
) -> Self;
39 fn visit_with
<V
: Visitor
>(&self, visitor
: &mut V
);
41 // The user-friendly name of the node type (e.g. "expression", "item", etc.) for diagnostics.
42 fn kind_name() -> &'
static str;
44 // Return a placeholder expansion to allow compilation to continue after an erroring expansion.
45 fn dummy(span
: Span
) -> Self {
46 Self::make_with(DummyResult
::any(span
)).unwrap()
50 macro_rules
! impl_macro_generable
{
51 ($
($ty
:ty
: $kind_name
:expr
, .$make
:ident
,
52 $
(.$fold
:ident
)* $
(lift
.$fold_elt
:ident
)*,
53 $
(.$visit
:ident
)* $
(lift
.$visit_elt
:ident
)*;)*) => { $
(
54 impl MacroGenerable
for $ty
{
55 fn kind_name() -> &'
static str { $kind_name }
56 fn make_with
<'a
>(result
: Box
<MacResult
+ 'a
>) -> Option
<Self> { result.$make() }
57 fn fold_with
<F
: Folder
>(self, folder
: &mut F
) -> Self {
58 $
( folder
.$
fold(self) )*
59 $
( self.into_iter().flat_map(|item
| folder
. $
fold_elt (item
)).collect() )*
61 fn visit_with
<V
: Visitor
>(&self, visitor
: &mut V
) {
62 $
( visitor
.$
visit(self) )*
63 $
( for item
in self.as_slice() { visitor. $visit_elt (item) }
)*
69 impl_macro_generable
! {
70 P
<ast
::Expr
>: "expression", .make_expr
, .fold_expr
, .visit_expr
;
71 P
<ast
::Pat
>: "pattern", .make_pat
, .fold_pat
, .visit_pat
;
72 P
<ast
::Ty
>: "type", .make_ty
, .fold_ty
, .visit_ty
;
73 SmallVector
<ast
::Stmt
>: "statement", .make_stmts
, lift
.fold_stmt
, lift
.visit_stmt
;
74 SmallVector
<P
<ast
::Item
>>: "item", .make_items
, lift
.fold_item
, lift
.visit_item
;
75 SmallVector
<ast
::TraitItem
>:
76 "trait item", .make_trait_items
, lift
.fold_trait_item
, lift
.visit_trait_item
;
77 SmallVector
<ast
::ImplItem
>:
78 "impl item", .make_impl_items
, lift
.fold_impl_item
, lift
.visit_impl_item
;
81 impl MacroGenerable
for Option
<P
<ast
::Expr
>> {
82 fn kind_name() -> &'
static str { "expression" }
83 fn make_with
<'a
>(result
: Box
<MacResult
+ 'a
>) -> Option
<Self> {
84 result
.make_expr().map(Some
)
86 fn fold_with
<F
: Folder
>(self, folder
: &mut F
) -> Self {
87 self.and_then(|expr
| folder
.fold_opt_expr(expr
))
89 fn visit_with
<V
: Visitor
>(&self, visitor
: &mut V
) {
90 self.as_ref().map(|expr
| visitor
.visit_expr(expr
));
94 pub fn expand_expr(expr
: ast
::Expr
, fld
: &mut MacroExpander
) -> P
<ast
::Expr
> {
96 // expr_mac should really be expr_ext or something; it's the
97 // entry-point for all syntax extensions.
98 ast
::ExprKind
::Mac(mac
) => {
99 return expand_mac_invoc(mac
, None
, expr
.attrs
.into(), expr
.span
, fld
);
101 _
=> P(noop_fold_expr(expr
, fld
)),
105 struct MacroScopePlaceholder
;
106 impl MacResult
for MacroScopePlaceholder
{
107 fn make_items(self: Box
<Self>) -> Option
<SmallVector
<P
<ast
::Item
>>> {
108 Some(SmallVector
::one(P(ast
::Item
{
109 ident
: keywords
::Invalid
.ident(),
111 id
: ast
::DUMMY_NODE_ID
,
112 node
: ast
::ItemKind
::Mac(dummy_spanned(ast
::Mac_
{
113 path
: ast
::Path { span: syntax_pos::DUMMY_SP, global: false, segments: Vec::new() }
,
116 vis
: ast
::Visibility
::Inherited
,
117 span
: syntax_pos
::DUMMY_SP
,
122 /// Expand a macro invocation. Returns the result of expansion.
123 fn expand_mac_invoc
<T
>(mac
: ast
::Mac
, ident
: Option
<Ident
>, attrs
: Vec
<ast
::Attribute
>, span
: Span
,
124 fld
: &mut MacroExpander
) -> T
125 where T
: MacroGenerable
,
127 // It would almost certainly be cleaner to pass the whole macro invocation in,
128 // rather than pulling it apart and marking the tts and the ctxt separately.
129 let Mac_ { path, tts, .. }
= mac
.node
;
130 let mark
= Mark
::fresh();
132 fn mac_result
<'a
>(path
: &ast
::Path
, ident
: Option
<Ident
>, tts
: Vec
<TokenTree
>, mark
: Mark
,
133 attrs
: Vec
<ast
::Attribute
>, call_site
: Span
, fld
: &'a
mut MacroExpander
)
134 -> Option
<Box
<MacResult
+ 'a
>> {
135 // Detect use of feature-gated or invalid attributes on macro invoations
136 // since they will not be detected after macro expansion.
137 for attr
in attrs
.iter() {
138 feature_gate
::check_attribute(&attr
, &fld
.cx
.parse_sess
.span_diagnostic
,
139 &fld
.cx
.parse_sess
.codemap(),
140 &fld
.cx
.ecfg
.features
.unwrap());
143 if path
.segments
.len() > 1 || path
.global
|| !path
.segments
[0].parameters
.is_empty() {
144 fld
.cx
.span_err(path
.span
, "expected macro name without module separators");
148 let extname
= path
.segments
[0].identifier
.name
;
149 let extension
= if let Some(extension
) = fld
.cx
.syntax_env
.find(extname
) {
152 let mut err
= fld
.cx
.struct_span_err(path
.span
,
153 &format
!("macro undefined: '{}!'", &extname
));
154 fld
.cx
.suggest_macro_name(&extname
.as_str(), &mut err
);
159 let ident
= ident
.unwrap_or(keywords
::Invalid
.ident());
160 let marked_tts
= mark_tts(&tts
, mark
);
162 NormalTT(ref expandfun
, exp_span
, allow_internal_unstable
) => {
163 if ident
.name
!= keywords
::Invalid
.name() {
165 format
!("macro {}! expects no ident argument, given '{}'", extname
, ident
);
166 fld
.cx
.span_err(path
.span
, &msg
);
170 fld
.cx
.bt_push(ExpnInfo
{
171 call_site
: call_site
,
172 callee
: NameAndSpan
{
173 format
: MacroBang(extname
),
175 allow_internal_unstable
: allow_internal_unstable
,
179 Some(expandfun
.expand(fld
.cx
, call_site
, &marked_tts
))
182 IdentTT(ref expander
, tt_span
, allow_internal_unstable
) => {
183 if ident
.name
== keywords
::Invalid
.name() {
184 fld
.cx
.span_err(path
.span
,
185 &format
!("macro {}! expects an ident argument", extname
));
189 fld
.cx
.bt_push(ExpnInfo
{
190 call_site
: call_site
,
191 callee
: NameAndSpan
{
192 format
: MacroBang(extname
),
194 allow_internal_unstable
: allow_internal_unstable
,
198 Some(expander
.expand(fld
.cx
, call_site
, ident
, marked_tts
))
202 if ident
.name
== keywords
::Invalid
.name() {
203 fld
.cx
.span_err(path
.span
,
204 &format
!("macro {}! expects an ident argument", extname
));
208 fld
.cx
.bt_push(ExpnInfo
{
209 call_site
: call_site
,
210 callee
: NameAndSpan
{
211 format
: MacroBang(extname
),
213 // `macro_rules!` doesn't directly allow unstable
214 // (this is orthogonal to whether the macro it creates allows it)
215 allow_internal_unstable
: false,
219 let def
= ast
::MacroDef
{
221 id
: ast
::DUMMY_NODE_ID
,
226 export
: attr
::contains_name(&attrs
, "macro_export"),
227 allow_internal_unstable
: attr
::contains_name(&attrs
, "allow_internal_unstable"),
231 fld
.cx
.insert_macro(def
.clone());
233 // macro_rules! has a side effect, but expands to nothing.
234 // If keep_macs is true, expands to a MacEager::items instead.
236 Some(MacEager
::items(SmallVector
::one(P(ast
::Item
{
238 attrs
: def
.attrs
.clone(),
239 id
: ast
::DUMMY_NODE_ID
,
240 node
: ast
::ItemKind
::Mac(ast
::Mac
{
244 tts
: def
.body
.clone(),
247 vis
: ast
::Visibility
::Inherited
,
251 Some(Box
::new(MacroScopePlaceholder
))
255 MultiDecorator(..) | MultiModifier(..) => {
256 fld
.cx
.span_err(path
.span
,
257 &format
!("`{}` can only be used in attributes", extname
));
263 let opt_expanded
= T
::make_with(match mac_result(&path
, ident
, tts
, mark
, attrs
, span
, fld
) {
264 Some(result
) => result
,
265 None
=> return T
::dummy(span
),
268 let expanded
= if let Some(expanded
) = opt_expanded
{
271 let msg
= format
!("non-{kind} macro in {kind} position: {name}",
272 name
= path
.segments
[0].identifier
.name
, kind
= T
::kind_name());
273 fld
.cx
.span_err(path
.span
, &msg
);
274 return T
::dummy(span
);
277 let marked
= expanded
.fold_with(&mut Marker { mark: mark, expn_id: Some(fld.cx.backtrace()) }
);
278 let configured
= marked
.fold_with(&mut fld
.strip_unconfigured());
279 fld
.load_macros(&configured
);
281 let fully_expanded
= if fld
.single_step
{
284 configured
.fold_with(fld
)
291 // eval $e with a new exts frame.
292 // must be a macro so that $e isn't evaluated too early.
293 macro_rules
! with_exts_frame
{
294 ($extsboxexpr
:expr
,$macros_escape
:expr
,$e
:expr
) =>
295 ({$extsboxexpr
.push_frame();
296 $extsboxexpr
.info().macros_escape
= $macros_escape
;
298 $extsboxexpr
.pop_frame();
303 // When we enter a module, record it, for the sake of `module!`
304 pub fn expand_item(it
: P
<ast
::Item
>, fld
: &mut MacroExpander
)
305 -> SmallVector
<P
<ast
::Item
>> {
306 expand_annotatable(Annotatable
::Item(it
), fld
)
307 .into_iter().map(|i
| i
.expect_item()).collect()
310 // does this attribute list contain "macro_use" ?
311 fn contains_macro_use(fld
: &mut MacroExpander
, attrs
: &[ast
::Attribute
]) -> bool
{
313 let mut is_use
= attr
.check_name("macro_use");
314 if attr
.check_name("macro_escape") {
316 fld
.cx
.struct_span_warn(attr
.span
,
317 "macro_escape is a deprecated synonym for macro_use");
319 if let ast
::AttrStyle
::Inner
= attr
.node
.style
{
320 err
.help("consider an outer attribute, \
321 #[macro_use] mod ...").emit();
329 fld
.cx
.span_err(attr
.span
, "arguments to macro_use are not allowed here");
338 fn expand_stmt(stmt
: Stmt
, fld
: &mut MacroExpander
) -> SmallVector
<Stmt
> {
339 let (mac
, style
, attrs
) = match stmt
.node
{
340 StmtKind
::Mac(mac
) => mac
.unwrap(),
341 _
=> return noop_fold_stmt(stmt
, fld
)
344 let mut fully_expanded
: SmallVector
<ast
::Stmt
> =
345 expand_mac_invoc(mac
, None
, attrs
.into(), stmt
.span
, fld
);
347 // If this is a macro invocation with a semicolon, then apply that
348 // semicolon to the final statement produced by expansion.
349 if style
== MacStmtStyle
::Semicolon
{
350 if let Some(stmt
) = fully_expanded
.pop() {
351 fully_expanded
.push(stmt
.add_trailing_semicolon());
358 fn expand_pat(p
: P
<ast
::Pat
>, fld
: &mut MacroExpander
) -> P
<ast
::Pat
> {
360 PatKind
::Mac(_
) => {}
361 _
=> return noop_fold_pat(p
, fld
)
363 p
.and_then(|ast
::Pat {node, span, ..}
| {
365 PatKind
::Mac(mac
) => expand_mac_invoc(mac
, None
, Vec
::new(), span
, fld
),
371 fn expand_multi_modified(a
: Annotatable
, fld
: &mut MacroExpander
) -> SmallVector
<Annotatable
> {
373 Annotatable
::Item(it
) => match it
.node
{
374 ast
::ItemKind
::Mac(..) => {
376 ItemKind
::Mac(ref mac
) => mac
.node
.path
.segments
.is_empty(),
379 return SmallVector
::one(Annotatable
::Item(it
));
381 it
.and_then(|it
| match it
.node
{
382 ItemKind
::Mac(mac
) =>
383 expand_mac_invoc(mac
, Some(it
.ident
), it
.attrs
, it
.span
, fld
),
387 ast
::ItemKind
::Mod(_
) | ast
::ItemKind
::ForeignMod(_
) => {
389 it
.ident
.name
!= keywords
::Invalid
.name();
392 fld
.cx
.mod_push(it
.ident
);
394 let macro_use
= contains_macro_use(fld
, &it
.attrs
);
395 let result
= with_exts_frame
!(fld
.cx
.syntax_env
,
397 noop_fold_item(it
, fld
));
403 _
=> noop_fold_item(it
, fld
),
404 }.into_iter().map(|i
| Annotatable
::Item(i
)).collect(),
406 Annotatable
::TraitItem(it
) => {
407 expand_trait_item(it
.unwrap(), fld
).into_iter().
408 map(|it
| Annotatable
::TraitItem(P(it
))).collect()
411 Annotatable
::ImplItem(ii
) => {
412 expand_impl_item(ii
.unwrap(), fld
).into_iter().
413 map(|ii
| Annotatable
::ImplItem(P(ii
))).collect()
418 fn expand_annotatable(mut item
: Annotatable
, fld
: &mut MacroExpander
) -> SmallVector
<Annotatable
> {
419 let mut multi_modifier
= None
;
420 item
= item
.map_attrs(|mut attrs
| {
421 for i
in 0..attrs
.len() {
422 if let Some(extension
) = fld
.cx
.syntax_env
.find(intern(&attrs
[i
].name())) {
424 MultiModifier(..) | MultiDecorator(..) => {
425 multi_modifier
= Some((attrs
.remove(i
), extension
));
435 match multi_modifier
{
436 None
=> expand_multi_modified(item
, fld
),
437 Some((attr
, extension
)) => {
438 attr
::mark_used(&attr
);
439 fld
.cx
.bt_push(ExpnInfo
{
440 call_site
: attr
.span
,
441 callee
: NameAndSpan
{
442 format
: MacroAttribute(intern(&attr
.name())),
443 span
: Some(attr
.span
),
444 // attributes can do whatever they like, for now
445 allow_internal_unstable
: true,
449 let modified
= match *extension
{
450 MultiModifier(ref mac
) => mac
.expand(fld
.cx
, attr
.span
, &attr
.node
.value
, item
),
451 MultiDecorator(ref mac
) => {
452 let mut items
= Vec
::new();
453 mac
.expand(fld
.cx
, attr
.span
, &attr
.node
.value
, &item
,
454 &mut |item
| items
.push(item
));
462 let configured
= modified
.into_iter().flat_map(|it
| {
463 it
.fold_with(&mut fld
.strip_unconfigured())
464 }).collect
::<SmallVector
<_
>>();
466 configured
.into_iter().flat_map(|it
| expand_annotatable(it
, fld
)).collect()
471 fn expand_impl_item(ii
: ast
::ImplItem
, fld
: &mut MacroExpander
)
472 -> SmallVector
<ast
::ImplItem
> {
474 ast
::ImplItemKind
::Macro(mac
) => {
475 expand_mac_invoc(mac
, None
, ii
.attrs
, ii
.span
, fld
)
477 _
=> fold
::noop_fold_impl_item(ii
, fld
)
481 fn expand_trait_item(ti
: ast
::TraitItem
, fld
: &mut MacroExpander
)
482 -> SmallVector
<ast
::TraitItem
> {
484 ast
::TraitItemKind
::Macro(mac
) => {
485 expand_mac_invoc(mac
, None
, ti
.attrs
, ti
.span
, fld
)
487 _
=> fold
::noop_fold_trait_item(ti
, fld
)
491 pub fn expand_type(t
: P
<ast
::Ty
>, fld
: &mut MacroExpander
) -> P
<ast
::Ty
> {
492 let t
= match t
.node
.clone() {
493 ast
::TyKind
::Mac(mac
) => {
494 if fld
.cx
.ecfg
.features
.unwrap().type_macros
{
495 expand_mac_invoc(mac
, None
, Vec
::new(), t
.span
, fld
)
497 feature_gate
::emit_feature_err(
498 &fld
.cx
.parse_sess
.span_diagnostic
,
501 feature_gate
::GateIssue
::Language
,
502 "type macros are experimental");
504 DummyResult
::raw_ty(t
.span
)
510 fold
::noop_fold_ty(t
, fld
)
513 /// A tree-folder that performs macro expansion
514 pub struct MacroExpander
<'a
, 'b
:'a
> {
515 pub cx
: &'a
mut ExtCtxt
<'b
>,
516 pub single_step
: bool
,
520 impl<'a
, 'b
> MacroExpander
<'a
, 'b
> {
521 pub fn new(cx
: &'a
mut ExtCtxt
<'b
>,
523 keep_macs
: bool
) -> MacroExpander
<'a
, 'b
> {
526 single_step
: single_step
,
531 fn strip_unconfigured(&mut self) -> StripUnconfigured
{
533 config
: &self.cx
.cfg
,
534 should_test
: self.cx
.ecfg
.should_test
,
535 sess
: self.cx
.parse_sess
,
536 features
: self.cx
.ecfg
.features
,
540 fn load_macros
<T
: MacroGenerable
>(&mut self, node
: &T
) {
541 struct MacroLoadingVisitor
<'a
, 'b
: 'a
>{
542 cx
: &'a
mut ExtCtxt
<'b
>,
546 impl<'a
, 'b
> Visitor
for MacroLoadingVisitor
<'a
, 'b
> {
547 fn visit_mac(&mut self, _
: &ast
::Mac
) {}
548 fn visit_item(&mut self, item
: &ast
::Item
) {
549 if let ast
::ItemKind
::ExternCrate(..) = item
.node
{
550 // We need to error on `#[macro_use] extern crate` when it isn't at the
551 // crate root, because `$crate` won't work properly.
552 for def
in self.cx
.loader
.load_crate(item
, self.at_crate_root
) {
553 self.cx
.insert_macro(def
);
556 let at_crate_root
= ::std
::mem
::replace(&mut self.at_crate_root
, false);
557 visit
::walk_item(self, item
);
558 self.at_crate_root
= at_crate_root
;
561 fn visit_block(&mut self, block
: &ast
::Block
) {
562 let at_crate_root
= ::std
::mem
::replace(&mut self.at_crate_root
, false);
563 visit
::walk_block(self, block
);
564 self.at_crate_root
= at_crate_root
;
568 node
.visit_with(&mut MacroLoadingVisitor
{
569 at_crate_root
: self.cx
.syntax_env
.is_crate_root(),
575 impl<'a
, 'b
> Folder
for MacroExpander
<'a
, 'b
> {
576 fn fold_crate(&mut self, c
: Crate
) -> Crate
{
577 self.cx
.filename
= Some(self.cx
.parse_sess
.codemap().span_to_filename(c
.span
));
578 noop_fold_crate(c
, self)
581 fn fold_expr(&mut self, expr
: P
<ast
::Expr
>) -> P
<ast
::Expr
> {
582 expr
.and_then(|expr
| expand_expr(expr
, self))
585 fn fold_opt_expr(&mut self, expr
: P
<ast
::Expr
>) -> Option
<P
<ast
::Expr
>> {
586 expr
.and_then(|expr
| match expr
.node
{
587 ast
::ExprKind
::Mac(mac
) =>
588 expand_mac_invoc(mac
, None
, expr
.attrs
.into(), expr
.span
, self),
589 _
=> Some(expand_expr(expr
, self)),
593 fn fold_pat(&mut self, pat
: P
<ast
::Pat
>) -> P
<ast
::Pat
> {
594 expand_pat(pat
, self)
597 fn fold_item(&mut self, item
: P
<ast
::Item
>) -> SmallVector
<P
<ast
::Item
>> {
598 use std
::mem
::replace
;
600 if let ast
::ItemKind
::Mod(ast
::Mod { inner, .. }
) = item
.node
{
601 if item
.span
.contains(inner
) {
602 self.push_mod_path(item
.ident
, &item
.attrs
);
603 result
= expand_item(item
, self);
606 let filename
= if inner
!= syntax_pos
::DUMMY_SP
{
607 Some(self.cx
.parse_sess
.codemap().span_to_filename(inner
))
609 let orig_filename
= replace(&mut self.cx
.filename
, filename
);
610 let orig_mod_path_stack
= replace(&mut self.cx
.mod_path_stack
, Vec
::new());
611 result
= expand_item(item
, self);
612 self.cx
.filename
= orig_filename
;
613 self.cx
.mod_path_stack
= orig_mod_path_stack
;
616 result
= expand_item(item
, self);
621 fn fold_stmt(&mut self, stmt
: ast
::Stmt
) -> SmallVector
<ast
::Stmt
> {
622 expand_stmt(stmt
, self)
625 fn fold_block(&mut self, block
: P
<Block
>) -> P
<Block
> {
626 let was_in_block
= ::std
::mem
::replace(&mut self.cx
.in_block
, true);
627 let result
= with_exts_frame
!(self.cx
.syntax_env
, false, noop_fold_block(block
, self));
628 self.cx
.in_block
= was_in_block
;
632 fn fold_trait_item(&mut self, i
: ast
::TraitItem
) -> SmallVector
<ast
::TraitItem
> {
633 expand_annotatable(Annotatable
::TraitItem(P(i
)), self)
634 .into_iter().map(|i
| i
.expect_trait_item()).collect()
637 fn fold_impl_item(&mut self, i
: ast
::ImplItem
) -> SmallVector
<ast
::ImplItem
> {
638 expand_annotatable(Annotatable
::ImplItem(P(i
)), self)
639 .into_iter().map(|i
| i
.expect_impl_item()).collect()
642 fn fold_ty(&mut self, ty
: P
<ast
::Ty
>) -> P
<ast
::Ty
> {
643 expand_type(ty
, self)
647 impl<'a
, 'b
> MacroExpander
<'a
, 'b
> {
648 fn push_mod_path(&mut self, id
: Ident
, attrs
: &[ast
::Attribute
]) {
649 let default_path
= id
.name
.as_str();
650 let file_path
= match ::attr
::first_attr_value_str_by_name(attrs
, "path") {
652 None
=> default_path
,
654 self.cx
.mod_path_stack
.push(file_path
)
657 fn pop_mod_path(&mut self) {
658 self.cx
.mod_path_stack
.pop().unwrap();
662 pub struct ExpansionConfig
<'feat
> {
663 pub crate_name
: String
,
664 pub features
: Option
<&'feat Features
>,
665 pub recursion_limit
: usize,
667 pub should_test
: bool
, // If false, strip `#[test]` nodes
670 macro_rules
! feature_tests
{
671 ($
( fn $getter
:ident
= $field
:ident
, )*) => {
673 pub fn $
getter(&self) -> bool
{
674 match self.features
{
675 Some(&Features { $field: true, .. }
) => true,
683 impl<'feat
> ExpansionConfig
<'feat
> {
684 pub fn default(crate_name
: String
) -> ExpansionConfig
<'
static> {
686 crate_name
: crate_name
,
695 fn enable_quotes
= quote
,
697 fn enable_log_syntax
= log_syntax
,
698 fn enable_concat_idents
= concat_idents
,
699 fn enable_trace_macros
= trace_macros
,
700 fn enable_allow_internal_unstable
= allow_internal_unstable
,
701 fn enable_custom_derive
= custom_derive
,
702 fn enable_pushpop_unsafe
= pushpop_unsafe
,
706 pub fn expand_crate(cx
: &mut ExtCtxt
,
707 user_exts
: Vec
<NamedSyntaxExtension
>,
709 let mut expander
= MacroExpander
::new(cx
, false, false);
710 expand_crate_with_expander(&mut expander
, user_exts
, c
)
713 // Expands crate using supplied MacroExpander - allows for
714 // non-standard expansion behaviour (e.g. step-wise).
715 pub fn expand_crate_with_expander(expander
: &mut MacroExpander
,
716 user_exts
: Vec
<NamedSyntaxExtension
>,
717 mut c
: Crate
) -> Crate
{
718 if std_inject
::no_core(&c
) {
719 expander
.cx
.crate_root
= None
;
720 } else if std_inject
::no_std(&c
) {
721 expander
.cx
.crate_root
= Some("core");
723 expander
.cx
.crate_root
= Some("std");
726 // User extensions must be added before expander.load_macros is called,
727 // so that macros from external crates shadow user defined extensions.
728 for (name
, extension
) in user_exts
{
729 expander
.cx
.syntax_env
.insert(name
, extension
);
732 let items
= SmallVector
::many(c
.module
.items
);
733 expander
.load_macros(&items
);
734 c
.module
.items
= items
.into();
736 let err_count
= expander
.cx
.parse_sess
.span_diagnostic
.err_count();
737 let mut ret
= expander
.fold_crate(c
);
738 ret
.exported_macros
= expander
.cx
.exported_macros
.clone();
740 if expander
.cx
.parse_sess
.span_diagnostic
.err_count() > err_count
{
741 expander
.cx
.parse_sess
.span_diagnostic
.abort_if_errors();
747 // A Marker adds the given mark to the syntax context and
748 // sets spans' `expn_id` to the given expn_id (unless it is `None`).
749 struct Marker { mark: Mark, expn_id: Option<ExpnId> }
751 impl Folder
for Marker
{
752 fn fold_ident(&mut self, mut ident
: Ident
) -> Ident
{
753 ident
.ctxt
= ident
.ctxt
.apply_mark(self.mark
);
756 fn fold_mac(&mut self, mac
: ast
::Mac
) -> ast
::Mac
{
757 noop_fold_mac(mac
, self)
760 fn new_span(&mut self, mut span
: Span
) -> Span
{
761 if let Some(expn_id
) = self.expn_id
{
762 span
.expn_id
= expn_id
;
768 // apply a given mark to the given token trees. Used prior to expansion of a macro.
769 fn mark_tts(tts
: &[TokenTree
], m
: Mark
) -> Vec
<TokenTree
> {
770 noop_fold_tts(tts
, &mut Marker{mark:m, expn_id: None}
)
776 use super::{expand_crate, ExpansionConfig}
;
778 use ext
::base
::{ExtCtxt, DummyMacroLoader}
;
780 use util
::parser_testing
::{string_to_parser}
;
784 // a visitor that extracts the paths
785 // from a given thingy and puts them in a mutable
786 // array (passed in to the traversal)
788 struct PathExprFinderContext
{
789 path_accumulator
: Vec
<ast
::Path
> ,
792 impl Visitor
for PathExprFinderContext
{
793 fn visit_expr(&mut self, expr
: &ast
::Expr
) {
794 if let ast
::ExprKind
::Path(None
, ref p
) = expr
.node
{
795 self.path_accumulator
.push(p
.clone());
797 visit
::walk_expr(self, expr
);
801 // these following tests are quite fragile, in that they don't test what
802 // *kind* of failure occurs.
804 fn test_ecfg() -> ExpansionConfig
<'
static> {
805 ExpansionConfig
::default("test".to_string())
808 // make sure that macros can't escape fns
810 #[test] fn macros_cant_escape_fns_test () {
811 let src
= "fn bogus() {macro_rules! z (() => (3+4));}\
812 fn inty() -> i32 { z!() }".to_string();
813 let sess
= parse
::ParseSess
::new();
814 let crate_ast
= parse
::parse_crate_from_source_str(
815 "<test>".to_string(),
817 Vec
::new(), &sess
).unwrap();
819 let mut loader
= DummyMacroLoader
;
820 let mut ecx
= ExtCtxt
::new(&sess
, vec
![], test_ecfg(), &mut loader
);
821 expand_crate(&mut ecx
, vec
![], crate_ast
);
824 // make sure that macros can't escape modules
826 #[test] fn macros_cant_escape_mods_test () {
827 let src
= "mod foo {macro_rules! z (() => (3+4));}\
828 fn inty() -> i32 { z!() }".to_string();
829 let sess
= parse
::ParseSess
::new();
830 let crate_ast
= parse
::parse_crate_from_source_str(
831 "<test>".to_string(),
833 Vec
::new(), &sess
).unwrap();
834 let mut loader
= DummyMacroLoader
;
835 let mut ecx
= ExtCtxt
::new(&sess
, vec
![], test_ecfg(), &mut loader
);
836 expand_crate(&mut ecx
, vec
![], crate_ast
);
839 // macro_use modules should allow macros to escape
840 #[test] fn macros_can_escape_flattened_mods_test () {
841 let src
= "#[macro_use] mod foo {macro_rules! z (() => (3+4));}\
842 fn inty() -> i32 { z!() }".to_string();
843 let sess
= parse
::ParseSess
::new();
844 let crate_ast
= parse
::parse_crate_from_source_str(
845 "<test>".to_string(),
847 Vec
::new(), &sess
).unwrap();
848 let mut loader
= DummyMacroLoader
;
849 let mut ecx
= ExtCtxt
::new(&sess
, vec
![], test_ecfg(), &mut loader
);
850 expand_crate(&mut ecx
, vec
![], crate_ast
);
853 fn expand_crate_str(crate_str
: String
) -> ast
::Crate
{
854 let ps
= parse
::ParseSess
::new();
855 let crate_ast
= panictry
!(string_to_parser(&ps
, crate_str
).parse_crate_mod());
856 // the cfg argument actually does matter, here...
857 let mut loader
= DummyMacroLoader
;
858 let mut ecx
= ExtCtxt
::new(&ps
, vec
![], test_ecfg(), &mut loader
);
859 expand_crate(&mut ecx
, vec
![], crate_ast
)
862 #[test] fn macro_tokens_should_match(){
864 "macro_rules! m((a)=>(13)) ;fn main(){m!(a);}".to_string());
867 // should be able to use a bound identifier as a literal in a macro definition:
868 #[test] fn self_macro_parsing(){
870 "macro_rules! foo ((zz) => (287;));
871 fn f(zz: i32) {foo!(zz);}".to_string()
875 // create a really evil test case where a $x appears inside a binding of $x
876 // but *shouldn't* bind because it was inserted by a different macro....
877 // can't write this test case until we have macro-generating macros.