about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc/lint/context.rs19
-rw-r--r--src/librustc/middle/check_match.rs3
-rw-r--r--src/librustc_driver/pretty.rs1
-rw-r--r--src/librustc_front/fold.rs8
-rw-r--r--src/librustc_front/hir.rs3
-rw-r--r--src/librustc_front/lowering.rs65
-rw-r--r--src/librustc_lint/bad_style.rs2
-rw-r--r--src/libsyntax/ast.rs142
-rw-r--r--src/libsyntax/attr.rs82
-rw-r--r--src/libsyntax/config.rs107
-rw-r--r--src/libsyntax/ext/asm.rs3
-rw-r--r--src/libsyntax/ext/base.rs1
-rw-r--r--src/libsyntax/ext/build.rs3
-rw-r--r--src/libsyntax/ext/concat_idents.rs1
-rw-r--r--src/libsyntax/ext/deriving/debug.rs1
-rw-r--r--src/libsyntax/ext/expand.rs37
-rw-r--r--src/libsyntax/ext/quote.rs1
-rw-r--r--src/libsyntax/fold.rs67
-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.rs229
-rw-r--r--src/libsyntax/test.rs9
-rw-r--r--src/libsyntax/visit.rs7
-rw-r--r--src/test/parse-fail/attr-before-ext.rs16
-rw-r--r--src/test/parse-fail/attr-before-let.rs16
-rw-r--r--src/test/parse-fail/attr-before-stmt.rs21
-rw-r--r--src/test/parse-fail/attr-dangling-in-fn.rs2
-rw-r--r--src/test/parse-fail/doc-before-macro.rs17
-rw-r--r--src/test/parse-fail/doc-before-rbrace.rs2
-rw-r--r--src/test/parse-fail/doc-before-semi.rs2
-rw-r--r--src/test/pretty/stmt_expr_attributes.rs281
-rw-r--r--src/test/run-pass-fulldeps/ast_stmt_expr_attr.rs306
-rw-r--r--src/test/run-pass/cfg_stmt_expr.rs97
34 files changed, 1602 insertions, 416 deletions
diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs
index 74a61f700e3..9a30ec1a2b6 100644
--- a/src/librustc/lint/context.rs
+++ b/src/librustc/lint/context.rs
@@ -41,7 +41,7 @@ use syntax::ast_util::{self, IdVisitingOperation};
 use syntax::attr::{self, AttrMetaMethods};
 use syntax::codemap::Span;
 use syntax::parse::token::InternedString;
-use syntax::ast;
+use syntax::ast::{self, ThinAttributesExt};
 use rustc_front::hir;
 use rustc_front::util;
 use rustc_front::intravisit as hir_visit;
@@ -674,11 +674,18 @@ impl<'a, 'tcx, 'v> hir_visit::Visitor<'v> for LateContext<'a, 'tcx> {
     }
 
     fn visit_expr(&mut self, e: &hir::Expr) {
-        run_lints!(self, check_expr, late_passes, e);
-        hir_visit::walk_expr(self, e);
+        self.with_lint_attrs(e.attrs.as_attrs(), |cx| {
+            run_lints!(cx, check_expr, late_passes, e);
+            hir_visit::walk_expr(cx, e);
+        })
     }
 
     fn visit_stmt(&mut self, s: &hir::Stmt) {
+        // statement attributes are actually just attributes on one of
+        // - item
+        // - local
+        // - expression
+        // so we keep track of lint levels there
         run_lints!(self, check_stmt, late_passes, s);
         hir_visit::walk_stmt(self, s);
     }
@@ -730,8 +737,10 @@ impl<'a, 'tcx, 'v> hir_visit::Visitor<'v> for LateContext<'a, 'tcx> {
     }
 
     fn visit_local(&mut self, l: &hir::Local) {
-        run_lints!(self, check_local, late_passes, l);
-        hir_visit::walk_local(self, l);
+        self.with_lint_attrs(l.attrs.as_attrs(), |cx| {
+            run_lints!(cx, check_local, late_passes, l);
+            hir_visit::walk_local(cx, l);
+        })
     }
 
     fn visit_block(&mut self, b: &hir::Block) {
diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs
index 3e6cf07d86f..a292c83682c 100644
--- a/src/librustc/middle/check_match.rs
+++ b/src/librustc/middle/check_match.rs
@@ -409,7 +409,8 @@ fn const_val_to_expr(value: &ConstVal) -> P<hir::Expr> {
     P(hir::Expr {
         id: 0,
         node: hir::ExprLit(P(Spanned { node: node, span: DUMMY_SP })),
-        span: DUMMY_SP
+        span: DUMMY_SP,
+        attrs: None,
     })
 }
 
diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs
index 630c42db68c..9a489ac6fdf 100644
--- a/src/librustc_driver/pretty.rs
+++ b/src/librustc_driver/pretty.rs
@@ -654,6 +654,7 @@ impl fold::Folder for ReplaceBodyWithLoop {
                 node: ast::ExprLoop(empty_block, None),
                 id: ast::DUMMY_NODE_ID,
                 span: codemap::DUMMY_SP,
+                attrs: None,
             });
 
             expr_to_block(b.rules, Some(loop_expr))
diff --git a/src/librustc_front/fold.rs b/src/librustc_front/fold.rs
index 7ec4e1ba331..028ecbc336e 100644
--- a/src/librustc_front/fold.rs
+++ b/src/librustc_front/fold.rs
@@ -13,7 +13,7 @@
 
 use hir::*;
 use syntax::ast::{Ident, Name, NodeId, DUMMY_NODE_ID, Attribute, Attribute_, MetaItem};
-use syntax::ast::{MetaWord, MetaList, MetaNameValue};
+use syntax::ast::{MetaWord, MetaList, MetaNameValue, ThinAttributesExt};
 use hir;
 use syntax::codemap::{respan, Span, Spanned};
 use syntax::owned_slice::OwnedSlice;
@@ -501,13 +501,14 @@ 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 }| {
+    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),
+            attrs: attrs.map_opt_attrs(|attrs| fold_attrs(attrs, fld)),
         }
     })
 }
@@ -1048,7 +1049,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 {
@@ -1171,6 +1172,7 @@ pub fn noop_fold_expr<T: Folder>(Expr { id, node, span }: Expr, folder: &mut T)
             }
         },
         span: folder.new_span(span),
+        attrs: attrs.map_opt_attrs(|attrs| fold_attrs(attrs, folder)),
     }
 }
 
diff --git a/src/librustc_front/hir.rs b/src/librustc_front/hir.rs
index 95d73daa632..f0f9512cd0f 100644
--- a/src/librustc_front/hir.rs
+++ b/src/librustc_front/hir.rs
@@ -41,6 +41,7 @@ use syntax::codemap::{self, Span, Spanned, DUMMY_SP, ExpnId};
 use syntax::abi::Abi;
 use syntax::ast::{Name, Ident, NodeId, DUMMY_NODE_ID, TokenTree, AsmDialect};
 use syntax::ast::{Attribute, Lit, StrStyle, FloatTy, IntTy, UintTy, CrateConfig};
