summary refs log tree commit diff
path: root/src/libsyntax
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2015-12-04 08:46:29 +0000
committerbors <bors@rust-lang.org>2015-12-04 08:46:29 +0000
commit77ed39cfe37a17737e0b2256b1a1689e01c32b26 (patch)
tree729dbf03cf54683035cb017448f4959ad9196f5d /src/libsyntax
parent5673a7b374c9645ba4bc947588555667f7162cdb (diff)
parentd06f48054cefec1fdb87e9c9196ce1f2a31c2a9f (diff)
downloadrust-77ed39cfe37a17737e0b2256b1a1689e01c32b26.tar.gz
rust-77ed39cfe37a17737e0b2256b1a1689e01c32b26.zip
Auto merge of #29850 - Kimundi:attributes_that_make_a_statement, r=pnkfelix
See https://github.com/rust-lang/rfcs/pull/16 and https://github.com/rust-lang/rust/issues/15701

- Added syntax support for attributes on expressions and all syntax nodes in statement position.
- Extended `#[cfg]` folder to allow removal of statements, and
of expressions in optional positions like expression lists and trailing
block expressions.
- Extended lint checker to recognize lint levels on expressions and
locals.
- As per RFC, attributes are not yet accepted on `if` expressions.

Examples:
  ```rust
let x = y;
{
        ...
}
assert_eq!((1, #[cfg(unset)] 2, 3), (1, 3));

let FOO = 0;
```

Implementation wise, there are a few rough corners and open questions:
- The parser work ended up a bit ugly.
- The pretty printer change was based mostly on guessing.
- Similar to the `if` case, there are some places in the grammar where a new `Expr` node starts,
  but where it seemed weird to accept attributes and hence the parser doesn't. This includes:
  - const expressions in patterns
  - in the middle of an postfix operator chain (that is, after `.`, before indexing, before calls)
  - on range expressions, since `#[attr] x .. y` parses as  `(#[attr] x) .. y`, which is inconsistent with
    `#[attr] .. y` which would parse as `#[attr] (.. y)`
- Attributes are added as additional `Option<Box<Vec<Attribute>>>` fields in expressions and locals.
- Memory impact has not been measured yet.
- A cfg-away trailing expression in a block does not currently promote the previous `StmtExpr` in a block to a new trailing expr. That is to say, this won't work:
```rust
let x = {
    #[cfg(foo)]
    Foo { data: x }
    #[cfg(not(foo))]
    Foo { data: y }
};
```
- One-element tuples can have their inner expression removed to become Unit, but just Parenthesis can't. Eg, `(#[cfg(unset)] x,) == ()` but `(#[cfg(unset)] x) == error`. This seemed reasonable to me since tuples and unit are type constructors, but could probably be argued either way.
- Attributes on macro nodes are currently unconditionally dropped during macro expansion, which seemed fine since macro disappear at that point?
- Attributes on `ast::ExprParens` will be prepend-ed to the inner expression in the hir folder.
- The work on pretty printer tests for this did trigger, but not fix errors regarding macros:
  - expression `foo![]` prints as `foo!()`
  - expression `foo!{}` prints as `foo!()`
  - statement `foo![];` prints as `foo!();`
  - statement `foo!{};` prints as `foo!();`
  - statement `foo!{}` triggers a `None` unwrap ICE.
Diffstat (limited to 'src/libsyntax')
-rw-r--r--src/libsyntax/ast.rs51
-rw-r--r--src/libsyntax/attr.rs184
-rw-r--r--src/libsyntax/config.rs348
-rw-r--r--src/libsyntax/ext/asm.rs3
-rw-r--r--src/libsyntax/ext/base.rs7
-rw-r--r--src/libsyntax/ext/build.rs3
-rw-r--r--src/libsyntax/ext/cfg.rs10
-rw-r--r--src/libsyntax/ext/concat_idents.rs1
-rw-r--r--src/libsyntax/ext/deriving/debug.rs1
-rw-r--r--src/libsyntax/ext/deriving/generic/mod.rs2
-rw-r--r--src/libsyntax/ext/expand.rs38
-rw-r--r--src/libsyntax/ext/quote.rs1
-rw-r--r--src/libsyntax/feature_gate.rs55
-rw-r--r--src/libsyntax/fold.rs116
-rw-r--r--src/libsyntax/lib.rs1
-rw-r--r--src/libsyntax/parse/mod.rs18
-rw-r--r--src/libsyntax/parse/parser.rs448
-rw-r--r--src/libsyntax/parse/token.rs1
-rw-r--r--src/libsyntax/print/pprust.rs228
-rw-r--r--src/libsyntax/test.rs12
-rw-r--r--src/libsyntax/util/move_map.rs79
-rw-r--r--src/libsyntax/util/small_vector.rs40
-rw-r--r--src/libsyntax/visit.rs8
23 files changed, 1234 insertions, 421 deletions
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index ab62c8d9421..f11291fc0f7 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -45,6 +45,7 @@ pub use self::ViewPath_::*;
 pub use self::Visibility::*;
 pub use self::PathParameters::*;
 
+use attr::ThinAttributes;
 use codemap::{Span, Spanned, DUMMY_SP, ExpnId};
 use abi::Abi;
 use ast_util;
@@ -692,8 +693,21 @@ pub enum Stmt_ {
     /// Expr with trailing semi-colon (may have any type):
     StmtSemi(P<Expr>, NodeId),
 
-    StmtMac(P<Mac>, MacStmtStyle),
+    StmtMac(P<Mac>, MacStmtStyle, ThinAttributes),
 }
+
+impl Stmt_ {
+    pub fn attrs(&self) -> &[Attribute] {
+        match *self {
+            StmtDecl(ref d, _) => d.attrs(),
+            StmtExpr(ref e, _) |
+            StmtSemi(ref e, _) => e.attrs(),
+            StmtMac(_, _, Some(ref b)) => b,
+            StmtMac(_, _, None) => &[],
+        }
+    }
+}
+
 #[derive(Clone, Copy, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub enum MacStmtStyle {
     /// The macro statement had a trailing semicolon, e.g. `foo! { ... };`
@@ -718,6 +732,16 @@ pub struct Local {
     pub init: Option<P<Expr>>,
     pub id: NodeId,
     pub span: Span,
+    pub attrs: ThinAttributes,
+}
+
+impl Local {
+    pub fn attrs(&self) -> &[Attribute] {
+        match self.attrs {
+            Some(ref b) => b,
+            None => &[],
+        }
+    }
 }
 
 pub type Decl = Spanned<Decl_>;
@@ -730,6 +754,15 @@ pub enum Decl_ {
     DeclItem(P<Item>),
 }
 
+impl Decl {
+    pub fn attrs(&self) -> &[Attribute] {
+        match self.node {
+            DeclLocal(ref l) => l.attrs(),
+            DeclItem(ref i) => i.attrs(),
+        }
+    }
+}
+
 /// represents one arm of a 'match'
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub struct Arm {
@@ -766,6 +799,16 @@ pub struct Expr {
     pub id: NodeId,
     pub node: Expr_,
     pub span: Span,
+    pub attrs: ThinAttributes
+}
+
+impl Expr {
+    pub fn attrs(&self) -> &[Attribute] {
+        match self.attrs {
+            Some(ref b) => b,
+            None => &[],
+        }
+    }
 }
 
 impl fmt::Debug for Expr {
@@ -1792,6 +1835,12 @@ pub struct Item {
     pub span: Span,
 }
 
+impl Item {
+    pub fn attrs(&self) -> &[Attribute] {
+        &self.attrs
+    }
+}
+
 #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
 pub enum Item_ {
     /// An`extern crate` item, with optional original crate name,
diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs
index 153a81e6c54..e828d8ae248 100644
--- a/src/libsyntax/attr.rs
+++ b/src/libsyntax/attr.rs
@@ -16,10 +16,13 @@ pub use self::IntType::*;
 
 use ast;
 use ast::{AttrId, Attribute, Attribute_, MetaItem, MetaWord, MetaNameValue, MetaList};
+use ast::{Stmt, StmtDecl, StmtExpr, StmtMac, StmtSemi, DeclItem, DeclLocal};
+use ast::{Expr, Item, Local, Decl};
 use codemap::{Span, Spanned, spanned, dummy_spanned};
 use codemap::BytePos;
+use config::CfgDiag;
 use diagnostic::SpanHandler;
-use feature_gate::GatedCfg;
+use feature_gate::{GatedCfg, GatedCfgAttr};
 use parse::lexer::comments::{doc_comment_style, strip_doc_comment_decoration};
 use parse::token::{InternedString, intern_and_get_ident};
 use parse::token;
@@ -356,26 +359,35 @@ pub fn requests_inline(attrs: &[Attribute]) -> bool {
 }
 
 /// Tests if a cfg-pattern matches the cfg set
-pub fn cfg_matches(diagnostic: &SpanHandler, cfgs: &[P<MetaItem>], cfg: &ast::MetaItem,
-                   feature_gated_cfgs: &mut Vec<GatedCfg>) -> bool {
+pub fn cfg_matches<T: CfgDiag>(cfgs: &[P<MetaItem>],
+                           cfg: &ast::MetaItem,
+                           diag: &mut T) -> bool {
     match cfg.node {
         ast::MetaList(ref pred, ref mis) if &pred[..] == "any" =>
-            mis.iter().any(|mi| cfg_matches(diagnostic, cfgs, &**mi, feature_gated_cfgs)),
+            mis.iter().any(|mi| cfg_matches(cfgs, &**mi, diag)),
         ast::MetaList(ref pred, ref mis) if &pred[..] == "all" =>
-            mis.iter().all(|mi| cfg_matches(diagnostic, cfgs, &**mi, feature_gated_cfgs)),
+            mis.iter().all(|mi| cfg_matches(cfgs, &**mi, diag)),
         ast::MetaList(ref pred, ref mis) if &pred[..] == "not" => {
             if mis.len() != 1 {
-                diagnostic.span_err(cfg.span, "expected 1 cfg-pattern");
+                diag.emit_error(|diagnostic| {
+                    diagnostic.span_err(cfg.span, "expected 1 cfg-pattern");
+                });
                 return false;
             }
-            !cfg_matches(diagnostic, cfgs, &*mis[0], feature_gated_cfgs)
+            !cfg_matches(cfgs, &*mis[0], diag)
         }
         ast::MetaList(ref pred, _) => {
-            diagnostic.span_err(cfg.span, &format!("invalid predicate `{}`", pred));
+            diag.emit_error(|diagnostic| {
+                diagnostic.span_err(cfg.span,
+                    &format!("invalid predicate `{}`", pred));
+            });
             false
         },
         ast::MetaWord(_) | ast::MetaNameValue(..) => {
-            feature_gated_cfgs.extend(GatedCfg::gate(cfg));
+            diag.flag_gated(|feature_gated_cfgs| {
+                feature_gated_cfgs.extend(
+                    GatedCfg::gate(cfg).map(GatedCfgAttr::GatedCfg));
+            });
             contains(cfgs, cfg)
         }
     }
@@ -720,3 +732,157 @@ impl IntType {
         }
     }
 }
+
+/// A list of attributes, behind a optional box as
+/// a space optimization.
+pub type ThinAttributes = Option<Box<Vec<Attribute>>>;
+
+pub trait ThinAttributesExt {
+    fn map_thin_attrs<F>(self, f: F) -> Self
+        where F: FnOnce(Vec<Attribute>) -> Vec<Attribute>;
+    fn prepend(mut self, attrs: Self) -> Self;
+    fn append(mut self, attrs: Self) -> Self;
+    fn update<F>(&mut self, f: F)
+        where Self: Sized,
+              F: FnOnce(Self) -> Self;
+    fn as_attr_slice(&self) -> &[Attribute];
+    fn into_attr_vec(self) -> Vec<Attribute>;
+}
+
+impl ThinAttributesExt for ThinAttributes {
+    fn map_thin_attrs<F>(self, f: F) -> Self
+        where F: FnOnce(Vec<Attribute>) -> Vec<Attribute>
+    {
+        f(self.map(|b| *b).unwrap_or(Vec::new())).into_thin_attrs()
+    }
+
+    fn prepend(self, attrs: ThinAttributes) -> Self {
+        attrs.map_thin_attrs(|mut attrs| {
+            attrs.extend(self.into_attr_vec());
+            attrs
+        })
+    }
+
+    fn append(self, attrs: ThinAttributes) -> Self {
+        self.map_thin_attrs(|mut self_| {
+            self_.extend(attrs.into_attr_vec());
+            self_
+        })
+    }
+
+    fn update<F>(&mut self, f: F)
+        where Self: Sized,
+              F: FnOnce(ThinAttributes) -> ThinAttributes
+    {
+        let self_ = f(self.take());
+        *self = self_;
+    }
+
+    fn as_attr_slice(&self) -> &[Attribute] {
+        match *self {
+            Some(ref b) => b,
+            None => &[],
+        }
+    }
+
+    fn into_attr_vec(self) -> Vec<Attribute> {
+        match self {
+            Some(b) => *b,
+            None => Vec::new(),
+        }
+    }
+}
+
+pub trait AttributesExt {
+    fn into_thin_attrs(self) -> ThinAttributes;
+}
+
+impl AttributesExt for Vec<Attribute> {
+    fn into_thin_attrs(self) -> ThinAttributes {
+        if self.len() == 0 {
+            None
+        } else {
+            Some(Box::new(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> {
+    fn with_attrs(self, attrs: ThinAttributes) -> Self {
+        self.map(|mut e| {
+            e.attrs.update(|a| a.append(attrs));
+            e
+        })
+    }
+}
+
+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 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 WithAttrs for P<Decl> {
+    fn with_attrs(self, attrs: ThinAttributes) -> Self {
+        self.map(|Spanned { span, node }| {
+            Spanned {
+                span: span,
+                node: match node {
+                    DeclLocal(local) => DeclLocal(local.with_attrs(attrs)),
+                    DeclItem(item) => DeclItem(item.with_attrs(attrs)),
+                }
+            }
+        })
+    }
+}
+
+impl WithAttrs for P<Stmt> {
+    fn with_attrs(self, attrs: ThinAttributes) -> Self {
+        self.map(|Spanned { span, node }| {
+            Spanned {
+                span: span,
+                node: match node {
+                    StmtDecl(decl, id) => StmtDecl(decl.with_attrs(attrs), id),
+                    StmtExpr(expr, id) => StmtExpr(expr.with_attrs(attrs), id),
+                    StmtSemi(expr, id) => StmtSemi(expr.with_attrs(attrs), id),
+                    StmtMac(mac, style, mut ats) => {
+                        ats.update(|a| a.append(attrs));
+                        StmtMac(mac, style, ats)
+                    }
+                },
+            }
+        })
+    }
+}
diff --git a/src/libsyntax/config.rs b/src/libsyntax/config.rs
index 10731178c06..1209c58fd5e 100644
--- a/src/libsyntax/config.rs
+++ b/src/libsyntax/config.rs
@@ -10,9 +10,10 @@
 
 use attr::AttrMetaMethods;
 use diagnostic::SpanHandler;
-use feature_gate::GatedCfg;
+use feature_gate::GatedCfgAttr;
 use fold::Folder;
 use {ast, fold, attr};
+use visit;
 use codemap::{Spanned, respan};
 use ptr::P;
 
@@ -20,28 +21,34 @@ use util::small_vector::SmallVector;
 
 /// A folder that strips out items that do not belong in the current
 /// configuration.
-struct Context<F> where F: FnMut(&[ast::Attribute]) -> bool {
+struct Context<'a, F> where F: FnMut(&[ast::Attribute]) -> bool {
     in_cfg: F,
+    diagnostic: &'a SpanHandler,
 }
 
 // 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: &SpanHandler, krate: ast::Crate,
-                                feature_gated_cfgs: &mut Vec<GatedCfg>)
+                                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(krate, |attrs| in_cfg(diagnostic, &config, attrs, feature_gated_cfgs))
+    strip_items(diagnostic,
+                krate,
+                |attrs| {
+                    let mut diag = CfgDiagReal {
+                        diag: diagnostic,
+                        feature_gated_cfgs: feature_gated_cfgs,
+                    };
+                    in_cfg(&config, attrs, &mut diag)
+                })
 }
 
-impl<F> fold::Folder for Context<F> where F: FnMut(&[ast::Attribute]) -> bool {
-    fn fold_mod(&mut self, module: ast::Mod) -> ast::Mod {
-        fold_mod(self, module)
-    }
-    fn fold_block(&mut self, block: P<ast::Block>) -> P<ast::Block> {
-        fold_block(self, block)
-    }
+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)
     }
@@ -49,8 +56,25 @@ impl<F> fold::Folder for Context<F> where F: FnMut(&[ast::Attribute]) -> bool {
         fold_item_underscore(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: P<ast::Stmt>) -> SmallVector<P<ast::Stmt>> {
+        fold_stmt(self, stmt)
+    }
     fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
         fold::noop_fold_mac(mac, self)
     }
@@ -59,28 +83,17 @@ impl<F> fold::Folder for Context<F> where F: FnMut(&[ast::Attribute]) -> bool {
     }
 }
 
-pub fn strip_items<F>(krate: ast::Crate, in_cfg: F) -> ast::Crate where
+pub fn strip_items<'a, F>(diagnostic: &'a SpanHandler,
+                          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)
 }
 
-fn fold_mod<F>(cx: &mut Context<F>,
-               ast::Mod {inner, items}: ast::Mod)
-               -> ast::Mod where
-    F: FnMut(&[ast::Attribute]) -> bool
-{
-    ast::Mod {
-        inner: inner,
-        items: items.into_iter().flat_map(|a| {
-            cx.fold_item(a).into_iter()
-        }).collect()
-    }
-}
-
 fn filter_foreign_item<F>(cx: &mut Context<F>,
                           item: P<ast::ForeignItem>)
                           -> Option<P<ast::ForeignItem>> where
@@ -182,45 +195,20 @@ fn fold_struct<F>(cx: &mut Context<F>, vdata: ast::VariantData) -> ast::VariantD
     }
 }
 
