about summary refs log tree commit diff
path: root/src/libsyntax
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2016-05-27 17:46:14 -0700
committerbors <bors@rust-lang.org>2016-05-27 17:46:14 -0700
commit8b012ed142f03c9082773f5091c58c82d47cae79 (patch)
treec475df9a0f4f8c46167dc60916e0b60dffaebe7d /src/libsyntax
parent7bddce693cec4ae4eb6970ed91289815b316cff3 (diff)
parent53ab1378419d48a96e3ae923462f4ba8b921ad53 (diff)
downloadrust-8b012ed142f03c9082773f5091c58c82d47cae79.tar.gz
rust-8b012ed142f03c9082773f5091c58c82d47cae79.zip
Auto merge of #33706 - jseyfried:refactor_cfg, r=nrc
Perform `cfg` attribute processing during macro expansion and fix bugs

This PR refactors `cfg` attribute processing and fixes bugs. More specifically:
 - It merges gated feature checking for stmt/expr attributes, `cfg_attr` processing, and `cfg` processing into a single fold.
  - This allows feature gated `cfg` variables to be used in `cfg_attr` on unconfigured items. All other feature gated attributes can already be used on unconfigured items.
 - It performs `cfg` attribute processing during macro expansion instead of after expansion so that macro-expanded items are configured the same as ordinary items. In particular, to match their non-expanded counterparts,
  - macro-expanded unconfigured macro invocations are no longer expanded,
  - macro-expanded unconfigured macro definitions are no longer usable, and
  - feature gated `cfg` variables on macro-expanded macro definitions/invocations are now errors.

This is a [breaking-change]. For example, the following would break:
```rust
macro_rules! m {
    () => {
        #[cfg(attr)]
        macro_rules! foo { () => {} }
        foo!(); // This will be an error

        macro_rules! bar { () => { fn f() {} } }
        #[cfg(attr)] bar!(); // This will no longer be expanded ...
        fn g() { f(); } // ... so that `f` will be unresolved.

        #[cfg(target_thread_local)] // This will be a gated feature error
        macro_rules! baz { () => {} }
    }
}

m!();
```

r? @nrc
Diffstat (limited to 'src/libsyntax')
-rw-r--r--src/libsyntax/ast.rs25
-rw-r--r--src/libsyntax/attr.rs141
-rw-r--r--src/libsyntax/codemap.rs25
-rw-r--r--src/libsyntax/config.rs596
-rw-r--r--src/libsyntax/ext/expand.rs147
-rw-r--r--src/libsyntax/test.rs17
6 files changed, 348 insertions, 603 deletions
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index 9ecaa5b346a..c8ded115db8 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -15,7 +15,7 @@ pub use self::UnsafeSource::*;
 pub use self::ViewPath_::*;
 pub use self::PathParameters::*;
 
-use attr::ThinAttributes;
+use attr::{ThinAttributes, HasAttrs};
 use codemap::{mk_sp, respan, Span, Spanned, DUMMY_SP, ExpnId};
 use abi::Abi;
 use errors;
@@ -831,13 +831,7 @@ impl StmtKind {
     }
 
     pub fn attrs(&self) -> &[Attribute] {
-        match *self {
-            StmtKind::Decl(ref d, _) => d.attrs(),
-            StmtKind::Expr(ref e, _) |
-            StmtKind::Semi(ref e, _) => e.attrs(),
-            StmtKind::Mac(_, _, Some(ref b)) => b,
-            StmtKind::Mac(_, _, None) => &[],
-        }
+        HasAttrs::attrs(self)
     }
 }
 