+use syntax::ast::ThinAttributes;
 use syntax::owned_slice::OwnedSlice;
 use syntax::parse::token::InternedString;
 use syntax::ptr::P;
@@ -558,6 +559,7 @@ pub struct Local {
     pub init: Option<P<Expr>>,
     pub id: NodeId,
     pub span: Span,
+    pub attrs: ThinAttributes,
 }
 
 pub type Decl = Spanned<Decl_>;
@@ -609,6 +611,7 @@ pub struct Expr {
     pub id: NodeId,
     pub node: Expr_,
     pub span: Span,
+    pub attrs: ThinAttributes,
 }
 
 impl fmt::Debug for Expr {
diff --git a/src/librustc_front/lowering.rs b/src/librustc_front/lowering.rs
index b984f23c4c0..0f89ddb9987 100644
--- a/src/librustc_front/lowering.rs
+++ b/src/librustc_front/lowering.rs
@@ -331,6 +331,7 @@ pub fn lower_local(lctx: &LoweringContext, l: &Local) -> P<hir::Local> {
         pat: lower_pat(lctx, &l.pat),
         init: l.init.as_ref().map(|e| lower_expr(lctx, e)),
         span: l.span,
+        attrs: l.attrs.clone(),
     })
 }
 
@@ -1215,7 +1216,14 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P<hir::Expr> {
                                 maybe_expr.as_ref().map(|x| lower_expr(lctx, x)))
             }
             ExprParen(ref ex) => {
-                return lower_expr(lctx, ex);
+                // merge attributes into the inner expression.
+                return lower_expr(lctx, ex).map(|mut ex| {
+                    ex.attrs.update(|attrs| {
+                        // FIXME: Badly named
+                        attrs.prepend_outer(e.attrs.clone())
+                    });
+                    ex
+                });
             }
 
             // Desugar ExprIfLet
@@ -1454,6 +1462,7 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P<hir::Expr> {
             ExprMac(_) => panic!("Shouldn't exist here"),
         },
         span: e.span,
+        attrs: e.attrs.clone(),
     })
 }
 
@@ -1552,52 +1561,62 @@ fn arm(pats: Vec<P<hir::Pat>>, expr: P<hir::Expr>) -> hir::Arm {
     }
 }
 
-fn expr_break(lctx: &LoweringContext, span: Span) -> P<hir::Expr> {
-    expr(lctx, span, hir::ExprBreak(None))
+fn expr_break(lctx: &LoweringContext, span: Span,
+              attrs: ThinAttributes) -> P<hir::Expr> {
+    expr(lctx, span, hir::ExprBreak(None), attrs)
 }
 
 fn expr_call(lctx: &LoweringContext,
              span: Span,
              e: P<hir::Expr>,
-             args: Vec<P<hir::Expr>>)
+             args: Vec<P<hir::Expr>>,
+             attrs: ThinAttributes)
              -> P<hir::Expr> {
-    expr(lctx, span, hir::ExprCall(e, args))
+    expr(lctx, span, hir::ExprCall(e, args), attrs)
 }
 
-fn expr_ident(lctx: &LoweringContext, span: Span, id: Ident) -> P<hir::Expr> {
-    expr_path(lctx, path_ident(span, id))
+fn expr_ident(lctx: &LoweringContext, span: Span, id: Ident,
+              attrs: ThinAttributes) -> P<hir::Expr> {
+    expr_path(lctx, path_ident(span, id), attrs)
 }
 
-fn expr_mut_addr_of(lctx: &LoweringContext, span: Span, e: P<hir::Expr>) -> P<hir::Expr> {
-    expr(lctx, span, hir::ExprAddrOf(hir::MutMutable, e))
+fn expr_mut_addr_of(lctx: &LoweringContext, span: Span, e: P<hir::Expr>,
+                    attrs: ThinAttributes) -> P<hir::Expr> {
+    expr(lctx, span, hir::ExprAddrOf(hir::MutMutable, e), attrs)
 }
 
-fn expr_path(lctx: &LoweringContext, path: hir::Path) -> P<hir::Expr> {
-    expr(lctx, path.span, hir::ExprPath(None, path))
+fn expr_path(lctx: &LoweringContext, path: hir::Path,
+             attrs: ThinAttributes) -> P<hir::Expr> {
+    expr(lctx, path.span, hir::ExprPath(None, path), attrs)
 }
 
 fn expr_match(lctx: &LoweringContext,
               span: Span,
               arg: P<hir::Expr>,
               arms: Vec<hir::Arm>,
-              source: hir::MatchSource)
+              source: hir::MatchSource,
+              attrs: ThinAttributes)
               -> P<hir::Expr> {
-    expr(lctx, span, hir::ExprMatch(arg, arms, source))
+    expr(lctx, span, hir::ExprMatch(arg, arms, source), attrs)
 }
 
-fn expr_block(lctx: &LoweringContext, b: P<hir::Block>) -> P<hir::Expr> {
-    expr(lctx, b.span, hir::ExprBlock(b))
+fn expr_block(lctx: &LoweringContext, b: P<hir::Block>,
+              attrs: ThinAttributes) -> P<hir::Expr> {
+    expr(lctx, b.span, hir::ExprBlock(b), attrs)
 }
 
-fn expr_tuple(lctx: &LoweringContext, sp: Span, exprs: Vec<P<hir::Expr>>) -> P<hir::Expr> {
-    expr(lctx, sp, hir::ExprTup(exprs))
+fn expr_tuple(lctx: &LoweringContext, sp: Span, exprs: Vec<P<hir::Expr>>,
+              attrs: ThinAttributes) -> P<hir::Expr> {
+    expr(lctx, sp, hir::ExprTup(exprs), attrs)
 }
 
-fn expr(lctx: &LoweringContext, span: Span, node: hir::Expr_) -> P<hir::Expr> {
+fn expr(lctx: &LoweringContext, span: Span, node: hir::Expr_,
+        attrs: ThinAttributes) -> P<hir::Expr> {
     P(hir::Expr {
         id: lctx.next_id(),
         node: node,
         span: span,
+        attrs: attrs,
     })
 }
 
@@ -1605,7 +1624,8 @@ fn stmt_let(lctx: &LoweringContext,
             sp: Span,
             mutbl: bool,
             ident: Ident,
-            ex: P<hir::Expr>)
+            ex: P<hir::Expr>,
+            attrs: ThinAttributes)
             -> P<hir::Stmt> {
     let pat = if mutbl {
         pat_ident_binding_mode(lctx, sp, ident, hir::BindByValue(hir::MutMutable))
@@ -1618,6 +1638,7 @@ fn stmt_let(lctx: &LoweringContext,
         init: Some(ex),
         id: lctx.next_id(),
         span: sp,
+        attrs: attrs,
     });
     let decl = respan(sp, hir::DeclLocal(local));
     P(respan(sp, hir::StmtDecl(P(decl), lctx.next_id())))
@@ -1755,7 +1776,8 @@ fn signal_block_expr(lctx: &LoweringContext,
                      stmts: Vec<P<hir::Stmt>>,
                      expr: P<hir::Expr>,
                      span: Span,
-                     rule: hir::BlockCheckMode)
+                     rule: hir::BlockCheckMode,
+                     attrs: ThinAttributes)
                      -> P<hir::Expr> {
     let id = lctx.next_id();
     expr_block(lctx,
@@ -1765,7 +1787,8 @@ fn signal_block_expr(lctx: &LoweringContext,
                    id: id,
                    stmts: stmts,
                    expr: Some(expr),
-               }))
+               }),
+               attrs)
 }
 
 