-fn retain_stmt<F>(cx: &mut Context<F>, stmt: &ast::Stmt) -> bool where
-    F: FnMut(&[ast::Attribute]) -> bool
+fn fold_opt_expr<F>(cx: &mut Context<F>, expr: P<ast::Expr>) -> Option<P<ast::Expr>>
+    where F: FnMut(&[ast::Attribute]) -> bool
 {
-    match stmt.node {
-        ast::StmtDecl(ref decl, _) => {
-            match decl.node {
-                ast::DeclItem(ref item) => {
-                    item_in_cfg(cx, item)
-                }
-                _ => true
-            }
-        }
-        _ => true
+    if expr_in_cfg(cx, &expr) {
+        Some(fold_expr(cx, expr))
+    } else {
+        None
     }
 }
 
-fn fold_block<F>(cx: &mut Context<F>, b: P<ast::Block>) -> P<ast::Block> where
-    F: FnMut(&[ast::Attribute]) -> bool
-{
-    b.map(|ast::Block {id, stmts, expr, rules, span}| {
-        let resulting_stmts: Vec<P<ast::Stmt>> =
-            stmts.into_iter().filter(|a| retain_stmt(cx, a)).collect();
-        let resulting_stmts = resulting_stmts.into_iter()
-            .flat_map(|stmt| cx.fold_stmt(stmt).into_iter())
-            .collect();
-        ast::Block {
-            id: id,
-            stmts: resulting_stmts,
-            expr: expr.map(|x| cx.fold_expr(x)),
-            rules: rules,
-            span: span,
-        }
-    })
-}
-
 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}| {
+    expr.map(|ast::Expr {id, span, node, attrs}| {
         fold::noop_fold_expr(ast::Expr {
             id: id,
             node: match node {
@@ -231,11 +219,34 @@ fn fold_expr<F>(cx: &mut Context<F>, expr: P<ast::Expr>) -> P<ast::Expr> where
                 }
                 _ => node
             },
-            span: span
+            span: span,
+            attrs: attrs,
         }, cx)
     })
 }
 
+fn fold_stmt<F>(cx: &mut Context<F>, stmt: P<ast::Stmt>) -> SmallVector<P<ast::Stmt>>
+    where F: FnMut(&[ast::Attribute]) -> bool
+{
+    if stmt_in_cfg(cx, &stmt) {
+        stmt.and_then(|s| fold::noop_fold_stmt(s, 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
 {
@@ -248,44 +259,51 @@ fn foreign_item_in_cfg<F>(cx: &mut Context<F>, item: &ast::ForeignItem) -> 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(diagnostic: &SpanHandler, cfg: &[P<ast::MetaItem>], attrs: &[ast::Attribute],
-          feature_gated_cfgs: &mut Vec<GatedCfg>) -> bool {
+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::MetaList(_, ref mis) if attr.check_name("cfg") => mis,
+            ast::MetaList(_, ref mis) if is_cfg(&attr) => mis,
             _ => return true
         };
 
         if mis.len() != 1 {
-            diagnostic.span_err(attr.span, "expected 1 cfg-pattern");
+            diag.emit_error(|diagnostic| {
+                diagnostic.span_err(attr.span, "expected 1 cfg-pattern");
+            });
             return true;
         }
 
-        attr::cfg_matches(diagnostic, cfg, &mis[0],
-                          feature_gated_cfgs)
+        attr::cfg_matches(cfg, &mis[0], diag)
     })
 }
 
-struct CfgAttrFolder<'a, 'b> {
-    diag: &'a SpanHandler,
-    config: ast::CrateConfig,
-    feature_gated_cfgs: &'b mut Vec<GatedCfg>
+struct CfgAttrFolder<'a, T> {
+    diag: T,
+    config: &'a ast::CrateConfig,
 }
 
 // Process `#[cfg_attr]`.
 fn process_cfg_attr(diagnostic: &SpanHandler, krate: ast::Crate,
-                    feature_gated_cfgs: &mut Vec<GatedCfg>) -> ast::Crate {
+                    feature_gated_cfgs: &mut Vec<GatedCfgAttr>) -> ast::Crate {
     let mut fld = CfgAttrFolder {
-        diag: diagnostic,
-        config: krate.config.clone(),
-        feature_gated_cfgs: feature_gated_cfgs,
+        diag: CfgDiagReal {
+            diag: diagnostic,
+            feature_gated_cfgs: feature_gated_cfgs,
+        },
+        config: &krate.config.clone(),
     };
     fld.fold_crate(krate)
 }
 
-impl<'a,'b> fold::Folder for CfgAttrFolder<'a,'b> {
+impl<'a, T: CfgDiag> fold::Folder for CfgAttrFolder<'a, T> {
     fn fold_attribute(&mut self, attr: ast::Attribute) -> Option<ast::Attribute> {
         if !attr.check_name("cfg_attr") {
             return fold::noop_fold_attribute(attr, self);
@@ -294,20 +312,25 @@ impl<'a,'b> fold::Folder for CfgAttrFolder<'a,'b> {
         let attr_list = match attr.meta_item_list() {
             Some(attr_list) => attr_list,
             None => {
-                self.diag.span_err(attr.span, "expected `#[cfg_attr(<cfg pattern>, <attr>)]`");
+                self.diag.emit_error(|diag| {
+                    diag.span_err(attr.span,
+                        "expected `#[cfg_attr(<cfg pattern>, <attr>)]`");
+                });
                 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.span_err(attr.span, "expected `#[cfg_attr(<cfg pattern>, <attr>)]`");
+                self.diag.emit_error(|diag| {
+                    diag.span_err(attr.span,
+                        "expected `#[cfg_attr(<cfg pattern>, <attr>)]`");
+                });
                 return None;
             }
         };
 
-        if attr::cfg_matches(self.diag, &self.config[..], &cfg,
-                             self.feature_gated_cfgs) {
+        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,
@@ -324,3 +347,174 @@ impl<'a,'b> fold::Folder for CfgAttrFolder<'a,'b> {
         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::StmtDecl(ref decl, _) = s.node {
+                if let ast::DeclItem(_) = decl.node {
+                    visit::walk_stmt(self, s);
+                    return;
+                }
+            }
+
+            // flag the offending attributes
+            for attr in stmt_attrs {
+                self.discovered.push(GatedCfgAttr::GatedAttr(attr.span));
+            }
+
+            // 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);
+        }
+    }
+
+    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));
+            }
+
+            // 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_foreign_item(&mut self, i: &'v ast::ForeignItem) {
+        if node_survives_cfg(&i.attrs, self.config) {
+            visit::walk_foreign_item(self, i);
+        }
+    }
+
+    fn visit_item(&mut self, i: &'v ast::Item) {
+        if node_survives_cfg(&i.attrs, self.config) {
+            visit::walk_item(self, i);
+        }
+    }
+
+    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 visit_trait_item(&mut self, ti: &'v ast::TraitItem) {
+        if node_survives_cfg(&ti.attrs, self.config) {
+            visit::walk_trait_item(self, ti);
+        }
+    }
+
+    fn visit_struct_field(&mut self, s: &'v ast::StructField) {
+        if node_survives_cfg(&s.node.attrs, self.config) {
+            visit::walk_struct_field(self, s);
+        }
+    }
+
+    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 visit_arm(&mut self, a: &'v ast::Arm) {
+        if node_survives_cfg(&a.attrs, self.config) {
+            visit::walk_arm(self, a);
+        }
+    }
+
+    // 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)
+    }
+}
+
+pub trait CfgDiag {
+    fn emit_error<F>(&mut self, f: F) where F: FnMut(&SpanHandler);
+    fn flag_gated<F>(&mut self, f: F) where F: FnMut(&mut Vec<GatedCfgAttr>);
+}
+
+pub struct CfgDiagReal<'a, 'b> {
+    pub diag: &'a SpanHandler,
+    pub feature_gated_cfgs: &'b mut Vec<GatedCfgAttr>,
+}
+
+impl<'a, 'b> CfgDiag for CfgDiagReal<'a, 'b> {
+    fn emit_error<F>(&mut self, mut f: F) where F: FnMut(&SpanHandler) {
+        f(self.diag)
+    }
+    fn flag_gated<F>(&mut self, mut f: F) where F: FnMut(&mut Vec<GatedCfgAttr>) {
+        f(self.feature_gated_cfgs)
+    }
+}
+
+struct CfgDiagSilent {
+    error: bool,
+}
+
+impl CfgDiag for CfgDiagSilent {
+    fn emit_error<F>(&mut self, _: F) where F: FnMut(&SpanHandler) {
+        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/asm.rs b/src/libsyntax/ext/asm.rs
index ac18b9c0e49..d968858f634 100644
--- a/src/libsyntax/ext/asm.rs
+++ b/src/libsyntax/ext/asm.rs
@@ -233,6 +233,7 @@ pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
             dialect: dialect,
             expn_id: expn_id,
         }),
-        span: sp
+        span: sp,
+        attrs: None,
     }))
 }
diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs
index cf6881ab650..3b613922bc9 100644
--- a/src/libsyntax/ext/base.rs
+++ b/src/libsyntax/ext/base.rs
@@ -17,7 +17,7 @@ use codemap::{CodeMap, Span, ExpnId, ExpnInfo, NO_EXPANSION};
 use ext;
 use ext::expand;
 use ext::tt::macro_rules;
-use feature_gate::GatedCfg;
+use feature_gate::GatedCfgAttr;
 use parse;
 use parse::parser;
 use parse::token;
@@ -350,6 +350,7 @@ impl DummyResult {
             id: ast::DUMMY_NODE_ID,
             node: ast::ExprLit(P(codemap::respan(sp, ast::LitBool(false)))),
             span: sp,
+            attrs: None,
         })
     }
 
@@ -572,7 +573,7 @@ pub struct ExtCtxt<'a> {
     pub backtrace: ExpnId,
     pub ecfg: expand::ExpansionConfig<'a>,
     pub crate_root: Option<&'static str>,
-    pub feature_gated_cfgs: &'a mut Vec<GatedCfg>,
+    pub feature_gated_cfgs: &'a mut Vec<GatedCfgAttr>,
 
     pub mod_path: Vec<ast::Ident> ,
     pub exported_macros: Vec<ast::MacroDef>,
@@ -584,7 +585,7 @@ pub struct ExtCtxt<'a> {
 impl<'a> ExtCtxt<'a> {
     pub fn new(parse_sess: &'a parse::ParseSess, cfg: ast::CrateConfig,
                ecfg: expand::ExpansionConfig<'a>,
-               feature_gated_cfgs: &'a mut Vec<GatedCfg>) -> ExtCtxt<'a> {
+               feature_gated_cfgs: &'a mut Vec<GatedCfgAttr>) -> ExtCtxt<'a> {
         let env = initial_syntax_expander_table(&ecfg);
         ExtCtxt {
             parse_sess: parse_sess,
diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs
index 4c10a749683..806f5a7ee22 100644
--- a/src/libsyntax/ext/build.rs
+++ b/src/libsyntax/ext/build.rs
@@ -525,6 +525,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
             init: Some(ex),
             id: ast::DUMMY_NODE_ID,
             span: sp,
+            attrs: None,
         });
         let decl = respan(sp, ast::DeclLocal(local));
         P(respan(sp, ast::StmtDecl(P(decl), ast::DUMMY_NODE_ID)))
@@ -548,6 +549,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
             init: Some(ex),
             id: ast::DUMMY_NODE_ID,
             span: sp,
+            attrs: None,
         });
         let decl = respan(sp, ast::DeclLocal(local));
         P(respan(sp, ast::StmtDecl(P(decl), ast::DUMMY_NODE_ID)))
@@ -584,6 +586,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
             id: ast::DUMMY_NODE_ID,
             node: node,
             span: span,
+            attrs: None,
         })
     }
 
diff --git a/src/libsyntax/ext/cfg.rs b/src/libsyntax/ext/cfg.rs
index 48199026204..e100355e4f8 100644
--- a/src/libsyntax/ext/cfg.rs
+++ b/src/libsyntax/ext/cfg.rs
@@ -20,6 +20,7 @@ use ext::build::AstBuilder;
 use attr;
 use attr::*;
 use parse::token;
+use config::CfgDiagReal;
 
 pub fn expand_cfg<'cx>(cx: &mut ExtCtxt,
                        sp: Span,
@@ -33,7 +34,12 @@ pub fn expand_cfg<'cx>(cx: &mut ExtCtxt,
         return DummyResult::expr(sp);
     }
 
-    let matches_cfg = attr::cfg_matches(&cx.parse_sess.span_diagnostic, &cx.cfg, &cfg,
-                                        cx.feature_gated_cfgs);
+    let matches_cfg = {
+        let mut diag = CfgDiagReal {
+            diag: &cx.parse_sess.span_diagnostic,
+            feature_gated_cfgs: cx.feature_gated_cfgs,
+        };
+        attr::cfg_matches(&cx.cfg, &cfg, &mut diag)
+    };
     MacEager::expr(cx.expr_bool(sp, matches_cfg))
 }
diff --git a/src/libsyntax/ext/concat_idents.rs b/src/libsyntax/ext/concat_idents.rs
index e9e36546ad6..c2233202b2f 100644
--- a/src/libsyntax/ext/concat_idents.rs
+++ b/src/libsyntax/ext/concat_idents.rs
@@ -67,6 +67,7 @@ pub fn expand_syntax_ext<'cx>(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree])
             }
         ),
         span: sp,
+        attrs: None,
     });
     MacEager::expr(e)
 }
diff --git a/src/libsyntax/ext/deriving/debug.rs b/src/libsyntax/ext/deriving/debug.rs
index 2b2e5309883..9488cfb86fc 100644
--- a/src/libsyntax/ext/deriving/debug.rs
+++ b/src/libsyntax/ext/deriving/debug.rs
@@ -148,6 +148,7 @@ fn stmt_let_undescore(cx: &mut ExtCtxt,
         init: Some(expr),
         id: ast::DUMMY_NODE_ID,
         span: sp,
+        attrs: None,
     });
     let decl = respan(sp, ast::DeclLocal(local));
     P(respan(sp, ast::StmtDecl(P(decl), ast::DUMMY_NODE_ID)))
diff --git a/src/libsyntax/ext/deriving/generic/mod.rs b/src/libsyntax/ext/deriving/generic/mod.rs
index 625abf706ca..bd89430d81d 100644
--- a/src/libsyntax/ext/deriving/generic/mod.rs
+++ b/src/libsyntax/ext/deriving/generic/mod.rs
@@ -204,7 +204,7 @@ use ext::build::AstBuilder;
 use codemap::{self, DUMMY_SP};
 use codemap::Span;
 use diagnostic::SpanHandler;
-use fold::MoveMap;
+use util::move_map::MoveMap;
 use owned_slice::OwnedSlice;
 use parse::token::{intern, InternedString};
 use parse::token::special_idents;
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index 01e72cf2a8d..573f4cfe8fa 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -17,13 +17,14 @@ use ast;
 use ext::mtwt;
 use ext::build::AstBuilder;
 use attr;
-use attr::AttrMetaMethods;
+use attr::{AttrMetaMethods, WithAttrs};
 use codemap;
 use codemap::{Span, Spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute};
 use ext::base::*;
-use feature_gate::{self, Features, GatedCfg};
+use feature_gate::{self, Features, GatedCfgAttr};
 use fold;
 use fold::*;
+use util::move_map::MoveMap;
 use parse;
 use parse::token::{fresh_mark, fresh_name, intern};
 use ptr::P;
