From 2a8f358de7ee71934b8129dff5d908730454d7b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20L=C3=B6bel?= Date: Tue, 3 Nov 2015 17:39:51 +0100 Subject: [PATCH] Add syntax support for attributes on expressions and all syntax nodes in statement position. Extended #[cfg] folder to allow removal of statements, and of expressions in optional positions like expression lists and trailing block expressions. Extended lint checker to recognize lint levels on expressions and locals. --- src/librustc/lint/context.rs | 19 +- src/librustc/middle/check_match.rs | 3 +- src/librustc_driver/pretty.rs | 1 + src/librustc_front/fold.rs | 8 +- src/librustc_front/hir.rs | 3 + src/librustc_front/lowering.rs | 65 ++- src/librustc_lint/bad_style.rs | 2 +- src/libsyntax/ast.rs | 142 +++++- src/libsyntax/attr.rs | 82 ++++ src/libsyntax/config.rs | 107 +++-- src/libsyntax/ext/asm.rs | 3 +- src/libsyntax/ext/base.rs | 1 + src/libsyntax/ext/build.rs | 3 + src/libsyntax/ext/concat_idents.rs | 1 + src/libsyntax/ext/deriving/debug.rs | 1 + src/libsyntax/ext/expand.rs | 37 +- src/libsyntax/ext/quote.rs | 1 + src/libsyntax/fold.rs | 67 ++- src/libsyntax/parse/mod.rs | 18 +- src/libsyntax/parse/parser.rs | 448 ++++++++++++------ src/libsyntax/parse/token.rs | 1 + src/libsyntax/print/pprust.rs | 229 +++++---- src/libsyntax/test.rs | 9 +- src/libsyntax/visit.rs | 7 +- src/test/parse-fail/attr-before-ext.rs | 16 - src/test/parse-fail/attr-before-let.rs | 16 - src/test/parse-fail/attr-before-stmt.rs | 21 - src/test/parse-fail/attr-dangling-in-fn.rs | 2 +- src/test/parse-fail/doc-before-macro.rs | 17 - src/test/parse-fail/doc-before-rbrace.rs | 2 +- src/test/parse-fail/doc-before-semi.rs | 2 +- src/test/pretty/stmt_expr_attributes.rs | 281 +++++++++++ .../run-pass-fulldeps/ast_stmt_expr_attr.rs | 306 ++++++++++++ src/test/run-pass/cfg_stmt_expr.rs | 97 ++++ 34 files changed, 1602 insertions(+), 416 deletions(-) delete mode 100644 src/test/parse-fail/attr-before-ext.rs delete mode 100644 src/test/parse-fail/attr-before-let.rs delete mode 100644 src/test/parse-fail/attr-before-stmt.rs delete mode 100644 src/test/parse-fail/doc-before-macro.rs create mode 100644 src/test/pretty/stmt_expr_attributes.rs create mode 100644 src/test/run-pass-fulldeps/ast_stmt_expr_attr.rs create mode 100644 src/test/run-pass/cfg_stmt_expr.rs diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs index 74a61f700e3..9a30ec1a2b6 100644 --- a/src/librustc/lint/context.rs +++ b/src/librustc/lint/context.rs @@ -41,7 +41,7 @@ use syntax::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) { diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs index 3e6cf07d86f..a292c83682c 100644 --- a/src/librustc/middle/check_match.rs +++ b/src/librustc/middle/check_match.rs @@ -409,7 +409,8 @@ fn const_val_to_expr(value: &ConstVal) -> P { P(hir::Expr { id: 0, node: hir::ExprLit(P(Spanned { node: node, span: DUMMY_SP })), - span: DUMMY_SP + span: DUMMY_SP, + attrs: None, }) } diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs index 630c42db68c..9a489ac6fdf 100644 --- a/src/librustc_driver/pretty.rs +++ b/src/librustc_driver/pretty.rs @@ -654,6 +654,7 @@ fn expr_to_block(rules: ast::BlockCheckMode, e: Option>) -> P(data: ParenthesizedPara } pub fn noop_fold_local(l: P, fld: &mut T) -> P { - 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(p: P, folder: &mut T) -> P { }) } -pub fn noop_fold_expr(Expr { id, node, span }: Expr, folder: &mut T) -> Expr { +pub fn noop_fold_expr(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(Expr { id, node, span }: Expr, folder: &mut T) } }, span: folder.new_span(span), + attrs: attrs.map_opt_attrs(|attrs| fold_attrs(attrs, folder)), } } diff --git a/src/librustc_front/hir.rs b/src/librustc_front/hir.rs index 95d73daa632..f0f9512cd0f 100644 --- a/src/librustc_front/hir.rs +++ b/src/librustc_front/hir.rs @@ -41,6 +41,7 @@ use syntax::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>, pub id: NodeId, pub span: Span, + pub attrs: ThinAttributes, } pub type Decl = Spanned; @@ -609,6 +611,7 @@ pub struct Expr { pub id: NodeId, pub node: Expr_, pub span: Span, + pub attrs: ThinAttributes, } impl fmt::Debug for Expr { diff --git a/src/librustc_front/lowering.rs b/src/librustc_front/lowering.rs index b984f23c4c0..0f89ddb9987 100644 --- a/src/librustc_front/lowering.rs +++ b/src/librustc_front/lowering.rs @@ -331,6 +331,7 @@ pub fn lower_local(lctx: &LoweringContext, l: &Local) -> P { 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 { 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 { ExprMac(_) => panic!("Shouldn't exist here"), }, span: e.span, + attrs: e.attrs.clone(), }) } @@ -1552,52 +1561,62 @@ fn arm(pats: Vec>, expr: P) -> hir::Arm { } } -fn expr_break(lctx: &LoweringContext, span: Span) -> P { - expr(lctx, span, hir::ExprBreak(None)) +fn expr_break(lctx: &LoweringContext, span: Span, + attrs: ThinAttributes) -> P { + expr(lctx, span, hir::ExprBreak(None), attrs) } fn expr_call(lctx: &LoweringContext, span: Span, e: P, - args: Vec>) + args: Vec>, + attrs: ThinAttributes) -> P { - 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 { - expr_path(lctx, path_ident(span, id)) +fn expr_ident(lctx: &LoweringContext, span: Span, id: Ident, + attrs: ThinAttributes) -> P { + expr_path(lctx, path_ident(span, id), attrs) } -fn expr_mut_addr_of(lctx: &LoweringContext, span: Span, e: P) -> P { - expr(lctx, span, hir::ExprAddrOf(hir::MutMutable, e)) +fn expr_mut_addr_of(lctx: &LoweringContext, span: Span, e: P, + attrs: ThinAttributes) -> P { + expr(lctx, span, hir::ExprAddrOf(hir::MutMutable, e), attrs) } -fn expr_path(lctx: &LoweringContext, path: hir::Path) -> P { - expr(lctx, path.span, hir::ExprPath(None, path)) +fn expr_path(lctx: &LoweringContext, path: hir::Path, + attrs: ThinAttributes) -> P { + expr(lctx, path.span, hir::ExprPath(None, path), attrs) } fn expr_match(lctx: &LoweringContext, span: Span, arg: P, arms: Vec, - source: hir::MatchSource) + source: hir::MatchSource, + attrs: ThinAttributes) -> P { - expr(lctx, span, hir::ExprMatch(arg, arms, source)) + expr(lctx, span, hir::ExprMatch(arg, arms, source), attrs) } -fn expr_block(lctx: &LoweringContext, b: P) -> P { - expr(lctx, b.span, hir::ExprBlock(b)) +fn expr_block(lctx: &LoweringContext, b: P, + attrs: ThinAttributes) -> P { + expr(lctx, b.span, hir::ExprBlock(b), attrs) } -fn expr_tuple(lctx: &LoweringContext, sp: Span, exprs: Vec>) -> P { - expr(lctx, sp, hir::ExprTup(exprs)) +fn expr_tuple(lctx: &LoweringContext, sp: Span, exprs: Vec>, + attrs: ThinAttributes) -> P { + expr(lctx, sp, hir::ExprTup(exprs), attrs) } -fn expr(lctx: &LoweringContext, span: Span, node: hir::Expr_) -> P { +fn expr(lctx: &LoweringContext, span: Span, node: hir::Expr_, + attrs: ThinAttributes) -> P { 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) + ex: P, + attrs: ThinAttributes) -> P { 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>, expr: P, span: Span, - rule: hir::BlockCheckMode) + rule: hir::BlockCheckMode, + attrs: ThinAttributes) -> P { let id = lctx.next_id(); expr_block(lctx, @@ -1765,7 +1787,8 @@ fn signal_block_expr(lctx: &LoweringContext, id: id, stmts: stmts, expr: Some(expr), - })) + }), + attrs) } diff --git a/src/librustc_lint/bad_style.rs b/src/librustc_lint/bad_style.rs index 2146dc8e9b9..b5f8be496fb 100644 --- a/src/librustc_lint/bad_style.rs +++ b/src/librustc_lint/bad_style.rs @@ -138,7 +138,7 @@ 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)] diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index ab62c8d9421..48c5be8e07e 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -692,8 +692,21 @@ pub enum Stmt_ { /// Expr with trailing semi-colon (may have any type): StmtSemi(P, NodeId), - StmtMac(P, MacStmtStyle), + StmtMac(P, 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>, 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; @@ -730,6 +753,15 @@ pub enum Decl_ { DeclItem(P), } +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, } +/// A list of attributes, behind a optional box as +/// a space optimization. +pub type ThinAttributes = Option>>; + +pub trait ThinAttributesExt { + fn map_opt_attrs(self, f: F) -> Self + where F: FnOnce(Vec) -> Vec; + fn prepend_outer(mut self, attrs: Self) -> Self; + fn append_inner(mut self, attrs: Self) -> Self; + fn update(&mut self, f: F) + where Self: Sized, + F: FnOnce(Self) -> Self; + fn as_attrs(&self) -> &[Attribute]; + fn into_attrs(self) -> Vec; +} + +// FIXME: Rename inner/outer +// FIXME: Rename opt_attrs + +impl ThinAttributesExt for ThinAttributes { + fn map_opt_attrs(self, f: F) -> Self + where F: FnOnce(Vec) -> Vec { + + // 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(&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 { + match self { + Some(b) => *b, + None => Vec::new(), + } + } +} + +pub trait AttributesExt { + fn into_opt_attrs(self) -> ThinAttributes; +} +impl AttributesExt for Vec { + fn into_opt_attrs(self) -> ThinAttributes { + if self.len() == 0 { + None + } else { + Some(Box::new(self)) + } + } +} + #[cfg(test)] mod tests { use serialize; diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs index 153a81e6c54..18f7311418f 100644 --- a/src/libsyntax/attr.rs +++ b/src/libsyntax/attr.rs @@ -16,6 +16,8 @@ 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 + fn with_attrs(self, attrs: ThinAttributes) -> Self; +} + +impl WithAttrs for P { + fn with_attrs(self, attrs: ThinAttributes) -> Self { + self.map(|mut e| { + e.attrs.update(|a| a.append_inner(attrs)); + e + }) + } +} + +impl WithAttrs for P { + 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 { + 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 { + 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 { + fn with_attrs(self, attrs: ThinAttributes) -> Self { + self.map(|Spanned { span, node }| { + Spanned { + span: span, + node: match node { + StmtDecl(decl, id) => StmtDecl(decl.with_attrs(attrs), id), + StmtExpr(expr, id) => StmtExpr(expr.with_attrs(attrs), id), + StmtSemi(expr, id) => StmtSemi(expr.with_attrs(attrs), id), + StmtMac(mac, style, mut ats) => { + ats.update(|a| a.append_inner(attrs)); + StmtMac(mac, style, ats) + } + }, + } + }) + } +} diff --git a/src/libsyntax/config.rs b/src/libsyntax/config.rs index 10731178c06..a867b45075f 100644 --- a/src/libsyntax/config.rs +++ b/src/libsyntax/config.rs @@ -20,8 +20,9 @@ /// A folder that strips out items that do not belong in the current /// configuration. -struct Context 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 fold::Folder for Context 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) -> P { - 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) -> P { + // 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) -> Option> { + fold_opt_expr(self, expr) + } + fn fold_stmt(&mut self, stmt: P) -> SmallVector> { + 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) -> SmallVector> { } } -pub fn strip_items(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(cx: &mut Context, vdata: ast::VariantData) -> ast::VariantD } } -fn retain_stmt(cx: &mut Context, stmt: &ast::Stmt) -> bool where - F: FnMut(&[ast::Attribute]) -> bool +fn fold_opt_expr(cx: &mut Context, expr: P) -> Option> + 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(cx: &mut Context, b: P) -> P where - F: FnMut(&[ast::Attribute]) -> bool -{ - b.map(|ast::Block {id, stmts, expr, rules, span}| { - let resulting_stmts: Vec> = - 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(cx: &mut Context, expr: P) -> P 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(cx: &mut Context, expr: P) -> P where } _ => node }, - span: span + span: span, + attrs: attrs, }, cx) }) } +fn fold_stmt(cx: &mut Context, stmt: P) -> SmallVector> + 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(cx: &mut Context, stmt: &ast::Stmt) -> bool where + F: FnMut(&[ast::Attribute]) -> bool +{ + (cx.in_cfg)(stmt.node.attrs()) +} + +fn expr_in_cfg(cx: &mut Context, expr: &ast::Expr) -> bool where + F: FnMut(&[ast::Attribute]) -> bool +{ + (cx.in_cfg)(expr.attrs()) +} + fn item_in_cfg(cx: &mut Context, item: &ast::Item) -> bool where F: FnMut(&[ast::Attribute]) -> bool { @@ -248,13 +265,19 @@ fn foreign_item_in_cfg(cx: &mut Context, 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], attrs: &[ast::Attribute], +fn in_cfg(diagnostic: &SpanHandler, + cfg: &[P], + attrs: &[ast::Attribute], feature_gated_cfgs: &mut Vec) -> bool { attrs.iter().all(|attr| { let mis = match attr.node.value.node { - ast::MetaList(_, ref mis) if attr.check_name("cfg") => mis, + ast::MetaList(_, ref mis) if is_cfg(&attr) => mis, _ => return true }; diff --git a/src/libsyntax/ext/asm.rs b/src/libsyntax/ext/asm.rs index ac18b9c0e49..d968858f634 100644 --- a/src/libsyntax/ext/asm.rs +++ b/src/libsyntax/ext/asm.rs @@ -233,6 +233,7 @@ pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) dialect: dialect, expn_id: expn_id, }), - span: sp + span: sp, + attrs: None, })) } diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 55f0fa5675a..8c93327c322 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -349,6 +349,7 @@ pub fn raw_expr(sp: Span) -> P { id: ast::DUMMY_NODE_ID, node: ast::ExprLit(P(codemap::respan(sp, ast::LitBool(false)))), span: sp, + attrs: None, }) } diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 4c10a749683..806f5a7ee22 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -525,6 +525,7 @@ 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 { id: ast::DUMMY_NODE_ID, node: node, span: span, + attrs: None, }) } diff --git a/src/libsyntax/ext/concat_idents.rs b/src/libsyntax/ext/concat_idents.rs index e9e36546ad6..c2233202b2f 100644 --- a/src/libsyntax/ext/concat_idents.rs +++ b/src/libsyntax/ext/concat_idents.rs @@ -67,6 +67,7 @@ pub fn expand_syntax_ext<'cx>(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) } ), span: sp, + attrs: None, }); MacEager::expr(e) } diff --git a/src/libsyntax/ext/deriving/debug.rs b/src/libsyntax/ext/deriving/debug.rs index 2b2e5309883..9488cfb86fc 100644 --- a/src/libsyntax/ext/deriving/debug.rs +++ b/src/libsyntax/ext/deriving/debug.rs @@ -148,6 +148,7 @@ fn stmt_let_undescore(cx: &mut ExtCtxt, init: Some(expr), id: ast::DUMMY_NODE_ID, span: sp, + attrs: None, }); let decl = respan(sp, ast::DeclLocal(local)); P(respan(sp, ast::StmtDecl(P(decl), ast::DUMMY_NODE_ID))) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 9b1a7a50201..132b29c7623 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -17,7 +17,7 @@ use ext::mtwt; use ext::build::AstBuilder; use attr; -use attr::AttrMetaMethods; +use attr::{AttrMetaMethods, WithAttrs}; use codemap; use codemap::{Span, Spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute}; use ext::base::*; @@ -37,11 +37,16 @@ pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { 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, fld: &mut MacroExpander) -> P { id: ast::DUMMY_NODE_ID, node: e.node, span: span, + attrs: e.attrs, }) } @@ -73,12 +79,14 @@ pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { 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, fld: &mut MacroExpander) -> P { 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, fld: &mut MacroExpander) -> P { 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, fld: &mut MacroExpander) -> P { 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, fld: &mut MacroExpander) -> P { 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, /// Expand a stmt fn expand_stmt(stmt: P, fld: &mut MacroExpander) -> SmallVector> { let stmt = stmt.and_then(|stmt| stmt); - let (mac, style) = match stmt.node { - StmtMac(mac, style) => (mac, style), + let (mac, style, attrs) = match stmt.node { + StmtMac(mac, style, attrs) => (mac, style, attrs), _ => return expand_non_macro_stmt(stmt, fld) }; + // FIXME: drop attrs for macros. + let _ = attrs; + let maybe_new_items = expand_mac_invoc(mac.and_then(|m| m), stmt.span, |r| r.make_stmts(), @@ -538,7 +556,7 @@ fn expand_non_macro_stmt(Spanned {node, span: stmt_span}: Stmt, fld: &mut MacroE StmtDecl(decl, node_id) => decl.and_then(|Spanned {node: decl, span}| match decl { DeclLocal(local) => { // take it apart: - let rewritten_local = local.map(|Local {id, pat, ty, init, span}| { + let rewritten_local = local.map(|Local {id, pat, ty, init, span, attrs}| { // expand the ty since TyFixedLengthVec contains an Expr // and thus may have a macro use let expanded_ty = ty.map(|t| fld.fold_ty(t)); @@ -568,7 +586,8 @@ fn expand_non_macro_stmt(Spanned {node, span: stmt_span}: Stmt, fld: &mut MacroE pat: rewritten_pat, // also, don't forget to expand the init: init: init.map(|e| fld.fold_expr(e)), - span: span + span: span, + attrs: attrs } }); SmallVector::one(P(Spanned { diff --git a/src/libsyntax/ext/quote.rs b/src/libsyntax/ext/quote.rs index 5e5b8158181..fc6cacb40f1 100644 --- a/src/libsyntax/ext/quote.rs +++ b/src/libsyntax/ext/quote.rs @@ -242,6 +242,7 @@ fn to_tokens(&self, cx: &ExtCtxt) -> Vec { id: ast::DUMMY_NODE_ID, node: ast::ExprLit(P(self.clone())), span: DUMMY_SP, + attrs: None, }).to_tokens(cx) } } diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 3dba6cbecbd..1d499bce3c6 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -134,6 +134,14 @@ fn fold_expr(&mut self, e: P) -> P { e.map(|e| noop_fold_expr(e, self)) } + fn fold_opt_expr(&mut self, e: P) -> Option> { + noop_fold_opt_expr(e, self) + } + + fn fold_exprs(&mut self, es: Vec>) -> Vec> { + noop_fold_exprs(es, self) + } + fn fold_ty(&mut self, t: P) -> P { noop_fold_ty(t, self) } @@ -508,12 +516,13 @@ pub fn noop_fold_parenthesized_parameter_data(data: ParenthesizedPara } pub fn noop_fold_local(l: P, fld: &mut T) -> P { - 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(b: P, folder: &mut T) -> P { 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(p: P, folder: &mut T) -> P { }) } -pub fn noop_fold_expr(Expr {id, node, span}: Expr, folder: &mut T) -> Expr { +pub fn noop_fold_expr(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(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(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(e: P, folder: &mut T) -> Option> { + Some(folder.fold_expr(e)) +} + +pub fn noop_fold_exprs(es: Vec>, folder: &mut T) -> Vec> { + // 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(Spanned {node, span}: Stmt, folder: &mut T) -> SmallVector> { let span = folder.new_span(span); @@ -1346,20 +1365,30 @@ pub fn noop_fold_stmt(Spanned {node, span}: Stmt, folder: &mut T) } StmtExpr(e, id) => { let id = folder.new_id(id); - SmallVector::one(P(Spanned { - node: StmtExpr(folder.fold_expr(e), id), - span: span - })) + if let Some(e) = folder.fold_opt_expr(e) { + SmallVector::one(P(Spanned { + node: StmtExpr(e, id), + span: span + })) + } else { + SmallVector::zero() + } } StmtSemi(e, id) => { let id = folder.new_id(id); - SmallVector::one(P(Spanned { - node: StmtSemi(folder.fold_expr(e), id), - span: span - })) + if let Some(e) = folder.fold_opt_expr(e) { + SmallVector::one(P(Spanned { + node: StmtSemi(e, id), + span: span + })) + } else { + SmallVector::zero() + } } - StmtMac(mac, semi) => SmallVector::one(P(Spanned { - node: StmtMac(mac.map(|m| folder.fold_mac(m)), semi), + StmtMac(mac, semi, attrs) => SmallVector::one(P(Spanned { + node: StmtMac(mac.map(|m| folder.fold_mac(m)), + semi, + attrs.map_opt_attrs(|v| fold_attrs(v, folder))), span: span })) } diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index 7e2fd09a373..e9c8173a4d9 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -699,7 +699,8 @@ 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, diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 56a06f70ed4..2f67ecad4e7 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -56,6 +56,7 @@ use ast::{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 @@ pub enum ParsePub { _ => 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 @@ pub enum ParsePub { _ => 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), +} + +impl From> for LhsExpr { + fn from(o: Option) -> Self { + if let Some(attrs) = o { + LhsExpr::AttributesParsed(attrs) + } else { + LhsExpr::NotYetParsed + } + } +} + +impl From> for LhsExpr { + fn from(expr: P) -> 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 { } /// matches '-' lit | lit - pub fn parse_literal_maybe_minus(&mut self) -> PResult> { + pub fn parse_pat_literal_maybe_minus(&mut self) -> PResult> { 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 { }) } - pub fn mk_expr(&mut self, lo: BytePos, hi: BytePos, node: Expr_) -> P { + pub fn mk_expr(&mut self, lo: BytePos, hi: BytePos, + node: Expr_, attrs: ThinAttributes) -> P { 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 { + pub fn mk_mac_expr(&mut self, lo: BytePos, hi: BytePos, + m: Mac_, attrs: ThinAttributes) -> P { 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 { + pub fn mk_lit_u32(&mut self, i: u32, attrs: ThinAttributes) -> P { 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 { id: ast::DUMMY_NODE_ID, node: ExprLit(lv_lit), span: *span, + attrs: attrs, }) } @@ -2002,9 +2028,20 @@ fn expect_open_delim(&mut self) -> PResult { /// At the bottom (top?) of the precedence hierarchy, /// parse things like parenthesized exprs, /// macros, return, etc. - pub fn parse_bottom_expr(&mut self) -> PResult> { + /// + /// 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> { 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> { 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> { 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> { 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> { 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> { 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> { 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> { 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> { 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> { 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> { } } - 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) + -> PResult { + 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> { + + 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> { + pub fn parse_dot_or_call_expr(&mut self, + already_parsed_attrs: Option) + -> PResult> { + 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, + attrs: ThinAttributes) + -> PResult> { + // 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) -> PResult> { + fn parse_dot_or_call_expr_with_(&mut self, e0: P) -> PResult> { 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) -> PResult> { 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) -> PResult> { 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) -> PResult> { 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) -> PResult> { 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) -> PResult> { 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> { } /// Parse a prefix-unary-operator expr - pub fn parse_prefix_expr(&mut self) -> PResult> { + pub fn parse_prefix_expr(&mut self, + already_parsed_attrs: Option) + -> PResult> { + 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> { - self.parse_assoc_expr_with(0, None) + pub fn parse_assoc_expr(&mut self, + already_parsed_attrs: Option) + -> PResult> { + 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>) + lhs: LhsExpr) -> PResult> { - 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> { + fn parse_prefix_range_expr(&mut self, + already_parsed_attrs: Option) + -> PResult> { 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> { 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> { + pub fn parse_if_expr(&mut self, attrs: ThinAttributes) -> PResult> { 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> = None; let mut hi = thn.span.hi; @@ -2815,16 +2938,17 @@ pub fn parse_if_expr(&mut self) -> PResult> { 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> { + pub fn parse_if_let_expr(&mut self, attrs: ThinAttributes) + -> PResult> { 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> { } 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> { 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> { 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, - span_lo: BytePos) -> PResult> { + span_lo: BytePos, + attrs: ThinAttributes) -> PResult> { // Parse: `for in ` 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, - span_lo: BytePos) -> PResult> { + span_lo: BytePos, + attrs: ThinAttributes) -> PResult> { 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, - span_lo: BytePos) -> PResult> { + span_lo: BytePos, + attrs: ThinAttributes) -> PResult> { 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, - span_lo: BytePos) -> PResult> { - let body = try!(self.parse_block()); + span_lo: BytePos, + attrs: ThinAttributes) -> PResult> { + 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> { + // `match` token already eaten + fn parse_match_expr(&mut self, attrs: ThinAttributes) -> PResult> { 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 = 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 { @@ -2949,7 +3093,7 @@ pub fn parse_arm(&mut self) -> PResult { 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 { /// Parse an expression pub fn parse_expr(&mut self) -> PResult> { - 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(&mut self, r: Restrictions, f: F) -> PResult> } /// Parse an expression, subject to the given restrictions - pub fn parse_expr_res(&mut self, r: Restrictions) -> PResult> { - self.with_res(r, |this| this.parse_assoc_expr()) + pub fn parse_expr_res(&mut self, r: Restrictions, + already_parsed_attrs: Option) + -> PResult> { + 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> { (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> { 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> { } } 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> { + fn parse_local(&mut self, attrs: ThinAttributes) -> PResult> { let lo = self.span.lo; let pat = try!(self.parse_pat()); @@ -3393,13 +3539,14 @@ fn parse_local(&mut self) -> PResult> { 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> { + fn parse_let(&mut self, attrs: ThinAttributes) -> PResult> { 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>> { fn parse_stmt_(&mut self) -> PResult> { 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 { 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 { + 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 { 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> { /// NB: this function no longer parses the items inside an /// extern crate. fn parse_item_(&mut self, attrs: Vec, - macros_allowed: bool) -> PResult>> { + macros_allowed: bool, attributes_allowed: bool) -> PResult>> { 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, 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>> { } // 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 , macros_allowed: bool, + attributes_allowed: bool, lo: BytePos, visibility: Visibility ) -> PResult>> { @@ -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>> { let attrs = try!(self.parse_outer_attributes()); - self.parse_item_(attrs, true) + self.parse_item_(attrs, true, false) } diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 5e4449af604..17b7d8dbaec 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -202,6 +202,7 @@ pub fn can_begin_expr(&self) -> bool { Interpolated(NtIdent(..)) => true, Interpolated(NtBlock(..)) => true, Interpolated(NtPath(..)) => true, + Pound => true, // for expression attributes _ => false, } } diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 6de86de9c54..6919bc4efdd 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -13,6 +13,7 @@ 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) -> State<'a> { pub fn rust_printer_annotated<'a>(writer: Box, 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, } } -#[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>, literals: Option>) -> 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]) -> io::Result<()> { - try!(self.ibox(indent_unit)); + fn print_expr_vec(&mut self, exprs: &[P], + 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]) -> 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>) -> io::Result<()> { + wth: &Option>, + 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]) -> io::Result<()> { + fn print_expr_tup(&mut self, exprs: &[P], + 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)); diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs index 3e02476443a..aab106b7a67 100644 --- a/src/libsyntax/test.rs +++ b/src/libsyntax/test.rs @@ -88,7 +88,7 @@ pub fn modify_for_testing(sess: &ParseSess, if should_test { generate_test_harness(sess, reexport_test_harness_main, krate, cfg, span_diagnostic) } else { - strip_test_functions(krate) + strip_test_functions(span_diagnostic, krate) } } @@ -314,10 +314,11 @@ fn generate_test_harness(sess: &ParseSess, return res; } -fn strip_test_functions(krate: ast::Crate) -> ast::Crate { +fn strip_test_functions(diagnostic: &diagnostic::SpanHandler, krate: ast::Crate) + -> ast::Crate { // When not compiling with --test we should not compile the // #[test] functions - config::strip_items(krate, |attrs| { + config::strip_items(diagnostic, krate, |attrs| { !attr::contains_name(&attrs[..], "test") && !attr::contains_name(&attrs[..], "bench") }) @@ -619,8 +620,10 @@ fn mk_test_descs(cx: &TestCtxt) -> P { mk_test_desc_and_fn_rec(cx, test) }).collect()), span: DUMMY_SP, + attrs: None, })), span: DUMMY_SP, + attrs: None, }) } diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 5d4a462e844..2d97e2680d7 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -628,7 +628,12 @@ pub fn walk_stmt<'v, V: Visitor<'v>>(visitor: &mut V, statement: &'v Stmt) { StmtExpr(ref expression, _) | StmtSemi(ref expression, _) => { visitor.visit_expr(expression) } - StmtMac(ref mac, _) => visitor.visit_mac(mac), + StmtMac(ref mac, _, ref attrs) => { + visitor.visit_mac(mac); + for attr in attrs.as_attrs() { + visitor.visit_attribute(attr); + } + } } } diff --git a/src/test/parse-fail/attr-before-ext.rs b/src/test/parse-fail/attr-before-ext.rs deleted file mode 100644 index e15350fcad7..00000000000 --- a/src/test/parse-fail/attr-before-ext.rs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// compile-flags: -Z parse-only - -fn main() { - #[attr] //~ ERROR expected item after attributes - println!("hi"); -} diff --git a/src/test/parse-fail/attr-before-let.rs b/src/test/parse-fail/attr-before-let.rs deleted file mode 100644 index 03dabb980f2..00000000000 --- a/src/test/parse-fail/attr-before-let.rs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// compile-flags: -Z parse-only - -fn main() { - #[attr] //~ ERROR expected item - let __isize = 0; -} diff --git a/src/test/parse-fail/attr-before-stmt.rs b/src/test/parse-fail/attr-before-stmt.rs deleted file mode 100644 index bc306048cdc..00000000000 --- a/src/test/parse-fail/attr-before-stmt.rs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// compile-flags: -Z parse-only - -// error-pattern:expected item - -fn f() { - #[foo = "bar"] - let x = 10; -} - -fn main() { -} diff --git a/src/test/parse-fail/attr-dangling-in-fn.rs b/src/test/parse-fail/attr-dangling-in-fn.rs index f2f4ecadd7a..7b731b6d6de 100644 --- a/src/test/parse-fail/attr-dangling-in-fn.rs +++ b/src/test/parse-fail/attr-dangling-in-fn.rs @@ -10,7 +10,7 @@ // compile-flags: -Z parse-only -// error-pattern:expected item +// error-pattern:expected statement fn f() { #[foo = "bar"] diff --git a/src/test/parse-fail/doc-before-macro.rs b/src/test/parse-fail/doc-before-macro.rs deleted file mode 100644 index 44435bde03c..00000000000 --- a/src/test/parse-fail/doc-before-macro.rs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// compile-flags: -Z parse-only - -fn main() { - /// hi - println!("hi"); - //~^^ ERROR expected item after doc comment -} diff --git a/src/test/parse-fail/doc-before-rbrace.rs b/src/test/parse-fail/doc-before-rbrace.rs index 8b69c385378..295d5ae432e 100644 --- a/src/test/parse-fail/doc-before-rbrace.rs +++ b/src/test/parse-fail/doc-before-rbrace.rs @@ -12,5 +12,5 @@ fn main() { println!("Hi"); /// hi - //~^ ERROR expected item after doc comment } +//~^ ERROR expected statement diff --git a/src/test/parse-fail/doc-before-semi.rs b/src/test/parse-fail/doc-before-semi.rs index 42c58af76d8..6a8906953be 100644 --- a/src/test/parse-fail/doc-before-semi.rs +++ b/src/test/parse-fail/doc-before-semi.rs @@ -13,5 +13,5 @@ fn main() { /// hi ; - //~^^ ERROR expected item after doc comment + //~^ ERROR expected statement } diff --git a/src/test/pretty/stmt_expr_attributes.rs b/src/test/pretty/stmt_expr_attributes.rs new file mode 100644 index 00000000000..48c2a0470e1 --- /dev/null +++ b/src/test/pretty/stmt_expr_attributes.rs @@ -0,0 +1,281 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// pp-exact + +#![feature(custom_attribute)] +#![feature(box_syntax)] +#![feature(placement_in_syntax)] + +fn main() { } + +fn _0() { + + #[attr] + foo(); +} + +fn _1() { + + #[attr] + unsafe { + // code + } +} + +fn _2() { + + #[attr] + { foo(); } + + { + #![attr] + + foo() + } +} + +fn _3() { + + #[attr] + match () { _ => { } } +} + +fn _4() { + + #[attr] + match () { + #![attr] + _ => (), + } + + let _ = + #[attr] match () { + #![attr] + () => (), + }; +} + +fn _5() { + + #[attr] + let x = 1; + + let x = #[attr] 1; + + let y = (); + let z = (); + + foo3(x, #[attr] y, z); + + qux(3 + #[attr] 2); +} + +fn _6() { + + #[attr] + [#![attr] 1, 2, 3]; + + let _ = #[attr] [#![attr] 1, 2, 3]; + + #[attr] + [#![attr] 1; 4]; + + let _ = #[attr] [#![attr] 1; 4]; +} + +struct Foo { + data: (), +} + +struct Bar(()); + +fn _7() { + + #[attr] + Foo{#![attr] data: (),}; + + let _ = #[attr] Foo{#![attr] data: (),}; +} + +fn _8() { + + #[attr] + (#![attr] ); + + #[attr] + (#![attr] 0); + + #[attr] + (#![attr] 0,); + + #[attr] + (#![attr] 0, 1); +} + +fn _9() { + macro_rules! stmt_mac(( ) => { let _ = ( ) ; }); + + #[attr] + stmt_mac!(); + + /* + // pre existing pp bug: delimiter styles gets lost: + + #[attr] + stmt_mac!{ }; + + #[attr] + stmt_mac![]; + + #[attr] + stmt_mac!{ } // pre-existing pp bug: compiler ICEs with a None unwrap + */ + + let _ = (); +} + +macro_rules! expr_mac(( ) => { ( ) }); + +fn _10() { + + let _ = #[attr] expr_mac!(); + + /* + // pre existing pp bug: delimiter styles gets lost: + let _ = #[attr] expr_mac![]; + let _ = #[attr] expr_mac!{}; + */ +} + +fn _11() { + let _ = #[attr] box 0; + let _: [(); 0] = #[attr] [#![attr] ]; + let _ = #[attr] [#![attr] 0, 0]; + let _ = #[attr] [#![attr] 0; 0]; + let _ = #[attr] foo(); + let _ = #[attr] 1i32.clone(); + let _ = #[attr] (#![attr] ); + let _ = #[attr] (#![attr] 0); + let _ = #[attr] (#![attr] 0,); + let _ = #[attr] (#![attr] 0, 0); + let _ = #[attr] 0 + #[attr] 0; + let _ = #[attr] !0; + let _ = #[attr] -0i32; + let _ = #[attr] false; + let _ = #[attr] 'c'; + let _ = #[attr] 0; + let _ = #[attr] 0 as usize; + let _ = + #[attr] while false { + #![attr] + }; + let _ = + #[attr] while let None = Some(()) { + #![attr] + }; + let _ = + #[attr] for _ in 0..0 { + #![attr] + }; + // FIXME: pp bug, two spaces after the loop + let _ = + #[attr] loop { + #![attr] + }; + let _ = + #[attr] match false { + #![attr] + _ => (), + }; + let _ = #[attr] || #[attr] (); + let _ = #[attr] move || #[attr] (); + let _ = #[attr] || { + #![attr] + #[attr] + () }; + let _ = #[attr] move || { + #![attr] + #[attr] + () }; + let _ = + #[attr] { + #![attr] + }; + let _ = + #[attr] { + #![attr] + let _ = (); + }; + let _ = + #[attr] { + #![attr] + let _ = (); + () + }; + let mut x = 0; + let _ = #[attr] x = 15; + let _ = #[attr] x += 15; + let s = Foo{data: (),}; + let _ = #[attr] s.data; + let _ = (#[attr] s).data; + let t = Bar(()); + let _ = #[attr] t.0; + let _ = (#[attr] t).0; + let v = vec!(0); + let _ = #[attr] v[0]; + let _ = (#[attr] v)[0]; + let _ = #[attr] 0..#[attr] 0; + let _ = #[attr] 0..; + let _ = #[attr] (0..0); + let _ = #[attr] (0..); + let _ = #[attr] (..0); + let _ = #[attr] (..); + let _: fn(&u32) -> u32 = #[attr] std::clone::Clone::clone; + let _ = #[attr] &0; + let _ = #[attr] &mut 0; + let _ = #[attr] &#[attr] 0; + let _ = #[attr] &mut #[attr] 0; + // FIXME: pp bug, extra space after keyword? + while false { let _ = #[attr] continue ; } + while true { let _ = #[attr] break ; } + || #[attr] return; + let _ = #[attr] expr_mac!(); + /* FIXME: pp bug, loosing delimiter styles + let _ = #[attr] expr_mac![]; + let _ = #[attr] expr_mac!{}; + */ + let _ = #[attr] Foo{#![attr] data: (),}; + let _ = #[attr] Foo{#![attr] ..s}; + let _ = #[attr] Foo{#![attr] data: (), ..s}; + let _ = #[attr] (#![attr] 0); +} + +fn _12() { + #[attr] + let _ = 0; + + #[attr] + 0; + + #[attr] + expr_mac!(); + + #[attr] + { + #![attr] + } +} + +///////////////// + +fn foo() { } +fn foo3(_: i32, _: (), _: ()) { } +fn qux(_: i32) { } diff --git a/src/test/run-pass-fulldeps/ast_stmt_expr_attr.rs b/src/test/run-pass-fulldeps/ast_stmt_expr_attr.rs new file mode 100644 index 00000000000..4c20b3997f9 --- /dev/null +++ b/src/test/run-pass-fulldeps/ast_stmt_expr_attr.rs @@ -0,0 +1,306 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , 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(s: String, f: F) -> PResult where + F: FnOnce(&mut Parser) -> PResult, +{ + 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> { + with_error_checking_parse(s.to_string(), |p| { + p.parse_expr_nopanic() + }) +} + +fn stmt(s: &str) -> PResult> { + with_error_checking_parse(s.to_string(), |p| { + p.parse_stmt_nopanic().map(|s| s.unwrap()) + }) +} + +fn attr(s: &str) -> PResult { + with_error_checking_parse(s.to_string(), |p| { + p.parse_attribute(true) + }) +} + +fn str_compare 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::>(), + 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::>(), + actual, + pprust::attribute_to_string); +} + +fn reject_expr_parse(es: &str) { + assert!(expr(es).is_err(), "parser did not reject `{}`", es); +} + +fn reject_stmt_parse(es: &str) { + assert!(stmt(es).is_err(), "parser did not reject `{}`", es); +} + +fn main() { + let both = &["#[attr]", "#![attr]"]; + let outer = &["#[attr]"]; + let none = &[]; + + check_expr_attrs("#[attr] box 0", outer); + reject_expr_parse("box #![attr] 0"); + + check_expr_attrs("#[attr] 0 <- #[attr] 0", none); + check_expr_attrs("#[attr] (0 <- 0)", outer); + reject_expr_parse("0 #[attr] <- 0"); + reject_expr_parse("0 <- #![attr] 0"); + + check_expr_attrs("in #[attr] 0 {#[attr] 0}", none); + check_expr_attrs("#[attr] (in 0 {0})", outer); + reject_expr_parse("in 0 #[attr] {0}"); + reject_expr_parse("in 0 {#![attr] 0}"); + + check_expr_attrs("#[attr] [#![attr]]", both); + check_expr_attrs("#[attr] [#![attr] 0]", both); + check_expr_attrs("#[attr] [#![attr] 0; 0]", both); + check_expr_attrs("#[attr] [#![attr] 0, 0, 0]", both); + reject_expr_parse("[#[attr]]"); + + check_expr_attrs("#[attr] foo()", outer); + check_expr_attrs("#[attr] x.foo()", outer); + reject_expr_parse("foo#[attr]()"); + reject_expr_parse("foo(#![attr])"); + reject_expr_parse("x.foo(#![attr])"); + reject_expr_parse("x.#[attr]foo()"); + reject_expr_parse("x.#![attr]foo()"); + + check_expr_attrs("#[attr] (#![attr])", both); + check_expr_attrs("#[attr] (#![attr] #[attr] 0,)", both); + check_expr_attrs("#[attr] (#![attr] #[attr] 0, 0)", both); + + check_expr_attrs("#[attr] 0 + #[attr] 0", none); + check_expr_attrs("#[attr] 0 / #[attr] 0", none); + check_expr_attrs("#[attr] 0 & #[attr] 0", none); + check_expr_attrs("#[attr] 0 % #[attr] 0", none); + check_expr_attrs("#[attr] (0 + 0)", outer); + reject_expr_parse("0 + #![attr] 0"); + + check_expr_attrs("#[attr] !0", outer); + check_expr_attrs("#[attr] -0", outer); + reject_expr_parse("!#![attr] 0"); + reject_expr_parse("-#![attr] 0"); + + check_expr_attrs("#[attr] false", outer); + check_expr_attrs("#[attr] 0", outer); + check_expr_attrs("#[attr] 'c'", outer); + + check_expr_attrs("#[attr] x as Y", none); + check_expr_attrs("#[attr] (x as Y)", outer); + reject_expr_parse("x #![attr] as Y"); + + reject_expr_parse("#[attr] if false {}"); + reject_expr_parse("if false #[attr] {}"); + reject_expr_parse("if false {#![attr]}"); + reject_expr_parse("if false {} #[attr] else {}"); + reject_expr_parse("if false {} else #[attr] {}"); + reject_expr_parse("if false {} else {#![attr]}"); + reject_expr_parse("if false {} else #[attr] if true {}"); + reject_expr_parse("if false {} else if true #[attr] {}"); + reject_expr_parse("if false {} else if true {#![attr]}"); + + reject_expr_parse("#[attr] if let Some(false) = false {}"); + reject_expr_parse("if let Some(false) = false #[attr] {}"); + reject_expr_parse("if let Some(false) = false {#![attr]}"); + reject_expr_parse("if let Some(false) = false {} #[attr] else {}"); + reject_expr_parse("if let Some(false) = false {} else #[attr] {}"); + reject_expr_parse("if let Some(false) = false {} else {#![attr]}"); + reject_expr_parse("if let Some(false) = false {} else #[attr] if let Some(false) = true {}"); + reject_expr_parse("if let Some(false) = false {} else if let Some(false) = true #[attr] {}"); + reject_expr_parse("if let Some(false) = false {} else if let Some(false) = true {#![attr]}"); + + check_expr_attrs("#[attr] while true {#![attr]}", both); + + check_expr_attrs("#[attr] while let Some(false) = true {#![attr]}", both); + + check_expr_attrs("#[attr] for x in y {#![attr]}", both); + + check_expr_attrs("#[attr] loop {#![attr]}", both); + + check_expr_attrs("#[attr] match true {#![attr] #[attr] _ => false}", both); + + check_expr_attrs("#[attr] || #[attr] foo", outer); + check_expr_attrs("#[attr] move || #[attr] foo", outer); + check_expr_attrs("#[attr] || #[attr] { #![attr] foo }", outer); + check_expr_attrs("#[attr] move || #[attr] { #![attr] foo }", outer); + check_expr_attrs("#[attr] || { #![attr] foo }", outer); + check_expr_attrs("#[attr] move || { #![attr] foo }", outer); + reject_expr_parse("|| #![attr] foo"); + reject_expr_parse("move || #![attr] foo"); + reject_expr_parse("|| #![attr] {foo}"); + reject_expr_parse("move || #![attr] {foo}"); + + check_expr_attrs("#[attr] { #![attr] }", both); + check_expr_attrs("#[attr] { #![attr] let _ = (); }", both); + check_expr_attrs("#[attr] { #![attr] let _ = (); foo }", both); + + check_expr_attrs("#[attr] x = y", none); + check_expr_attrs("#[attr] (x = y)", outer); + + check_expr_attrs("#[attr] x += y", none); + check_expr_attrs("#[attr] (x += y)", outer); + + check_expr_attrs("#[attr] foo.bar", outer); + check_expr_attrs("(#[attr] foo).bar", none); + + check_expr_attrs("#[attr] foo.0", outer); + check_expr_attrs("(#[attr] foo).0", none); + + check_expr_attrs("#[attr] foo[bar]", outer); + check_expr_attrs("(#[attr] foo)[bar]", none); + + check_expr_attrs("#[attr] 0..#[attr] 0", none); + check_expr_attrs("#[attr] 0..", none); + reject_expr_parse("#[attr] ..#[attr] 0"); + reject_expr_parse("#[attr] .."); + + check_expr_attrs("#[attr] (0..0)", outer); + check_expr_attrs("#[attr] (0..)", outer); + check_expr_attrs("#[attr] (..0)", outer); + check_expr_attrs("#[attr] (..)", outer); + + check_expr_attrs("#[attr] foo::bar::baz", outer); + + check_expr_attrs("#[attr] &0", outer); + check_expr_attrs("#[attr] &mut 0", outer); + check_expr_attrs("#[attr] & #[attr] 0", outer); + check_expr_attrs("#[attr] &mut #[attr] 0", outer); + reject_expr_parse("#[attr] &#![attr] 0"); + reject_expr_parse("#[attr] &mut #![attr] 0"); + + check_expr_attrs("#[attr] break", outer); + check_expr_attrs("#[attr] continue", outer); + check_expr_attrs("#[attr] return", outer); + + check_expr_attrs("#[attr] foo!()", outer); + check_expr_attrs("#[attr] foo!(#![attr])", outer); + check_expr_attrs("#[attr] foo![]", outer); + check_expr_attrs("#[attr] foo![#![attr]]", outer); + check_expr_attrs("#[attr] foo!{}", outer); + check_expr_attrs("#[attr] foo!{#![attr]}", outer); + + check_expr_attrs("#[attr] Foo { #![attr] bar: baz }", both); + check_expr_attrs("#[attr] Foo { #![attr] ..foo }", both); + check_expr_attrs("#[attr] Foo { #![attr] bar: baz, ..foo }", both); + + check_expr_attrs("#[attr] (#![attr] 0)", both); + + // Look at statements in their natural habitat... + check_expr_attrs("{ + #[attr] let _ = 0; + #[attr] 0; + #[attr] foo!(); + #[attr] foo!{} + #[attr] foo![]; + }", none); + + check_stmt_attrs("#[attr] let _ = 0", outer); + check_stmt_attrs("#[attr] 0", outer); + check_stmt_attrs("#[attr] {#![attr]}", both); + check_stmt_attrs("#[attr] foo!()", outer); + check_stmt_attrs("#[attr] foo![]", outer); + check_stmt_attrs("#[attr] foo!{}", outer); + + reject_stmt_parse("#[attr] #![attr] let _ = 0"); + reject_stmt_parse("#[attr] #![attr] 0"); + reject_stmt_parse("#[attr] #![attr] foo!()"); + reject_stmt_parse("#[attr] #![attr] foo![]"); + reject_stmt_parse("#[attr] #![attr] foo!{}"); + + // FIXME: Allow attributes in pattern constexprs? + // would require parens in patterns to allow disambiguation... + + reject_expr_parse("match 0 { + 0...#[attr] 10 => () + }"); + reject_expr_parse("match 0 { + 0...#[attr] -10 => () + }"); + reject_expr_parse("match 0 { + 0...-#[attr] 10 => () + }"); + reject_expr_parse("match 0 { + 0...#[attr] FOO => () + }"); + + // make sure we don't catch this bug again... + reject_expr_parse("{ + fn foo() { + #[attr]; + } + }"); + reject_expr_parse("{ + fn foo() { + #[attr] + } + }"); +} diff --git a/src/test/run-pass/cfg_stmt_expr.rs b/src/test/run-pass/cfg_stmt_expr.rs new file mode 100644 index 00000000000..f2b2a567b04 --- /dev/null +++ b/src/test/run-pass/cfg_stmt_expr.rs @@ -0,0 +1,97 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , 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() {} + } +} -- GitLab