]> git.lizzy.rs Git - rust.git/commitdiff
Add syntax support for attributes on expressions and all syntax
authorMarvin Löbel <loebel.marvin@gmail.com>
Tue, 3 Nov 2015 16:39:51 +0000 (17:39 +0100)
committerMarvin Löbel <loebel.marvin@gmail.com>
Thu, 26 Nov 2015 20:46:12 +0000 (21:46 +0100)
nodes in statement position.

Extended #[cfg] folder to allow removal of statements, and
of expressions in optional positions like expression lists and trailing
block expressions.

Extended lint checker to recognize lint levels on expressions and
locals.

34 files changed:
src/librustc/lint/context.rs
src/librustc/middle/check_match.rs
src/librustc_driver/pretty.rs
src/librustc_front/fold.rs
src/librustc_front/hir.rs
src/librustc_front/lowering.rs
src/librustc_lint/bad_style.rs
src/libsyntax/ast.rs
src/libsyntax/attr.rs
src/libsyntax/config.rs
src/libsyntax/ext/asm.rs
src/libsyntax/ext/base.rs
src/libsyntax/ext/build.rs
src/libsyntax/ext/concat_idents.rs
src/libsyntax/ext/deriving/debug.rs
src/libsyntax/ext/expand.rs
src/libsyntax/ext/quote.rs
src/libsyntax/fold.rs
src/libsyntax/parse/mod.rs
src/libsyntax/parse/parser.rs
src/libsyntax/parse/token.rs
src/libsyntax/print/pprust.rs
src/libsyntax/test.rs
src/libsyntax/visit.rs
src/test/parse-fail/attr-before-ext.rs [deleted file]
src/test/parse-fail/attr-before-let.rs [deleted file]
src/test/parse-fail/attr-before-stmt.rs [deleted file]
src/test/parse-fail/attr-dangling-in-fn.rs
src/test/parse-fail/doc-before-macro.rs [deleted file]
src/test/parse-fail/doc-before-rbrace.rs
src/test/parse-fail/doc-before-semi.rs
src/test/pretty/stmt_expr_attributes.rs [new file with mode: 0644]
src/test/run-pass-fulldeps/ast_stmt_expr_attr.rs [new file with mode: 0644]
src/test/run-pass/cfg_stmt_expr.rs [new file with mode: 0644]

index 74a61f700e3b8ac172fcfa3185f72f569c15a8b8..9a30ec1a2b6034b162e4ec0ccedca713a4286047 100644 (file)
@@ -41,7 +41,7 @@
 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 @@ fn visit_pat(&mut self, p: &hir::Pat) {
     }
 
     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 @@ fn visit_mod(&mut self, m: &hir::Mod, s: Span, n: ast::NodeId) {
     }
 
     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) {
index 3e6cf07d86f0b055415be50e5c3f4f3a39ea73c2..a292c83682c0e3340da742af8f078f4e70925c3a 100644 (file)
@@ -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,
     })
 }
 
index 630c42db68c79051d987b8f4a1f3d34f411b912f..9a489ac6fdf71a29a408d82a0ae28a7f21bfea9f 100644 (file)
@@ -654,6 +654,7 @@ fn expr_to_block(rules: ast::BlockCheckMode, e: Option<P<ast::Expr>>) -> P<ast::
                 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))
index 7ec4e1ba33121d99f67fa8de3b78c1c8397ba6b9..028ecbc336e7e64466352b7c79367fa2a3baddf3 100644 (file)
@@ -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)),
     }
 }
 
index 95d73daa632b15eaea6752a8f7d6c2d48ddde8bb..f0f9512cd0f0ee372c25ab5b733d06b2527589a5 100644 (file)
@@ -41,6 +41,7 @@
 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 {
index b984f23c4c02a8541bd63f8dad750fc597a6a1ed..0f89ddb99870fa76251e9f6932fb35307ef650ce 100644 (file)
@@ -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)
 }
 
 
index 2146dc8e9b94bcd204018d3a076ee1b1b4c61aac..b5f8be496fb02f451867a5d26e61e2d4ca1f5a39 100644 (file)
@@ -138,7 +138,7 @@ fn check_generics(&mut self, cx: &LateContext, it: &hir::Generics) {
 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)]
index ab62c8d9421ae5aae0b6a7bda649f9b590b1539d..48c5be8e07eab8bfbd59382ecf0c174dea9bf36b 100644 (file)
@@ -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;
index 153a81e6c54756099562bf026ab7035d049e1c42..18f7311418f6a8b3cb39f4b9f02dca0901acf568 100644 (file)
@@ -16,6 +16,8 @@
 
 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 @@ fn is_ffi_safe(self) -> bool {
         }
     }
 }