@@ -37,11 +38,15 @@ use std::collections::HashSet;
 
 pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> {
     let expr_span = e.span;
-    return e.and_then(|ast::Expr {id, node, span}| match node {
+    return e.and_then(|ast::Expr {id, node, span, attrs}| match node {
 
         // expr_mac should really be expr_ext or something; it's the
         // entry-point for all syntax extensions.
         ast::ExprMac(mac) => {
+
+            // Assert that we drop any macro attributes on the floor here
+            drop(attrs);
+
             let expanded_expr = match expand_mac_invoc(mac, span,
                                                        |r| r.make_expr(),
                                                        mark_expr, fld) {
@@ -60,6 +65,7 @@ pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> {
                 id: ast::DUMMY_NODE_ID,
                 node: e.node,
                 span: span,
+                attrs: e.attrs,
             })
         }
 
@@ -73,12 +79,14 @@ pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> {
             let placer = fld.fold_expr(placer);
             let value_expr = fld.fold_expr(value_expr);
             fld.cx.expr(span, ast::ExprInPlace(placer, value_expr))
+                .with_attrs(fold_thin_attrs(attrs, fld))
         }
 
         ast::ExprWhile(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::ExprWhile(cond, body, opt_ident))
+                .with_attrs(fold_thin_attrs(attrs, fld))
         }
 
         ast::ExprWhileLet(pat, expr, body, opt_ident) => {
@@ -96,11 +104,13 @@ pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> {
             assert!(rewritten_pats.len() == 1);
 
             fld.cx.expr(span, ast::ExprWhileLet(rewritten_pats.remove(0), expr, body, opt_ident))
+                .with_attrs(fold_thin_attrs(attrs, fld))
         }
 
         ast::ExprLoop(loop_block, opt_ident) => {
             let (loop_block, opt_ident) = expand_loop_block(loop_block, opt_ident, fld);
             fld.cx.expr(span, ast::ExprLoop(loop_block, opt_ident))
+                .with_attrs(fold_thin_attrs(attrs, fld))
         }
 
         ast::ExprForLoop(pat, head, body, opt_ident) => {
@@ -118,6 +128,7 @@ pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> {
 
             let head = fld.fold_expr(head);
             fld.cx.expr(span, ast::ExprForLoop(rewritten_pats.remove(0), head, body, opt_ident))
+                .with_attrs(fold_thin_attrs(attrs, fld))
         }
 
         ast::ExprIfLet(pat, sub_expr, body, else_opt) => {
@@ -136,6 +147,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);
             fld.cx.expr(span, ast::ExprIfLet(rewritten_pats.remove(0), sub_expr, body, else_opt))
+                .with_attrs(fold_thin_attrs(attrs, fld))
         }
 
         ast::ExprClosure(capture_clause, fn_decl, block) => {
@@ -144,14 +156,16 @@ pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> {
             let new_node = ast::ExprClosure(capture_clause,
                                             rewritten_fn_decl,
                                             rewritten_block);
-            P(ast::Expr{id:id, node: new_node, span: fld.new_span(span)})
+            P(ast::Expr{id:id, node: new_node, span: fld.new_span(span),
+                        attrs: fold_thin_attrs(attrs, fld)})
         }
 
         _ => {
             P(noop_fold_expr(ast::Expr {
                 id: id,
                 node: node,
-                span: span
+                span: span,
+                attrs: attrs
             }, fld))
         }
     });
@@ -487,11 +501,14 @@ pub fn expand_item_mac(it: P<ast::Item>,
 /// Expand a stmt
 fn expand_stmt(stmt: P<Stmt>, fld: &mut MacroExpander) -> SmallVector<P<Stmt>> {
     let stmt = stmt.and_then(|stmt| stmt);
-    let (mac, style) = match stmt.node {
-        StmtMac(mac, style) => (mac, style),
+    let (mac, style, attrs) = match stmt.node {
+        StmtMac(mac, style, attrs) => (mac, style, attrs),
         _ => return expand_non_macro_stmt(stmt, fld)
     };
 
+    // Assert that we drop any macro attributes on the floor here
+    drop(attrs);
+
     let maybe_new_items =
         expand_mac_invoc(mac.and_then(|m| m), stmt.span,
                          |r| r.make_stmts(),
@@ -539,7 +556,7 @@ fn expand_non_macro_stmt(Spanned {node, span: stmt_span}: Stmt, fld: &mut MacroE
         StmtDecl(decl, node_id) => decl.and_then(|Spanned {node: decl, span}| match decl {
             DeclLocal(local) => {
                 // take it apart:
-                let rewritten_local = local.map(|Local {id, pat, ty, init, span}| {
+                let rewritten_local = local.map(|Local {id, pat, ty, init, span, attrs}| {
                     // expand the ty since TyFixedLengthVec contains an Expr
                     // and thus may have a macro use
                     let expanded_ty = ty.map(|t| fld.fold_ty(t));
@@ -569,7 +586,8 @@ fn expand_non_macro_stmt(Spanned {node, span: stmt_span}: Stmt, fld: &mut MacroE
                         pat: rewritten_pat,
                         // also, don't forget to expand the init:
                         init: init.map(|e| fld.fold_expr(e)),
-                        span: span
+                        span: span,
+                        attrs: fold::fold_thin_attrs(attrs, fld),
                     }
                 });
                 SmallVector::one(P(Spanned {
@@ -1263,7 +1281,7 @@ pub fn expand_crate<'feat>(parse_sess: &parse::ParseSess,
                            // these are the macros being imported to this crate:
                            imported_macros: Vec<ast::MacroDef>,
                            user_exts: Vec<NamedSyntaxExtension>,
-                           feature_gated_cfgs: &mut Vec<GatedCfg>,
+                           feature_gated_cfgs: &mut Vec<GatedCfgAttr>,
                            c: Crate) -> (Crate, HashSet<Name>) {
     let mut cx = ExtCtxt::new(parse_sess, c.config.clone(), cfg,
                               feature_gated_cfgs);
diff --git a/src/libsyntax/ext/quote.rs b/src/libsyntax/ext/quote.rs
index 5e5b8158181..fc6cacb40f1 100644
--- a/src/libsyntax/ext/quote.rs
+++ b/src/libsyntax/ext/quote.rs
@@ -242,6 +242,7 @@ pub mod rt {
                 id: ast::DUMMY_NODE_ID,
                 node: ast::ExprLit(P(self.clone())),
                 span: DUMMY_SP,
+                attrs: None,
             }).to_tokens(cx)
         }
     }
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 8fc739946b4..e00c3e3bea4 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -227,6 +227,9 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Option<u32>, Status
 
     // Allows cfg(target_vendor = "...").
     ("cfg_target_vendor", "1.5.0", Some(29718), Active),
+
+    // Allow attributes on expressions and non-item statements
+    ("stmt_expr_attributes", "1.6.0", Some(15701), Active),
 ];
 // (changing above list without updating src/doc/reference.md makes @cmr sad)
 
@@ -408,24 +411,56 @@ const GATED_CFGS: &'static [(&'static str, &'static str, fn(&Features) -> bool)]
 ];
 
 #[derive(Debug, Eq, PartialEq)]
+pub enum GatedCfgAttr {
+    GatedCfg(GatedCfg),
+    GatedAttr(Span),
+}
+
+#[derive(Debug, Eq, PartialEq)]
 pub struct GatedCfg {
     span: Span,
     index: usize,
 }
 
-impl Ord for GatedCfg {
-    fn cmp(&self, other: &GatedCfg) -> cmp::Ordering {
-        (self.span.lo.0, self.span.hi.0, self.index)
-            .cmp(&(other.span.lo.0, other.span.hi.0, other.index))
+impl Ord for GatedCfgAttr {
+    fn cmp(&self, other: &GatedCfgAttr) -> cmp::Ordering {
+        let to_tup = |s: &GatedCfgAttr| match *s {
+            GatedCfgAttr::GatedCfg(ref gated_cfg) => {
+                (gated_cfg.span.lo.0, gated_cfg.span.hi.0, gated_cfg.index)
+            }
+            GatedCfgAttr::GatedAttr(ref span) => {
+                (span.lo.0, span.hi.0, GATED_CFGS.len())
+            }
+        };
+        to_tup(self).cmp(&to_tup(other))
     }
 }
 
-impl PartialOrd for GatedCfg {
-    fn partial_cmp(&self, other: &GatedCfg) -> Option<cmp::Ordering> {
+impl PartialOrd for GatedCfgAttr {
+    fn partial_cmp(&self, other: &GatedCfgAttr) -> Option<cmp::Ordering> {
         Some(self.cmp(other))
     }
 }
 
+impl GatedCfgAttr {
+    pub fn check_and_emit(&self, diagnostic: &SpanHandler, features: &Features) {
+        match *self {
+            GatedCfgAttr::GatedCfg(ref cfg) => {
+                cfg.check_and_emit(diagnostic, features);
+            }
+            GatedCfgAttr::GatedAttr(span) => {
+                if !features.stmt_expr_attributes {
+                    emit_feature_err(diagnostic,
+                                     "stmt_expr_attributes",
+                                     span,
+                                     GateIssue::Language,
+                                     EXPLAIN_STMT_ATTR_SYNTAX);
+                }
+            }
+        }
+    }
+}
+
 impl GatedCfg {
     pub fn gate(cfg: &ast::MetaItem) -> Option<GatedCfg> {
         let name = cfg.name();
@@ -438,7 +473,7 @@ impl GatedCfg {
                       }
                   })
     }
-    pub fn check_and_emit(&self, diagnostic: &SpanHandler, features: &Features) {
+    fn check_and_emit(&self, diagnostic: &SpanHandler, features: &Features) {
         let (cfg, feature, has_feature) = GATED_CFGS[self.index];
         if !has_feature(features) {
             let explain = format!("`cfg({})` is experimental and subject to change", cfg);
@@ -504,6 +539,7 @@ pub struct Features {
     pub augmented_assignments: bool,
     pub braced_empty_structs: bool,
     pub staged_api: bool,
+    pub stmt_expr_attributes: bool,
 }
 
 impl Features {
@@ -537,6 +573,7 @@ impl Features {
             augmented_assignments: false,
             braced_empty_structs: false,
             staged_api: false,
+            stmt_expr_attributes: false,
         }
     }
 }
@@ -550,6 +587,9 @@ const EXPLAIN_PLACEMENT_IN: &'static str =
 const EXPLAIN_PUSHPOP_UNSAFE: &'static str =
     "push/pop_unsafe macros are experimental and subject to change.";
 
+const EXPLAIN_STMT_ATTR_SYNTAX: &'static str =
+    "attributes on non-item statements and expressions are experimental.";
+
 pub fn check_for_box_syntax(f: Option<&Features>, diag: &SpanHandler, span: Span) {
     if let Some(&Features { allow_box: true, .. }) = f {
         return;
@@ -1111,6 +1151,7 @@ fn check_crate_inner<F>(cm: &CodeMap, span_handler: &SpanHandler,
         augmented_assignments: cx.has_feature("augmented_assignments"),
         braced_empty_structs: cx.has_feature("braced_empty_structs"),
         staged_api: cx.has_feature("staged_api"),
+        stmt_expr_attributes: cx.has_feature("stmt_expr_attributes"),
     }
 }
 
diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs
index 3dba6cbecbd..73f2c51b246 100644
--- a/src/libsyntax/fold.rs
+++ b/src/libsyntax/fold.rs
@@ -20,39 +20,17 @@
 
 use ast::*;
 use ast;
+use attr::{ThinAttributes, ThinAttributesExt};
 use ast_util;
 use codemap::{respan, Span, Spanned};
 use owned_slice::OwnedSlice;
 use parse::token;
 use ptr::P;
-use std::ptr;
 use util::small_vector::SmallVector;
+use util::move_map::MoveMap;
 
 use std::rc::Rc;
 
-// This could have a better place to live.
-pub trait MoveMap<T> {
-    fn move_map<F>(self, f: F) -> Self where F: FnMut(T) -> T;
-}
-
-impl<T> MoveMap<T> for Vec<T> {
-    fn move_map<F>(mut self, mut f: F) -> Vec<T> where F: FnMut(T) -> T {
-        for p in &mut self {
-            unsafe {
-                // FIXME(#5016) this shouldn't need to zero to be safe.
-                ptr::write(p, f(ptr::read_and_drop(p)));
-            }
-        }
-        self
-    }
-}
-
-impl<T> MoveMap<T> for OwnedSlice<T> {
-    fn move_map<F>(self, f: F) -> OwnedSlice<T> where F: FnMut(T) -> T {
-        OwnedSlice::from_vec(self.into_vec().move_map(f))
-    }
-}
-
 pub trait Folder : Sized {
     // Any additions to this trait should happen in form
     // of a call to a public `noop_*` function that only calls
@@ -134,6 +112,14 @@ pub trait Folder : Sized {
         e.map(|e| noop_fold_expr(e, self))
     }
 
+    fn fold_opt_expr(&mut self, e: P<Expr>) -> Option<P<Expr>> {
+        noop_fold_opt_expr(e, self)
+    }
+
+    fn fold_exprs(&mut self, es: Vec<P<Expr>>) -> Vec<P<Expr>> {
+        noop_fold_exprs(es, self)
+    }
+
     fn fold_ty(&mut self, t: P<Ty>) -> P<Ty> {
         noop_fold_ty(t, self)
     }
@@ -353,7 +339,11 @@ pub fn noop_fold_view_path<T: Folder>(view_path: P<ViewPath>, fld: &mut T) -> P<
 }
 
 pub fn fold_attrs<T: Folder>(attrs: Vec<Attribute>, fld: &mut T) -> Vec<Attribute> {
-    attrs.into_iter().flat_map(|x| fld.fold_attribute(x)).collect()
+    attrs.move_flat_map(|x| fld.fold_attribute(x))
+}
+
+pub fn fold_thin_attrs<T: Folder>(attrs: ThinAttributes, fld: &mut T) -> ThinAttributes {
+    attrs.map_thin_attrs(|v| fold_attrs(v, fld))
 }
 
 pub fn noop_fold_arm<T: Folder>(Arm {attrs, pats, guard, body}: Arm, fld: &mut T) -> Arm {
@@ -508,12 +498,13 @@ pub fn noop_fold_parenthesized_parameter_data<T: Folder>(data: ParenthesizedPara
 }
 
 pub fn noop_fold_local<T: Folder>(l: P<Local>, fld: &mut T) -> P<Local> {
-    l.map(|Local {id, pat, ty, init, span}| Local {
+    l.map(|Local {id, pat, ty, init, span, attrs}| Local {
         id: fld.new_id(id),
         ty: ty.map(|t| fld.fold_ty(t)),
         pat: fld.fold_pat(pat),
         init: init.map(|e| fld.fold_expr(e)),
-        span: fld.new_span(span)
+        span: fld.new_span(span),
+        attrs: attrs.map_thin_attrs(|v| fold_attrs(v, fld)),
     })
 }
 
@@ -609,6 +600,8 @@ pub fn noop_fold_tt<T: Folder>(tt: &TokenTree, fld: &mut T) -> TokenTree {
 }
 
 pub fn noop_fold_tts<T: Folder>(tts: &[TokenTree], fld: &mut T) -> Vec<TokenTree> {
+    // FIXME: Does this have to take a tts slice?
+    // Could use move_map otherwise...
     tts.iter().map(|tt| fld.fold_tt(tt)).collect()
 }
 
@@ -890,8 +883,8 @@ fn noop_fold_bounds<T: Folder>(bounds: TyParamBounds, folder: &mut T)
 pub fn noop_fold_block<T: Folder>(b: P<Block>, folder: &mut T) -> P<Block> {
     b.map(|Block {id, stmts, expr, rules, span}| Block {
         id: folder.new_id(id),
-        stmts: stmts.into_iter().flat_map(|s| folder.fold_stmt(s).into_iter()).collect(),
-        expr: expr.map(|x| folder.fold_expr(x)),
+        stmts: stmts.move_flat_map(|s| folder.fold_stmt(s).into_iter()),
+        expr: expr.and_then(|x| folder.fold_opt_expr(x)),
         rules: rules,
         span: folder.new_span(span),
     })
@@ -939,9 +932,9 @@ pub fn noop_fold_item_underscore<T: Folder>(i: Item_, folder: &mut T) -> Item_ {
             ItemDefaultImpl(unsafety, folder.fold_trait_ref((*trait_ref).clone()))
         }
         ItemImpl(unsafety, polarity, generics, ifce, ty, impl_items) => {
-            let new_impl_items = impl_items.into_iter().flat_map(|item| {
-                folder.fold_impl_item(item).into_iter()
-            }).collect();
+            let new_impl_items = impl_items.move_flat_map(|item| {
+                folder.fold_impl_item(item)
+            });
             let ifce = match ifce {
                 None => None,
                 Some(ref trait_ref) => {
@@ -957,9 +950,9 @@ pub fn noop_fold_item_underscore<T: Folder>(i: Item_, folder: &mut T) -> Item_ {
         }
         ItemTrait(unsafety, generics, bounds, items) => {
             let bounds = folder.fold_bounds(bounds);
-            let items = items.into_iter().flat_map(|item| {
-                folder.fold_trait_item(item).into_iter()
-            }).collect();
+            let items = items.move_flat_map(|item| {
+                folder.fold_trait_item(item)
+            });
             ItemTrait(unsafety,
                       folder.fold_generics(generics),
                       bounds,
@@ -1018,7 +1011,7 @@ pub fn noop_fold_impl_item<T: Folder>(i: P<ImplItem>, folder: &mut T)
 pub fn noop_fold_mod<T: Folder>(Mod {inner, items}: Mod, folder: &mut T) -> Mod {
     Mod {
         inner: folder.new_span(inner),
-        items: items.into_iter().flat_map(|x| folder.fold_item(x).into_iter()).collect(),
+        items: items.move_flat_map(|x| folder.fold_item(x)),
     }
 }
 
@@ -1171,7 +1164,7 @@ pub fn noop_fold_pat<T: Folder>(p: P<Pat>, folder: &mut T) -> P<Pat> {
     })
 }
 
-pub fn noop_fold_expr<T: Folder>(Expr {id, node, span}: Expr, folder: &mut T) -> Expr {
+pub fn noop_fold_expr<T: Folder>(Expr {id, node, span, attrs}: Expr, folder: &mut T) -> Expr {
     Expr {
         id: folder.new_id(id),
         node: match node {
@@ -1182,21 +1175,21 @@ pub fn noop_fold_expr<T: Folder>(Expr {id, node, span}: Expr, folder: &mut T) ->
                 ExprInPlace(folder.fold_expr(p), folder.fold_expr(e))
             }
             ExprVec(exprs) => {
-                ExprVec(exprs.move_map(|x| folder.fold_expr(x)))
+                ExprVec(folder.fold_exprs(exprs))
             }
             ExprRepeat(expr, count) => {
                 ExprRepeat(folder.fold_expr(expr), folder.fold_expr(count))
             }
-            ExprTup(elts) => ExprTup(elts.move_map(|x| folder.fold_expr(x))),
+            ExprTup(exprs) => ExprTup(folder.fold_exprs(exprs)),
             ExprCall(f, args) => {
                 ExprCall(folder.fold_expr(f),
-                         args.move_map(|x| folder.fold_expr(x)))
+                         folder.fold_exprs(args))
             }
             ExprMethodCall(i, tps, args) => {
                 ExprMethodCall(
                     respan(folder.new_span(i.span), folder.fold_ident(i.node)),
                     tps.move_map(|x| folder.fold_ty(x)),
-                    args.move_map(|x| folder.fold_expr(x)))
+                    folder.fold_exprs(args))
             }
             ExprBinary(binop, lhs, rhs) => {
                 ExprBinary(binop,
@@ -1329,10 +1322,19 @@ pub fn noop_fold_expr<T: Folder>(Expr {id, node, span}: Expr, folder: &mut T) ->
             },
             ExprParen(ex) => ExprParen(folder.fold_expr(ex))
         },
-        span: folder.new_span(span)
+        span: folder.new_span(span),
+        attrs: attrs.map_thin_attrs(|v| fold_attrs(v, folder)),
     }
 }
 
+pub fn noop_fold_opt_expr<T: Folder>(e: P<Expr>, folder: &mut T) -> Option<P<Expr>> {
+    Some(folder.fold_expr(e))
+}
+
+pub fn noop_fold_exprs<T: Folder>(es: Vec<P<Expr>>, folder: &mut T) -> Vec<P<Expr>> {
+    es.move_flat_map(|e| folder.fold_opt_expr(e))
+}
+
 pub fn noop_fold_stmt<T: Folder>(Spanned {node, span}: Stmt, folder: &mut T)
                                  -> SmallVector<P<Stmt>> {
     let span = folder.new_span(span);
@@ -1346,20 +1348,30 @@ pub fn noop_fold_stmt<T: Folder>(Spanned {node, span}: Stmt, folder: &mut T)
         }
         StmtExpr(e, id) => {
             let id = folder.new_id(id);
-            SmallVector::one(P(Spanned {
-                node: StmtExpr(folder.fold_expr(e), id),
-                span: span
-            }))
+            if let Some(e) = folder.fold_opt_expr(e) {
+                SmallVector::one(P(Spanned {
+                    node: StmtExpr(e, id),
+                    span: span
+                }))
+            } else {
+                SmallVector::zero()
+            }
         }
         StmtSemi(e, id) => {
             let id = folder.new_id(id);
-            SmallVector::one(P(Spanned {
-                node: StmtSemi(folder.fold_expr(e), id),
-                span: span
-            }))
+            if let Some(e) = folder.fold_opt_expr(e) {
+                SmallVector::one(P(Spanned {
+                    node: StmtSemi(e, id),
+                    span: span
+                }))
+            } else {
+                SmallVector::zero()
+            }
         }
-        StmtMac(mac, semi) => SmallVector::one(P(Spanned {
-            node: StmtMac(mac.map(|m| folder.fold_mac(m)), semi),
+        StmtMac(mac, semi, attrs) => SmallVector::one(P(Spanned {
+            node: StmtMac(mac.map(|m| folder.fold_mac(m)),
+                          semi,
+                          attrs.map_thin_attrs(|v| fold_attrs(v, folder))),
             span: span
         }))
     }
diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs
index 93daecce5dd..9401999465b 100644
--- a/src/libsyntax/lib.rs
+++ b/src/libsyntax/lib.rs
@@ -71,6 +71,7 @@ pub mod util {
     #[cfg(test)]
     pub mod parser_testing;
     pub mod small_vector;
+    pub mod move_map;
 }
 
 pub mod diagnostics {
diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs
index 7e2fd09a373..e9c8173a4d9 100644
--- a/src/libsyntax/parse/mod.rs
+++ b/src/libsyntax/parse/mod.rs
@@ -699,7 +699,8 @@ mod tests {
                             }
                         ),
                     }),
-                    span: sp(0, 1)
+                    span: sp(0, 1),
+                    attrs: None,
                    }))
     }
 
@@ -721,7 +722,8 @@ mod tests {
                                 }
                             )
                         }),
-                    span: sp(0, 6)
+                    span: sp(0, 6),
+                    attrs: None,
                    }))
     }
 
@@ -848,9 +850,11 @@ mod tests {
                                 }
                             ),
                         }),
-                        span:sp(7,8)
+                        span:sp(7,8),
+                        attrs: None,
                     }))),
-                    span:sp(0,8)
+                    span:sp(0,8),
+                    attrs: None,
                    }))
     }
 
@@ -869,7 +873,8 @@ mod tests {
                                 }
                                ),
                             }),