diff --git a/src/librustc_lint/bad_style.rs b/src/librustc_lint/bad_style.rs
index 2146dc8e9b9..b5f8be496fb 100644
--- a/src/librustc_lint/bad_style.rs
+++ b/src/librustc_lint/bad_style.rs
@@ -138,7 +138,7 @@ impl LateLintPass for NonCamelCaseTypes {
 declare_lint! {
     pub NON_SNAKE_CASE,
     Warn,
-    "methods, functions, lifetime parameters and modules should have snake case names"
+    "variables, methods, functions, lifetime parameters and modules should have snake case names"
 }
 
 #[derive(Copy, Clone)]
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index ab62c8d9421..48c5be8e07e 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -692,8 +692,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 +731,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 +753,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 +798,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 +1834,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,
@@ -1904,6 +1952,98 @@ pub struct MacroDef {
     pub body: Vec<TokenTree>,
 }
 
+/// A list of attributes, behind a optional box as
+/// a space optimization.
+pub type ThinAttributes = Option<Box<Vec<Attribute>>>;
+
+pub trait ThinAttributesExt {
+    fn map_opt_attrs<F>(self, f: F) -> Self
+        where F: FnOnce(Vec<Attribute>) -> Vec<Attribute>;
+    fn prepend_outer(mut self, attrs: Self) -> Self;
+    fn append_inner(mut self, attrs: Self) -> Self;
+    fn update<F>(&mut self, f: F)
+        where Self: Sized,
+              F: FnOnce(Self) -> Self;
+    fn as_attrs(&self) -> &[Attribute];
+    fn into_attrs(self) -> Vec<Attribute>;
+}
+
+// FIXME: Rename inner/outer
+// FIXME: Rename opt_attrs
+
+impl ThinAttributesExt for ThinAttributes {
+    fn map_opt_attrs<F>(self, f: F) -> Self
+        where F: FnOnce(Vec<Attribute>) -> Vec<Attribute> {
+
+        // This is kinda complicated... Ensure the function is
+        // always called, and that None inputs or results are
+        // correctly handled.
+        if let Some(mut b) = self {
+            use std::mem::replace;
+
+            let vec = replace(&mut *b, Vec::new());
+            let vec = f(vec);
+            if vec.len() == 0 {
+                None
+            } else {
+                replace(&mut*b, vec);
+                Some(b)
+            }
+        } else {
+            f(Vec::new()).into_opt_attrs()
+        }
+    }
+
+    fn prepend_outer(self, attrs: ThinAttributes) -> Self {
+        attrs.map_opt_attrs(|mut attrs| {
+            attrs.extend(self.into_attrs());
+            attrs
+        })
+    }
+
+    fn append_inner(self, attrs: ThinAttributes) -> Self {
+        self.map_opt_attrs(|mut self_| {
+            self_.extend(attrs.into_attrs());
+            self_
+        })
+    }
+
+    fn update<F>(&mut self, f: F)
+        where Self: Sized,
+              F: FnOnce(ThinAttributes) -> ThinAttributes
+    {
+        let self_ = f(self.take());
+        *self = self_;
+    }
+
+    fn as_attrs(&self) -> &[Attribute] {
+        match *self {
+            Some(ref b) => b,
+            None => &[],
+        }
+    }
+
+    fn into_attrs(self) -> Vec<Attribute> {
+        match self {
+            Some(b) => *b,
+            None => Vec::new(),
+        }
+    }
+}
+
+pub trait AttributesExt {
+    fn into_opt_attrs(self) -> ThinAttributes;
+}
+impl AttributesExt for Vec<Attribute> {
+    fn into_opt_attrs(self) -> ThinAttributes {
+        if self.len() == 0 {
+            None
+        } else {
+            Some(Box::new(self))
+        }
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use serialize;
diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs
index 153a81e6c54..18f7311418f 100644
--- a/src/libsyntax/attr.rs
+++ b/src/libsyntax/attr.rs
@@ -16,6 +16,8 @@ pub use self::IntType::*;
 
 use ast;
 use ast::{AttrId, Attribute, Attribute_, MetaItem, MetaWord, MetaNameValue, MetaList};
+use ast::{Stmt, StmtDecl, StmtExpr, StmtMac, StmtSemi, DeclItem, DeclLocal, ThinAttributes};
+use ast::{Expr, ThinAttributesExt, Item, Local, Decl};
 use codemap::{Span, Spanned, spanned, dummy_spanned};
 use codemap::BytePos;
 use diagnostic::SpanHandler;
@@ -720,3 +722,83 @@ impl IntType {
         }
     }
 }
+
+/// 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_inner(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_attrs());
+            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_inner(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_inner(attrs));
+                        StmtMac(mac, style, ats)
+                    }
+                },
+            }
+        })
+    }
+}
diff --git a/src/libsyntax/config.rs b/src/libsyntax/config.rs
index 10731178c06..a867b45075f 100644
--- a/src/libsyntax/config.rs
+++ b/src/libsyntax/config.rs
@@ -20,8 +20,9 @@ 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
@@ -32,16 +33,15 @@ pub fn strip_unconfigured_items(diagnostic: &SpanHandler, krate: ast::Crate,
 {
     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| in_cfg(diagnostic, &config, attrs, feature_gated_cfgs))
 }
 
-impl<F> fold::Folder for Context<F> where F: FnMut(&[ast::Attribute]) -> bool {
+impl<'a, F> fold::Folder for Context<'a, 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)
-    }
     fn fold_foreign_mod(&mut self, foreign_mod: ast::ForeignMod) -> ast::ForeignMod {
         fold_foreign_mod(self, foreign_mod)
     }
@@ -49,8 +49,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 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,11 +76,13 @@ 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)
 }