+
+/// 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)
+                    }
+                },
+            }
+        })
+    }
+}
index 10731178c0610fb85f7d02c62f712b2d8b430430..a867b45075f327e784a10b9f3788267869ff2b1b 100644 (file)
@@ -20,8 +20,9 @@
 
 /// 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 @@ fn fold_item_underscore(&mut self, item: ast::Item_) -> ast::Item_ {
         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 @@ fn fold_item(&mut self, item: P<ast::Item>) -> SmallVector<P<ast::Item>> {
     }
 }
 
-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
         };
 
index ac18b9c0e49406e45accb6b4f28c35c31b20be03..d968858f634eb60f20267808233d1cb90f0604ce 100644 (file)
@@ -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,
     }))
 }
index 55f0fa5675ac84d10250bb82b27f5c2698c73d01..8c93327c322b25fc8140ac20cb2750c2f3c9af31 100644 (file)
@@ -349,6 +349,7 @@ pub fn raw_expr(sp: Span) -> P<ast::Expr> {
             id: ast::DUMMY_NODE_ID,
             node: ast::ExprLit(P(codemap::respan(sp, ast::LitBool(false)))),
             span: sp,
+            attrs: None,
         })
     }
 
index 4c10a7496835bfaba720230c9ca2baa259bcbf0a..806f5a7ee22ed36cec7529a9d89836dd503eb515 100644 (file)
@@ -525,6 +525,7 @@ fn stmt_let(&self, sp: Span, mutbl: bool, ident: ast::Ident,
             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 @@ fn stmt_let_typed(&self,
             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 @@ fn expr(&self, span: Span, node: ast::Expr_) -> P<ast::Expr> {
             id: ast::DUMMY_NODE_ID,
             node: node,
             span: span,
+            attrs: None,
         })
     }
 
index e9e36546ad6db6c7ff035ad2de9f4f06b09b5560..c2233202b2f81067ee9abbd9413f358e9d7cc09e 100644 (file)
@@ -67,6 +67,7 @@ pub fn expand_syntax_ext<'cx>(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree])
             }
         ),
         span: sp,
+        attrs: None,
     });
     MacEager::expr(e)
 }
index 2b2e5309883871101b14f5ea28caf52d8116a85d..9488cfb86fc9fd6d2439acb2d65517e192b76a06 100644 (file)
@@ -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)))
index 9b1a7a50201b022aee19c636107c7dc3acf0b27a..132b29c76232e28b5ea175397cbb68b9b95a5261 100644 (file)
@@ -17,7 +17,7 @@
 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::*;
 
 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 {
index 5e5b815818161de75828c14193a6c4511d8de809..fc6cacb40f1f37d95511ad8f083cce399c59fe0a 100644 (file)
@@ -242,6 +242,7 @@ fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
                 id: ast::DUMMY_NODE_ID,
                 node: ast::ExprLit(P(self.clone())),
                 span: DUMMY_SP,
+                attrs: None,
             }).to_tokens(cx)
         }
     }
index 3dba6cbecbd1cb62ff278f8ca079b63e41fc8546..1d499bce3c695ecb501263a9c1763aec5f01db5b 100644 (file)
@@ -134,6 +134,14 @@ fn fold_expr(&mut self, e: P<Expr>) -> P<Expr> {
         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
         }))
     }
index 7e2fd09a37316ecb267c98a6cc8b71fdb460646b..e9c8173a4d9802e4bc6b0f5d7a36f0e6a06aa8e9 100644 (file)
@@ -699,7 +699,8 @@ fn sp(a: u32, b: u32) -> Span {
                             }
                         ),
                     }),
-                    span: sp(0, 1)
+                    span: sp(0, 1),
+                    attrs: None,
                    }))
     }
 
@@ -721,7 +722,8 @@ fn sp(a: u32, b: u32) -> Span {
                                 }
                             )
                         }),
-                    span: sp(0, 6)
+                    span: sp(0, 6),
+                    attrs: None,
                    }))
     }
 
@@ -848,9 +850,11 @@ fn string_to_tts_1() {
                                 }
                             ),
                         }),
-                        span:sp(7,8)
+                        span:sp(7,8),
+                        attrs: None,
                     }))),
