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
;
13 use feature_gate
::GatedCfgAttr
;
15 use {ast, fold, attr}
;
17 use codemap
::{Spanned, respan}
;
20 use util
::small_vector
::SmallVector
;
22 /// A folder that strips out items that do not belong in the current
24 struct Context
<'a
, F
> where F
: FnMut(&[ast
::Attribute
]) -> bool
{
26 diagnostic
: &'a Handler
,
29 // Support conditional compilation by transforming the AST, stripping out
30 // any items that do not belong in the current configuration
31 pub fn strip_unconfigured_items(diagnostic
: &Handler
, krate
: ast
::Crate
,
32 feature_gated_cfgs
: &mut Vec
<GatedCfgAttr
>)
35 // Need to do this check here because cfg runs before feature_gates
36 check_for_gated_stmt_expr_attributes(&krate
, feature_gated_cfgs
);
38 let krate
= process_cfg_attr(diagnostic
, krate
, feature_gated_cfgs
);
39 let config
= krate
.config
.clone();
40 strip_items(diagnostic
,
43 let mut diag
= CfgDiagReal
{
45 feature_gated_cfgs
: feature_gated_cfgs
,
47 in_cfg(&config
, attrs
, &mut diag
)
51 impl<'a
, F
> fold
::Folder
for Context
<'a
, F
> where F
: FnMut(&[ast
::Attribute
]) -> bool
{
52 fn fold_foreign_mod(&mut self, foreign_mod
: ast
::ForeignMod
) -> ast
::ForeignMod
{
53 fold_foreign_mod(self, foreign_mod
)
55 fn fold_item_kind(&mut self, item
: ast
::ItemKind
) -> ast
::ItemKind
{
56 fold_item_kind(self, item
)
58 fn fold_expr(&mut self, expr
: P
<ast
::Expr
>) -> P
<ast
::Expr
> {
59 // If an expr is valid to cfg away it will have been removed by the
60 // outer stmt or expression folder before descending in here.
61 // Anything else is always required, and thus has to error out
62 // in case of a cfg attr.
64 // NB: This is intentionally not part of the fold_expr() function
65 // in order for fold_opt_expr() to be able to avoid this check
66 if let Some(attr
) = expr
.attrs().iter().find(|a
| is_cfg(a
)) {
67 self.diagnostic
.span_err(attr
.span
,
68 "removing an expression is not supported in this position");
72 fn fold_opt_expr(&mut self, expr
: P
<ast
::Expr
>) -> Option
<P
<ast
::Expr
>> {
73 fold_opt_expr(self, expr
)
75 fn fold_stmt(&mut self, stmt
: ast
::Stmt
) -> SmallVector
<ast
::Stmt
> {
78 fn fold_mac(&mut self, mac
: ast
::Mac
) -> ast
::Mac
{
79 fold
::noop_fold_mac(mac
, self)
81 fn fold_item(&mut self, item
: P
<ast
::Item
>) -> SmallVector
<P
<ast
::Item
>> {
86 pub fn strip_items
<'a
, F
>(diagnostic
: &'a Handler
,
87 krate
: ast
::Crate
, in_cfg
: F
) -> ast
::Crate
where
88 F
: FnMut(&[ast
::Attribute
]) -> bool
,
90 let mut ctxt
= Context
{
92 diagnostic
: diagnostic
,
94 ctxt
.fold_crate(krate
)
97 fn filter_foreign_item
<F
>(cx
: &mut Context
<F
>,
98 item
: ast
::ForeignItem
)
99 -> Option
<ast
::ForeignItem
> where
100 F
: FnMut(&[ast
::Attribute
]) -> bool
102 if foreign_item_in_cfg(cx
, &item
) {
109 fn fold_foreign_mod
<F
>(cx
: &mut Context
<F
>,
110 ast
::ForeignMod {abi, items}
: ast
::ForeignMod
)
111 -> ast
::ForeignMod
where
112 F
: FnMut(&[ast
::Attribute
]) -> bool
116 items
: items
.into_iter()
117 .filter_map(|a
| filter_foreign_item(cx
, a
))
122 fn fold_item
<F
>(cx
: &mut Context
<F
>, item
: P
<ast
::Item
>) -> SmallVector
<P
<ast
::Item
>> where
123 F
: FnMut(&[ast
::Attribute
]) -> bool
125 if item_in_cfg(cx
, &item
) {
126 SmallVector
::one(item
.map(|i
| cx
.fold_item_simple(i
)))
132 fn fold_item_kind
<F
>(cx
: &mut Context
<F
>, item
: ast
::ItemKind
) -> ast
::ItemKind
where
133 F
: FnMut(&[ast
::Attribute
]) -> bool
135 let item
= match item
{
136 ast
::ItemKind
::Impl(u
, o
, a
, b
, c
, impl_items
) => {
137 let impl_items
= impl_items
.into_iter()
138 .filter(|ii
| (cx
.in_cfg
)(&ii
.attrs
))
140 ast
::ItemKind
::Impl(u
, o
, a
, b
, c
, impl_items
)
142 ast
::ItemKind
::Trait(u
, a
, b
, methods
) => {
143 let methods
= methods
.into_iter()
144 .filter(|ti
| (cx
.in_cfg
)(&ti
.attrs
))
146 ast
::ItemKind
::Trait(u
, a
, b
, methods
)
148 ast
::ItemKind
::Struct(def
, generics
) => {
149 ast
::ItemKind
::Struct(fold_struct(cx
, def
), generics
)
151 ast
::ItemKind
::Enum(def
, generics
) => {
152 let variants
= def
.variants
.into_iter().filter_map(|v
| {
153 if !(cx
.in_cfg
)(&v
.node
.attrs
) {
157 node
: ast
::Variant_
{
160 data
: fold_struct(cx
, v
.node
.data
),
161 disr_expr
: v
.node
.disr_expr
,
167 ast
::ItemKind
::Enum(ast
::EnumDef
{
168 variants
: variants
.collect(),
174 fold
::noop_fold_item_kind(item
, cx
)
177 fn fold_struct
<F
>(cx
: &mut Context
<F
>, vdata
: ast
::VariantData
) -> ast
::VariantData
where
178 F
: FnMut(&[ast
::Attribute
]) -> bool
181 ast
::VariantData
::Struct(fields
, id
) => {
182 ast
::VariantData
::Struct(fields
.into_iter().filter(|m
| {
183 (cx
.in_cfg
)(&m
.attrs
)
186 ast
::VariantData
::Tuple(fields
, id
) => {
187 ast
::VariantData
::Tuple(fields
.into_iter().filter(|m
| {
188 (cx
.in_cfg
)(&m
.attrs
)
191 ast
::VariantData
::Unit(id
) => ast
::VariantData
::Unit(id
)
195 fn fold_opt_expr
<F
>(cx
: &mut Context
<F
>, expr
: P
<ast
::Expr
>) -> Option
<P
<ast
::Expr
>>
196 where F
: FnMut(&[ast
::Attribute
]) -> bool
198 if expr_in_cfg(cx
, &expr
) {
199 Some(fold_expr(cx
, expr
))
205 fn fold_expr
<F
>(cx
: &mut Context
<F
>, expr
: P
<ast
::Expr
>) -> P
<ast
::Expr
> where
206 F
: FnMut(&[ast
::Attribute
]) -> bool
208 expr
.map(|ast
::Expr {id, span, node, attrs}
| {
209 fold
::noop_fold_expr(ast
::Expr
{
212 ast
::ExprKind
::Match(m
, arms
) => {
213 ast
::ExprKind
::Match(m
, arms
.into_iter()
214 .filter(|a
| (cx
.in_cfg
)(&a
.attrs
))
225 fn fold_stmt
<F
>(cx
: &mut Context
<F
>, stmt
: ast
::Stmt
) -> SmallVector
<ast
::Stmt
>
226 where F
: FnMut(&[ast
::Attribute
]) -> bool
228 if stmt_in_cfg(cx
, &stmt
) {
229 fold
::noop_fold_stmt(stmt
, cx
)
235 fn stmt_in_cfg
<F
>(cx
: &mut Context
<F
>, stmt
: &ast
::Stmt
) -> bool
where
236 F
: FnMut(&[ast
::Attribute
]) -> bool
238 (cx
.in_cfg
)(stmt
.node
.attrs())
241 fn expr_in_cfg
<F
>(cx
: &mut Context
<F
>, expr
: &ast
::Expr
) -> bool
where
242 F
: FnMut(&[ast
::Attribute
]) -> bool
244 (cx
.in_cfg
)(expr
.attrs())
247 fn item_in_cfg
<F
>(cx
: &mut Context
<F
>, item
: &ast
::Item
) -> bool
where
248 F
: FnMut(&[ast
::Attribute
]) -> bool
250 return (cx
.in_cfg
)(&item
.attrs
);
253 fn foreign_item_in_cfg
<F
>(cx
: &mut Context
<F
>, item
: &ast
::ForeignItem
) -> bool
where
254 F
: FnMut(&[ast
::Attribute
]) -> bool
256 return (cx
.in_cfg
)(&item
.attrs
);
259 fn is_cfg(attr
: &ast
::Attribute
) -> bool
{
260 attr
.check_name("cfg")
263 // Determine if an item should be translated in the current crate
264 // configuration based on the item's attributes
265 fn in_cfg
<T
: CfgDiag
>(cfg
: &[P
<ast
::MetaItem
>],
266 attrs
: &[ast
::Attribute
],
267 diag
: &mut T
) -> bool
{
268 attrs
.iter().all(|attr
| {
269 let mis
= match attr
.node
.value
.node
{
270 ast
::MetaItemKind
::List(_
, ref mis
) if is_cfg(&attr
) => mis
,
275 diag
.emit_error(|diagnostic
| {
276 diagnostic
.span_err(attr
.span
, "expected 1 cfg-pattern");
281 attr
::cfg_matches(cfg
, &mis
[0], diag
)
285 struct CfgAttrFolder
<'a
, T
> {
287 config
: &'a ast
::CrateConfig
,
290 // Process `#[cfg_attr]`.
291 fn process_cfg_attr(diagnostic
: &Handler
, krate
: ast
::Crate
,
292 feature_gated_cfgs
: &mut Vec
<GatedCfgAttr
>) -> ast
::Crate
{
293 let mut fld
= CfgAttrFolder
{
296 feature_gated_cfgs
: feature_gated_cfgs
,
298 config
: &krate
.config
.clone(),
300 fld
.fold_crate(krate
)
303 impl<'a
, T
: CfgDiag
> fold
::Folder
for CfgAttrFolder
<'a
, T
> {
304 fn fold_attribute(&mut self, attr
: ast
::Attribute
) -> Option
<ast
::Attribute
> {
305 if !attr
.check_name("cfg_attr") {
306 return fold
::noop_fold_attribute(attr
, self);
309 let attr_list
= match attr
.meta_item_list() {
310 Some(attr_list
) => attr_list
,
312 self.diag
.emit_error(|diag
| {
313 diag
.span_err(attr
.span
,
314 "expected `#[cfg_attr(<cfg pattern>, <attr>)]`");
319 let (cfg
, mi
) = match (attr_list
.len(), attr_list
.get(0), attr_list
.get(1)) {
320 (2, Some(cfg
), Some(mi
)) => (cfg
, mi
),
322 self.diag
.emit_error(|diag
| {
323 diag
.span_err(attr
.span
,
324 "expected `#[cfg_attr(<cfg pattern>, <attr>)]`");
330 if attr
::cfg_matches(&self.config
[..], &cfg
, &mut self.diag
) {
331 Some(respan(mi
.span
, ast
::Attribute_
{
332 id
: attr
::mk_attr_id(),
333 style
: attr
.node
.style
,
335 is_sugared_doc
: false,
342 // Need the ability to run pre-expansion.
343 fn fold_mac(&mut self, mac
: ast
::Mac
) -> ast
::Mac
{
344 fold
::noop_fold_mac(mac
, self)
348 fn check_for_gated_stmt_expr_attributes(krate
: &ast
::Crate
,
349 discovered
: &mut Vec
<GatedCfgAttr
>) {
350 let mut v
= StmtExprAttrFeatureVisitor
{
351 config
: &krate
.config
,
352 discovered
: discovered
,
354 visit
::walk_crate(&mut v
, krate
);
357 /// To cover this feature, we need to discover all attributes
358 /// so we need to run before cfg.
359 struct StmtExprAttrFeatureVisitor
<'a
, 'b
> {
360 config
: &'a ast
::CrateConfig
,
361 discovered
: &'b
mut Vec
<GatedCfgAttr
>,
364 // Runs the cfg_attr and cfg folders locally in "silent" mode
365 // to discover attribute use on stmts or expressions ahead of time
366 impl<'v
, 'a
, 'b
> visit
::Visitor
<'v
> for StmtExprAttrFeatureVisitor
<'a
, 'b
> {
367 fn visit_stmt(&mut self, s
: &'v ast
::Stmt
) {
368 // check if there even are any attributes on this node
369 let stmt_attrs
= s
.node
.attrs();
370 if stmt_attrs
.len() > 0 {
371 // attributes on items are fine
372 if let ast
::StmtKind
::Decl(ref decl
, _
) = s
.node
{
373 if let ast
::DeclKind
::Item(_
) = decl
.node
{
374 visit
::walk_stmt(self, s
);
379 // flag the offending attributes
380 for attr
in stmt_attrs
{
381 self.discovered
.push(GatedCfgAttr
::GatedAttr(attr
.span
));
384 // if the node does not end up being cfg-d away, walk down
385 if node_survives_cfg(stmt_attrs
, self.config
) {
386 visit
::walk_stmt(self, s
);
389 visit
::walk_stmt(self, s
);
393 fn visit_expr(&mut self, ex
: &'v ast
::Expr
) {
394 // check if there even are any attributes on this node
395 let expr_attrs
= ex
.attrs();
396 if expr_attrs
.len() > 0 {
398 // flag the offending attributes
399 for attr
in expr_attrs
{
400 self.discovered
.push(GatedCfgAttr
::GatedAttr(attr
.span
));
403 // if the node does not end up being cfg-d away, walk down
404 if node_survives_cfg(expr_attrs
, self.config
) {
405 visit
::walk_expr(self, ex
);
408 visit
::walk_expr(self, ex
);
412 fn visit_foreign_item(&mut self, i
: &'v ast
::ForeignItem
) {
413 if node_survives_cfg(&i
.attrs
, self.config
) {
414 visit
::walk_foreign_item(self, i
);
418 fn visit_item(&mut self, i
: &'v ast
::Item
) {
419 if node_survives_cfg(&i
.attrs
, self.config
) {
420 visit
::walk_item(self, i
);
424 fn visit_impl_item(&mut self, ii
: &'v ast
::ImplItem
) {
425 if node_survives_cfg(&ii
.attrs
, self.config
) {
426 visit
::walk_impl_item(self, ii
);
430 fn visit_trait_item(&mut self, ti
: &'v ast
::TraitItem
) {
431 if node_survives_cfg(&ti
.attrs
, self.config
) {
432 visit
::walk_trait_item(self, ti
);
436 fn visit_struct_field(&mut self, s
: &'v ast
::StructField
) {
437 if node_survives_cfg(&s
.attrs
, self.config
) {
438 visit
::walk_struct_field(self, s
);
442 fn visit_variant(&mut self, v
: &'v ast
::Variant
,
443 g
: &'v ast
::Generics
, item_id
: ast
::NodeId
) {
444 if node_survives_cfg(&v
.node
.attrs
, self.config
) {
445 visit
::walk_variant(self, v
, g
, item_id
);
449 fn visit_arm(&mut self, a
: &'v ast
::Arm
) {
450 if node_survives_cfg(&a
.attrs
, self.config
) {
451 visit
::walk_arm(self, a
);
455 // This visitor runs pre expansion, so we need to prevent
456 // the default panic here
457 fn visit_mac(&mut self, mac
: &'v ast
::Mac
) {
458 visit
::walk_mac(self, mac
)
463 fn emit_error
<F
>(&mut self, f
: F
) where F
: FnMut(&Handler
);
464 fn flag_gated
<F
>(&mut self, f
: F
) where F
: FnMut(&mut Vec
<GatedCfgAttr
>);
467 pub struct CfgDiagReal
<'a
, 'b
> {
468 pub diag
: &'a Handler
,
469 pub feature_gated_cfgs
: &'b
mut Vec
<GatedCfgAttr
>,
472 impl<'a
, 'b
> CfgDiag
for CfgDiagReal
<'a
, 'b
> {
473 fn emit_error
<F
>(&mut self, mut f
: F
) where F
: FnMut(&Handler
) {
476 fn flag_gated
<F
>(&mut self, mut f
: F
) where F
: FnMut(&mut Vec
<GatedCfgAttr
>) {
477 f(self.feature_gated_cfgs
)
481 struct CfgDiagSilent
{
485 impl CfgDiag
for CfgDiagSilent
{
486 fn emit_error
<F
>(&mut self, _
: F
) where F
: FnMut(&Handler
) {
489 fn flag_gated
<F
>(&mut self, _
: F
) where F
: FnMut(&mut Vec
<GatedCfgAttr
>) {}
492 fn node_survives_cfg(attrs
: &[ast
::Attribute
],
493 config
: &ast
::CrateConfig
) -> bool
{
494 let mut survives_cfg
= true;
497 let mut fld
= CfgAttrFolder
{
498 diag
: CfgDiagSilent { error: false }
,
501 let attr
= fld
.fold_attribute(attr
.clone());
503 // In case of error we can just return true,
504 // since the actual cfg folders will end compilation anyway.
506 if fld
.diag
.error { return true; }
508 survives_cfg
&= attr
.map(|attr
| {
509 let mut diag
= CfgDiagSilent { error: false }
;
510 let r
= in_cfg(config
, &[attr
], &mut diag
);
511 if diag
.error { return true; }