@@ -182,45 +201,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 +225,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,13 +265,19 @@ 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],
+fn in_cfg(diagnostic: &SpanHandler,
+          cfg: &[P<ast::MetaItem>],
+          attrs: &[ast::Attribute],
           feature_gated_cfgs: &mut Vec<GatedCfg>) -> 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
         };
 
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 55f0fa5675a..8c93327c322 100644
--- a/src/libsyntax/ext/base.rs
+++ b/src/libsyntax/ext/base.rs
@@ -349,6 +349,7 @@ impl DummyResult {
             id: ast::DUMMY_NODE_ID,
             node: ast::ExprLit(P(codemap::respan(sp, ast::LitBool(false)))),
             span: sp,
+            attrs: None,
         })
     }
 
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/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/expand.rs b/src/libsyntax/ext/expand.rs
index 9b1a7a50201..132b29c7623 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -17,7 +17,7 @@ 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::*;
@@ -37,11 +37,16 @@ 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 {
+    // FIXME: Drop attrs on the floor for now.
+    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) => {
+
+            // drop attributes on the macro itself
+            let _ = 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(attrs)
         }
 
         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(attrs)
         }
 
         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(attrs)
         }
 
         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(attrs)
         }
 
         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(attrs)
         }
 
         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(attrs)
         }
 
         ast::ExprClosure(capture_clause, fn_decl, block) => {
@@ -144,15 +156,18 @@ 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: None})
+                .with_attrs(attrs)
         }
 
         _ => {
             P(noop_fold_expr(ast::Expr {
                 id: id,
                 node: node,
-                span: span
-            }, fld))
+                span: span,
+                attrs: None
+            }, fld)).with_attrs(attrs)
         }
     });
 }
@@ -486,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)
     };
 
+    // FIXME: drop attrs for macros.
+    let _ = attrs;
+
     let maybe_new_items =
         expand_mac_invoc(mac.and_then(|m| m), stmt.span,
                          |r| r.make_stmts(),
@@ -538,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));
@@ -568,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: attrs
                     }
                 });
                 SmallVector::one(P(Spanned {
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/fold.rs b/src/libsyntax/fold.rs
index 3dba6cbecbd..1d499bce3c6 100644
--- a/src/libsyntax/fold.rs
+++ b/src/libsyntax/fold.rs
@@ -134,6 +134,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)
     }
@@ -508,12 +516,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_opt_attrs(|v| fold_attrs(v, fld)),
     })
 }
 
@@ -891,7 +900,7 @@ 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)),
+        expr: expr.and_then(|x| folder.fold_opt_expr(x)),
         rules: rules,
         span: folder.new_span(span),
     })
@@ -1171,7 +1180,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 +1191,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 +1338,20 @@ 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_opt_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>> {
+    // FIXME: Needs a efficient in-place flat_map
+    es.into_iter().flat_map(|e| folder.fold_opt_expr(e)).collect()
+}
+
 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 +1365,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_opt_attrs(|v| fold_attrs(v, folder))),
             span: span
         }))
     }
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 56a06f70ed4..2f67ecad4e7 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 ast::{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_opt_attrs()
+                    .prepend_outer(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_opt_attrs();
+                attrs.update(|attrs| attrs.append_inner(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_inner(
+                                try!(self.parse_inner_attributes())
+                                    .into_opt_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_opt_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_opt_attrs();
+        let attrs = outer_attrs.append_inner(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_outer(attrs));
+                match expr.node {
+                    ExprIf(..) | ExprIfLet(..) => {
+                        if !expr.attrs.as_attrs().is_empty() {
+                            // Just point to the first attribute in there...
+                            let span = expr.attrs.as_attrs()[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_inner(iattrs.into_opt_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_inner(iattrs.into_opt_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_inner(iattrs.into_opt_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_inner(iattrs.into_opt_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_inner(
+            try!(self.parse_inner_attributes()).into_opt_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()
         }
     }
 
@@ -3274,7 +3420,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);
@@ -3321,7 +3467,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);
@@ -3378,7 +3524,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());
 
@@ -3393,13 +3539,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))))
     }
 
@@ -3444,28 +3591,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_opt_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));
@@ -3511,11 +3650,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_opt_attrs());
+                spanned(lo, hi, stmt)
             } else {
                 // if it has a special ident, it's definitely an item
                 //
@@ -3535,30 +3675,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_opt_attrs())));
+                    let hi = e.span.hi;
+                    let stmt = StmtExpr(e, ast::DUMMY_NODE_ID);
+                    spanned(lo, hi, stmt)
                 }
             }
         }))
@@ -3614,22 +3767,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,
@@ -3638,12 +3792,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());
@@ -3652,11 +3806,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
                             }));
                         }
@@ -5210,7 +5365,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())
@@ -5468,7 +5623,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.
@@ -5487,7 +5642,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"));
             }
@@ -5500,6 +5655,7 @@ impl<'a> Parser<'a> {
         &mut self,
         attrs: Vec<Attribute> ,
         macros_allowed: bool,
+        attributes_allowed: bool,
         lo: BytePos,
         visibility: Visibility
     ) -> PResult<Option<P<Item>>> {
@@ -5566,7 +5722,7 @@ impl<'a> Parser<'a> {
             }
         }
 
-        if !attrs.is_empty() {
+        if !attributes_allowed && !attrs.is_empty() {
             self.expected_item_err(&attrs);
         }
         Ok(None)
@@ -5574,7 +5730,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..6919bc4efdd 100644
--- a/src/libsyntax/print/pprust.rs
+++ b/src/libsyntax/print/pprust.rs
@@ -13,6 +13,7 @@ pub use self::AnnNode::*;
 use abi;
 use ast::{self, TokenTree};
 use ast::{RegionTyParamBound, TraitTyParamBound, TraitBoundModifier};
+use ast::{ThinAttributesExt, Attribute};
 use ast_util;
 use util::parser::AssocOp;
 use attr;
@@ -77,7 +78,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 +91,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 +169,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 +400,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 +706,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 +775,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 +796,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 +833,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 +840,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 +870,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 +880,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 +1218,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 +1332,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 +1388,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 +1610,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 +1629,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_attrs()));
                 let delim = match style {
                     ast::MacStmtWithBraces => token::Brace,
                     _ => token::Paren
@@ -1633,6 +1652,8 @@ impl<'a> State<'a> {
         }
         if parse::classify::stmt_ends_with_semi(&st.node) {
             try!(word(&mut self.s, ";"));
+        } else {
+            //try!(word(&mut self.s, ""));
         }
         self.maybe_print_trailing_comment(st.span, None)
     }
