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 attr
::AttrMetaMethods
;
12 use diagnostic
::SpanHandler
;
14 use {ast, fold, attr}
;
15 use codemap
::{Spanned, respan}
;
18 use util
::small_vector
::SmallVector
;
20 /// A folder that strips out items that do not belong in the current
22 struct Context
<F
> where F
: FnMut(&[ast
::Attribute
]) -> bool
{
26 // Support conditional compilation by transforming the AST, stripping out
27 // any items that do not belong in the current configuration
28 pub fn strip_unconfigured_items(diagnostic
: &SpanHandler
, krate
: ast
::Crate
) -> ast
::Crate
{
29 let krate
= process_cfg_attr(diagnostic
, krate
);
30 let config
= krate
.config
.clone();
31 strip_items(krate
, |attrs
| in_cfg(diagnostic
, &config
, attrs
))
34 impl<F
> fold
::Folder
for Context
<F
> where F
: FnMut(&[ast
::Attribute
]) -> bool
{
35 fn fold_mod(&mut self, module
: ast
::Mod
) -> ast
::Mod
{
36 fold_mod(self, module
)
38 fn fold_block(&mut self, block
: P
<ast
::Block
>) -> P
<ast
::Block
> {
39 fold_block(self, block
)
41 fn fold_foreign_mod(&mut self, foreign_mod
: ast
::ForeignMod
) -> ast
::ForeignMod
{
42 fold_foreign_mod(self, foreign_mod
)
44 fn fold_item_underscore(&mut self, item
: ast
::Item_
) -> ast
::Item_
{
45 fold_item_underscore(self, item
)
47 fn fold_expr(&mut self, expr
: P
<ast
::Expr
>) -> P
<ast
::Expr
> {
50 fn fold_mac(&mut self, mac
: ast
::Mac
) -> ast
::Mac
{
51 fold
::noop_fold_mac(mac
, self)
53 fn fold_item(&mut self, item
: P
<ast
::Item
>) -> SmallVector
<P
<ast
::Item
>> {
58 pub fn strip_items
<F
>(krate
: ast
::Crate
, in_cfg
: F
) -> ast
::Crate
where
59 F
: FnMut(&[ast
::Attribute
]) -> bool
,
61 let mut ctxt
= Context
{
64 ctxt
.fold_crate(krate
)
67 fn fold_mod
<F
>(cx
: &mut Context
<F
>,
68 ast
::Mod {inner, items}
: ast
::Mod
)
70 F
: FnMut(&[ast
::Attribute
]) -> bool
74 items
: items
.into_iter().flat_map(|a
| {
75 cx
.fold_item(a
).into_iter()
80 fn filter_foreign_item
<F
>(cx
: &mut Context
<F
>,
81 item
: P
<ast
::ForeignItem
>)
82 -> Option
<P
<ast
::ForeignItem
>> where
83 F
: FnMut(&[ast
::Attribute
]) -> bool
85 if foreign_item_in_cfg(cx
, &*item
) {
92 fn fold_foreign_mod
<F
>(cx
: &mut Context
<F
>,
93 ast
::ForeignMod {abi, items}
: ast
::ForeignMod
)
94 -> ast
::ForeignMod
where
95 F
: FnMut(&[ast
::Attribute
]) -> bool
99 items
: items
.into_iter()
100 .filter_map(|a
| filter_foreign_item(cx
, a
))
105 fn fold_item
<F
>(cx
: &mut Context
<F
>, item
: P
<ast
::Item
>) -> SmallVector
<P
<ast
::Item
>> where
106 F
: FnMut(&[ast
::Attribute
]) -> bool
108 if item_in_cfg(cx
, &*item
) {
109 SmallVector
::one(item
.map(|i
| cx
.fold_item_simple(i
)))
115 fn fold_item_underscore
<F
>(cx
: &mut Context
<F
>, item
: ast
::Item_
) -> ast
::Item_
where
116 F
: FnMut(&[ast
::Attribute
]) -> bool
118 let item
= match item
{
119 ast
::ItemImpl(u
, o
, a
, b
, c
, impl_items
) => {
120 let impl_items
= impl_items
.into_iter()
121 .filter(|ii
| (cx
.in_cfg
)(&ii
.attrs
))
123 ast
::ItemImpl(u
, o
, a
, b
, c
, impl_items
)
125 ast
::ItemTrait(u
, a
, b
, methods
) => {
126 let methods
= methods
.into_iter()
127 .filter(|ti
| (cx
.in_cfg
)(&ti
.attrs
))
129 ast
::ItemTrait(u
, a
, b
, methods
)
131 ast
::ItemStruct(def
, generics
) => {
132 ast
::ItemStruct(fold_struct(cx
, def
), generics
)
134 ast
::ItemEnum(def
, generics
) => {
135 let variants
= def
.variants
.into_iter().filter_map(|v
| {
136 if !(cx
.in_cfg
)(&v
.node
.attrs
) {
139 Some(v
.map(|Spanned
{node
: ast
::Variant_
{id
, name
, attrs
, kind
,
140 disr_expr
, vis
}, span
}| {
142 node
: ast
::Variant_
{
147 ast
::TupleVariantKind(..) => kind
,
148 ast
::StructVariantKind(def
) => {
149 ast
::StructVariantKind(fold_struct(cx
, def
))
152 disr_expr
: disr_expr
,
160 ast
::ItemEnum(ast
::EnumDef
{
161 variants
: variants
.collect(),
167 fold
::noop_fold_item_underscore(item
, cx
)
170 fn fold_struct
<F
>(cx
: &mut Context
<F
>, def
: P
<ast
::StructDef
>) -> P
<ast
::StructDef
> where
171 F
: FnMut(&[ast
::Attribute
]) -> bool
173 def
.map(|ast
::StructDef { fields, ctor_id }
| {
175 fields
: fields
.into_iter().filter(|m
| {
176 (cx
.in_cfg
)(&m
.node
.attrs
)
183 fn retain_stmt
<F
>(cx
: &mut Context
<F
>, stmt
: &ast
::Stmt
) -> bool
where
184 F
: FnMut(&[ast
::Attribute
]) -> bool
187 ast
::StmtDecl(ref decl
, _
) => {
189 ast
::DeclItem(ref item
) => {
190 item_in_cfg(cx
, &**item
)
199 fn fold_block
<F
>(cx
: &mut Context
<F
>, b
: P
<ast
::Block
>) -> P
<ast
::Block
> where
200 F
: FnMut(&[ast
::Attribute
]) -> bool
202 b
.map(|ast
::Block {id, stmts, expr, rules, span}
| {
203 let resulting_stmts
: Vec
<P
<ast
::Stmt
>> =
204 stmts
.into_iter().filter(|a
| retain_stmt(cx
, &**a
)).collect();
205 let resulting_stmts
= resulting_stmts
.into_iter()
206 .flat_map(|stmt
| cx
.fold_stmt(stmt
).into_iter())
210 stmts
: resulting_stmts
,
211 expr
: expr
.map(|x
| cx
.fold_expr(x
)),
218 fn fold_expr
<F
>(cx
: &mut Context
<F
>, expr
: P
<ast
::Expr
>) -> P
<ast
::Expr
> where
219 F
: FnMut(&[ast
::Attribute
]) -> bool
221 expr
.map(|ast
::Expr {id, span, node}
| {
222 fold
::noop_fold_expr(ast
::Expr
{
225 ast
::ExprMatch(m
, arms
, source
) => {
226 ast
::ExprMatch(m
, arms
.into_iter()
227 .filter(|a
| (cx
.in_cfg
)(&a
.attrs
))
237 fn item_in_cfg
<F
>(cx
: &mut Context
<F
>, item
: &ast
::Item
) -> bool
where
238 F
: FnMut(&[ast
::Attribute
]) -> bool
240 return (cx
.in_cfg
)(&item
.attrs
);
243 fn foreign_item_in_cfg
<F
>(cx
: &mut Context
<F
>, item
: &ast
::ForeignItem
) -> bool
where
244 F
: FnMut(&[ast
::Attribute
]) -> bool
246 return (cx
.in_cfg
)(&item
.attrs
);
249 // Determine if an item should be translated in the current crate
250 // configuration based on the item's attributes
251 fn in_cfg(diagnostic
: &SpanHandler
, cfg
: &[P
<ast
::MetaItem
>], attrs
: &[ast
::Attribute
]) -> bool
{
252 attrs
.iter().all(|attr
| {
253 let mis
= match attr
.node
.value
.node
{
254 ast
::MetaList(_
, ref mis
) if attr
.check_name("cfg") => mis
,
259 diagnostic
.span_err(attr
.span
, "expected 1 cfg-pattern");
263 attr
::cfg_matches(diagnostic
, cfg
, &*mis
[0])
267 struct CfgAttrFolder
<'a
> {
268 diag
: &'a SpanHandler
,
269 config
: ast
::CrateConfig
,
272 // Process `#[cfg_attr]`.
273 fn process_cfg_attr(diagnostic
: &SpanHandler
, krate
: ast
::Crate
) -> ast
::Crate
{
274 let mut fld
= CfgAttrFolder
{
276 config
: krate
.config
.clone(),
278 fld
.fold_crate(krate
)
281 impl<'a
> fold
::Folder
for CfgAttrFolder
<'a
> {
282 fn fold_attribute(&mut self, attr
: ast
::Attribute
) -> Option
<ast
::Attribute
> {
283 if !attr
.check_name("cfg_attr") {
284 return fold
::noop_fold_attribute(attr
, self);
287 let attr_list
= match attr
.meta_item_list() {
288 Some(attr_list
) => attr_list
,
290 self.diag
.span_err(attr
.span
, "expected `#[cfg_attr(<cfg pattern>, <attr>)]`");
294 let (cfg
, mi
) = match (attr_list
.len(), attr_list
.get(0), attr_list
.get(1)) {
295 (2, Some(cfg
), Some(mi
)) => (cfg
, mi
),
297 self.diag
.span_err(attr
.span
, "expected `#[cfg_attr(<cfg pattern>, <attr>)]`");
302 if attr
::cfg_matches(self.diag
, &self.config
[..], &cfg
) {
303 Some(respan(mi
.span
, ast
::Attribute_
{
304 id
: attr
::mk_attr_id(),
305 style
: attr
.node
.style
,
307 is_sugared_doc
: false,
314 // Need the ability to run pre-expansion.
315 fn fold_mac(&mut self, mac
: ast
::Mac
) -> ast
::Mac
{
316 fold
::noop_fold_mac(mac
, self)