-                    span:sp(0,8)
+                    span:sp(0,8),
+                    attrs: None,
                    }))
     }
 
@@ -869,7 +873,8 @@ fn string_to_tts_1() {
                                 }
                                ),
                             }),
-                           span: sp(0,1)}),
+                           span: sp(0,1),
+                           attrs: None}),
                                            ast::DUMMY_NODE_ID),
                        span: sp(0,1)})))
 
@@ -963,7 +968,8 @@ fn parser_done(p: Parser){
                                                             }
                                                         ),
                                                       }),
-                                                span: sp(17,18)}),
+                                                span: sp(17,18),
+                                                attrs: None,}),
                                                 ast::DUMMY_NODE_ID),
                                             span: sp(17,19)})),
                                         expr: None,
index 56a06f70ed4b4406dded7611872c3361d21ed0af..2f67ecad4e74d86502838e816bed3e5db0d0d61c 100644 (file)
@@ -56,6 +56,7 @@
 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 @@ pub fn parse_lit(&mut self) -> PResult<Lit> {
     }
 
     /// 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 @@ pub fn parse_field(&mut self) -> PResult<Field> {
         })
     }
 
-    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 @@ pub fn mk_assign_op(&mut self, binop: ast::BinOp,
         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 @@ pub fn mk_lit_u32(&mut self, i: u32) -> P<Expr> {
             id: ast::DUMMY_NODE_ID,
             node: ExprLit(lv_lit),
             span: *span,
+            attrs: attrs,
         })
     }
 