@@ -1642,7 +1663,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 +1680,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 +1704,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 +1719,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 +1729,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 +1742,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 +1785,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 +1844,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 +1856,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 +1871,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 +1889,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 +1906,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 +1975,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_attrs();
+        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 +2001,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 +2054,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 +2067,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 +2080,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 +2089,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 +2119,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_attrs()));
                         }
                         _ => {
                             // 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 +2140,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 +2281,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 +2303,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_attrs()));
                 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 +2513,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 +2586,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 +2610,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 +2968,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 +2996,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 +3021,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..aab106b7a67 100644
--- a/src/libsyntax/test.rs
+++ b/src/libsyntax/test.rs
@@ -88,7 +88,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 +314,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 +620,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/visit.rs b/src/libsyntax/visit.rs
index 5d4a462e844..2d97e2680d7 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -628,7 +628,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_attrs() {
+                visitor.visit_attribute(attr);
+            }
+        }
     }
 }
 
diff --git a/src/test/parse-fail/attr-before-ext.rs b/src/test/parse-fail/attr-before-ext.rs
deleted file mode 100644
index e15350fcad7..00000000000
--- a/src/test/parse-fail/attr-before-ext.rs
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2012 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.
-
-// compile-flags: -Z parse-only
-
-fn main() {
-    #[attr] //~ ERROR expected item after attributes
-    println!("hi");
-}
diff --git a/src/test/parse-fail/attr-before-let.rs b/src/test/parse-fail/attr-before-let.rs
deleted file mode 100644
index 03dabb980f2..00000000000
--- a/src/test/parse-fail/attr-before-let.rs
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2012 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.
-
-// compile-flags: -Z parse-only
-
-fn main() {
-    #[attr] //~ ERROR expected item
-    let __isize = 0;
-}
diff --git a/src/test/parse-fail/attr-before-stmt.rs b/src/test/parse-fail/attr-before-stmt.rs
deleted file mode 100644
index bc306048cdc..00000000000
--- a/src/test/parse-fail/attr-before-stmt.rs
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2012 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.
-
-// compile-flags: -Z parse-only
-
-// error-pattern:expected item
-
-fn f() {
-  #[foo = "bar"]
-  let x = 10;
-}
-
-fn main() {
-}
diff --git a/src/test/parse-fail/attr-dangling-in-fn.rs b/src/test/parse-fail/attr-dangling-in-fn.rs
index f2f4ecadd7a..7b731b6d6de 100644
--- a/src/test/parse-fail/attr-dangling-in-fn.rs
+++ b/src/test/parse-fail/attr-dangling-in-fn.rs
@@ -10,7 +10,7 @@
 
 // compile-flags: -Z parse-only
 