-                           span: sp(0,1)}),
+                           span: sp(0,1),
+                           attrs: None}),
                                            ast::DUMMY_NODE_ID),
                        span: sp(0,1)})))
 
@@ -963,7 +968,8 @@ mod tests {
                                                             }
                                                         ),
                                                       }),
-                                                span: sp(17,18)}),
+                                                span: sp(17,18),
+                                                attrs: None,}),
                                                 ast::DUMMY_NODE_ID),
                                             span: sp(17,19)})),
                                         expr: None,
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index ce65f99e2f0..7502a8cbc35 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -56,6 +56,7 @@ use ast::TypeTraitItem;
 use ast::{UnnamedField, UnsafeBlock};
 use ast::{ViewPath, ViewPathGlob, ViewPathList, ViewPathSimple};
 use ast::{Visibility, WhereClause};
+use attr::{ThinAttributes, ThinAttributesExt, AttributesExt};
 use ast;
 use ast_util::{self, ident_to_path};
 use codemap::{self, Span, BytePos, Spanned, spanned, mk_sp, CodeMap};
@@ -140,7 +141,7 @@ macro_rules! maybe_whole_expr {
                         _ => unreachable!()
                     };
                     let span = $p.span;
-                    Some($p.mk_expr(span.lo, span.hi, ExprPath(None, pt)))
+                    Some($p.mk_expr(span.lo, span.hi, ExprPath(None, pt), None))
                 }
                 token::Interpolated(token::NtBlock(_)) => {
                     // FIXME: The following avoids an issue with lexical borrowck scopes,
@@ -150,7 +151,7 @@ macro_rules! maybe_whole_expr {
                         _ => unreachable!()
                     };
                     let span = $p.span;
-                    Some($p.mk_expr(span.lo, span.hi, ExprBlock(b)))
+                    Some($p.mk_expr(span.lo, span.hi, ExprBlock(b), None))
                 }
                 _ => None
             };
@@ -319,6 +320,27 @@ pub struct ModulePathError {
     pub help_msg: String,
 }
 