@@ -2002,9 +2028,20 @@ fn expect_open_delim(&mut self) -> PResult<token::DelimToken> {
     /// 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 @@ pub fn parse_bottom_expr(&mut self) -> PResult<P<Expr>> {
             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 @@ pub fn parse_bottom_expr(&mut self) -> PResult<P<Expr>> {
 
                 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 @@ pub fn parse_bottom_expr(&mut self) -> PResult<P<Expr>> {
             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 @@ pub fn parse_bottom_expr(&mut self) -> PResult<P<Expr>> {
                     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 @@ pub fn parse_bottom_expr(&mut self) -> PResult<P<Expr>> {
                     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 @@ pub fn parse_bottom_expr(&mut self) -> PResult<P<Expr>> {
                         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 @@ pub fn parse_bottom_expr(&mut self) -> PResult<P<Expr>> {
 
                         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 @@ pub fn parse_bottom_expr(&mut self) -> PResult<P<Expr>> {
                             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 @@ pub fn parse_bottom_expr(&mut self) -> PResult<P<Expr>> {
                             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 @@ pub fn parse_bottom_expr(&mut self) -> PResult<P<Expr>> {
             }
         }
 
-        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 @@ pub fn parse_dot_or_call_expr_with(&mut self, e0: P<Expr>) -> PResult<P<Expr>> {
                             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 @@ pub fn parse_dot_or_call_expr_with(&mut self, e0: P<Expr>) -> PResult<P<Expr>> {
 
                             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 @@ pub fn parse_dot_or_call_expr_with(&mut self, e0: P<Expr>) -> PResult<P<Expr>> {
                         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 @@ pub fn parse_dot_or_call_expr_with(&mut self, e0: P<Expr>) -> PResult<P<Expr>> {
                 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 @@ pub fn parse_dot_or_call_expr_with(&mut self, e0: P<Expr>) -> PResult<P<Expr>> {
                 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 @@ pub fn parse_all_token_trees(&mut self) -> PResult<Vec<TokenTree>> {
     }
 
     /// 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 @@ pub fn parse_assoc_expr_with(&mut self,
             // 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 @@ pub fn parse_assoc_expr_with(&mut self,
                     // 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 @@ pub fn parse_assoc_expr_with(&mut self,
                         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 @@ pub fn parse_assoc_expr_with(&mut self,
                     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 @@ pub fn parse_assoc_expr_with(&mut self,
                     };
                     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 @@ fn check_no_chained_comparison(&mut self, lhs: &Expr, outer_op: &AssocOp) {
     }
 
     /// 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 @@ fn parse_prefix_range_expr(&mut self) -> PResult<P<Expr>> {
             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 @@ fn is_at_start_of_range_notation_rhs(&self) -> bool {
     }
 
     /// 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 @@ pub fn parse_if_expr(&mut self) -> PResult<P<Expr>> {
             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 @@ pub fn parse_if_let_expr(&mut self) -> PResult<P<Expr>> {
         } 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 @@ pub fn parse_lambda_expr(&mut self, lo: BytePos, capture_clause: CaptureClause)
         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 @@ pub fn parse_arm(&mut self) -> PResult<Arm> {
             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 @@ pub fn parse_arm(&mut self) -> PResult<Arm> {
 
     /// 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 @@ pub fn with_res<F>(&mut self, r: Restrictions, f: F) -> PResult<P<Expr>>
     }
 
     /// 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 @@ fn parse_pat_range_end(&mut self) -> PResult<P<Expr>> {
                 (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 @@ pub fn parse_pat(&mut self) -> PResult<P<Pat>> {
                       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 @@ pub fn parse_pat(&mut self) -> PResult<P<Pat>> {
                 }
             } 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 @@ fn parse_pat_ident(&mut self,
     }
 
     /// 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 @@ fn parse_local(&mut self) -> PResult<P<Local>> {
             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 @@ pub fn parse_stmt(&mut self) -> PResult<Option<P<Stmt>>> {
     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 @@ fn check_expected_item(p: &mut Parser, attrs: &[Attribute]) {
             };
 
             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 @@ fn check_expected_item(p: &mut Parser, attrs: &[Attribute]) {
                             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 @@ fn parse_block_tail(&mut self, lo: BytePos, s: BlockCheckMode) -> PResult<P<Bloc
                 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 @@ fn parse_block_tail(&mut self, lo: BytePos, s: BlockCheckMode) -> PResult<P<Bloc
                         }
                     }
                 }
-                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 @@ fn parse_block_tail(&mut self, lo: BytePos, s: BlockCheckMode) -> PResult<P<Bloc
                             // 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 @@ fn parse_opt_abi(&mut self) -> PResult<Option<abi::Abi>> {
     /// 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 @@ fn parse_item_(&mut self, attrs: Vec<Attribute>,
                                     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 @@ fn parse_foreign_item(&mut self) -> PResult<Option<P<ForeignItem>>> {
         }
 
         // 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 @@ fn parse_macro_use_or_failure(
         &mut self,
         attrs: Vec<Attribute> ,
         macros_allowed: bool,
+        attributes_allowed: bool,
         lo: BytePos,
         visibility: Visibility
     ) -> PResult<Option<P<Item>>> {
@@ -5566,7 +5722,7 @@ fn parse_macro_use_or_failure(
             }
         }
 
-        if !attrs.is_empty() {
+        if !attributes_allowed && !attrs.is_empty() {
             self.expected_item_err(&attrs);
         }
         Ok(None)
@@ -5574,7 +5730,7 @@ fn parse_macro_use_or_failure(
 
     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)
     }
 
 
index 5e4449af60405876b19c54ef4c86b14b5096d5d1..17b7d8dbaece9588d1dbcf846a0ac32391381619 100644 (file)
@@ -202,6 +202,7 @@ pub fn can_begin_expr(&self) -> bool {
             Interpolated(NtIdent(..))   => true,
             Interpolated(NtBlock(..))   => true,
             Interpolated(NtPath(..))    => true,
+            Pound                       => true, // for expression attributes
             _                           => false,
         }
     }
index 6de86de9c54eb6dfb57e150b92302c1e5b5d0ce6..6919bc4efdd8f3131048cd1bfc568dd67894e3e4 100644 (file)
@@ -13,6 +13,7 @@
 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 @@ pub fn new(cm: &'a CodeMap,
                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 @@ fn print_string(&mut self, st: &str,
     }
 
     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 @@ fn print_attribute(&mut self, attr: &ast::Attribute) -> io::Result<()> {
     }
 
     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 @@ fn print_meta_item(&mut self, item: &ast::MetaItem) -> io::Result<()> {
         }
         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 @@ pub fn cbox(&mut self, u: usize) -> io::Result<()> {
         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 @@ pub fn word_nbsp(&mut self, w: &str) -> io::Result<()> {
 
     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 @@ pub fn bclose_maybe_open (&mut self, span: codemap::Span,
         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 @@ pub fn in_cbox(&self) -> bool {
         }
     }
 
-    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 @@ pub fn print_item(&mut self, item: &ast::Item) -> io::Result<()> {
                 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 @@ pub fn print_item(&mut self, item: &ast::Item) -> io::Result<()> {
                 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 @@ pub fn print_variants(&mut self,
             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 @@ pub fn print_impl_item(&mut self, ii: &ast::ImplItem) -> io::Result<()> {
                 // 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 @@ pub fn print_stmt(&mut self, st: &ast::Stmt) -> io::Result<()> {
             }
             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 @@ pub fn print_stmt(&mut self, st: &ast::Stmt) -> io::Result<()> {
         }
         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 @@ pub fn print_block(&mut self, blk: &ast::Block) -> io::Result<()> {
     }
 
     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 @@ pub fn print_block_unclosed_indent(&mut self, blk: &ast::Block,
     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 @@ pub fn print_block_maybe_unclosed(&mut self,
         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 @@ fn print_else(&mut self, els: Option<&ast::Expr>) -> io::Result<()> {
                 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 @@ fn print_else(&mut self, els: Option<&ast::Expr>) -> io::Result<()> {
                     }
                     // "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 @@ fn print_else(&mut self, els: Option<&ast::Expr>) -> io::Result<()> {
                     }
                     // "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 @@ pub fn print_mac(&mut self, m: &ast::Mac, delim: token::DelimToken)
         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 @@ fn print_expr_in_place(&mut self,
         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 @@ fn print_expr_vec(&mut self, exprs: &[P<ast::Expr>]) -> io::Result<()> {
 
     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 @@ fn print_expr_repeat(&mut self,
     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 @@ fn print_expr_struct(&mut self,
             |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 @@ fn print_expr_struct(&mut self,
         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 @@ fn print_expr_addr_of(&mut self,
     }
 
     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 @@ pub fn print_expr(&mut self, expr: &ast::Expr) -> io::Result<()> {
                 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 @@ pub fn print_expr(&mut self, expr: &ast::Expr) -> io::Result<()> {
                 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 @@ pub fn print_expr(&mut self, expr: &ast::Expr) -> io::Result<()> {
                 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 @@ pub fn print_expr(&mut self, expr: &ast::Expr) -> io::Result<()> {
                 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 @@ pub fn print_expr(&mut self, expr: &ast::Expr) -> io::Result<()> {
                 }
                 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 @@ pub fn print_expr(&mut self, expr: &ast::Expr) -> io::Result<()> {
                     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 @@ pub fn print_expr(&mut self, expr: &ast::Expr) -> io::Result<()> {
             }
             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 @@ pub fn print_expr(&mut self, expr: &ast::Expr) -> io::Result<()> {
             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 @@ pub fn print_decl(&mut self, decl: &ast::Decl) -> io::Result<()> {
         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 @@ pub fn print_pat(&mut self, pat: &ast::Pat) -> io::Result<()> {
                 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 @@ fn print_arm(&mut self, arm: &ast::Arm) -> io::Result<()> {
         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 @@ fn print_arm(&mut self, arm: &ast::Arm) -> io::Result<()> {
         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 @@ pub fn print_mt(&mut self, mt: &ast::MutTy) -> io::Result<()> {
     }
 
     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 @@ pub fn print_fn_output(&mut self, decl: &ast::FnDecl) -> io::Result<()> {
         }
 
         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 @@ pub fn print_ty_fn(&mut self,
                        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));
index 3e02476443a990a04ea1822606cda11ea8c00d9a..aab106b7a6702df8daec46cb1b32c25743c88355 100644 (file)
@@ -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,
     })
 }
 
index 5d4a462e844916dd9dd5303ddfb0be783d57f62d..2d97e2680d7fb4abfedb6800581278b3ef072a41 100644 (file)
@@ -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 (file)
index e15350f..0000000
+++ /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 (file)
index 03dabb9..0000000
+++ /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 (file)
index bc30604..0000000
+++ /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() {
-}
index f2f4ecadd7a1dcfd3f65dd96cfc7438e334b53e9..7b731b6d6de609632241c3aebeb5b62d1253a0f1 100644 (file)
@@ -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 (file)
index 44435bd..0000000
+++ /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
-}
index 8b69c385378f851eb6339d926acca4aad22d9362..295d5ae432ecb3f48f1c3a1216c115582919daaf 100644 (file)
@@ -12,5 +12,5 @@
 
 fn main() {
     println!("Hi"); /// hi
-    //~^ ERROR expected item after doc comment
 }
+//~^ ERROR expected statement
index 42c58af76d8c517e19eaa30d2c1276ae82c1703a..6a8906953be091ade684055cd72cf09527813445 100644 (file)
@@ -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 (file)
index 0000000..48c2a04
--- /dev/null
@@ -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 (file)
index 0000000..4c20b39
--- /dev/null
@@ -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 (file)
index 0000000..f2b2a56
--- /dev/null
@@ -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() {}
+    }
+}