-// error-pattern:expected item
+// error-pattern:expected statement
 
 fn f() {
   #[foo = "bar"]
diff --git a/src/test/parse-fail/doc-before-macro.rs b/src/test/parse-fail/doc-before-macro.rs
deleted file mode 100644
index 44435bde03c..00000000000
--- a/src/test/parse-fail/doc-before-macro.rs
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2012 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.
-
-// compile-flags: -Z parse-only
-
-fn main() {
-    /// hi
-    println!("hi");
-    //~^^ ERROR expected item after doc comment
-}
diff --git a/src/test/parse-fail/doc-before-rbrace.rs b/src/test/parse-fail/doc-before-rbrace.rs
index 8b69c385378..295d5ae432e 100644
--- a/src/test/parse-fail/doc-before-rbrace.rs
+++ b/src/test/parse-fail/doc-before-rbrace.rs
@@ -12,5 +12,5 @@
 
 fn main() {
     println!("Hi"); /// hi
-    //~^ ERROR expected item after doc comment
 }
+//~^ ERROR expected statement
diff --git a/src/test/parse-fail/doc-before-semi.rs b/src/test/parse-fail/doc-before-semi.rs
index 42c58af76d8..6a8906953be 100644
--- a/src/test/parse-fail/doc-before-semi.rs
+++ b/src/test/parse-fail/doc-before-semi.rs
@@ -13,5 +13,5 @@
 fn main() {
     /// hi
     ;
-    //~^^ ERROR expected item after doc comment
+    //~^ ERROR expected statement
 }
diff --git a/src/test/pretty/stmt_expr_attributes.rs b/src/test/pretty/stmt_expr_attributes.rs
new file mode 100644
index 00000000000..48c2a0470e1
--- /dev/null
+++ b/src/test/pretty/stmt_expr_attributes.rs
@@ -0,0 +1,281 @@
+// 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.
+
+// pp-exact
+
+#![feature(custom_attribute)]
+#![feature(box_syntax)]
+#![feature(placement_in_syntax)]
+
+fn main() { }
+
+fn _0() {
+
+    #[attr]
+    foo();
+}
+
+fn _1() {
+
+    #[attr]
+    unsafe {
+        // code
+    }
+}
+
+fn _2() {
+
+    #[attr]
+    { foo(); }
+
+    {
+        #![attr]
+
+        foo()
+    }
+}
+
+fn _3() {
+
+    #[attr]
+    match () { _ => { } }
+}
+
+fn _4() {
+
+    #[attr]
+    match () {
+        #![attr]
+        _ => (),
+    }
+
+    let _ =
+        #[attr] match () {
+                    #![attr]
+                    () => (),
+                };
+}
+
+fn _5() {
+
+    #[attr]
+    let x = 1;
+
+    let x = #[attr] 1;
+
+    let y = ();
+    let z = ();
+
+    foo3(x, #[attr] y, z);
+
+    qux(3 + #[attr] 2);
+}
+
+fn _6() {
+
+    #[attr]
+    [#![attr] 1, 2, 3];
+
+    let _ = #[attr] [#![attr] 1, 2, 3];
+
+    #[attr]
+    [#![attr] 1; 4];
+
+    let _ = #[attr] [#![attr] 1; 4];
+}
+
+struct Foo {
+    data: (),
+}
+
+struct Bar(());
+
+fn _7() {
+
+    #[attr]
+    Foo{#![attr] data: (),};
+
+    let _ = #[attr] Foo{#![attr] data: (),};
+}
+
+fn _8() {
+
+    #[attr]
+    (#![attr] );
+
+    #[attr]
+    (#![attr] 0);
+
+    #[attr]
+    (#![attr] 0,);
+
+    #[attr]
+    (#![attr] 0, 1);
+}
+
+fn _9() {
+    macro_rules! stmt_mac((  ) => { let _ = (  ) ; });
+
+    #[attr]
+    stmt_mac!();
+
+    /*
+    // pre existing pp bug: delimiter styles gets lost:
+
+    #[attr]
+    stmt_mac!{ };
+
+    #[attr]
+    stmt_mac![];
+
+    #[attr]
+    stmt_mac!{ } // pre-existing pp bug: compiler ICEs with a None unwrap
+    */
+
+    let _ = ();
+}
+
+macro_rules! expr_mac((  ) => { (  ) });
+
+fn _10() {
+
+    let _ = #[attr] expr_mac!();
+
+    /*
+    // pre existing pp bug: delimiter styles gets lost:
+    let _ = #[attr] expr_mac![];
+    let _ = #[attr] expr_mac!{};
+    */
+}
+
+fn _11() {
+    let _ = #[attr] box 0;
+    let _: [(); 0] = #[attr] [#![attr] ];
+    let _ = #[attr] [#![attr] 0, 0];
+    let _ = #[attr] [#![attr] 0; 0];
+    let _ = #[attr] foo();
+    let _ = #[attr] 1i32.clone();
+    let _ = #[attr] (#![attr] );
+    let _ = #[attr] (#![attr] 0);
+    let _ = #[attr] (#![attr] 0,);
+    let _ = #[attr] (#![attr] 0, 0);
+    let _ = #[attr] 0 + #[attr] 0;
+    let _ = #[attr] !0;
+    let _ = #[attr] -0i32;
+    let _ = #[attr] false;
+    let _ = #[attr] 'c';
+    let _ = #[attr] 0;
+    let _ = #[attr] 0 as usize;
+    let _ =
+        #[attr] while false {
+                    #![attr]
+                };
+    let _ =
+        #[attr] while let None = Some(()) {
+                    #![attr]
+                };
+    let _ =
+        #[attr] for _ in 0..0 {
+                    #![attr]
+                };
+    // FIXME: pp bug, two spaces after the loop
+    let _ =
+        #[attr] loop  {
+                    #![attr]
+                };
+    let _ =
+        #[attr] match false {
+                    #![attr]
+                    _ => (),
+                };
+    let _ = #[attr] || #[attr] ();
+    let _ = #[attr] move || #[attr] ();
+    let _ = #[attr] || {
+        #![attr]
+        #[attr]
+        () };
+    let _ = #[attr] move || {
+        #![attr]
+        #[attr]
+        () };
+    let _ =
+        #[attr] {
+                    #![attr]
+                };
+    let _ =
+        #[attr] {
+                    #![attr]
+                    let _ = ();
+                };
+    let _ =
+        #[attr] {
+                    #![attr]
+                    let _ = ();
+                    ()
+                };
+    let mut x = 0;
+    let _ = #[attr] x = 15;
+    let _ = #[attr] x += 15;
+    let s = Foo{data: (),};
+    let _ = #[attr] s.data;
+    let _ = (#[attr] s).data;
+    let t = Bar(());
+    let _ = #[attr] t.0;
+    let _ = (#[attr] t).0;
+    let v = vec!(0);
+    let _ = #[attr] v[0];
+    let _ = (#[attr] v)[0];
+    let _ = #[attr] 0..#[attr] 0;
+    let _ = #[attr] 0..;
+    let _ = #[attr] (0..0);
+    let _ = #[attr] (0..);
+    let _ = #[attr] (..0);
+    let _ = #[attr] (..);
+    let _: fn(&u32) -> u32 = #[attr] std::clone::Clone::clone;
+    let _ = #[attr] &0;
+    let _ = #[attr] &mut 0;
+    let _ = #[attr] &#[attr] 0;
+    let _ = #[attr] &mut #[attr] 0;
+    // FIXME: pp bug, extra space after keyword?
+    while false { let _ = #[attr] continue ; }
+    while true { let _ = #[attr] break ; }
+    || #[attr] return;
+    let _ = #[attr] expr_mac!();
+    /* FIXME: pp bug, loosing delimiter styles
+    let _ = #[attr] expr_mac![];
+    let _ = #[attr] expr_mac!{};
+    */
+    let _ = #[attr] Foo{#![attr] data: (),};
+    let _ = #[attr] Foo{#![attr] ..s};
+    let _ = #[attr] Foo{#![attr] data: (), ..s};
+    let _ = #[attr] (#![attr] 0);
+}
+
+fn _12() {
+    #[attr]
+    let _ = 0;
+
+    #[attr]
+    0;
+
+    #[attr]
+    expr_mac!();
+
+    #[attr]
+    {
+        #![attr]
+    }
+}
+
+/////////////////
+
+fn foo() { }
+fn foo3(_: i32, _: (), _: ()) { }
+fn qux(_: i32) { }
diff --git a/src/test/run-pass-fulldeps/ast_stmt_expr_attr.rs b/src/test/run-pass-fulldeps/ast_stmt_expr_attr.rs
new file mode 100644
index 00000000000..4c20b3997f9
--- /dev/null
+++ b/src/test/run-pass-fulldeps/ast_stmt_expr_attr.rs
@@ -0,0 +1,306 @@
+// 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.
+
+#![feature(rustc_private)]
+
+extern crate syntax;
+
+use syntax::ast::*;
+use syntax::ast;
+use syntax::parse;
+use syntax::parse::{ParseSess,filemap_to_tts, PResult};
+use syntax::parse::new_parser_from_source_str;
+use syntax::parse::parser::Parser;
+use syntax::parse::token;
+use syntax::ptr::P;
+use syntax::str::char_at;
+use syntax::parse::attr::*;
+use syntax::print::pprust;
+use std::fmt;
+
+
+// Copied out of syntax::util::parser_testing
+
+pub fn string_to_parser<'a>(ps: &'a ParseSess, source_str: String) -> Parser<'a> {
+    new_parser_from_source_str(ps,
+                               Vec::new(),
+                               "bogofile".to_string(),
+                               source_str)
+}
+
+fn with_error_checking_parse<T, F>(s: String, f: F) -> PResult<T> where
+    F: FnOnce(&mut Parser) -> PResult<T>,
+{
+    let ps = ParseSess::new();
+    let mut p = string_to_parser(&ps, s);
+    let x = f(&mut p);
+
+    if ps.span_diagnostic.handler().has_errors() || p.token != token::Eof {
+        return Err(p.fatal("parse error"));
+    }
+
+    x
+}
+
+fn expr(s: &str) -> PResult<P<ast::Expr>> {
+    with_error_checking_parse(s.to_string(), |p| {
+        p.parse_expr_nopanic()
+    })
+}
+
+fn stmt(s: &str) -> PResult<P<ast::Stmt>> {
+    with_error_checking_parse(s.to_string(), |p| {
+        p.parse_stmt_nopanic().map(|s| s.unwrap())
+    })
+}
+
+fn attr(s: &str) -> PResult<ast::Attribute> {
+    with_error_checking_parse(s.to_string(), |p| {
+        p.parse_attribute(true)
+    })
+}
+
+fn str_compare<T, F: Fn(&T) -> String>(e: &str, expected: &[T], actual: &[T], f: F) {
+    let expected: Vec<_> = expected.iter().map(|e| f(e)).collect();
+    let actual: Vec<_> = actual.iter().map(|e| f(e)).collect();
+
+    if expected != actual {
+        panic!("parsed `{}` as {:?}, expected {:?}", e, actual, expected);
+    }
+}
+
+fn check_expr_attrs(es: &str, expected: &[&str]) {
+    let e = expr(es).expect("parse error");
+    let actual = &e.attrs;
+    str_compare(es,
+                &expected.iter().map(|r| attr(r).unwrap()).collect::<Vec<_>>(),
+                actual.as_attrs(),
+                pprust::attribute_to_string);
+}
+
+fn check_stmt_attrs(es: &str, expected: &[&str]) {
+    let e = stmt(es).expect("parse error");
+    let actual = e.node.attrs();
+    str_compare(es,
+                &expected.iter().map(|r| attr(r).unwrap()).collect::<Vec<_>>(),
+                actual,
+                pprust::attribute_to_string);
+}
+
+fn reject_expr_parse(es: &str) {
+    assert!(expr(es).is_err(), "parser did not reject `{}`", es);
+}
+
+fn reject_stmt_parse(es: &str) {
+    assert!(stmt(es).is_err(), "parser did not reject `{}`", es);
+}
+
+fn main() {
+    let both = &["#[attr]", "#![attr]"];
+    let outer = &["#[attr]"];
+    let none = &[];
+
+    check_expr_attrs("#[attr] box 0", outer);
+    reject_expr_parse("box #![attr] 0");
+
+    check_expr_attrs("#[attr] 0 <- #[attr] 0", none);
+    check_expr_attrs("#[attr] (0 <- 0)", outer);
+    reject_expr_parse("0 #[attr] <- 0");
+    reject_expr_parse("0 <- #![attr] 0");
+
+    check_expr_attrs("in #[attr] 0 {#[attr] 0}", none);
+    check_expr_attrs("#[attr] (in 0 {0})", outer);
+    reject_expr_parse("in 0 #[attr] {0}");
+    reject_expr_parse("in 0 {#![attr] 0}");
+
+    check_expr_attrs("#[attr] [#![attr]]", both);
+    check_expr_attrs("#[attr] [#![attr] 0]", both);
+    check_expr_attrs("#[attr] [#![attr] 0; 0]", both);
+    check_expr_attrs("#[attr] [#![attr] 0, 0, 0]", both);
+    reject_expr_parse("[#[attr]]");
+
+    check_expr_attrs("#[attr] foo()", outer);
+    check_expr_attrs("#[attr] x.foo()", outer);
+    reject_expr_parse("foo#[attr]()");
+    reject_expr_parse("foo(#![attr])");
+    reject_expr_parse("x.foo(#![attr])");
+    reject_expr_parse("x.#[attr]foo()");
+    reject_expr_parse("x.#![attr]foo()");
+
+    check_expr_attrs("#[attr] (#![attr])", both);
+    check_expr_attrs("#[attr] (#![attr] #[attr] 0,)", both);
+    check_expr_attrs("#[attr] (#![attr] #[attr] 0, 0)", both);
+
+    check_expr_attrs("#[attr] 0 + #[attr] 0", none);
+    check_expr_attrs("#[attr] 0 / #[attr] 0", none);
+    check_expr_attrs("#[attr] 0 & #[attr] 0", none);
+    check_expr_attrs("#[attr] 0 % #[attr] 0", none);
+    check_expr_attrs("#[attr] (0 + 0)", outer);
+    reject_expr_parse("0 + #![attr] 0");
+
+    check_expr_attrs("#[attr] !0", outer);
+    check_expr_attrs("#[attr] -0", outer);
+    reject_expr_parse("!#![attr] 0");
+    reject_expr_parse("-#![attr] 0");
+
+    check_expr_attrs("#[attr] false", outer);
+    check_expr_attrs("#[attr] 0", outer);
+    check_expr_attrs("#[attr] 'c'", outer);
+
+    check_expr_attrs("#[attr] x as Y", none);
+    check_expr_attrs("#[attr] (x as Y)", outer);
+    reject_expr_parse("x #![attr] as Y");
+
+    reject_expr_parse("#[attr] if false {}");
+    reject_expr_parse("if false #[attr] {}");
+    reject_expr_parse("if false {#![attr]}");
+    reject_expr_parse("if false {} #[attr] else {}");
+    reject_expr_parse("if false {} else #[attr] {}");
+    reject_expr_parse("if false {} else {#![attr]}");
+    reject_expr_parse("if false {} else #[attr] if true {}");
+    reject_expr_parse("if false {} else if true #[attr] {}");
+    reject_expr_parse("if false {} else if true {#![attr]}");
+
+    reject_expr_parse("#[attr] if let Some(false) = false {}");
+    reject_expr_parse("if let Some(false) = false #[attr] {}");
+    reject_expr_parse("if let Some(false) = false {#![attr]}");
+    reject_expr_parse("if let Some(false) = false {} #[attr] else {}");
+    reject_expr_parse("if let Some(false) = false {} else #[attr] {}");
+    reject_expr_parse("if let Some(false) = false {} else {#![attr]}");
+    reject_expr_parse("if let Some(false) = false {} else #[attr] if let Some(false) = true {}");
+    reject_expr_parse("if let Some(false) = false {} else if let Some(false) = true #[attr] {}");
+    reject_expr_parse("if let Some(false) = false {} else if let Some(false) = true {#![attr]}");
+
+    check_expr_attrs("#[attr] while true {#![attr]}", both);
+
+    check_expr_attrs("#[attr] while let Some(false) = true {#![attr]}", both);
+
+    check_expr_attrs("#[attr] for x in y {#![attr]}", both);
+
+    check_expr_attrs("#[attr] loop {#![attr]}", both);
+
+    check_expr_attrs("#[attr] match true {#![attr] #[attr] _ => false}", both);
+
+    check_expr_attrs("#[attr]      || #[attr] foo", outer);
+    check_expr_attrs("#[attr] move || #[attr] foo", outer);
+    check_expr_attrs("#[attr]      || #[attr] { #![attr] foo }", outer);
+    check_expr_attrs("#[attr] move || #[attr] { #![attr] foo }", outer);
+    check_expr_attrs("#[attr]      || { #![attr] foo }", outer);
+    check_expr_attrs("#[attr] move || { #![attr] foo }", outer);
+    reject_expr_parse("|| #![attr] foo");
+    reject_expr_parse("move || #![attr] foo");
+    reject_expr_parse("|| #![attr] {foo}");
+    reject_expr_parse("move || #![attr] {foo}");
+
+    check_expr_attrs("#[attr] { #![attr] }", both);
+    check_expr_attrs("#[attr] { #![attr] let _ = (); }", both);
+    check_expr_attrs("#[attr] { #![attr] let _ = (); foo }", both);
+
+    check_expr_attrs("#[attr] x = y", none);
+    check_expr_attrs("#[attr] (x = y)", outer);
+
+    check_expr_attrs("#[attr] x += y", none);
+    check_expr_attrs("#[attr] (x += y)", outer);
+
+    check_expr_attrs("#[attr] foo.bar", outer);
+    check_expr_attrs("(#[attr] foo).bar", none);
+
+    check_expr_attrs("#[attr] foo.0", outer);
+    check_expr_attrs("(#[attr] foo).0", none);
+
+    check_expr_attrs("#[attr] foo[bar]", outer);
+    check_expr_attrs("(#[attr] foo)[bar]", none);
+
+    check_expr_attrs("#[attr] 0..#[attr] 0", none);
+    check_expr_attrs("#[attr] 0..", none);
+    reject_expr_parse("#[attr] ..#[attr] 0");
+    reject_expr_parse("#[attr] ..");
+
+    check_expr_attrs("#[attr] (0..0)", outer);
+    check_expr_attrs("#[attr] (0..)", outer);
+    check_expr_attrs("#[attr] (..0)", outer);
+    check_expr_attrs("#[attr] (..)", outer);
+
+    check_expr_attrs("#[attr] foo::bar::baz", outer);
+
+    check_expr_attrs("#[attr] &0", outer);
+    check_expr_attrs("#[attr] &mut 0", outer);
+    check_expr_attrs("#[attr] & #[attr] 0", outer);
+    check_expr_attrs("#[attr] &mut #[attr] 0", outer);
+    reject_expr_parse("#[attr] &#![attr] 0");
+    reject_expr_parse("#[attr] &mut #![attr] 0");
+
+    check_expr_attrs("#[attr] break", outer);
+    check_expr_attrs("#[attr] continue", outer);
+    check_expr_attrs("#[attr] return", outer);
+
+    check_expr_attrs("#[attr] foo!()", outer);
+    check_expr_attrs("#[attr] foo!(#![attr])", outer);
+    check_expr_attrs("#[attr] foo![]", outer);
+    check_expr_attrs("#[attr] foo![#![attr]]", outer);
+    check_expr_attrs("#[attr] foo!{}", outer);
+    check_expr_attrs("#[attr] foo!{#![attr]}", outer);
+
+    check_expr_attrs("#[attr] Foo { #![attr] bar: baz }", both);
+    check_expr_attrs("#[attr] Foo { #![attr] ..foo }", both);
+    check_expr_attrs("#[attr] Foo { #![attr] bar: baz, ..foo }", both);
+
+    check_expr_attrs("#[attr] (#![attr] 0)", both);
+
+    // Look at statements in their natural habitat...
+    check_expr_attrs("{
+        #[attr] let _ = 0;
+        #[attr] 0;
+        #[attr] foo!();
+        #[attr] foo!{}
+        #[attr] foo![];
+    }", none);
+
+    check_stmt_attrs("#[attr] let _ = 0", outer);
+    check_stmt_attrs("#[attr] 0",         outer);
+    check_stmt_attrs("#[attr] {#![attr]}", both);
+    check_stmt_attrs("#[attr] foo!()",    outer);
+    check_stmt_attrs("#[attr] foo![]",    outer);
+    check_stmt_attrs("#[attr] foo!{}",    outer);
+
+    reject_stmt_parse("#[attr] #![attr] let _ = 0");
+    reject_stmt_parse("#[attr] #![attr] 0");
+    reject_stmt_parse("#[attr] #![attr] foo!()");
+    reject_stmt_parse("#[attr] #![attr] foo![]");
+    reject_stmt_parse("#[attr] #![attr] foo!{}");
+
+    // FIXME: Allow attributes in pattern constexprs?
+    // would require parens in patterns to allow disambiguation...
+
+    reject_expr_parse("match 0 {
+        0...#[attr] 10 => ()
+    }");
+    reject_expr_parse("match 0 {
+        0...#[attr] -10 => ()
+    }");
+    reject_expr_parse("match 0 {
+        0...-#[attr] 10 => ()
+    }");
+    reject_expr_parse("match 0 {
+        0...#[attr] FOO => ()
+    }");
+
+    // make sure we don't catch this bug again...
+    reject_expr_parse("{
+        fn foo() {
+            #[attr];
+        }
+    }");
+    reject_expr_parse("{
+        fn foo() {
+            #[attr]
+        }
+    }");
+}
diff --git a/src/test/run-pass/cfg_stmt_expr.rs b/src/test/run-pass/cfg_stmt_expr.rs
new file mode 100644
index 00000000000..f2b2a567b04
--- /dev/null
+++ b/src/test/run-pass/cfg_stmt_expr.rs
@@ -0,0 +1,97 @@
+// 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.
+
+#![deny(non_snake_case)]
+
+fn main() {
+    let a = 413;
+    #[cfg(unset)]
+    let a = ();
+    assert_eq!(a, 413);
+
+    let mut b = 612;
+    #[cfg(unset)]
+    {
+        b = 1111;
+    }
+    assert_eq!(b, 612);
+
+    #[cfg(unset)]
+    undefined_fn();
+
+    #[cfg(unset)]
+    undefined_macro!();
+    #[cfg(unset)]
+    undefined_macro![];
+    #[cfg(unset)]
+    undefined_macro!{};
+
+    // pretty printer bug...
+    // #[cfg(unset)]
+    // undefined_macro!{}
+
+    let () = (#[cfg(unset)] 341,); // Should this also work on parens?
+    let t = (1, #[cfg(unset)] 3, 4);
+    assert_eq!(t, (1, 4));
+
+    let f = |_: u32, _: u32| ();
+    f(2, 1, #[cfg(unset)] 6);
+
+    let _: u32 = a.clone(#[cfg(unset)] undefined);
+
+    let _: [(); 0] = [#[cfg(unset)] 126];
+    let t = [#[cfg(unset)] 1, 2, 6];
+    assert_eq!(t, [2, 6]);
+
+    {
+        let r;
+        #[cfg(unset)]
+        (r = 5);
+        #[cfg(not(unset))]
+        (r = 10);
+        assert_eq!(r, 10);
+    }
+
+    // check that macro expanded code works
+
+    macro_rules! if_cfg {
+        ($cfg:meta $ib:block else $eb:block) => {
+            {
+                let r;
+                #[cfg($cfg)]
+                (r = $ib);
+                #[cfg(not($cfg))]
+                (r = $eb);
+                r
+            }
+        }
+    }
+
+    let n = if_cfg!(unset {
+        413
+    } else {
+        612
+    });
+
+    assert_eq!((#[cfg(unset)] 1, #[cfg(not(unset))] 2), (2,));
+    assert_eq!(n, 612);
+
+    // check that lints work
+
+    #[allow(non_snake_case)]
+    let FOOBAR = {
+        fn SYLADEX() {}
+    };
+
+    #[allow(non_snake_case)]
+    {
+        fn CRUXTRUDER() {}
+    }
+}