+pub enum LhsExpr {
+    NotYetParsed,
+    AttributesParsed(ThinAttributes),
+    AlreadyParsed(P<Expr>),
+}
+
+impl From<Option<ThinAttributes>> for LhsExpr {
+    fn from(o: Option<ThinAttributes>) -> Self {
+        if let Some(attrs) = o {
+            LhsExpr::AttributesParsed(attrs)
+        } else {
+            LhsExpr::NotYetParsed
+        }
+    }
+}
+
+impl From<P<Expr>> for LhsExpr {
+    fn from(expr: P<Expr>) -> Self {
+        LhsExpr::AlreadyParsed(expr)
+    }
+}
 
 impl<'a> Parser<'a> {
     pub fn new(sess: &'a ParseSess,
@@ -1557,19 +1579,18 @@ impl<'a> Parser<'a> {
     }
 
     /// matches '-' lit | lit
-    pub fn parse_literal_maybe_minus(&mut self) -> PResult<P<Expr>> {
+    pub fn parse_pat_literal_maybe_minus(&mut self) -> PResult<P<Expr>> {
         let minus_lo = self.span.lo;
         let minus_present = try!(self.eat(&token::BinOp(token::Minus)));
-
         let lo = self.span.lo;
         let literal = P(try!(self.parse_lit()));
         let hi = self.last_span.hi;
-        let expr = self.mk_expr(lo, hi, ExprLit(literal));
+        let expr = self.mk_expr(lo, hi, ExprLit(literal), None);
 
         if minus_present {
             let minus_hi = self.last_span.hi;
             let unary = self.mk_unary(UnNeg, expr);
-            Ok(self.mk_expr(minus_lo, minus_hi, unary))
+            Ok(self.mk_expr(minus_lo, minus_hi, unary, None))
         } else {
             Ok(expr)
         }
@@ -1914,11 +1935,13 @@ impl<'a> Parser<'a> {
         })
     }
 
-    pub fn mk_expr(&mut self, lo: BytePos, hi: BytePos, node: Expr_) -> P<Expr> {
+    pub fn mk_expr(&mut self, lo: BytePos, hi: BytePos,
+                   node: Expr_, attrs: ThinAttributes) -> P<Expr> {
         P(Expr {
             id: ast::DUMMY_NODE_ID,
             node: node,
             span: mk_sp(lo, hi),
+            attrs: attrs,
         })
     }
 
@@ -1966,15 +1989,17 @@ impl<'a> Parser<'a> {
         ExprAssignOp(binop, lhs, rhs)
     }
 
-    pub fn mk_mac_expr(&mut self, lo: BytePos, hi: BytePos, m: Mac_) -> P<Expr> {
+    pub fn mk_mac_expr(&mut self, lo: BytePos, hi: BytePos,
+                       m: Mac_, attrs: ThinAttributes) -> P<Expr> {
         P(Expr {
             id: ast::DUMMY_NODE_ID,
             node: ExprMac(codemap::Spanned {node: m, span: mk_sp(lo, hi)}),
             span: mk_sp(lo, hi),
+            attrs: attrs,
         })
     }
 
-    pub fn mk_lit_u32(&mut self, i: u32) -> P<Expr> {
+    pub fn mk_lit_u32(&mut self, i: u32, attrs: ThinAttributes) -> P<Expr> {
         let span = &self.span;
         let lv_lit = P(codemap::Spanned {
             node: LitInt(i as u64, ast::UnsignedIntLit(TyU32)),
@@ -1985,6 +2010,7 @@ impl<'a> Parser<'a> {
             id: ast::DUMMY_NODE_ID,
             node: ExprLit(lv_lit),
             span: *span,
+            attrs: attrs,
         })
     }
 
@@ -2002,9 +2028,20 @@ impl<'a> Parser<'a> {
     /// At the bottom (top?) of the precedence hierarchy,
     /// parse things like parenthesized exprs,
     /// macros, return, etc.
-    pub fn parse_bottom_expr(&mut self) -> PResult<P<Expr>> {
+    ///
+    /// NB: This does not parse outer attributes,
+    ///     and is private because it only works
+    ///     correctly if called from parse_dot_or_call_expr().
+    fn parse_bottom_expr(&mut self) -> PResult<P<Expr>> {
         maybe_whole_expr!(self);
 
+        // Outer attributes are already parsed and will be
+        // added to the return value after the fact.
+        //
+        // Therefore, prevent sub-parser from parsing
+        // attributes by giving them a empty "already parsed" list.
+        let mut attrs = None;
+
         let lo = self.span.lo;
         let mut hi = self.span.hi;
 
@@ -2015,6 +2052,10 @@ impl<'a> Parser<'a> {
             token::OpenDelim(token::Paren) => {
                 try!(self.bump());
 
+                let attrs = try!(self.parse_inner_attributes())
+                    .into_thin_attrs()
+                    .prepend(attrs);
+
                 // (e) is parenthesized e
                 // (e,) is a tuple with only one field, e
                 let mut es = vec![];
@@ -2036,17 +2077,17 @@ impl<'a> Parser<'a> {
 
                 hi = self.last_span.hi;
                 return if es.len() == 1 && !trailing_comma {
-                    Ok(self.mk_expr(lo, hi, ExprParen(es.into_iter().nth(0).unwrap())))
+                    Ok(self.mk_expr(lo, hi, ExprParen(es.into_iter().nth(0).unwrap()), attrs))
                 } else {
-                    Ok(self.mk_expr(lo, hi, ExprTup(es)))
+                    Ok(self.mk_expr(lo, hi, ExprTup(es), attrs))
                 }
             },
             token::OpenDelim(token::Brace) => {
-                return self.parse_block_expr(lo, DefaultBlock);
+                return self.parse_block_expr(lo, DefaultBlock, attrs);
             },
             token::BinOp(token::Or) |  token::OrOr => {
                 let lo = self.span.lo;
-                return self.parse_lambda_expr(lo, CaptureByRef);
+                return self.parse_lambda_expr(lo, CaptureByRef, attrs);
             },
             token::Ident(id @ ast::Ident {
                             name: token::SELF_KEYWORD_NAME,
@@ -2060,6 +2101,10 @@ impl<'a> Parser<'a> {
             token::OpenDelim(token::Bracket) => {
                 try!(self.bump());
 
+                let inner_attrs = try!(self.parse_inner_attributes())
+                    .into_thin_attrs();
+                attrs.update(|attrs| attrs.append(inner_attrs));
+
                 if self.check(&token::CloseDelim(token::Bracket)) {
                     // Empty vector.
                     try!(self.bump());
@@ -2097,22 +2142,22 @@ impl<'a> Parser<'a> {
                     let (qself, path) =
                         try!(self.parse_qualified_path(LifetimeAndTypesWithColons));
                     hi = path.span.hi;
-                    return Ok(self.mk_expr(lo, hi, ExprPath(Some(qself), path)));
+                    return Ok(self.mk_expr(lo, hi, ExprPath(Some(qself), path), attrs));
                 }
                 if try!(self.eat_keyword(keywords::Move) ){
                     let lo = self.last_span.lo;
-                    return self.parse_lambda_expr(lo, CaptureByValue);
+                    return self.parse_lambda_expr(lo, CaptureByValue, attrs);
                 }
                 if try!(self.eat_keyword(keywords::If)) {
-                    return self.parse_if_expr();
+                    return self.parse_if_expr(attrs);
                 }
                 if try!(self.eat_keyword(keywords::For) ){
                     let lo = self.last_span.lo;
-                    return self.parse_for_expr(None, lo);
+                    return self.parse_for_expr(None, lo, attrs);
                 }
                 if try!(self.eat_keyword(keywords::While) ){
                     let lo = self.last_span.lo;
-                    return self.parse_while_expr(None, lo);
+                    return self.parse_while_expr(None, lo, attrs);
                 }
                 if self.token.is_lifetime() {
                     let lifetime = self.get_lifetime();
@@ -2120,19 +2165,19 @@ impl<'a> Parser<'a> {
                     try!(self.bump());
                     try!(self.expect(&token::Colon));
                     if try!(self.eat_keyword(keywords::While) ){
-                        return self.parse_while_expr(Some(lifetime), lo)
+                        return self.parse_while_expr(Some(lifetime), lo, attrs)
                     }
                     if try!(self.eat_keyword(keywords::For) ){
-                        return self.parse_for_expr(Some(lifetime), lo)
+                        return self.parse_for_expr(Some(lifetime), lo, attrs)
                     }
                     if try!(self.eat_keyword(keywords::Loop) ){
-                        return self.parse_loop_expr(Some(lifetime), lo)
+                        return self.parse_loop_expr(Some(lifetime), lo, attrs)
                     }
                     return Err(self.fatal("expected `while`, `for`, or `loop` after a label"))
                 }
                 if try!(self.eat_keyword(keywords::Loop) ){
                     let lo = self.last_span.lo;
-                    return self.parse_loop_expr(None, lo);
+                    return self.parse_loop_expr(None, lo, attrs);
                 }
                 if try!(self.eat_keyword(keywords::Continue) ){
                     let ex = if self.token.is_lifetime() {
@@ -2146,15 +2191,16 @@ impl<'a> Parser<'a> {
                         ExprAgain(None)
                     };
                     let hi = self.last_span.hi;
-                    return Ok(self.mk_expr(lo, hi, ex));
+                    return Ok(self.mk_expr(lo, hi, ex, attrs));
                 }
                 if try!(self.eat_keyword(keywords::Match) ){
-                    return self.parse_match_expr();
+                    return self.parse_match_expr(attrs);
                 }
                 if try!(self.eat_keyword(keywords::Unsafe) ){
                     return self.parse_block_expr(
                         lo,
-                        UnsafeBlock(ast::UserProvided));
+                        UnsafeBlock(ast::UserProvided),
+                        attrs);
                 }
                 if try!(self.eat_keyword(keywords::Return) ){
                     if self.token.can_begin_expr() {
@@ -2196,7 +2242,8 @@ impl<'a> Parser<'a> {
 
                         return Ok(self.mk_mac_expr(lo,
                                                    hi,
-                                                   Mac_ { path: pth, tts: tts, ctxt: EMPTY_CTXT }));
+                                                   Mac_ { path: pth, tts: tts, ctxt: EMPTY_CTXT },
+                                                   attrs));
                     }
                     if self.check(&token::OpenDelim(token::Brace)) {
                         // This is a struct literal, unless we're prohibited
@@ -2210,6 +2257,10 @@ impl<'a> Parser<'a> {
                             let mut fields = Vec::new();
                             let mut base = None;
 
+                            let attrs = attrs.append(
+                                try!(self.parse_inner_attributes())
+                                    .into_thin_attrs());
+
                             while self.token != token::CloseDelim(token::Brace) {
                                 if try!(self.eat(&token::DotDot) ){
                                     base = Some(try!(self.parse_expr()));
@@ -2225,7 +2276,7 @@ impl<'a> Parser<'a> {
                             hi = self.span.hi;
                             try!(self.expect(&token::CloseDelim(token::Brace)));
                             ex = ExprStruct(pth, fields, base);
-                            return Ok(self.mk_expr(lo, hi, ex));
+                            return Ok(self.mk_expr(lo, hi, ex, attrs));
                         }
                     }
 
@@ -2240,24 +2291,74 @@ impl<'a> Parser<'a> {
             }
         }
 
-        return Ok(self.mk_expr(lo, hi, ex));
+        return Ok(self.mk_expr(lo, hi, ex, attrs));
+    }
+
+    fn parse_or_use_outer_attributes(&mut self,
+                                     already_parsed_attrs: Option<ThinAttributes>)
+                                     -> PResult<ThinAttributes> {
+        if let Some(attrs) = already_parsed_attrs {
+            Ok(attrs)
+        } else {
+            self.parse_outer_attributes().map(|a| a.into_thin_attrs())
+        }
     }
 
     /// Parse a block or unsafe block
-    pub fn parse_block_expr(&mut self, lo: BytePos, blk_mode: BlockCheckMode)
+    pub fn parse_block_expr(&mut self, lo: BytePos, blk_mode: BlockCheckMode,
+                            attrs: ThinAttributes)
                             -> PResult<P<Expr>> {
+
+        let outer_attrs = attrs;
         try!(self.expect(&token::OpenDelim(token::Brace)));
+
+        let inner_attrs = try!(self.parse_inner_attributes()).into_thin_attrs();
+        let attrs = outer_attrs.append(inner_attrs);
+
         let blk = try!(self.parse_block_tail(lo, blk_mode));
-        return Ok(self.mk_expr(blk.span.lo, blk.span.hi, ExprBlock(blk)));
+        return Ok(self.mk_expr(blk.span.lo, blk.span.hi, ExprBlock(blk), attrs));
     }
 
     /// parse a.b or a(13) or a[4] or just a
-    pub fn parse_dot_or_call_expr(&mut self) -> PResult<P<Expr>> {
+    pub fn parse_dot_or_call_expr(&mut self,
+                                  already_parsed_attrs: Option<ThinAttributes>)
+                                  -> PResult<P<Expr>> {
+        let attrs = try!(self.parse_or_use_outer_attributes(already_parsed_attrs));
+
         let b = try!(self.parse_bottom_expr());
-        self.parse_dot_or_call_expr_with(b)
+        self.parse_dot_or_call_expr_with(b, attrs)
+    }
+
+    pub fn parse_dot_or_call_expr_with(&mut self,
+                                       e0: P<Expr>,
+                                       attrs: ThinAttributes)
+                                       -> PResult<P<Expr>> {
+        // Stitch the list of outer attributes onto the return value.
+        // A little bit ugly, but the best way given the current code
+        // structure
+        self.parse_dot_or_call_expr_with_(e0)
+        .map(|expr|
+            expr.map(|mut expr| {
+                expr.attrs.update(|a| a.prepend(attrs));
+                match expr.node {
+                    ExprIf(..) | ExprIfLet(..) => {
+                        if !expr.attrs.as_attr_slice().is_empty() {
+                            // Just point to the first attribute in there...
+                            let span = expr.attrs.as_attr_slice()[0].span;
+
+                            self.span_err(span,
+                                "attributes are not yet allowed on `if` \
+                                expressions");
+                        }
+                    }
+                    _ => {}
+                }
+                expr
+            })
+        )
     }
 
-    pub fn parse_dot_or_call_expr_with(&mut self, e0: P<Expr>) -> PResult<P<Expr>> {
+    fn parse_dot_or_call_expr_with_(&mut self, e0: P<Expr>) -> PResult<P<Expr>> {
         let mut e = e0;
         let lo = e.span.lo;
         let mut hi;
@@ -2295,7 +2396,7 @@ impl<'a> Parser<'a> {
                             es.insert(0, e);
                             let id = spanned(dot, hi, i);
                             let nd = self.mk_method_call(id, tys, es);
-                            e = self.mk_expr(lo, hi, nd);
+                            e = self.mk_expr(lo, hi, nd, None);
                         }
                         _ => {
                             if !tys.is_empty() {
@@ -2307,7 +2408,7 @@ impl<'a> Parser<'a> {
 
                             let id = spanned(dot, hi, i);
                             let field = self.mk_field(e, id);
-                            e = self.mk_expr(lo, hi, field);
+                            e = self.mk_expr(lo, hi, field, None);
                         }
                     }
                   }
@@ -2326,7 +2427,7 @@ impl<'a> Parser<'a> {
                         Some(n) => {
                             let id = spanned(dot, hi, n);
                             let field = self.mk_tup_field(e, id);
-                            e = self.mk_expr(lo, hi, field);
+                            e = self.mk_expr(lo, hi, field, None);
                         }
                         None => {
                             let last_span = self.last_span;
@@ -2370,7 +2471,7 @@ impl<'a> Parser<'a> {
                 hi = self.last_span.hi;
 
                 let nd = self.mk_call(e, es);
-                e = self.mk_expr(lo, hi, nd);
+                e = self.mk_expr(lo, hi, nd, None);
               }
 
               // expr[...]
@@ -2381,7 +2482,7 @@ impl<'a> Parser<'a> {
                 hi = self.span.hi;
                 try!(self.commit_expr_expecting(&*ix, token::CloseDelim(token::Bracket)));
                 let index = self.mk_index(e, ix);
-                e = self.mk_expr(lo, hi, index)
+                e = self.mk_expr(lo, hi, index, None)
               }
               _ => return Ok(e)
             }
@@ -2578,75 +2679,90 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse a prefix-unary-operator expr
-    pub fn parse_prefix_expr(&mut self) -> PResult<P<Expr>> {
+    pub fn parse_prefix_expr(&mut self,
+                             already_parsed_attrs: Option<ThinAttributes>)
+                             -> PResult<P<Expr>> {
+        let attrs = try!(self.parse_or_use_outer_attributes(already_parsed_attrs));
         let lo = self.span.lo;
         let hi;
         // Note: when adding new unary operators, don't forget to adjust Token::can_begin_expr()
         let ex = match self.token {
             token::Not => {
                 try!(self.bump());
-                let e = try!(self.parse_prefix_expr());
+                let e = try!(self.parse_prefix_expr(None));
                 hi = e.span.hi;
                 self.mk_unary(UnNot, e)
             }
             token::BinOp(token::Minus) => {
                 try!(self.bump());
-                let e = try!(self.parse_prefix_expr());
+                let e = try!(self.parse_prefix_expr(None));
                 hi = e.span.hi;
                 self.mk_unary(UnNeg, e)
             }
             token::BinOp(token::Star) => {
                 try!(self.bump());
-                let e = try!(self.parse_prefix_expr());
+                let e = try!(self.parse_prefix_expr(None));
                 hi = e.span.hi;
                 self.mk_unary(UnDeref, e)
             }
             token::BinOp(token::And) | token::AndAnd => {
                 try!(self.expect_and());
                 let m = try!(self.parse_mutability());
-                let e = try!(self.parse_prefix_expr());
+                let e = try!(self.parse_prefix_expr(None));
                 hi = e.span.hi;
                 ExprAddrOf(m, e)
             }
             token::Ident(..) if self.token.is_keyword(keywords::In) => {
                 try!(self.bump());
-                let place = try!(self.parse_expr_res(Restrictions::RESTRICTION_NO_STRUCT_LITERAL));
+                let place = try!(self.parse_expr_res(
+                    Restrictions::RESTRICTION_NO_STRUCT_LITERAL,
+                    None,
+                ));
                 let blk = try!(self.parse_block());
                 let span = blk.span;
                 hi = span.hi;
-                let blk_expr = self.mk_expr(span.lo, span.hi, ExprBlock(blk));
+                let blk_expr = self.mk_expr(span.lo, span.hi, ExprBlock(blk),
+                                            None);
                 ExprInPlace(place, blk_expr)
             }
             token::Ident(..) if self.token.is_keyword(keywords::Box) => {
                 try!(self.bump());
-                let subexpression = try!(self.parse_prefix_expr());
+                let subexpression = try!(self.parse_prefix_expr(None));
                 hi = subexpression.span.hi;
                 ExprBox(subexpression)
             }
-            _ => return self.parse_dot_or_call_expr()
+            _ => return self.parse_dot_or_call_expr(Some(attrs))
         };
-        return Ok(self.mk_expr(lo, hi, ex));
+        return Ok(self.mk_expr(lo, hi, ex, attrs));
     }
 
     /// Parse an associative expression
     ///
     /// This parses an expression accounting for associativity and precedence of the operators in
     /// the expression.
-    pub fn parse_assoc_expr(&mut self) -> PResult<P<Expr>> {
-        self.parse_assoc_expr_with(0, None)
+    pub fn parse_assoc_expr(&mut self,
+                            already_parsed_attrs: Option<ThinAttributes>)
+                            -> PResult<P<Expr>> {
+        self.parse_assoc_expr_with(0, already_parsed_attrs.into())
     }
 
     /// Parse an associative expression with operators of at least `min_prec` precedence
     pub fn parse_assoc_expr_with(&mut self,
                                  min_prec: usize,
-                                 lhs: Option<P<Expr>>)
+                                 lhs: LhsExpr)
                                  -> PResult<P<Expr>> {
-        let mut lhs = if lhs.is_some() {
-            lhs.unwrap()
-        } else if self.token == token::DotDot {
-            return self.parse_prefix_range_expr();
+        let mut lhs = if let LhsExpr::AlreadyParsed(expr) = lhs {
+            expr
         } else {
-            try!(self.parse_prefix_expr())
+            let attrs = match lhs {
+                LhsExpr::AttributesParsed(attrs) => Some(attrs),
+                _ => None,
+            };
+            if self.token == token::DotDot {
+                return self.parse_prefix_range_expr(attrs);
+            } else {
+                try!(self.parse_prefix_expr(attrs))
+            }
         };
         if self.expr_is_complete(&*lhs) {
             // Semi-statement forms are odd. See https://github.com/rust-lang/rust/issues/29071
@@ -2670,7 +2786,8 @@ impl<'a> Parser<'a> {
             // Special cases:
             if op == AssocOp::As {
                 let rhs = try!(self.parse_ty());
-                lhs = self.mk_expr(lhs.span.lo, rhs.span.hi, ExprCast(lhs, rhs));
+                lhs = self.mk_expr(lhs.span.lo, rhs.span.hi,
+                                   ExprCast(lhs, rhs), None);
                 continue
             } else if op == AssocOp::DotDot {
                     // If we didn’t have to handle `x..`, it would be pretty easy to generalise
@@ -2679,7 +2796,8 @@ impl<'a> Parser<'a> {
                     // We have 2 alternatives here: `x..y` and `x..` The other two variants are
                     // handled with `parse_prefix_range_expr` call above.
                     let rhs = if self.is_at_start_of_range_notation_rhs() {
-                        self.parse_assoc_expr_with(op.precedence() + 1, None).ok()
+                        self.parse_assoc_expr_with(op.precedence() + 1,
+                                                   LhsExpr::NotYetParsed).ok()
                     } else {
                         None
                     };
@@ -2689,22 +2807,22 @@ impl<'a> Parser<'a> {
                         cur_op_span
                     });
                     let r = self.mk_range(Some(lhs), rhs);
-                    lhs = self.mk_expr(lhs_span.lo, rhs_span.hi, r);
+                    lhs = self.mk_expr(lhs_span.lo, rhs_span.hi, r, None);
                     break
             }
 
 
             let rhs = try!(match op.fixity() {
                 Fixity::Right => self.with_res(restrictions, |this|{
-                    this.parse_assoc_expr_with(op.precedence(), None)
+                    this.parse_assoc_expr_with(op.precedence(), LhsExpr::NotYetParsed)
                 }),
                 Fixity::Left => self.with_res(restrictions, |this|{
-                    this.parse_assoc_expr_with(op.precedence() + 1, None)
+                    this.parse_assoc_expr_with(op.precedence() + 1, LhsExpr::NotYetParsed)
                 }),
                 // We currently have no non-associative operators that are not handled above by
                 // the special cases. The code is here only for future convenience.
                 Fixity::None => self.with_res(restrictions, |this|{
-                    this.parse_assoc_expr_with(op.precedence() + 1, None)
+                    this.parse_assoc_expr_with(op.precedence() + 1, LhsExpr::NotYetParsed)
                 }),
             });
 
@@ -2717,12 +2835,12 @@ impl<'a> Parser<'a> {
                     let ast_op = op.to_ast_binop().unwrap();
                     let (lhs_span, rhs_span) = (lhs.span, rhs.span);
                     let binary = self.mk_binary(codemap::respan(cur_op_span, ast_op), lhs, rhs);
-                    self.mk_expr(lhs_span.lo, rhs_span.hi, binary)
+                    self.mk_expr(lhs_span.lo, rhs_span.hi, binary, None)
                 }
                 AssocOp::Assign =>
-                    self.mk_expr(lhs.span.lo, rhs.span.hi, ExprAssign(lhs, rhs)),
+                    self.mk_expr(lhs.span.lo, rhs.span.hi, ExprAssign(lhs, rhs), None),
                 AssocOp::Inplace =>
-                    self.mk_expr(lhs.span.lo, rhs.span.hi, ExprInPlace(lhs, rhs)),
+                    self.mk_expr(lhs.span.lo, rhs.span.hi, ExprInPlace(lhs, rhs), None),
                 AssocOp::AssignOp(k) => {
                     let aop = match k {
                         token::Plus =>    BiAdd,
@@ -2738,7 +2856,7 @@ impl<'a> Parser<'a> {
                     };
                     let (lhs_span, rhs_span) = (lhs.span, rhs.span);
                     let aopexpr = self.mk_assign_op(codemap::respan(cur_op_span, aop), lhs, rhs);
-                    self.mk_expr(lhs_span.lo, rhs_span.hi, aopexpr)
+                    self.mk_expr(lhs_span.lo, rhs_span.hi, aopexpr, None)
                 }
                 AssocOp::As | AssocOp::DotDot => self.bug("As or DotDot branch reached")
             };
@@ -2769,15 +2887,20 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse prefix-forms of range notation: `..expr` and `..`
-    fn parse_prefix_range_expr(&mut self) -> PResult<P<Expr>> {
+    fn parse_prefix_range_expr(&mut self,
+                               already_parsed_attrs: Option<ThinAttributes>)
+                               -> PResult<P<Expr>> {
         debug_assert!(self.token == token::DotDot);
+        let attrs = try!(self.parse_or_use_outer_attributes(already_parsed_attrs));
         let lo = self.span.lo;
         let mut hi = self.span.hi;
         try!(self.bump());
         let opt_end = if self.is_at_start_of_range_notation_rhs() {
             // RHS must be parsed with more associativity than DotDot.
             let next_prec = AssocOp::from_token(&token::DotDot).unwrap().precedence() + 1;
-            Some(try!(self.parse_assoc_expr_with(next_prec, None).map(|x|{
+            Some(try!(self.parse_assoc_expr_with(next_prec,
+                                                 LhsExpr::NotYetParsed)
+            .map(|x|{
                 hi = x.span.hi;
                 x
             })))
@@ -2785,7 +2908,7 @@ impl<'a> Parser<'a> {
             None
         };
         let r = self.mk_range(None, opt_end);
-        Ok(self.mk_expr(lo, hi, r))
+        Ok(self.mk_expr(lo, hi, r, attrs))
     }
 
     fn is_at_start_of_range_notation_rhs(&self) -> bool {
@@ -2801,12 +2924,12 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse an 'if' or 'if let' expression ('if' token already eaten)
-    pub fn parse_if_expr(&mut self) -> PResult<P<Expr>> {
+    pub fn parse_if_expr(&mut self, attrs: ThinAttributes) -> PResult<P<Expr>> {
         if self.check_keyword(keywords::Let) {
-            return self.parse_if_let_expr();
+            return self.parse_if_let_expr(attrs);
         }
         let lo = self.last_span.lo;
-        let cond = try!(self.parse_expr_res(Restrictions::RESTRICTION_NO_STRUCT_LITERAL));
+        let cond = try!(self.parse_expr_res(Restrictions::RESTRICTION_NO_STRUCT_LITERAL, None));
         let thn = try!(self.parse_block());
         let mut els: Option<P<Expr>> = None;
         let mut hi = thn.span.hi;
@@ -2815,16 +2938,17 @@ impl<'a> Parser<'a> {
             hi = elexpr.span.hi;
             els = Some(elexpr);
         }
-        Ok(self.mk_expr(lo, hi, ExprIf(cond, thn, els)))
+        Ok(self.mk_expr(lo, hi, ExprIf(cond, thn, els), attrs))
     }
 
     /// Parse an 'if let' expression ('if' token already eaten)
-    pub fn parse_if_let_expr(&mut self) -> PResult<P<Expr>> {
+    pub fn parse_if_let_expr(&mut self, attrs: ThinAttributes)
+                             -> PResult<P<Expr>> {
         let lo = self.last_span.lo;
         try!(self.expect_keyword(keywords::Let));
         let pat = try!(self.parse_pat());
         try!(self.expect(&token::Eq));
-        let expr = try!(self.parse_expr_res(Restrictions::RESTRICTION_NO_STRUCT_LITERAL));
+        let expr = try!(self.parse_expr_res(Restrictions::RESTRICTION_NO_STRUCT_LITERAL, None));
         let thn = try!(self.parse_block());
         let (hi, els) = if try!(self.eat_keyword(keywords::Else) ){
             let expr = try!(self.parse_else_expr());
@@ -2832,11 +2956,13 @@ impl<'a> Parser<'a> {
         } else {
             (thn.span.hi, None)
         };
-        Ok(self.mk_expr(lo, hi, ExprIfLet(pat, expr, thn, els)))
+        Ok(self.mk_expr(lo, hi, ExprIfLet(pat, expr, thn, els), attrs))
     }
 
     // `|args| expr`
-    pub fn parse_lambda_expr(&mut self, lo: BytePos, capture_clause: CaptureClause)
+    pub fn parse_lambda_expr(&mut self, lo: BytePos,
+                             capture_clause: CaptureClause,
+                             attrs: ThinAttributes)
                              -> PResult<P<Expr>>
     {
         let decl = try!(self.parse_fn_block_decl());
@@ -2863,80 +2989,98 @@ impl<'a> Parser<'a> {
         Ok(self.mk_expr(
             lo,
             body.span.hi,
-            ExprClosure(capture_clause, decl, body)))
+            ExprClosure(capture_clause, decl, body), attrs))
     }
 
+    // `else` token already eaten
     pub fn parse_else_expr(&mut self) -> PResult<P<Expr>> {
         if try!(self.eat_keyword(keywords::If) ){
-            return self.parse_if_expr();
+            return self.parse_if_expr(None);
         } else {
             let blk = try!(self.parse_block());
-            return Ok(self.mk_expr(blk.span.lo, blk.span.hi, ExprBlock(blk)));
+            return Ok(self.mk_expr(blk.span.lo, blk.span.hi, ExprBlock(blk), None));
         }
     }
 
     /// Parse a 'for' .. 'in' expression ('for' token already eaten)
     pub fn parse_for_expr(&mut self, opt_ident: Option<ast::Ident>,
-                          span_lo: BytePos) -> PResult<P<Expr>> {
+                          span_lo: BytePos,
+                          attrs: ThinAttributes) -> PResult<P<Expr>> {
         // Parse: `for <src_pat> in <src_expr> <src_loop_block>`
 
         let pat = try!(self.parse_pat());
         try!(self.expect_keyword(keywords::In));
-        let expr = try!(self.parse_expr_res(Restrictions::RESTRICTION_NO_STRUCT_LITERAL));
-        let loop_block = try!(self.parse_block());
+        let expr = try!(self.parse_expr_res(Restrictions::RESTRICTION_NO_STRUCT_LITERAL, None));
+        let (iattrs, loop_block) = try!(self.parse_inner_attrs_and_block());
+        let attrs = attrs.append(iattrs.into_thin_attrs());
+
         let hi = self.last_span.hi;
 
-        Ok(self.mk_expr(span_lo, hi, ExprForLoop(pat, expr, loop_block, opt_ident)))
+        Ok(self.mk_expr(span_lo, hi,
+                        ExprForLoop(pat, expr, loop_block, opt_ident),
+                        attrs))
     }
 
     /// Parse a 'while' or 'while let' expression ('while' token already eaten)
     pub fn parse_while_expr(&mut self, opt_ident: Option<ast::Ident>,
-                            span_lo: BytePos) -> PResult<P<Expr>> {
+                            span_lo: BytePos,
+                            attrs: ThinAttributes) -> PResult<P<Expr>> {
         if self.token.is_keyword(keywords::Let) {
-            return self.parse_while_let_expr(opt_ident, span_lo);
+            return self.parse_while_let_expr(opt_ident, span_lo, attrs);
         }
-        let cond = try!(self.parse_expr_res(Restrictions::RESTRICTION_NO_STRUCT_LITERAL));
-        let body = try!(self.parse_block());
+        let cond = try!(self.parse_expr_res(Restrictions::RESTRICTION_NO_STRUCT_LITERAL, None));
+        let (iattrs, body) = try!(self.parse_inner_attrs_and_block());
+        let attrs = attrs.append(iattrs.into_thin_attrs());
         let hi = body.span.hi;
-        return Ok(self.mk_expr(span_lo, hi, ExprWhile(cond, body, opt_ident)));
+        return Ok(self.mk_expr(span_lo, hi, ExprWhile(cond, body, opt_ident),
+                               attrs));
     }
 
     /// Parse a 'while let' expression ('while' token already eaten)
     pub fn parse_while_let_expr(&mut self, opt_ident: Option<ast::Ident>,
-                                span_lo: BytePos) -> PResult<P<Expr>> {
+                                span_lo: BytePos,
+                                attrs: ThinAttributes) -> PResult<P<Expr>> {
         try!(self.expect_keyword(keywords::Let));
         let pat = try!(self.parse_pat());
         try!(self.expect(&token::Eq));
-        let expr = try!(self.parse_expr_res(Restrictions::RESTRICTION_NO_STRUCT_LITERAL));
-        let body = try!(self.parse_block());
+        let expr = try!(self.parse_expr_res(Restrictions::RESTRICTION_NO_STRUCT_LITERAL, None));
+        let (iattrs, body) = try!(self.parse_inner_attrs_and_block());
+        let attrs = attrs.append(iattrs.into_thin_attrs());
         let hi = body.span.hi;
-        return Ok(self.mk_expr(span_lo, hi, ExprWhileLet(pat, expr, body, opt_ident)));
+        return Ok(self.mk_expr(span_lo, hi, ExprWhileLet(pat, expr, body, opt_ident), attrs));
     }
 
+    // parse `loop {...}`, `loop` token already eaten
     pub fn parse_loop_expr(&mut self, opt_ident: Option<ast::Ident>,
-                           span_lo: BytePos) -> PResult<P<Expr>> {
-        let body = try!(self.parse_block());
+                           span_lo: BytePos,
+                           attrs: ThinAttributes) -> PResult<P<Expr>> {
+        let (iattrs, body) = try!(self.parse_inner_attrs_and_block());
+        let attrs = attrs.append(iattrs.into_thin_attrs());
         let hi = body.span.hi;
-        Ok(self.mk_expr(span_lo, hi, ExprLoop(body, opt_ident)))
+        Ok(self.mk_expr(span_lo, hi, ExprLoop(body, opt_ident), attrs))
     }
 
-    fn parse_match_expr(&mut self) -> PResult<P<Expr>> {
+    // `match` token already eaten
+    fn parse_match_expr(&mut self, attrs: ThinAttributes) -> PResult<P<Expr>> {
         let match_span = self.last_span;
         let lo = self.last_span.lo;
-        let discriminant = try!(self.parse_expr_res(Restrictions::RESTRICTION_NO_STRUCT_LITERAL));
+        let discriminant = try!(self.parse_expr_res(
+            Restrictions::RESTRICTION_NO_STRUCT_LITERAL, None));
         if let Err(e) = self.commit_expr_expecting(&*discriminant, token::OpenDelim(token::Brace)) {
             if self.token == token::Token::Semi {
                 self.span_note(match_span, "did you mean to remove this `match` keyword?");
             }
             return Err(e)
         }
+        let attrs = attrs.append(
+            try!(self.parse_inner_attributes()).into_thin_attrs());
         let mut arms: Vec<Arm> = Vec::new();
         while self.token != token::CloseDelim(token::Brace) {
             arms.push(try!(self.parse_arm()));
         }
         let hi = self.span.hi;
         try!(self.bump());
-        return Ok(self.mk_expr(lo, hi, ExprMatch(discriminant, arms)));
+        return Ok(self.mk_expr(lo, hi, ExprMatch(discriminant, arms), attrs));
     }
 
     pub fn parse_arm(&mut self) -> PResult<Arm> {
@@ -2949,7 +3093,7 @@ impl<'a> Parser<'a> {
             guard = Some(try!(self.parse_expr()));
         }
         try!(self.expect(&token::FatArrow));
-        let expr = try!(self.parse_expr_res(Restrictions::RESTRICTION_STMT_EXPR));
+        let expr = try!(self.parse_expr_res(Restrictions::RESTRICTION_STMT_EXPR, None));
 
         let require_comma =
             !classify::expr_is_simple_block(&*expr)
@@ -2971,7 +3115,7 @@ impl<'a> Parser<'a> {
 
     /// Parse an expression
     pub fn parse_expr(&mut self) -> PResult<P<Expr>> {
-        self.parse_expr_res(Restrictions::empty())
+        self.parse_expr_res(Restrictions::empty(), None)
     }
 
     /// Evaluate the closure with restrictions in place.
@@ -2988,8 +3132,10 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse an expression, subject to the given restrictions
-    pub fn parse_expr_res(&mut self, r: Restrictions) -> PResult<P<Expr>> {
-        self.with_res(r, |this| this.parse_assoc_expr())
+    pub fn parse_expr_res(&mut self, r: Restrictions,
+                          already_parsed_attrs: Option<ThinAttributes>)
+                          -> PResult<P<Expr>> {
+        self.with_res(r, |this| this.parse_assoc_expr(already_parsed_attrs))
     }
 
     /// Parse the RHS of a local variable declaration (e.g. '= 14;')
@@ -3173,9 +3319,9 @@ impl<'a> Parser<'a> {
                 (None, try!(self.parse_path(LifetimeAndTypesWithColons)))
             };
             let hi = self.last_span.hi;
-            Ok(self.mk_expr(lo, hi, ExprPath(qself, path)))
+            Ok(self.mk_expr(lo, hi, ExprPath(qself, path), None))
         } else {
-            self.parse_literal_maybe_minus()
+            self.parse_pat_literal_maybe_minus()
         }
     }
 
@@ -3275,7 +3421,7 @@ impl<'a> Parser<'a> {
                       token::DotDotDot => {
                         // Parse range
                         let hi = self.last_span.hi;
-                        let begin = self.mk_expr(lo, hi, ExprPath(qself, path));
+                        let begin = self.mk_expr(lo, hi, ExprPath(qself, path), None);
                         try!(self.bump());
                         let end = try!(self.parse_pat_range_end());
                         pat = PatRange(begin, end);
@@ -3322,7 +3468,7 @@ impl<'a> Parser<'a> {
                 }
             } else {
                 // Try to parse everything else as literal with optional minus
-                let begin = try!(self.parse_literal_maybe_minus());
+                let begin = try!(self.parse_pat_literal_maybe_minus());
                 if try!(self.eat(&token::DotDotDot)) {
                     let end = try!(self.parse_pat_range_end());
                     pat = PatRange(begin, end);
@@ -3379,7 +3525,7 @@ impl<'a> Parser<'a> {
     }
 
     /// Parse a local variable declaration
-    fn parse_local(&mut self) -> PResult<P<Local>> {
+    fn parse_local(&mut self, attrs: ThinAttributes) -> PResult<P<Local>> {
         let lo = self.span.lo;
         let pat = try!(self.parse_pat());
 
@@ -3394,13 +3540,14 @@ impl<'a> Parser<'a> {
             init: init,
             id: ast::DUMMY_NODE_ID,
             span: mk_sp(lo, self.last_span.hi),
+            attrs: attrs,
         }))
     }
 
     /// Parse a "let" stmt
-    fn parse_let(&mut self) -> PResult<P<Decl>> {
+    fn parse_let(&mut self, attrs: ThinAttributes) -> PResult<P<Decl>> {
         let lo = self.span.lo;
-        let local = try!(self.parse_local());
+        let local = try!(self.parse_local(attrs));
         Ok(P(spanned(lo, self.last_span.hi, DeclLocal(local))))
     }
 
@@ -3445,28 +3592,20 @@ impl<'a> Parser<'a> {
     fn parse_stmt_(&mut self) -> PResult<Option<Stmt>> {
         maybe_whole!(Some deref self, NtStmt);
 
-        fn check_expected_item(p: &mut Parser, attrs: &[Attribute]) {
-            // If we have attributes then we should have an item
-            if !attrs.is_empty() {
-                p.expected_item_err(attrs);
-            }
-        }
-
         let attrs = try!(self.parse_outer_attributes());
         let lo = self.span.lo;
 
         Ok(Some(if self.check_keyword(keywords::Let) {
-            check_expected_item(self, &attrs);
             try!(self.expect_keyword(keywords::Let));
-            let decl = try!(self.parse_let());
-            spanned(lo, decl.span.hi, StmtDecl(decl, ast::DUMMY_NODE_ID))
+            let decl = try!(self.parse_let(attrs.into_thin_attrs()));
+            let hi = decl.span.hi;
+            let stmt = StmtDecl(decl, ast::DUMMY_NODE_ID);
+            spanned(lo, hi, stmt)
         } else if self.token.is_ident()
             && !self.token.is_any_keyword()
             && self.look_ahead(1, |t| *t == token::Not) {
             // it's a macro invocation:
 
-            check_expected_item(self, &attrs);
-
             // Potential trouble: if we allow macros with paths instead of
             // idents, we'd need to look ahead past the whole path here...
             let pth = try!(self.parse_path(NoTypesAllowed));
@@ -3512,11 +3651,12 @@ impl<'a> Parser<'a> {
             };
 
             if id.name == token::special_idents::invalid.name {
-                spanned(lo, hi,
-                        StmtMac(P(spanned(lo,
-                                          hi,
-                                          Mac_ { path: pth, tts: tts, ctxt: EMPTY_CTXT })),
-                                  style))
+                let stmt = StmtMac(P(spanned(lo,
+                                             hi,
+                                             Mac_ { path: pth, tts: tts, ctxt: EMPTY_CTXT })),
+                                   style,
+                                   attrs.into_thin_attrs());
+                spanned(lo, hi, stmt)
             } else {
                 // if it has a special ident, it's definitely an item
                 //
@@ -3536,30 +3676,43 @@ impl<'a> Parser<'a> {
                             lo, hi, id /*id is good here*/,
                             ItemMac(spanned(lo, hi,
                                             Mac_ { path: pth, tts: tts, ctxt: EMPTY_CTXT })),
-                            Inherited, Vec::new(/*no attrs*/))))),
+                            Inherited, attrs)))),
                     ast::DUMMY_NODE_ID))
             }
         } else {
-            match try!(self.parse_item_(attrs, false)) {
+            // FIXME: Bad copy of attrs
+            match try!(self.parse_item_(attrs.clone(), false, true)) {
                 Some(i) => {
                     let hi = i.span.hi;
                     let decl = P(spanned(lo, hi, DeclItem(i)));
                     spanned(lo, hi, StmtDecl(decl, ast::DUMMY_NODE_ID))
                 }
                 None => {
+                    let unused_attrs = |attrs: &[_], s: &mut Self| {
+                        if attrs.len() > 0 {
+                            s.span_err(s.span,
+                                "expected statement after outer attribute");
+                        }
+                    };
+
                     // Do not attempt to parse an expression if we're done here.
                     if self.token == token::Semi {
+                        unused_attrs(&attrs, self);
                         try!(self.bump());
                         return Ok(None);
                     }
 
                     if self.token == token::CloseDelim(token::Brace) {
+                        unused_attrs(&attrs, self);
                         return Ok(None);
                     }
 
                     // Remainder are line-expr stmts.
-                    let e = try!(self.parse_expr_res(Restrictions::RESTRICTION_STMT_EXPR));
-                    spanned(lo, e.span.hi, StmtExpr(e, ast::DUMMY_NODE_ID))
+                    let e = try!(self.parse_expr_res(
+                        Restrictions::RESTRICTION_STMT_EXPR, Some(attrs.into_thin_attrs())));
+                    let hi = e.span.hi;
+                    let stmt = StmtExpr(e, ast::DUMMY_NODE_ID);
+                    spanned(lo, hi, stmt)
                 }
             }
         }))
@@ -3615,22 +3768,23 @@ impl<'a> Parser<'a> {
                 StmtExpr(e, _) => {
                     try!(self.handle_expression_like_statement(e, span, &mut stmts, &mut expr));
                 }
-                StmtMac(mac, MacStmtWithoutBraces) => {
+                StmtMac(mac, MacStmtWithoutBraces, attrs) => {
                     // statement macro without braces; might be an
                     // expr depending on whether a semicolon follows
                     match self.token {
                         token::Semi => {
                             stmts.push(P(Spanned {
-                                node: StmtMac(mac, MacStmtWithSemicolon),
+                                node: StmtMac(mac, MacStmtWithSemicolon, attrs),
                                 span: mk_sp(span.lo, self.span.hi),
                             }));
                             try!(self.bump());
                         }
                         _ => {
                             let e = self.mk_mac_expr(span.lo, span.hi,
-                                                     mac.and_then(|m| m.node));
-                            let e = try!(self.parse_dot_or_call_expr_with(e));
-                            let e = try!(self.parse_assoc_expr_with(0, Some(e)));
+                                                     mac.and_then(|m| m.node),
+                                                     None);
+                            let e = try!(self.parse_dot_or_call_expr_with(e, attrs));
+                            let e = try!(self.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(e)));
                             try!(self.handle_expression_like_statement(
                                 e,
                                 span,
@@ -3639,12 +3793,12 @@ impl<'a> Parser<'a> {
                         }
                     }
                 }
-                StmtMac(m, style) => {
+                StmtMac(m, style, attrs) => {
                     // statement macro; might be an expr
                     match self.token {
                         token::Semi => {
                             stmts.push(P(Spanned {
-                                node: StmtMac(m, MacStmtWithSemicolon),
+                                node: StmtMac(m, MacStmtWithSemicolon, attrs),
                                 span: mk_sp(span.lo, self.span.hi),
                             }));
                             try!(self.bump());
@@ -3653,11 +3807,12 @@ impl<'a> Parser<'a> {
                             // if a block ends in `m!(arg)` without
                             // a `;`, it must be an expr
                             expr = Some(self.mk_mac_expr(span.lo, span.hi,
-                                                         m.and_then(|x| x.node)));
+                                                         m.and_then(|x| x.node),
+                                                         attrs));
                         }
                         _ => {
                             stmts.push(P(Spanned {
-                                node: StmtMac(m, style),
+                                node: StmtMac(m, style, attrs),
                                 span: span
                             }));
                         }
@@ -5212,7 +5367,7 @@ impl<'a> Parser<'a> {
     /// NB: this function no longer parses the items inside an
     /// extern crate.
     fn parse_item_(&mut self, attrs: Vec<Attribute>,
-                   macros_allowed: bool) -> PResult<Option<P<Item>>> {
+                   macros_allowed: bool, attributes_allowed: bool) -> PResult<Option<P<Item>>> {
         let nt_item = match self.token {
             token::Interpolated(token::NtItem(ref item)) => {
                 Some((**item).clone())
@@ -5470,7 +5625,7 @@ impl<'a> Parser<'a> {
                                     maybe_append(attrs, extra_attrs));
             return Ok(Some(item));
         }
-        self.parse_macro_use_or_failure(attrs,macros_allowed,lo,visibility)
+        self.parse_macro_use_or_failure(attrs,macros_allowed,attributes_allowed,lo,visibility)
     }
 
     /// Parse a foreign item.
@@ -5489,7 +5644,7 @@ impl<'a> Parser<'a> {
         }
 
         // FIXME #5668: this will occur for a macro invocation:
-        match try!(self.parse_macro_use_or_failure(attrs, true, lo, visibility)) {
+        match try!(self.parse_macro_use_or_failure(attrs, true, false, lo, visibility)) {
             Some(item) => {
                 return Err(self.span_fatal(item.span, "macros cannot expand to foreign items"));
             }
@@ -5502,6 +5657,7 @@ impl<'a> Parser<'a> {
         &mut self,
         attrs: Vec<Attribute> ,
         macros_allowed: bool,
+        attributes_allowed: bool,
         lo: BytePos,
         visibility: Visibility
     ) -> PResult<Option<P<Item>>> {
@@ -5570,7 +5726,7 @@ impl<'a> Parser<'a> {
             }
         }
 
-        if !attrs.is_empty() {
+        if !attributes_allowed && !attrs.is_empty() {
             self.expected_item_err(&attrs);
         }
         Ok(None)
@@ -5578,7 +5734,7 @@ impl<'a> Parser<'a> {
 
     pub fn parse_item(&mut self) -> PResult<Option<P<Item>>> {
         let attrs = try!(self.parse_outer_attributes());
-        self.parse_item_(attrs, true)
+        self.parse_item_(attrs, true, false)
     }
 
 
diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs
index 5e4449af604..17b7d8dbaec 100644
--- a/src/libsyntax/parse/token.rs
+++ b/src/libsyntax/parse/token.rs
@@ -202,6 +202,7 @@ impl Token {
             Interpolated(NtIdent(..))   => true,
             Interpolated(NtBlock(..))   => true,
             Interpolated(NtPath(..))    => true,
+            Pound                       => true, // for expression attributes
             _                           => false,
         }
     }
diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs
index 6de86de9c54..aa55cb847fa 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -13,6 +13,8 @@ pub use self::AnnNode::*;
 use abi;
 use ast::{self, TokenTree};
 use ast::{RegionTyParamBound, TraitTyParamBound, TraitBoundModifier};
+use ast::Attribute;
+use attr::ThinAttributesExt;
 use ast_util;
 use util::parser::AssocOp;
 use attr;
@@ -77,7 +79,7 @@ pub fn rust_printer<'a>(writer: Box<Write+'a>) -> State<'a> {
 pub fn rust_printer_annotated<'a>(writer: Box<Write+'a>,
                                   ann: &'a PpAnn) -> State<'a> {
     State {
-        s: pp::mk_printer(writer, default_columns),
+        s: pp::mk_printer(writer, DEFAULT_COLUMNS),
         cm: None,
         comments: None,
         literals: None,
@@ -90,11 +92,9 @@ pub fn rust_printer_annotated<'a>(writer: Box<Write+'a>,
     }
 }
 
-#[allow(non_upper_case_globals)]
-pub const indent_unit: usize = 4;
+pub const INDENT_UNIT: usize = 4;
 
-#[allow(non_upper_case_globals)]
-pub const default_columns: usize = 78;
+pub const DEFAULT_COLUMNS: usize = 78;
 
 /// Requires you to pass an input filename and reader so that
 /// it can scan the input text for comments and literals to
@@ -170,7 +170,7 @@ impl<'a> State<'a> {
                comments: Option<Vec<comments::Comment>>,
                literals: Option<Vec<comments::Literal>>) -> State<'a> {
         State {
-            s: pp::mk_printer(out, default_columns),
+            s: pp::mk_printer(out, DEFAULT_COLUMNS),
             cm: Some(cm),
             comments: comments,
             literals: literals,
@@ -401,7 +401,7 @@ pub fn fun_to_string(decl: &ast::FnDecl,
 pub fn block_to_string(blk: &ast::Block) -> String {
     to_string(|s| {
         // containing cbox, will be closed by print-block at }
-        try!(s.cbox(indent_unit));
+        try!(s.cbox(INDENT_UNIT));
         // head-ibox, will be closed by print-block after {
         try!(s.ibox(0));
         s.print_block(blk)
@@ -707,43 +707,61 @@ pub trait PrintState<'a> {
     }
 
     fn print_inner_attributes(&mut self,
-                                  attrs: &[ast::Attribute]) -> io::Result<()> {
-        let mut count = 0;
-        for attr in attrs {
-            match attr.node.style {
-                ast::AttrStyle::Inner => {
-                    try!(self.print_attribute(attr));
-                    count += 1;
-                }
-                _ => {/* fallthrough */ }
-            }
-        }
-        if count > 0 {
-            try!(self.hardbreak_if_not_bol());
-        }
-        Ok(())
+                              attrs: &[ast::Attribute]) -> io::Result<()> {
+        self.print_either_attributes(attrs, ast::AttrStyle::Inner, false, true)
+    }
+
+    fn print_inner_attributes_no_trailing_hardbreak(&mut self,
+                                                   attrs: &[ast::Attribute])
+                                                   -> io::Result<()> {
+        self.print_either_attributes(attrs, ast::AttrStyle::Inner, false, false)
     }
 
     fn print_outer_attributes(&mut self,
                               attrs: &[ast::Attribute]) -> io::Result<()> {
+        self.print_either_attributes(attrs, ast::AttrStyle::Outer, false, true)
+    }
+
+    fn print_inner_attributes_inline(&mut self,
+                                     attrs: &[ast::Attribute]) -> io::Result<()> {
+        self.print_either_attributes(attrs, ast::AttrStyle::Inner, true, true)
+    }
+
+    fn print_outer_attributes_inline(&mut self,
+                                     attrs: &[ast::Attribute]) -> io::Result<()> {
+        self.print_either_attributes(attrs, ast::AttrStyle::Outer, true, true)
+    }
+
+    fn print_either_attributes(&mut self,
+                              attrs: &[ast::Attribute],
+                              kind: ast::AttrStyle,
+                              is_inline: bool,
+                              trailing_hardbreak: bool) -> io::Result<()> {
         let mut count = 0;
         for attr in attrs {
-            match attr.node.style {
-                ast::AttrStyle::Outer => {
-                    try!(self.print_attribute(attr));
+            if attr.node.style == kind {
+                    try!(self.print_attribute_inline(attr, is_inline));
+                    if is_inline {
+                        try!(self.nbsp());
+                    }
                     count += 1;
-                }
-                _ => {/* fallthrough */ }
             }
         }
-        if count > 0 {
+        if count > 0 && trailing_hardbreak && !is_inline {
             try!(self.hardbreak_if_not_bol());
         }
         Ok(())
     }
 
     fn print_attribute(&mut self, attr: &ast::Attribute) -> io::Result<()> {
-        try!(self.hardbreak_if_not_bol());
+        self.print_attribute_inline(attr, false)
+    }
+
+    fn print_attribute_inline(&mut self, attr: &ast::Attribute,
+                              is_inline: bool) -> io::Result<()> {
+        if !is_inline {
+            try!(self.hardbreak_if_not_bol());
+        }
         try!(self.maybe_print_comment(attr.span.lo));
         if attr.node.is_sugared_doc {
             word(self.writer(), &attr.value_str().unwrap())
@@ -758,7 +776,7 @@ pub trait PrintState<'a> {
     }
 
     fn print_meta_item(&mut self, item: &ast::MetaItem) -> io::Result<()> {
-        try!(self.ibox(indent_unit));
+        try!(self.ibox(INDENT_UNIT));
         match item.node {
             ast::MetaWord(ref name) => {
                 try!(word(self.writer(), &name));
@@ -779,6 +797,13 @@ pub trait PrintState<'a> {
         }
         self.end()
     }
+
+    fn space_if_not_bol(&mut self) -> io::Result<()> {
+        if !self.is_bol() { try!(space(self.writer())); }
+        Ok(())
+    }
+
+    fn nbsp(&mut self) -> io::Result<()> { word(self.writer(), " ") }
 }
 
 impl<'a> PrintState<'a> for State<'a> {
@@ -809,8 +834,6 @@ impl<'a> State<'a> {
         pp::cbox(&mut self.s, u)
     }
 
-    pub fn nbsp(&mut self) -> io::Result<()> { word(&mut self.s, " ") }
-
     pub fn word_nbsp(&mut self, w: &str) -> io::Result<()> {
         try!(word(&mut self.s, w));
         self.nbsp()
@@ -818,7 +841,7 @@ impl<'a> State<'a> {
 
     pub fn head(&mut self, w: &str) -> io::Result<()> {
         // outer-box is consistent
-        try!(self.cbox(indent_unit));
+        try!(self.cbox(INDENT_UNIT));
         // head-box is inconsistent
         try!(self.ibox(w.len() + 1));
         // keyword that starts the head
@@ -848,7 +871,7 @@ impl<'a> State<'a> {
         Ok(())
     }
     pub fn bclose(&mut self, span: codemap::Span) -> io::Result<()> {
-        self.bclose_(span, indent_unit)
+        self.bclose_(span, INDENT_UNIT)
     }
 
     pub fn in_cbox(&self) -> bool {
@@ -858,10 +881,6 @@ impl<'a> State<'a> {
         }
     }
 
-    pub fn space_if_not_bol(&mut self) -> io::Result<()> {
-        if !self.is_bol() { try!(space(&mut self.s)); }
-        Ok(())
-    }
     pub fn break_offset_if_not_bol(&mut self, n: usize,
                                    off: isize) -> io::Result<()> {
         if !self.is_bol() {
@@ -1200,7 +1219,7 @@ impl<'a> State<'a> {
                 try!(self.bclose(item.span));
             }
             ast::ItemTy(ref ty, ref params) => {
-                try!(self.ibox(indent_unit));
+                try!(self.ibox(INDENT_UNIT));
                 try!(self.ibox(0));
                 try!(self.word_nbsp(&visibility_qualified(item.vis, "type")));
                 try!(self.print_ident(item.ident));
@@ -1314,7 +1333,7 @@ impl<'a> State<'a> {
                 try!(self.print_path(&node.path, false, 0));
                 try!(word(&mut self.s, "! "));
                 try!(self.print_ident(item.ident));
-                try!(self.cbox(indent_unit));
+                try!(self.cbox(INDENT_UNIT));
                 try!(self.popen());
                 try!(self.print_tts(&node.tts[..]));
                 try!(self.pclose());
@@ -1370,7 +1389,7 @@ impl<'a> State<'a> {
             try!(self.space_if_not_bol());
             try!(self.maybe_print_comment(v.span.lo));
             try!(self.print_outer_attributes(&v.node.attrs));
-            try!(self.ibox(indent_unit));
+            try!(self.ibox(INDENT_UNIT));
             try!(self.print_variant(&**v));
             try!(word(&mut self.s, ","));
             try!(self.end());
@@ -1592,7 +1611,7 @@ impl<'a> State<'a> {
                 // code copied from ItemMac:
                 try!(self.print_path(&node.path, false, 0));
                 try!(word(&mut self.s, "! "));
-                try!(self.cbox(indent_unit));
+                try!(self.cbox(INDENT_UNIT));
                 try!(self.popen());
                 try!(self.print_tts(&node.tts[..]));
                 try!(self.pclose());
@@ -1611,15 +1630,16 @@ impl<'a> State<'a> {
             }
             ast::StmtExpr(ref expr, _) => {
                 try!(self.space_if_not_bol());
-                try!(self.print_expr(&**expr));
+                try!(self.print_expr_outer_attr_style(&**expr, false));
             }
             ast::StmtSemi(ref expr, _) => {
                 try!(self.space_if_not_bol());
-                try!(self.print_expr(&**expr));
+                try!(self.print_expr_outer_attr_style(&**expr, false));
                 try!(word(&mut self.s, ";"));
             }
-            ast::StmtMac(ref mac, style) => {
+            ast::StmtMac(ref mac, style, ref attrs) => {
                 try!(self.space_if_not_bol());
+                try!(self.print_outer_attributes(attrs.as_attr_slice()));
                 let delim = match style {
                     ast::MacStmtWithBraces => token::Brace,
                     _ => token::Paren
@@ -1642,7 +1662,13 @@ impl<'a> State<'a> {
     }
 
     pub fn print_block_unclosed(&mut self, blk: &ast::Block) -> io::Result<()> {
-        self.print_block_unclosed_indent(blk, indent_unit)
+        self.print_block_unclosed_indent(blk, INDENT_UNIT)
+    }
+
+    pub fn print_block_unclosed_with_attrs(&mut self, blk: &ast::Block,
+                                            attrs: &[ast::Attribute])
+                                           -> io::Result<()> {
+        self.print_block_maybe_unclosed(blk, INDENT_UNIT, attrs, false)
     }
 
     pub fn print_block_unclosed_indent(&mut self, blk: &ast::Block,
@@ -1653,7 +1679,7 @@ impl<'a> State<'a> {
     pub fn print_block_with_attrs(&mut self,
                                   blk: &ast::Block,
                                   attrs: &[ast::Attribute]) -> io::Result<()> {
-        self.print_block_maybe_unclosed(blk, indent_unit, attrs, true)
+        self.print_block_maybe_unclosed(blk, INDENT_UNIT, attrs, true)
     }
 
     pub fn print_block_maybe_unclosed(&mut self,
@@ -1677,7 +1703,7 @@ impl<'a> State<'a> {
         match blk.expr {
             Some(ref expr) => {
                 try!(self.space_if_not_bol());
-                try!(self.print_expr(&**expr));
+                try!(self.print_expr_outer_attr_style(&**expr, false));
                 try!(self.maybe_print_trailing_comment(expr.span, Some(blk.span.hi)));
             }
             _ => ()
@@ -1692,7 +1718,7 @@ impl<'a> State<'a> {
                 match _else.node {
                     // "another else-if"
                     ast::ExprIf(ref i, ref then, ref e) => {
-                        try!(self.cbox(indent_unit - 1));
+                        try!(self.cbox(INDENT_UNIT - 1));
                         try!(self.ibox(0));
                         try!(word(&mut self.s, " else if "));
                         try!(self.print_expr(&**i));
@@ -1702,7 +1728,7 @@ impl<'a> State<'a> {
                     }
                     // "another else-if-let"
                     ast::ExprIfLet(ref pat, ref expr, ref then, ref e) => {
-                        try!(self.cbox(indent_unit - 1));
+                        try!(self.cbox(INDENT_UNIT - 1));
                         try!(self.ibox(0));
                         try!(word(&mut self.s, " else if let "));
                         try!(self.print_pat(&**pat));
@@ -1715,7 +1741,7 @@ impl<'a> State<'a> {
                     }
                     // "final else"
                     ast::ExprBlock(ref b) => {
-                        try!(self.cbox(indent_unit - 1));
+                        try!(self.cbox(INDENT_UNIT - 1));
                         try!(self.ibox(0));
                         try!(word(&mut self.s, " else "));
                         self.print_block(&**b)
@@ -1758,7 +1784,13 @@ impl<'a> State<'a> {
         match delim {
             token::Paren => try!(self.popen()),
             token::Bracket => try!(word(&mut self.s, "[")),
-            token::Brace => try!(self.bopen()),
+            token::Brace => {
+                // head-ibox, will be closed by bopen()
+                try!(self.ibox(0));
+                // Don't ask me why the regular bopen() does
+                // more then just opening a brace...
+                try!(self.bopen())
+            }
         }
         try!(self.print_tts(&m.node.tts));
         match delim {
@@ -1811,9 +1843,11 @@ impl<'a> State<'a> {
         self.print_expr_maybe_paren(expr)
     }
 
-    fn print_expr_vec(&mut self, exprs: &[P<ast::Expr>]) -> io::Result<()> {
-        try!(self.ibox(indent_unit));
+    fn print_expr_vec(&mut self, exprs: &[P<ast::Expr>],
+                      attrs: &[Attribute]) -> io::Result<()> {
+        try!(self.ibox(INDENT_UNIT));
         try!(word(&mut self.s, "["));
+        try!(self.print_inner_attributes_inline(attrs));
         try!(self.commasep_exprs(Inconsistent, &exprs[..]));
         try!(word(&mut self.s, "]"));
         self.end()
@@ -1821,9 +1855,11 @@ impl<'a> State<'a> {
 
     fn print_expr_repeat(&mut self,
                          element: &ast::Expr,
-                         count: &ast::Expr) -> io::Result<()> {
-        try!(self.ibox(indent_unit));
+                         count: &ast::Expr,
+                         attrs: &[Attribute]) -> io::Result<()> {
+        try!(self.ibox(INDENT_UNIT));
         try!(word(&mut self.s, "["));
+        try!(self.print_inner_attributes_inline(attrs));
         try!(self.print_expr(element));
         try!(self.word_space(";"));
         try!(self.print_expr(count));
@@ -1834,14 +1870,16 @@ impl<'a> State<'a> {
     fn print_expr_struct(&mut self,
                          path: &ast::Path,
                          fields: &[ast::Field],
-                         wth: &Option<P<ast::Expr>>) -> io::Result<()> {
+                         wth: &Option<P<ast::Expr>>,
+                         attrs: &[Attribute]) -> io::Result<()> {
         try!(self.print_path(path, true, 0));
         try!(word(&mut self.s, "{"));
+        try!(self.print_inner_attributes_inline(attrs));
         try!(self.commasep_cmnt(
             Consistent,
             &fields[..],
             |s, field| {
-                try!(s.ibox(indent_unit));
+                try!(s.ibox(INDENT_UNIT));
                 try!(s.print_ident(field.ident.node));
                 try!(s.word_space(":"));
                 try!(s.print_expr(&*field.expr));
@@ -1850,7 +1888,7 @@ impl<'a> State<'a> {
             |f| f.span));
         match *wth {
             Some(ref expr) => {
-                try!(self.ibox(indent_unit));
+                try!(self.ibox(INDENT_UNIT));
                 if !fields.is_empty() {
                     try!(word(&mut self.s, ","));
                     try!(space(&mut self.s));
@@ -1867,8 +1905,10 @@ impl<'a> State<'a> {
         Ok(())
     }
 
-    fn print_expr_tup(&mut self, exprs: &[P<ast::Expr>]) -> io::Result<()> {
+    fn print_expr_tup(&mut self, exprs: &[P<ast::Expr>],
+                      attrs: &[Attribute]) -> io::Result<()> {
         try!(self.popen());
+        try!(self.print_inner_attributes_inline(attrs));
         try!(self.commasep_exprs(Inconsistent, &exprs[..]));
         if exprs.len() == 1 {
             try!(word(&mut self.s, ","));
@@ -1934,8 +1974,22 @@ impl<'a> State<'a> {
     }
 
     pub fn print_expr(&mut self, expr: &ast::Expr) -> io::Result<()> {
+        self.print_expr_outer_attr_style(expr, true)
+    }
+
+    fn print_expr_outer_attr_style(&mut self,
+                                  expr: &ast::Expr,
+                                  is_inline: bool) -> io::Result<()> {
         try!(self.maybe_print_comment(expr.span.lo));
-        try!(self.ibox(indent_unit));
+
+        let attrs = expr.attrs.as_attr_slice();
+        if is_inline {
+            try!(self.print_outer_attributes_inline(attrs));
+        } else {
+            try!(self.print_outer_attributes(attrs));
+        }
+
+        try!(self.ibox(INDENT_UNIT));
         try!(self.ann.pre(self, NodeExpr(expr)));
         match expr.node {
             ast::ExprBox(ref expr) => {
@@ -1946,16 +2000,16 @@ impl<'a> State<'a> {
                 try!(self.print_expr_in_place(place, expr));
             }
             ast::ExprVec(ref exprs) => {
-                try!(self.print_expr_vec(&exprs[..]));
+                try!(self.print_expr_vec(&exprs[..], attrs));
             }
             ast::ExprRepeat(ref element, ref count) => {
-                try!(self.print_expr_repeat(&**element, &**count));
+                try!(self.print_expr_repeat(&**element, &**count, attrs));
             }
             ast::ExprStruct(ref path, ref fields, ref wth) => {
-                try!(self.print_expr_struct(path, &fields[..], wth));
+                try!(self.print_expr_struct(path, &fields[..], wth, attrs));
             }
             ast::ExprTup(ref exprs) => {
-                try!(self.print_expr_tup(&exprs[..]));
+                try!(self.print_expr_tup(&exprs[..], attrs));
             }
             ast::ExprCall(ref func, ref args) => {
                 try!(self.print_expr_call(&**func, &args[..]));
@@ -1999,7 +2053,7 @@ impl<'a> State<'a> {
                 try!(self.head("while"));
                 try!(self.print_expr(&**test));
                 try!(space(&mut self.s));
-                try!(self.print_block(&**blk));
+                try!(self.print_block_with_attrs(&**blk, attrs));
             }
             ast::ExprWhileLet(ref pat, ref expr, ref blk, opt_ident) => {
                 if let Some(ident) = opt_ident {
@@ -2012,7 +2066,7 @@ impl<'a> State<'a> {
                 try!(self.word_space("="));
                 try!(self.print_expr(&**expr));
                 try!(space(&mut self.s));
-                try!(self.print_block(&**blk));
+                try!(self.print_block_with_attrs(&**blk, attrs));
             }
             ast::ExprForLoop(ref pat, ref iter, ref blk, opt_ident) => {
                 if let Some(ident) = opt_ident {
@@ -2025,7 +2079,7 @@ impl<'a> State<'a> {
                 try!(self.word_space("in"));
                 try!(self.print_expr(&**iter));
                 try!(space(&mut self.s));
-                try!(self.print_block(&**blk));
+                try!(self.print_block_with_attrs(&**blk, attrs));
             }
             ast::ExprLoop(ref blk, opt_ident) => {
                 if let Some(ident) = opt_ident {
@@ -2034,19 +2088,20 @@ impl<'a> State<'a> {
                 }
                 try!(self.head("loop"));
                 try!(space(&mut self.s));
-                try!(self.print_block(&**blk));
+                try!(self.print_block_with_attrs(&**blk, attrs));
             }
             ast::ExprMatch(ref expr, ref arms) => {
-                try!(self.cbox(indent_unit));
+                try!(self.cbox(INDENT_UNIT));
                 try!(self.ibox(4));
                 try!(self.word_nbsp("match"));
                 try!(self.print_expr(&**expr));
                 try!(space(&mut self.s));
                 try!(self.bopen());
+                try!(self.print_inner_attributes_no_trailing_hardbreak(attrs));
                 for arm in arms {
                     try!(self.print_arm(arm));
                 }
-                try!(self.bclose_(expr.span, indent_unit));
+                try!(self.bclose_(expr.span, INDENT_UNIT));
             }
             ast::ExprClosure(capture_clause, ref decl, ref body) => {
                 try!(self.print_capture_clause(capture_clause));
@@ -2063,13 +2118,16 @@ impl<'a> State<'a> {
                     try!(self.print_block_unclosed(&**body));
                 } else {
                     // we extract the block, so as not to create another set of boxes
-                    match body.expr.as_ref().unwrap().node {
+                    let i_expr = body.expr.as_ref().unwrap();
+                    match i_expr.node {
                         ast::ExprBlock(ref blk) => {
-                            try!(self.print_block_unclosed(&**blk));
+                            try!(self.print_block_unclosed_with_attrs(
+                                &**blk,
+                                i_expr.attrs.as_attr_slice()));
                         }
                         _ => {
                             // this is a bare expression
-                            try!(self.print_expr(body.expr.as_ref().map(|e| &**e).unwrap()));
+                            try!(self.print_expr(&**i_expr));
                             try!(self.end()); // need to close a box
                         }
                     }
@@ -2081,10 +2139,10 @@ impl<'a> State<'a> {
             }
             ast::ExprBlock(ref blk) => {
                 // containing cbox, will be closed by print-block at }
-                try!(self.cbox(indent_unit));
+                try!(self.cbox(INDENT_UNIT));
                 // head-box, will be closed by print-block after {
                 try!(self.ibox(0));
-                try!(self.print_block(&**blk));
+                try!(self.print_block_with_attrs(&**blk, attrs));
             }
             ast::ExprAssign(ref lhs, ref rhs) => {
                 try!(self.print_expr(&**lhs));
@@ -2222,6 +2280,7 @@ impl<'a> State<'a> {
             ast::ExprMac(ref m) => try!(self.print_mac(m, token::Paren)),
             ast::ExprParen(ref e) => {
                 try!(self.popen());
+                try!(self.print_inner_attributes_inline(attrs));
                 try!(self.print_expr(&**e));
                 try!(self.pclose());
             }
@@ -2243,11 +2302,12 @@ impl<'a> State<'a> {
         try!(self.maybe_print_comment(decl.span.lo));
         match decl.node {
             ast::DeclLocal(ref loc) => {
+                try!(self.print_outer_attributes(loc.attrs.as_attr_slice()));
                 try!(self.space_if_not_bol());
-                try!(self.ibox(indent_unit));
+                try!(self.ibox(INDENT_UNIT));
                 try!(self.word_nbsp("let"));
 
-                try!(self.ibox(indent_unit));
+                try!(self.ibox(INDENT_UNIT));
                 try!(self.print_local_decl(&**loc));
                 try!(self.end());
                 if let Some(ref init) = loc.init {
@@ -2452,7 +2512,7 @@ impl<'a> State<'a> {
                 try!(self.commasep_cmnt(
                     Consistent, &fields[..],
                     |s, f| {
-                        try!(s.cbox(indent_unit));
+                        try!(s.cbox(INDENT_UNIT));
                         if !f.node.is_shorthand {
                             try!(s.print_ident(f.node.ident));
                             try!(s.word_nbsp(":"));
@@ -2525,7 +2585,7 @@ impl<'a> State<'a> {
         if arm.attrs.is_empty() {
             try!(space(&mut self.s));
         }
-        try!(self.cbox(indent_unit));
+        try!(self.cbox(INDENT_UNIT));
         try!(self.ibox(0));
         try!(self.print_outer_attributes(&arm.attrs));
         let mut first = true;
@@ -2549,7 +2609,7 @@ impl<'a> State<'a> {
         match arm.body.node {
             ast::ExprBlock(ref blk) => {
                 // the block will close the pattern's ibox
-                try!(self.print_block_unclosed_indent(&**blk, indent_unit));
+                try!(self.print_block_unclosed_indent(&**blk, INDENT_UNIT));
 
                 // If it is a user-provided unsafe block, print a comma after it
                 if let ast::UnsafeBlock(ast::UserProvided) = blk.rules {
@@ -2907,7 +2967,7 @@ impl<'a> State<'a> {
     }
 
     pub fn print_arg(&mut self, input: &ast::Arg) -> io::Result<()> {
-        try!(self.ibox(indent_unit));
+        try!(self.ibox(INDENT_UNIT));
         match input.ty.node {
             ast::TyInfer => try!(self.print_pat(&*input.pat)),
             _ => {
@@ -2935,7 +2995,7 @@ impl<'a> State<'a> {
         }
 
         try!(self.space_if_not_bol());
-        try!(self.ibox(indent_unit));
+        try!(self.ibox(INDENT_UNIT));
         try!(self.word_space("->"));
         match decl.output {
             ast::NoReturn(_) =>
@@ -2960,7 +3020,7 @@ impl<'a> State<'a> {
                        generics: &ast::Generics,
                        opt_explicit_self: Option<&ast::ExplicitSelf_>)
                        -> io::Result<()> {
-        try!(self.ibox(indent_unit));
+        try!(self.ibox(INDENT_UNIT));
         if !generics.lifetimes.is_empty() || !generics.ty_params.is_empty() {
             try!(word(&mut self.s, "for"));
             try!(self.print_generics(generics));
diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs
index 3e02476443a..6fd3833a3cd 100644
--- a/src/libsyntax/test.rs
+++ b/src/libsyntax/test.rs
@@ -29,7 +29,8 @@ use entry::{self, EntryPointType};
 use ext::base::ExtCtxt;
 use ext::build::AstBuilder;
 use ext::expand::ExpansionConfig;
-use fold::{Folder, MoveMap};
+use fold::Folder;
+use util::move_map::MoveMap;
 use fold;
 use owned_slice::OwnedSlice;
 use parse::token::{intern, InternedString};
@@ -88,7 +89,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(krate)
+        strip_test_functions(span_diagnostic, krate)
     }
 }
 
@@ -314,10 +315,11 @@ fn generate_test_harness(sess: &ParseSess,
     return res;
 }
 
-fn strip_test_functions(krate: ast::Crate) -> ast::Crate {
+fn strip_test_functions(diagnostic: &diagnostic::SpanHandler, krate: ast::Crate)
+                        -> ast::Crate {
     // When not compiling with --test we should not compile the
     // #[test] functions
-    config::strip_items(krate, |attrs| {
+    config::strip_items(diagnostic, krate, |attrs| {
         !attr::contains_name(&attrs[..], "test") &&
         !attr::contains_name(&attrs[..], "bench")
     })
@@ -619,8 +621,10 @@ fn mk_test_descs(cx: &TestCtxt) -> P<ast::Expr> {
                     mk_test_desc_and_fn_rec(cx, test)
                 }).collect()),
                 span: DUMMY_SP,
+                attrs: None,
             })),
         span: DUMMY_SP,
+        attrs: None,
     })
 }
 
diff --git a/src/libsyntax/util/move_map.rs b/src/libsyntax/util/move_map.rs
new file mode 100644
index 00000000000..95c24c66630
--- /dev/null
+++ b/src/libsyntax/util/move_map.rs
@@ -0,0 +1,79 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use owned_slice::OwnedSlice;
+
+use std::ptr;
+
+pub trait MoveMap<T>: Sized {
+    fn move_map<F>(self, mut f: F) -> Self where F: FnMut(T) -> T {
+        self.move_flat_map(|e| Some(f(e)))
+    }
+
+    fn move_flat_map<F, I>(self, f: F) -> Self
+        where F: FnMut(T) -> I,
+              I: IntoIterator<Item=T>;
+}
+
+impl<T> MoveMap<T> for Vec<T> {
+    fn move_flat_map<F, I>(mut self, mut f: F) -> Self
+        where F: FnMut(T) -> I,
+              I: IntoIterator<Item=T>
+    {
+        let mut read_i = 0;
+        let mut write_i = 0;
+        unsafe {
+            let mut old_len = self.len();
+            self.set_len(0); // make sure we just leak elements in case of panic
+
+            while read_i < old_len {
+                // move the read_i'th item out of the vector and map it
+                // to an iterator
+                let e = ptr::read(self.get_unchecked(read_i));
+                let mut iter = f(e).into_iter();
+                read_i += 1;
+
+                while let Some(e) = iter.next() {
+                    if write_i < read_i {
+                        ptr::write(self.get_unchecked_mut(write_i), e);
+                        write_i += 1;
+                    } else {
+                        // If this is reached we ran out of space
+                        // in the middle of the vector.
+                        // However, the vector is in a valid state here,
+                        // so we just do a somewhat inefficient insert.
+                        self.set_len(old_len);
+                        self.insert(write_i, e);
+
+                        old_len = self.len();
+                        self.set_len(0);
+
+                        read_i += 1;
+                        write_i += 1;
+                    }
+                }
+            }
+
+            // write_i tracks the number of actually written new items.
+            self.set_len(write_i);
+        }
+
+        self
+    }
+}
+
+impl<T> MoveMap<T> for OwnedSlice<T> {
+    fn move_flat_map<F, I>(self, f: F) -> Self
+        where F: FnMut(T) -> I,
+              I: IntoIterator<Item=T>
+    {
+        OwnedSlice::from_vec(self.into_vec().move_flat_map(f))
+    }
+}
diff --git a/src/libsyntax/util/small_vector.rs b/src/libsyntax/util/small_vector.rs
index dac3b054165..ee183d7f3e9 100644
--- a/src/libsyntax/util/small_vector.rs
+++ b/src/libsyntax/util/small_vector.rs
@@ -16,7 +16,7 @@ use std::mem;
 use std::slice;
 use std::vec;
 
-use fold::MoveMap;
+use util::move_map::MoveMap;
 
 /// A vector type optimized for cases where the size is almost always 0 or 1
 pub struct SmallVector<T> {
@@ -134,15 +134,6 @@ impl<T> SmallVector<T> {
         self.into_iter()
     }
 
-    pub fn into_iter(self) -> IntoIter<T> {
-        let repr = match self.repr {
-            Zero => ZeroIterator,
-            One(v) => OneIterator(v),
-            Many(vs) => ManyIterator(vs.into_iter())
-        };
-        IntoIter { repr: repr }
-    }
-
     pub fn len(&self) -> usize {
         match self.repr {
             Zero => 0,
@@ -154,6 +145,19 @@ impl<T> SmallVector<T> {
     pub fn is_empty(&self) -> bool { self.len() == 0 }
 }
 
+impl<T> IntoIterator for SmallVector<T> {
+    type Item = T;
+    type IntoIter = IntoIter<T>;
+    fn into_iter(self) -> Self::IntoIter {
+        let repr = match self.repr {
+            Zero => ZeroIterator,
+            One(v) => OneIterator(v),
+            Many(vs) => ManyIterator(vs.into_iter())
+        };
+        IntoIter { repr: repr }
+    }
+}
+
 pub struct IntoIter<T> {
     repr: IntoIterRepr<T>,
 }
@@ -192,13 +196,15 @@ impl<T> Iterator for IntoIter<T> {
 }
 
 impl<T> MoveMap<T> for SmallVector<T> {
-    fn move_map<F>(self, mut f: F) -> SmallVector<T> where F: FnMut(T) -> T {
-        let repr = match self.repr {
-            Zero => Zero,
-            One(v) => One(f(v)),
-            Many(vs) => Many(vs.move_map(f))
-        };
-        SmallVector { repr: repr }
+    fn move_flat_map<F, I>(self, mut f: F) -> Self
+        where F: FnMut(T) -> I,
+              I: IntoIterator<Item=T>
+    {
+        match self.repr {
+            Zero => Self::zero(),
+            One(v) => f(v).into_iter().collect(),
+            Many(vs) => SmallVector { repr: Many(vs.move_flat_map(f)) },
+        }
     }
 }
 
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index 5d4a462e844..cdc11fb2c1c 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -25,6 +25,7 @@
 
 use abi::Abi;
 use ast::*;
+use attr::ThinAttributesExt;
 use codemap::Span;
 
 #[derive(Copy, Clone, PartialEq, Eq)]
@@ -628,7 +629,12 @@ pub fn walk_stmt<'v, V: Visitor<'v>>(visitor: &mut V, statement: &'v Stmt) {
         StmtExpr(ref expression, _) | StmtSemi(ref expression, _) => {
             visitor.visit_expr(expression)
         }
-        StmtMac(ref mac, _) => visitor.visit_mac(mac),
+        StmtMac(ref mac, _, ref attrs) => {
+            visitor.visit_mac(mac);
+            for attr in attrs.as_attr_slice() {
+                visitor.visit_attribute(attr);
+            }
+        }
     }
 }