@@ -870,10 +864,7 @@ pub struct Local {
 
 impl Local {
     pub fn attrs(&self) -> &[Attribute] {
-        match self.attrs {
-            Some(ref b) => b,
-            None => &[],
-        }
+        HasAttrs::attrs(self)
     }
 }
 
@@ -889,10 +880,7 @@ pub enum DeclKind {
 
 impl Decl {
     pub fn attrs(&self) -> &[Attribute] {
-        match self.node {
-            DeclKind::Local(ref l) => l.attrs(),
-            DeclKind::Item(ref i) => i.attrs(),
-        }
+        HasAttrs::attrs(self)
     }
 }
 
@@ -937,10 +925,7 @@ pub struct Expr {
 
 impl Expr {
     pub fn attrs(&self) -> &[Attribute] {
-        match self.attrs {
-            Some(ref b) => b,
-            None => &[],
-        }
+        HasAttrs::attrs(self)
     }
 }
 
diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs
index 8761ca37178..c3c3deea187 100644
--- a/src/libsyntax/attr.rs
+++ b/src/libsyntax/attr.rs
@@ -884,82 +884,109 @@ impl AttributesExt for Vec<Attribute> {
     }
 }
 
+pub trait HasAttrs: Sized {
+    fn attrs(&self) -> &[ast::Attribute];
+    fn map_attrs<F: FnOnce(Vec<ast::Attribute>) -> Vec<ast::Attribute>>(self, f: F) -> Self;
+}
+
 /// A cheap way to add Attributes to an AST node.
 pub trait WithAttrs {
     // FIXME: Could be extended to anything IntoIter<Item=Attribute>
     fn with_attrs(self, attrs: ThinAttributes) -> Self;
 }
 
-impl WithAttrs for P<Expr> {
+impl<T: HasAttrs> WithAttrs for T {
     fn with_attrs(self, attrs: ThinAttributes) -> Self {
-        self.map(|mut e| {
-            e.attrs.update(|a| a.append(attrs));
-            e
+        self.map_attrs(|mut orig_attrs| {
+            orig_attrs.extend(attrs.into_attr_vec());
+            orig_attrs
         })
     }
 }
 
-impl WithAttrs for P<Item> {
-    fn with_attrs(self, attrs: ThinAttributes) -> Self {
-        self.map(|Item { ident, attrs: mut ats, id, node, vis, span }| {
-            ats.extend(attrs.into_attr_vec());
-            Item {
-                ident: ident,
-                attrs: ats,
-                id: id,
-                node: node,
-                vis: vis,
-                span: span,
-            }
-        })
+impl HasAttrs for Vec<Attribute> {
+    fn attrs(&self) -> &[Attribute] {
+        &self
+    }
+    fn map_attrs<F: FnOnce(Vec<Attribute>) -> Vec<Attribute>>(self, f: F) -> Self {
+        f(self)
     }
 }
 
-impl WithAttrs for P<Local> {
-    fn with_attrs(self, attrs: ThinAttributes) -> Self {
-        self.map(|Local { pat, ty, init, id, span, attrs: mut ats }| {
-            ats.update(|a| a.append(attrs));
-            Local {
-                pat: pat,
-                ty: ty,
-                init: init,
-                id: id,
-                span: span,
-                attrs: ats,
-            }
-        })
+impl HasAttrs for ThinAttributes {
+    fn attrs(&self) -> &[Attribute] {
+        self.as_attr_slice()
+    }
+    fn map_attrs<F: FnOnce(Vec<Attribute>) -> Vec<Attribute>>(self, f: F) -> Self {
+        self.map_thin_attrs(f)
     }
 }
 
-impl WithAttrs for P<Decl> {
-    fn with_attrs(self, attrs: ThinAttributes) -> Self {
-        self.map(|Spanned { span, node }| {
-            Spanned {
-                span: span,
-                node: match node {
-                    DeclKind::Local(local) => DeclKind::Local(local.with_attrs(attrs)),
-                    DeclKind::Item(item) => DeclKind::Item(item.with_attrs(attrs)),
-                }
-            }
-        })
+impl<T: HasAttrs + 'static> HasAttrs for P<T> {
+    fn attrs(&self) -> &[Attribute] {
+        (**self).attrs()
+    }
+    fn map_attrs<F: FnOnce(Vec<Attribute>) -> Vec<Attribute>>(self, f: F) -> Self {
+        self.map(|t| t.map_attrs(f))
     }
 }
 
-impl WithAttrs for P<Stmt> {
-    fn with_attrs(self, attrs: ThinAttributes) -> Self {
-        self.map(|Spanned { span, node }| {
-            Spanned {
-                span: span,
-                node: match node {
-                    StmtKind::Decl(decl, id) => StmtKind::Decl(decl.with_attrs(attrs), id),
-                    StmtKind::Expr(expr, id) => StmtKind::Expr(expr.with_attrs(attrs), id),
-                    StmtKind::Semi(expr, id) => StmtKind::Semi(expr.with_attrs(attrs), id),
-                    StmtKind::Mac(mac, style, mut ats) => {
-                        ats.update(|a| a.append(attrs));
-                        StmtKind::Mac(mac, style, ats)
-                    }
-                },
-            }
-        })
+impl HasAttrs for DeclKind {
+    fn attrs(&self) -> &[Attribute] {
+        match *self {
+            DeclKind::Local(ref local) => local.attrs(),
+            DeclKind::Item(ref item) => item.attrs(),
+        }
+    }
+
+    fn map_attrs<F: FnOnce(Vec<Attribute>) -> Vec<Attribute>>(self, f: F) -> Self {
+        match self {
+            DeclKind::Local(local) => DeclKind::Local(local.map_attrs(f)),
+            DeclKind::Item(item) => DeclKind::Item(item.map_attrs(f)),
+        }
     }
 }
+
+impl HasAttrs for StmtKind {
+    fn attrs(&self) -> &[Attribute] {
+        match *self {
+            StmtKind::Decl(ref decl, _) => decl.attrs(),
+            StmtKind::Expr(ref expr, _) | StmtKind::Semi(ref expr, _) => expr.attrs(),
+            StmtKind::Mac(_, _, ref attrs) => attrs.attrs(),
+        }
+    }
+
+    fn map_attrs<F: FnOnce(Vec<Attribute>) -> Vec<Attribute>>(self, f: F) -> Self {
+        match self {
+            StmtKind::Decl(decl, id) => StmtKind::Decl(decl.map_attrs(f), id),
+            StmtKind::Expr(expr, id) => StmtKind::Expr(expr.map_attrs(f), id),
+            StmtKind::Semi(expr, id) => StmtKind::Semi(expr.map_attrs(f), id),
+            StmtKind::Mac(mac, style, attrs) =>
+                StmtKind::Mac(mac, style, attrs.map_attrs(f)),
+        }
+    }
+}
+
+macro_rules! derive_has_attrs_from_field {
+    ($($ty:path),*) => { derive_has_attrs_from_field!($($ty: .attrs),*); };
+    ($($ty:path : $(.$field:ident)*),*) => { $(
+        impl HasAttrs for $ty {
+            fn attrs(&self) -> &[Attribute] {
+                self $(.$field)* .attrs()
+            }
+
+            fn map_attrs<F>(mut self, f: F) -> Self
+                where F: FnOnce(Vec<Attribute>) -> Vec<Attribute>,
+            {
+                self $(.$field)* = self $(.$field)* .map_attrs(f);
+                self
+            }
+        }
+    )* }
+}
+
+derive_has_attrs_from_field! {
+    Item, Expr, Local, ast::ForeignItem, ast::StructField, ast::ImplItem, ast::TraitItem, ast::Arm
+}
+
+derive_has_attrs_from_field! { Decl: .node, Stmt: .node, ast::Variant: .node.attrs }
diff --git a/src/libsyntax/codemap.rs b/src/libsyntax/codemap.rs
index 3b13bf2fc50..d391cd0be7b 100644
--- a/src/libsyntax/codemap.rs
+++ b/src/libsyntax/codemap.rs
@@ -1258,31 +1258,6 @@ impl CodeMap {
         return a;
     }
 
-    /// Check if the backtrace `subtrace` contains `suptrace` as a prefix.
-    pub fn more_specific_trace(&self,
-                              mut subtrace: ExpnId,
-                              suptrace: ExpnId)
-                              -> bool {
-        loop {
-            if subtrace == suptrace {
-                return true;
-            }
-
-            let stop = self.with_expn_info(subtrace, |opt_expn_info| {
-                if let Some(expn_info) = opt_expn_info {
-                    subtrace = expn_info.call_site.expn_id;
-                    false
-                } else {
-                    true
-                }
-            });
-
-            if stop {
-                return false;
-            }
-        }
-    }
-
     pub fn record_expansion(&self, expn_info: ExpnInfo) -> ExpnId {
         let mut expansions = self.expansions.borrow_mut();
         expansions.push(expn_info);
diff --git a/src/libsyntax/config.rs b/src/libsyntax/config.rs
index 4554a280e5f..14035d8d116 100644
--- a/src/libsyntax/config.rs
+++ b/src/libsyntax/config.rs
@@ -8,326 +8,76 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use attr::AttrMetaMethods;
+use attr::{AttrMetaMethods, HasAttrs};
 use errors::Handler;
 use feature_gate::GatedCfgAttr;
 use fold::Folder;
 use {ast, fold, attr};
-use visit;
 use codemap::{Spanned, respan};
 use ptr::P;
 
 use util::small_vector::SmallVector;
 
-/// A folder that strips out items that do not belong in the current
-/// configuration.
-struct Context<'a, F> where F: FnMut(&[ast::Attribute]) -> bool {
-    in_cfg: F,
-    diagnostic: &'a Handler,
-}
+pub trait CfgFolder: fold::Folder {
+    // Check if a node with the given attributes is in this configuration.
+    fn in_cfg(&mut self, attrs: &[ast::Attribute]) -> bool;
 
-// Support conditional compilation by transforming the AST, stripping out
-// any items that do not belong in the current configuration
-pub fn strip_unconfigured_items(diagnostic: &Handler, krate: ast::Crate,
-                                feature_gated_cfgs: &mut Vec<GatedCfgAttr>)
-                                -> ast::Crate
-{
-    // Need to do this check here because cfg runs before feature_gates
-    check_for_gated_stmt_expr_attributes(&krate, feature_gated_cfgs);
-
-    let krate = process_cfg_attr(diagnostic, krate, feature_gated_cfgs);
-    let config = krate.config.clone();
-    strip_items(diagnostic,
-                krate,
-                |attrs| {
-                    let mut diag = CfgDiagReal {
-                        diag: diagnostic,
-                        feature_gated_cfgs: feature_gated_cfgs,
-                    };
-                    in_cfg(&config, attrs, &mut diag)
-                })
-}
+    // Update a node before checking if it is in this configuration (used to implement `cfg_attr`).
+    fn process_attrs<T: HasAttrs>(&mut self, node: T) -> T { node }
 
-impl<'a, F> fold::Folder for Context<'a, F> where F: FnMut(&[ast::Attribute]) -> bool {
-    fn fold_foreign_mod(&mut self, foreign_mod: ast::ForeignMod) -> ast::ForeignMod {
-        fold_foreign_mod(self, foreign_mod)
-    }
-    fn fold_item_kind(&mut self, item: ast::ItemKind) -> ast::ItemKind {
-        fold_item_kind(self, item)
-    }
-    fn fold_expr(&mut self, expr: P<ast::Expr>) -> P<ast::Expr> {
-        // If an expr is valid to cfg away it will have been removed by the
-        // outer stmt or expression folder before descending in here.
-        // Anything else is always required, and thus has to error out
-        // in case of a cfg attr.
-        //
-        // NB: This is intentionally not part of the fold_expr() function
-        //     in order for fold_opt_expr() to be able to avoid this check
-        if let Some(attr) = expr.attrs().iter().find(|a| is_cfg(a)) {
-            self.diagnostic.span_err(attr.span,
-                "removing an expression is not supported in this position");
-        }
-        fold_expr(self, expr)
-    }
-    fn fold_opt_expr(&mut self, expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
-        fold_opt_expr(self, expr)
-    }
-    fn fold_stmt(&mut self, stmt: ast::Stmt) -> SmallVector<ast::Stmt> {
-        fold_stmt(self, stmt)
-    }
-    fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
-        fold::noop_fold_mac(mac, self)
-    }
-    fn fold_item(&mut self, item: P<ast::Item>) -> SmallVector<P<ast::Item>> {
-        fold_item(self, item)
-    }
-}
+    // Visit attributes on expression and statements (but not attributes on items in blocks).
+    fn visit_stmt_or_expr_attrs(&mut self, _attrs: &[ast::Attribute]) {}
 
-pub fn strip_items<'a, F>(diagnostic: &'a Handler,
-                          krate: ast::Crate, in_cfg: F) -> ast::Crate where
-    F: FnMut(&[ast::Attribute]) -> bool,
-{
-    let mut ctxt = Context {
-        in_cfg: in_cfg,
-        diagnostic: diagnostic,
-    };
-    ctxt.fold_crate(krate)
-}
+    // Visit unremovable (non-optional) expressions -- c.f. `fold_expr` vs `fold_opt_expr`.
+    fn visit_unremovable_expr(&mut self, _expr: &ast::Expr) {}
 
-fn filter_foreign_item<F>(cx: &mut Context<F>,
-                          item: ast::ForeignItem)
-                          -> Option<ast::ForeignItem> where
-    F: FnMut(&[ast::Attribute]) -> bool
-{
-    if foreign_item_in_cfg(cx, &item) {
-        Some(item)
-    } else {
-        None
+    fn configure<T: HasAttrs>(&mut self, node: T) -> Option<T> {
+        let node = self.process_attrs(node);
+        if self.in_cfg(node.attrs()) { Some(node) } else { None }
     }
 }
 
-fn fold_foreign_mod<F>(cx: &mut Context<F>,
-                       ast::ForeignMod {abi, items}: ast::ForeignMod)
-                       -> ast::ForeignMod where
-    F: FnMut(&[ast::Attribute]) -> bool
-{
-    ast::ForeignMod {
-        abi: abi,
-        items: items.into_iter()
-                    .filter_map(|a| filter_foreign_item(cx, a))
-                    .collect()
-    }
-}
-
-fn fold_item<F>(cx: &mut Context<F>, item: P<ast::Item>) -> SmallVector<P<ast::Item>> where
-    F: FnMut(&[ast::Attribute]) -> bool
-{
-    if item_in_cfg(cx, &item) {
-        SmallVector::one(item.map(|i| cx.fold_item_simple(i)))
-    } else {
-        SmallVector::zero()
-    }
-}
-
-fn fold_item_kind<F>(cx: &mut Context<F>, item: ast::ItemKind) -> ast::ItemKind where
-    F: FnMut(&[ast::Attribute]) -> bool
-{
-    let item = match item {
-        ast::ItemKind::Impl(u, o, a, b, c, impl_items) => {
-            let impl_items = impl_items.into_iter()
-                                       .filter(|ii| (cx.in_cfg)(&ii.attrs))
-                                       .collect();
-            ast::ItemKind::Impl(u, o, a, b, c, impl_items)
-        }
-        ast::ItemKind::Trait(u, a, b, methods) => {
-            let methods = methods.into_iter()
-                                 .filter(|ti| (cx.in_cfg)(&ti.attrs))
-                                 .collect();
-            ast::ItemKind::Trait(u, a, b, methods)
-        }
-        ast::ItemKind::Struct(def, generics) => {
-            ast::ItemKind::Struct(fold_struct(cx, def), generics)
-        }
-        ast::ItemKind::Enum(def, generics) => {
-            let variants = def.variants.into_iter().filter_map(|v| {
-                if !(cx.in_cfg)(&v.node.attrs) {
-                    None
-                } else {
-                    Some(Spanned {
-                        node: ast::Variant_ {
-                            name: v.node.name,
-                            attrs: v.node.attrs,
-                            data: fold_struct(cx, v.node.data),
-                            disr_expr: v.node.disr_expr,
-                        },
-                        span: v.span
-                    })
-                }
-            });
-            ast::ItemKind::Enum(ast::EnumDef {
-                variants: variants.collect(),
-            }, generics)
-        }
-        item => item,
-    };
-
-    fold::noop_fold_item_kind(item, cx)
+/// A folder that strips out items that do not belong in the current
+/// configuration.
+pub struct StripUnconfigured<'a> {
+    diag: CfgDiagReal<'a, 'a>,
+    config: &'a ast::CrateConfig,
 }
 
-fn fold_struct<F>(cx: &mut Context<F>, vdata: ast::VariantData) -> ast::VariantData where
-    F: FnMut(&[ast::Attribute]) -> bool
-{
-    match vdata {
-        ast::VariantData::Struct(fields, id) => {
-            ast::VariantData::Struct(fields.into_iter().filter(|m| {
-                (cx.in_cfg)(&m.attrs)
-            }).collect(), id)
-        }
-        ast::VariantData::Tuple(fields, id) => {
-            ast::VariantData::Tuple(fields.into_iter().filter(|m| {
-                (cx.in_cfg)(&m.attrs)
-            }).collect(), id)
+impl<'a> StripUnconfigured<'a> {
+    pub fn new(config: &'a ast::CrateConfig,
+               diagnostic: &'a Handler,
+               feature_gated_cfgs: &'a mut Vec<GatedCfgAttr>)
+               -> Self {
+        StripUnconfigured {
+            config: config,
+            diag: CfgDiagReal { diag: diagnostic, feature_gated_cfgs: feature_gated_cfgs },
         }
-        ast::VariantData::Unit(id) => ast::VariantData::Unit(id)
-    }
-}
-
-fn fold_opt_expr<F>(cx: &mut Context<F>, expr: P<ast::Expr>) -> Option<P<ast::Expr>>
-    where F: FnMut(&[ast::Attribute]) -> bool
-{
-    if expr_in_cfg(cx, &expr) {
-        Some(fold_expr(cx, expr))
-    } else {
-        None
-    }
-}
-
-fn fold_expr<F>(cx: &mut Context<F>, expr: P<ast::Expr>) -> P<ast::Expr> where
-    F: FnMut(&[ast::Attribute]) -> bool
-{
-    expr.map(|ast::Expr {id, span, node, attrs}| {
-        fold::noop_fold_expr(ast::Expr {
-            id: id,
-            node: match node {
-                ast::ExprKind::Match(m, arms) => {
-                    ast::ExprKind::Match(m, arms.into_iter()
-                                        .filter(|a| (cx.in_cfg)(&a.attrs))
-                                        .collect())
-                }
-                _ => node
-            },
-            span: span,
-            attrs: attrs,
-        }, cx)
-    })
-}
-
-fn fold_stmt<F>(cx: &mut Context<F>, stmt: ast::Stmt) -> SmallVector<ast::Stmt>
-    where F: FnMut(&[ast::Attribute]) -> bool
-{
-    if stmt_in_cfg(cx, &stmt) {
-        fold::noop_fold_stmt(stmt, cx)
-    } else {
-        SmallVector::zero()
     }
-}
-
-fn stmt_in_cfg<F>(cx: &mut Context<F>, stmt: &ast::Stmt) -> bool where
-    F: FnMut(&[ast::Attribute]) -> bool
-{
-    (cx.in_cfg)(stmt.node.attrs())
-}
-
-fn expr_in_cfg<F>(cx: &mut Context<F>, expr: &ast::Expr) -> bool where
-    F: FnMut(&[ast::Attribute]) -> bool
-{
-    (cx.in_cfg)(expr.attrs())
-}
-
-fn item_in_cfg<F>(cx: &mut Context<F>, item: &ast::Item) -> bool where
-    F: FnMut(&[ast::Attribute]) -> bool
-{
-    return (cx.in_cfg)(&item.attrs);
-}
-
-fn foreign_item_in_cfg<F>(cx: &mut Context<F>, item: &ast::ForeignItem) -> bool where
-    F: FnMut(&[ast::Attribute]) -> bool
-{
-    return (cx.in_cfg)(&item.attrs);
-}
-
-fn is_cfg(attr: &ast::Attribute) -> bool {
-    attr.check_name("cfg")
-}
-
-// Determine if an item should be translated in the current crate
-// configuration based on the item's attributes
-fn in_cfg<T: CfgDiag>(cfg: &[P<ast::MetaItem>],
-                      attrs: &[ast::Attribute],
-                      diag: &mut T) -> bool {
-    attrs.iter().all(|attr| {
-        let mis = match attr.node.value.node {
-            ast::MetaItemKind::List(_, ref mis) if is_cfg(&attr) => mis,
-            _ => return true
-        };
-
-        if mis.len() != 1 {
-            diag.emit_error(|diagnostic| {
-                diagnostic.span_err(attr.span, "expected 1 cfg-pattern");
-            });
-            return true;
-        }
-
-        attr::cfg_matches(cfg, &mis[0], diag)
-    })
-}
 
-struct CfgAttrFolder<'a, T> {
-    diag: T,
-    config: &'a ast::CrateConfig,
-}
-
-// Process `#[cfg_attr]`.
-fn process_cfg_attr(diagnostic: &Handler, krate: ast::Crate,
-                    feature_gated_cfgs: &mut Vec<GatedCfgAttr>) -> ast::Crate {
-    let mut fld = CfgAttrFolder {
-        diag: CfgDiagReal {
-            diag: diagnostic,
-            feature_gated_cfgs: feature_gated_cfgs,
-        },
-        config: &krate.config.clone(),
-    };
-    fld.fold_crate(krate)
-}
-
-impl<'a, T: CfgDiag> fold::Folder for CfgAttrFolder<'a, T> {
-    fn fold_attribute(&mut self, attr: ast::Attribute) -> Option<ast::Attribute> {
+    fn process_cfg_attr(&mut self, attr: ast::Attribute) -> Option<ast::Attribute> {
         if !attr.check_name("cfg_attr") {
-            return fold::noop_fold_attribute(attr, self);
+            return Some(attr);
         }
 
         let attr_list = match attr.meta_item_list() {
             Some(attr_list) => attr_list,
             None => {
-                self.diag.emit_error(|diag| {
-                    diag.span_err(attr.span,
-                        "expected `#[cfg_attr(<cfg pattern>, <attr>)]`");
-                });
+                let msg = "expected `#[cfg_attr(<cfg pattern>, <attr>)]`";
+                self.diag.diag.span_err(attr.span, msg);
                 return None;
             }
         };
         let (cfg, mi) = match (attr_list.len(), attr_list.get(0), attr_list.get(1)) {
             (2, Some(cfg), Some(mi)) => (cfg, mi),
             _ => {
-                self.diag.emit_error(|diag| {
-                    diag.span_err(attr.span,
-                        "expected `#[cfg_attr(<cfg pattern>, <attr>)]`");
-                });
+                let msg = "expected `#[cfg_attr(<cfg pattern>, <attr>)]`";
+                self.diag.diag.span_err(attr.span, msg);
                 return None;
             }
         };
 
-        if attr::cfg_matches(&self.config[..], &cfg, &mut self.diag) {
+        if attr::cfg_matches(self.config, &cfg, &mut self.diag) {
             Some(respan(mi.span, ast::Attribute_ {
                 id: attr::mk_attr_id(),
                 style: attr.node.style,
@@ -338,127 +88,187 @@ impl<'a, T: CfgDiag> fold::Folder for CfgAttrFolder<'a, T> {
             None
         }
     }
-
-    // Need the ability to run pre-expansion.
-    fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
-        fold::noop_fold_mac(mac, self)
-    }
 }
 
-fn check_for_gated_stmt_expr_attributes(krate: &ast::Crate,
-                                        discovered: &mut Vec<GatedCfgAttr>) {
-    let mut v = StmtExprAttrFeatureVisitor {
-        config: &krate.config,
-        discovered: discovered,
-    };
-    visit::walk_crate(&mut v, krate);
-}
-
-/// To cover this feature, we need to discover all attributes
-/// so we need to run before cfg.
-struct StmtExprAttrFeatureVisitor<'a, 'b> {
-    config: &'a ast::CrateConfig,
-    discovered: &'b mut Vec<GatedCfgAttr>,
-}
-
-// Runs the cfg_attr and cfg folders locally in "silent" mode
-// to discover attribute use on stmts or expressions ahead of time
-impl<'v, 'a, 'b> visit::Visitor<'v> for StmtExprAttrFeatureVisitor<'a, 'b> {
-    fn visit_stmt(&mut self, s: &'v ast::Stmt) {
-        // check if there even are any attributes on this node
-        let stmt_attrs = s.node.attrs();
-        if stmt_attrs.len() > 0 {
-            // attributes on items are fine
-            if let ast::StmtKind::Decl(ref decl, _) = s.node {
-                if let ast::DeclKind::Item(_) = decl.node {
-                    visit::walk_stmt(self, s);
-                    return;
-                }
-            }
-
-            // flag the offending attributes
-            for attr in stmt_attrs {
-                self.discovered.push(GatedCfgAttr::GatedAttr(attr.span));
+impl<'a> CfgFolder for StripUnconfigured<'a> {
+    // Determine if an item should be translated in the current crate
+    // configuration based on the item's attributes
+    fn in_cfg(&mut self, attrs: &[ast::Attribute]) -> bool {
+        attrs.iter().all(|attr| {
+            let mis = match attr.node.value.node {
+                ast::MetaItemKind::List(_, ref mis) if is_cfg(&attr) => mis,
+                _ => return true
+            };
+
+            if mis.len() != 1 {
+                self.diag.emit_error(|diagnostic| {
+                    diagnostic.span_err(attr.span, "expected 1 cfg-pattern");
+                });
+                return true;
             }
 
-            // if the node does not end up being cfg-d away, walk down
-            if node_survives_cfg(stmt_attrs, self.config) {
-                visit::walk_stmt(self, s);
-            }
-        } else {
-            visit::walk_stmt(self, s);
-        }
+            attr::cfg_matches(self.config, &mis[0], &mut self.diag)
+        })
     }
 
-    fn visit_expr(&mut self, ex: &'v ast::Expr) {
-        // check if there even are any attributes on this node
-        let expr_attrs = ex.attrs();
-        if expr_attrs.len() > 0 {
-
-            // flag the offending attributes
-            for attr in expr_attrs {
-                self.discovered.push(GatedCfgAttr::GatedAttr(attr.span));
-            }
+    fn process_attrs<T: HasAttrs>(&mut self, node: T) -> T {
+        node.map_attrs(|attrs| {
+            attrs.into_iter().filter_map(|attr| self.process_cfg_attr(attr)).collect()
+        })
+    }
 
-            // if the node does not end up being cfg-d away, walk down
-            if node_survives_cfg(expr_attrs, self.config) {
-                visit::walk_expr(self, ex);
-            }
-        } else {
-            visit::walk_expr(self, ex);
+    fn visit_stmt_or_expr_attrs(&mut self, attrs: &[ast::Attribute]) {
+        // flag the offending attributes
+        for attr in attrs.iter() {
+            self.diag.feature_gated_cfgs.push(GatedCfgAttr::GatedAttr(attr.span));
         }
     }
 
-    fn visit_foreign_item(&mut self, i: &'v ast::ForeignItem) {
-        if node_survives_cfg(&i.attrs, self.config) {
-            visit::walk_foreign_item(self, i);
+    fn visit_unremovable_expr(&mut self, expr: &ast::Expr) {
+        if let Some(attr) = expr.attrs().iter().find(|a| is_cfg(a)) {
+            let msg = "removing an expression is not supported in this position";
+            self.diag.diag.span_err(attr.span, msg);
         }
     }
+}
+
+// Support conditional compilation by transforming the AST, stripping out
+// any items that do not belong in the current configuration
+pub fn strip_unconfigured_items(diagnostic: &Handler, krate: ast::Crate,
+                                feature_gated_cfgs: &mut Vec<GatedCfgAttr>)
+                                -> ast::Crate
+{
+    let config = &krate.config.clone();
+    StripUnconfigured::new(config, diagnostic, feature_gated_cfgs).fold_crate(krate)
+}
 
-    fn visit_item(&mut self, i: &'v ast::Item) {
-        if node_survives_cfg(&i.attrs, self.config) {
-            visit::walk_item(self, i);
+impl<T: CfgFolder> fold::Folder for T {
+    fn fold_foreign_mod(&mut self, foreign_mod: ast::ForeignMod) -> ast::ForeignMod {
+        ast::ForeignMod {
+            abi: foreign_mod.abi,
+            items: foreign_mod.items.into_iter().filter_map(|item| {
+                self.configure(item).map(|item| fold::noop_fold_foreign_item(item, self))
+            }).collect(),
         }
     }
 
-    fn visit_impl_item(&mut self, ii: &'v ast::ImplItem) {
-        if node_survives_cfg(&ii.attrs, self.config) {
-            visit::walk_impl_item(self, ii);
-        }
+    fn fold_item_kind(&mut self, item: ast::ItemKind) -> ast::ItemKind {
+        let fold_struct = |this: &mut Self, vdata| match vdata {
+            ast::VariantData::Struct(fields, id) => {
+                let fields = fields.into_iter().filter_map(|field| this.configure(field));
+                ast::VariantData::Struct(fields.collect(), id)
+            }
+            ast::VariantData::Tuple(fields, id) => {
+                let fields = fields.into_iter().filter_map(|field| this.configure(field));
+                ast::VariantData::Tuple(fields.collect(), id)
+            }
+            ast::VariantData::Unit(id) => ast::VariantData::Unit(id)
+        };
+
+        let item = match item {
+            ast::ItemKind::Impl(u, o, a, b, c, items) => {
+                let items = items.into_iter().filter_map(|item| self.configure(item)).collect();
+                ast::ItemKind::Impl(u, o, a, b, c, items)
+            }
+            ast::ItemKind::Trait(u, a, b, items) => {
+                let items = items.into_iter().filter_map(|item| self.configure(item)).collect();
+                ast::ItemKind::Trait(u, a, b, items)
+            }
+            ast::ItemKind::Struct(def, generics) => {
+                ast::ItemKind::Struct(fold_struct(self, def), generics)
+            }
+            ast::ItemKind::Enum(def, generics) => {
+                let variants = def.variants.into_iter().filter_map(|v| {
+                    self.configure(v).map(|v| {
+                        Spanned {
+                            node: ast::Variant_ {
+                                name: v.node.name,
+                                attrs: v.node.attrs,
+                                data: fold_struct(self, v.node.data),
+                                disr_expr: v.node.disr_expr,
+                            },
+                            span: v.span
+                        }
+                    })
+                });
+                ast::ItemKind::Enum(ast::EnumDef {
+                    variants: variants.collect(),
+                }, generics)
+            }
+            item => item,
+        };
+
+        fold::noop_fold_item_kind(item, self)
     }
 
-    fn visit_trait_item(&mut self, ti: &'v ast::TraitItem) {
-        if node_survives_cfg(&ti.attrs, self.config) {
-            visit::walk_trait_item(self, ti);
-        }
+    fn fold_expr(&mut self, expr: P<ast::Expr>) -> P<ast::Expr> {
+        self.visit_stmt_or_expr_attrs(expr.attrs());
+        // If an expr is valid to cfg away it will have been removed by the
+        // outer stmt or expression folder before descending in here.
+        // Anything else is always required, and thus has to error out
+        // in case of a cfg attr.
+        //
+        // NB: This is intentionally not part of the fold_expr() function
+        //     in order for fold_opt_expr() to be able to avoid this check
+        self.visit_unremovable_expr(&expr);
+        let expr = self.process_attrs(expr);
+        fold_expr(self, expr)
     }
 
-    fn visit_struct_field(&mut self, s: &'v ast::StructField) {
-        if node_survives_cfg(&s.attrs, self.config) {
-            visit::walk_struct_field(self, s);
-        }
+    fn fold_opt_expr(&mut self, expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
+        self.configure(expr).map(|expr| fold_expr(self, expr))
     }
 
-    fn visit_variant(&mut self, v: &'v ast::Variant,
-                     g: &'v ast::Generics, item_id: ast::NodeId) {
-        if node_survives_cfg(&v.node.attrs, self.config) {
-            visit::walk_variant(self, v, g, item_id);
+    fn fold_stmt(&mut self, stmt: ast::Stmt) -> SmallVector<ast::Stmt> {
+        let is_item = match stmt.node {
+            ast::StmtKind::Decl(ref decl, _) => match decl.node {
+                ast::DeclKind::Item(_) => true,
+                _ => false,
+            },
+            _ => false,
+        };
+
+        // avoid calling `visit_stmt_or_expr_attrs` on items
+        if !is_item {
+            self.visit_stmt_or_expr_attrs(stmt.attrs());
         }
+
+        self.configure(stmt).map(|stmt| fold::noop_fold_stmt(stmt, self))
+                            .unwrap_or(SmallVector::zero())
     }
 
-    fn visit_arm(&mut self, a: &'v ast::Arm) {
-        if node_survives_cfg(&a.attrs, self.config) {
-            visit::walk_arm(self, a);
-        }
+    fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
+        fold::noop_fold_mac(mac, self)
     }
 
-    // This visitor runs pre expansion, so we need to prevent
-    // the default panic here
-    fn visit_mac(&mut self, mac: &'v ast::Mac) {
-        visit::walk_mac(self, mac)
+    fn fold_item(&mut self, item: P<ast::Item>) -> SmallVector<P<ast::Item>> {
+        self.configure(item).map(|item| SmallVector::one(item.map(|i| self.fold_item_simple(i))))
+                            .unwrap_or(SmallVector::zero())
     }
 }
 
+fn fold_expr<F: CfgFolder>(folder: &mut F, expr: P<ast::Expr>) -> P<ast::Expr> {
+    expr.map(|ast::Expr {id, span, node, attrs}| {
+        fold::noop_fold_expr(ast::Expr {
+            id: id,
+            node: match node {
+                ast::ExprKind::Match(m, arms) => {
+                    ast::ExprKind::Match(m, arms.into_iter()
+                                        .filter_map(|a| folder.configure(a))
+                                        .collect())
+                }
+                _ => node
+            },
+            span: span,
+            attrs: attrs,
+        }, folder)
+    })
+}
+
+fn is_cfg(attr: &ast::Attribute) -> bool {
+    attr.check_name("cfg")
+}
+
 pub trait CfgDiag {
     fn emit_error<F>(&mut self, f: F) where F: FnMut(&Handler);
     fn flag_gated<F>(&mut self, f: F) where F: FnMut(&mut Vec<GatedCfgAttr>);
@@ -477,41 +287,3 @@ impl<'a, 'b> CfgDiag for CfgDiagReal<'a, 'b> {
         f(self.feature_gated_cfgs)
     }
 }
-
-struct CfgDiagSilent {
-    error: bool,
-}
-
-impl CfgDiag for CfgDiagSilent {
-    fn emit_error<F>(&mut self, _: F) where F: FnMut(&Handler) {
-        self.error = true;
-    }
-    fn flag_gated<F>(&mut self, _: F) where F: FnMut(&mut Vec<GatedCfgAttr>) {}
-}
-
-fn node_survives_cfg(attrs: &[ast::Attribute],
-                     config: &ast::CrateConfig) -> bool {
-    let mut survives_cfg = true;
-
-    for attr in attrs {
-        let mut fld = CfgAttrFolder {
-            diag: CfgDiagSilent { error: false },
-            config: config,
-        };
-        let attr = fld.fold_attribute(attr.clone());
-
-        // In case of error we can just return true,
-        // since the actual cfg folders will end compilation anyway.
-
-        if fld.diag.error { return true; }
-
-        survives_cfg &= attr.map(|attr| {
-            let mut diag = CfgDiagSilent { error: false };
-            let r = in_cfg(config, &[attr], &mut diag);
-            if diag.error { return true; }
-            r
-        }).unwrap_or(true)
-    }
-
-    survives_cfg
-}
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index ecca370fb8a..c3202dbdbb4 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -18,7 +18,8 @@ use ext::build::AstBuilder;
 use attr;
 use attr::{AttrMetaMethods, WithAttrs, ThinAttributesExt};
 use codemap;
-use codemap::{Span, Spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute};
+use codemap::{Span, Spanned, ExpnInfo, ExpnId, NameAndSpan, MacroBang, MacroAttribute};
+use config::StripUnconfigured;
 use ext::base::*;
 use feature_gate::{self, Features};
 use fold;
@@ -33,7 +34,6 @@ use visit::Visitor;
 use std_inject;
 
 use std::collections::HashSet;
-use std::env;
 
 // A trait for AST nodes and AST node lists into which macro invocations may expand.
 trait MacroGenerable: Sized {
@@ -77,25 +77,35 @@ impl_macro_generable! {
         "statement", .make_stmts,      lift .fold_stmt,      |_span| SmallVector::zero();
 }
 
-pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> {
-    return e.and_then(|ast::Expr {id, node, span, attrs}| match node {
+impl MacroGenerable for Option<P<ast::Expr>> {
+    fn kind_name() -> &'static str { "expression" }
+    fn dummy(_span: Span) -> Self { None }
+    fn make_with<'a>(result: Box<MacResult + 'a>) -> Option<Self> {
+        result.make_expr().map(Some)
+    }
+    fn fold_with<F: Folder>(self, folder: &mut F) -> Self {
+        self.and_then(|expr| folder.fold_opt_expr(expr))
+    }
+}
 
+pub fn expand_expr(expr: ast::Expr, fld: &mut MacroExpander) -> P<ast::Expr> {
+    match expr.node {
         // expr_mac should really be expr_ext or something; it's the
         // entry-point for all syntax extensions.
         ast::ExprKind::Mac(mac) => {
-            expand_mac_invoc(mac, None, attrs.into_attr_vec(), span, fld)
+            expand_mac_invoc(mac, None, expr.attrs.into_attr_vec(), expr.span, fld)
         }
 
         ast::ExprKind::While(cond, body, opt_ident) => {
             let cond = fld.fold_expr(cond);
             let (body, opt_ident) = expand_loop_block(body, opt_ident, fld);
-            fld.cx.expr(span, ast::ExprKind::While(cond, body, opt_ident))
-                .with_attrs(fold_thin_attrs(attrs, fld))
+            fld.cx.expr(expr.span, ast::ExprKind::While(cond, body, opt_ident))
+                .with_attrs(fold_thin_attrs(expr.attrs, fld))
         }
 
-        ast::ExprKind::WhileLet(pat, expr, body, opt_ident) => {
+        ast::ExprKind::WhileLet(pat, cond, body, opt_ident) => {
             let pat = fld.fold_pat(pat);
-            let expr = fld.fold_expr(expr);
+            let cond = fld.fold_expr(cond);
 
             // Hygienic renaming of the body.
             let ((body, opt_ident), mut rewritten_pats) =
@@ -107,14 +117,14 @@ pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> {
             });
             assert!(rewritten_pats.len() == 1);
 
-            let wl = ast::ExprKind::WhileLet(rewritten_pats.remove(0), expr, body, opt_ident);
-            fld.cx.expr(span, wl).with_attrs(fold_thin_attrs(attrs, fld))
+            let wl = ast::ExprKind::WhileLet(rewritten_pats.remove(0), cond, body, opt_ident);
+            fld.cx.expr(expr.span, wl).with_attrs(fold_thin_attrs(expr.attrs, fld))
         }
 
         ast::ExprKind::Loop(loop_block, opt_ident) => {
             let (loop_block, opt_ident) = expand_loop_block(loop_block, opt_ident, fld);
-            fld.cx.expr(span, ast::ExprKind::Loop(loop_block, opt_ident))
-                .with_attrs(fold_thin_attrs(attrs, fld))
+            fld.cx.expr(expr.span, ast::ExprKind::Loop(loop_block, opt_ident))
+                .with_attrs(fold_thin_attrs(expr.attrs, fld))
         }
 
         ast::ExprKind::ForLoop(pat, head, body, opt_ident) => {
@@ -132,7 +142,7 @@ pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> {
 
             let head = fld.fold_expr(head);
             let fl = ast::ExprKind::ForLoop(rewritten_pats.remove(0), head, body, opt_ident);
-            fld.cx.expr(span, fl).with_attrs(fold_thin_attrs(attrs, fld))
+            fld.cx.expr(expr.span, fl).with_attrs(fold_thin_attrs(expr.attrs, fld))
         }
 
         ast::ExprKind::IfLet(pat, sub_expr, body, else_opt) => {
@@ -151,7 +161,7 @@ pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> {
             let else_opt = else_opt.map(|else_opt| fld.fold_expr(else_opt));
             let sub_expr = fld.fold_expr(sub_expr);
             let il = ast::ExprKind::IfLet(rewritten_pats.remove(0), sub_expr, body, else_opt);
-            fld.cx.expr(span, il).with_attrs(fold_thin_attrs(attrs, fld))
+            fld.cx.expr(expr.span, il).with_attrs(fold_thin_attrs(expr.attrs, fld))
         }
 
         ast::ExprKind::Closure(capture_clause, fn_decl, block, fn_decl_span) => {
@@ -160,22 +170,15 @@ pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> {
             let new_node = ast::ExprKind::Closure(capture_clause,
                                                   rewritten_fn_decl,
                                                   rewritten_block,
-                                                  fld.new_span(fn_decl_span));
-            P(ast::Expr{ id:id,
+                                                  fn_decl_span);
+            P(ast::Expr{ id: expr.id,
                          node: new_node,
-                         span: fld.new_span(span),
-                         attrs: fold_thin_attrs(attrs, fld) })
+                         span: expr.span,
+                         attrs: fold_thin_attrs(expr.attrs, fld) })
         }
 
-        _ => {
-            P(noop_fold_expr(ast::Expr {
-                id: id,
-                node: node,
-                span: span,
-                attrs: attrs
-            }, fld))
-        }
-    });
+        _ => P(noop_fold_expr(expr, fld)),
+    }
 }
 
 /// Expand a macro invocation. Returns the result of expansion.
@@ -322,8 +325,9 @@ fn expand_mac_invoc<T>(mac: ast::Mac, ident: Option<Ident>, attrs: Vec<ast::Attr
         return T::dummy(span);
     };
 
-    let marked = expanded.fold_with(&mut Marker { mark: mark });
-    let fully_expanded = marked.fold_with(fld);
+    let marked = expanded.fold_with(&mut Marker { mark: mark, expn_id: Some(fld.cx.backtrace()) });
+    let configured = marked.fold_with(&mut fld.strip_unconfigured());
+    let fully_expanded = configured.fold_with(fld);
     fld.cx.bt_pop();
     fully_expanded
 }
@@ -699,12 +703,12 @@ impl<'a> Folder for PatIdentRenamer<'a> {
                                            mtwt::apply_renames(self.renames, ident.ctxt));
                 let new_node =
                     PatKind::Ident(binding_mode,
-                                  Spanned{span: self.new_span(sp), node: new_ident},
+                                  Spanned{span: sp, node: new_ident},
                                   sub.map(|p| self.fold_pat(p)));
                 ast::Pat {
                     id: id,
                     node: new_node,
-                    span: self.new_span(span)
+                    span: span,
                 }
             },
             _ => unreachable!()
@@ -774,7 +778,7 @@ fn expand_annotatable(a: Annotatable,
                         }
                         _ => unreachable!()
                     },
-                    span: fld.new_span(ti.span)
+                    span: ti.span,
                 })
             }
             _ => fold::noop_fold_trait_item(it.unwrap(), fld)
@@ -914,7 +918,7 @@ fn expand_impl_item(ii: ast::ImplItem, fld: &mut MacroExpander)
                 }
                 _ => unreachable!()
             },
-            span: fld.new_span(ii.span)
+            span: ii.span,
         }),
         ast::ImplItemKind::Macro(mac) => {
             expand_mac_invoc(mac, None, ii.attrs, ii.span, fld)
@@ -987,6 +991,12 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
     pub fn new(cx: &'a mut ExtCtxt<'b>) -> MacroExpander<'a, 'b> {
         MacroExpander { cx: cx }
     }
+
+    fn strip_unconfigured(&mut self) -> StripUnconfigured {
+        StripUnconfigured::new(&self.cx.cfg,
+                               &self.cx.parse_sess.span_diagnostic,
+                               self.cx.feature_gated_cfgs)
+    }
 }
 
 impl<'a, 'b> Folder for MacroExpander<'a, 'b> {
@@ -996,7 +1006,15 @@ impl<'a, 'b> Folder for MacroExpander<'a, 'b> {
     }
 
     fn fold_expr(&mut self, expr: P<ast::Expr>) -> P<ast::Expr> {
-        expand_expr(expr, self)
+        expr.and_then(|expr| expand_expr(expr, self))
+    }
+
+    fn fold_opt_expr(&mut self, expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
+        expr.and_then(|expr| match expr.node {
+            ast::ExprKind::Mac(mac) =>
+                expand_mac_invoc(mac, None, expr.attrs.into_attr_vec(), expr.span, self),
+            _ => Some(expand_expr(expr, self)),
+        })
     }
 
     fn fold_pat(&mut self, pat: P<ast::Pat>) -> P<ast::Pat> {
@@ -1059,10 +1077,6 @@ impl<'a, 'b> Folder for MacroExpander<'a, 'b> {
     fn fold_ty(&mut self, ty: P<ast::Ty>) -> P<ast::Ty> {
         expand_type(ty, self)
     }
-
-    fn new_span(&mut self, span: Span) -> Span {
-        new_span(self.cx, span)
-    }
 }
 
 impl<'a, 'b> MacroExpander<'a, 'b> {
@@ -1080,45 +1094,6 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
     }
 }
 
-fn new_span(cx: &ExtCtxt, sp: Span) -> Span {
-    debug!("new_span(sp={:?})", sp);
-
-    if cx.codemap().more_specific_trace(sp.expn_id, cx.backtrace()) {
-        // If the span we are looking at has a backtrace that has more
-        // detail than our current backtrace, then we keep that
-        // backtrace.  Honestly, I have no idea if this makes sense,
-        // because I have no idea why we are stripping the backtrace
-        // below. But the reason I made this change is because, in
-        // deriving, we were generating attributes with a specific
-        // backtrace, which was essential for `#[structural_match]` to
-        // be properly supported, but these backtraces were being
-        // stripped and replaced with a null backtrace. Sort of
-        // unclear why this is the case. --nmatsakis
-        debug!("new_span: keeping trace from {:?} because it is more specific",
-               sp.expn_id);
-        sp
-    } else {
-        // This discards information in the case of macro-defining macros.
-        //
-        // The comment above was originally added in
-        // b7ec2488ff2f29681fe28691d20fd2c260a9e454 in Feb 2012. I
-        // *THINK* the reason we are doing this is because we want to
-        // replace the backtrace of the macro contents with the
-        // backtrace that contains the macro use. But it's pretty
-        // unclear to me. --nmatsakis
-        let sp1 = Span {
-            lo: sp.lo,
-            hi: sp.hi,
-            expn_id: cx.backtrace(),
-        };
-        debug!("new_span({:?}) = {:?}", sp, sp1);
-        if sp.expn_id.into_u32() == 0 && env::var_os("NDM").is_some() {
-            panic!("NDM");
-        }
-        sp1
-    }
-}
-
 pub struct ExpansionConfig<'feat> {
     pub crate_name: String,
     pub features: Option<&'feat Features>,
@@ -1205,8 +1180,9 @@ pub fn expand_crate(mut cx: ExtCtxt,
 // the ones defined here include:
 // Marker - add a mark to a context
 
-// A Marker adds the given mark to the syntax context
-struct Marker { mark: Mrk }
+// A Marker adds the given mark to the syntax context and
+// sets spans' `expn_id` to the given expn_id (unless it is `None`).
+struct Marker { mark: Mrk, expn_id: Option<ExpnId> }
 
 impl Folder for Marker {
     fn fold_ident(&mut self, id: Ident) -> Ident {
@@ -1219,14 +1195,21 @@ impl Folder for Marker {
                 tts: self.fold_tts(&node.tts),
                 ctxt: mtwt::apply_mark(self.mark, node.ctxt),
             },
-            span: span,
+            span: self.new_span(span),
+        }
+    }
+
+    fn new_span(&mut self, mut span: Span) -> Span {
+        if let Some(expn_id) = self.expn_id {
+            span.expn_id = expn_id;
         }
+        span
     }
 }
 
 // apply a given mark to the given token trees. Used prior to expansion of a macro.
 fn mark_tts(tts: &[TokenTree], m: Mrk) -> Vec<TokenTree> {
-    noop_fold_tts(tts, &mut Marker{mark:m})
+    noop_fold_tts(tts, &mut Marker{mark:m, expn_id: None})
 }
 
 /// Check that there are no macro invocations left in the AST:
diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs
index 8eeb61e0de4..45f349eff31 100644
--- a/src/libsyntax/test.rs
+++ b/src/libsyntax/test.rs
@@ -87,7 +87,7 @@ pub fn modify_for_testing(sess: &ParseSess,
     if should_test {
         generate_test_harness(sess, reexport_test_harness_main, krate, cfg, span_diagnostic)
     } else {
-        strip_test_functions(span_diagnostic, krate)
+        strip_test_functions(krate)
     }
 }
 
@@ -312,14 +312,17 @@ fn generate_test_harness(sess: &ParseSess,
     return res;
 }
 
-fn strip_test_functions(diagnostic: &errors::Handler, krate: ast::Crate)
-                        -> ast::Crate {
+fn strip_test_functions(krate: ast::Crate) -> ast::Crate {
     // When not compiling with --test we should not compile the
     // #[test] functions
-    config::strip_items(diagnostic, krate, |attrs| {
-        !attr::contains_name(&attrs[..], "test") &&
-        !attr::contains_name(&attrs[..], "bench")
-    })
+    struct StripTests;
+    impl config::CfgFolder for StripTests {
+        fn in_cfg(&mut self, attrs: &[ast::Attribute]) -> bool {
+            !attr::contains_name(attrs, "test") && !attr::contains_name(attrs, "bench")
+        }
+    }
+
+    StripTests.fold_crate(krate)
 }
 
 /// Craft a span that will be ignored by the stability lint's