diff --git a/src/doc/reference.md b/src/doc/reference.md index 80194ea27bf5d..98074f09441c4 100644 --- a/src/doc/reference.md +++ b/src/doc/reference.md @@ -2368,6 +2368,9 @@ The currently implemented features of the reference compiler are: influence type inference. * - `braced_empty_structs` - Allows use of empty structs and enum variants with braces. +* - `stmt_expr_attributes` - Allows attributes on expressions and + non-item statements. + If a feature is promoted to a language feature, then all existing programs will start to receive compilation warnings about `#![feature]` directives which enabled the new feature (because the directive is no longer necessary). However, if a diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs index 74a61f700e3b8..1ed873f0508d5 100644 --- a/src/librustc/lint/context.rs +++ b/src/librustc/lint/context.rs @@ -42,6 +42,7 @@ use syntax::attr::{self, AttrMetaMethods}; use syntax::codemap::Span; use syntax::parse::token::InternedString; use syntax::ast; +use syntax::attr::ThinAttributesExt; use rustc_front::hir; use rustc_front::util; use rustc_front::intravisit as hir_visit; @@ -674,11 +675,18 @@ impl<'a, 'tcx, 'v> hir_visit::Visitor<'v> for LateContext<'a, 'tcx> { } fn visit_expr(&mut self, e: &hir::Expr) { - run_lints!(self, check_expr, late_passes, e); - hir_visit::walk_expr(self, e); + self.with_lint_attrs(e.attrs.as_attr_slice(), |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 +738,10 @@ impl<'a, 'tcx, 'v> hir_visit::Visitor<'v> for LateContext<'a, 'tcx> { } fn visit_local(&mut self, l: &hir::Local) { - run_lints!(self, check_local, late_passes, l); - hir_visit::walk_local(self, l); + self.with_lint_attrs(l.attrs.as_attr_slice(), |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 3e6cf07d86f0b..a292c83682c0e 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 630c42db68c79..9a489ac6fdf71 100644 --- a/src/librustc_driver/pretty.rs +++ b/src/librustc_driver/pretty.rs @@ -654,6 +654,7 @@ impl fold::Folder for ReplaceBodyWithLoop { node: ast::ExprLoop(empty_block, None), id: ast::DUMMY_NODE_ID, span: codemap::DUMMY_SP, + attrs: None, }); expr_to_block(b.rules, Some(loop_expr)) diff --git a/src/librustc_front/fold.rs b/src/librustc_front/fold.rs index 7ec4e1ba33121..5f39376d15603 100644 --- a/src/librustc_front/fold.rs +++ b/src/librustc_front/fold.rs @@ -14,39 +14,13 @@ use hir::*; use syntax::ast::{Ident, Name, NodeId, DUMMY_NODE_ID, Attribute, Attribute_, MetaItem}; use syntax::ast::{MetaWord, MetaList, MetaNameValue}; +use syntax::attr::ThinAttributesExt; use hir; use syntax::codemap::{respan, Span, Spanned}; use syntax::owned_slice::OwnedSlice; use syntax::ptr::P; use syntax::parse::token; -use std::ptr; - -// This could have a better place to live. -pub trait MoveMap { - fn move_map(self, f: F) -> Self where F: FnMut(T) -> T; -} - -impl MoveMap for Vec { - fn move_map(mut self, mut f: F) -> Vec - where F: FnMut(T) -> T - { - for p in &mut self { - unsafe { - // FIXME(#5016) this shouldn't need to zero to be safe. - ptr::write(p, f(ptr::read_and_drop(p))); - } - } - self - } -} - -impl MoveMap for OwnedSlice { - fn move_map(self, f: F) -> OwnedSlice - where F: FnMut(T) -> T - { - OwnedSlice::from_vec(self.into_vec().move_map(f)) - } -} +use syntax::util::move_map::MoveMap; pub trait Folder : Sized { // Any additions to this trait should happen in form @@ -332,7 +306,7 @@ pub fn noop_fold_view_path(view_path: P, fld: &mut T) -> P< } pub fn fold_attrs(attrs: Vec, fld: &mut T) -> Vec { - attrs.into_iter().flat_map(|x| fld.fold_attribute(x)).collect() + attrs.move_flat_map(|x| fld.fold_attribute(x)) } pub fn noop_fold_arm(Arm { attrs, pats, guard, body }: Arm, fld: &mut T) -> Arm { @@ -501,13 +475,14 @@ 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 }| { + 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_thin_attrs(|attrs| fold_attrs(attrs, fld)), } }) } @@ -769,7 +744,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().map(|s| folder.fold_stmt(s)).collect(), + stmts: stmts.move_map(|s| folder.fold_stmt(s)), expr: expr.map(|x| folder.fold_expr(x)), rules: rules, span: folder.new_span(span), @@ -816,9 +791,8 @@ pub fn noop_fold_item_underscore(i: Item_, folder: &mut T) -> Item_ { ItemDefaultImpl(unsafety, folder.fold_trait_ref((*trait_ref).clone())) } ItemImpl(unsafety, polarity, generics, ifce, ty, impl_items) => { - let new_impl_items = impl_items.into_iter() - .map(|item| folder.fold_impl_item(item)) - .collect(); + let new_impl_items = impl_items + .move_map(|item| folder.fold_impl_item(item)); let ifce = match ifce { None => None, Some(ref trait_ref) => { @@ -834,9 +808,7 @@ pub fn noop_fold_item_underscore(i: Item_, folder: &mut T) -> Item_ { } ItemTrait(unsafety, generics, bounds, items) => { let bounds = folder.fold_bounds(bounds); - let items = items.into_iter() - .map(|item| folder.fold_trait_item(item)) - .collect(); + let items = items.move_map(|item| folder.fold_trait_item(item)); ItemTrait(unsafety, folder.fold_generics(generics), bounds, items) } } @@ -892,7 +864,7 @@ pub fn noop_fold_impl_item(i: P, folder: &mut T) -> P(Mod { inner, item_ids }: Mod, folder: &mut T) -> Mod { Mod { inner: folder.new_span(inner), - item_ids: item_ids.into_iter().map(|x| folder.fold_item_id(x)).collect(), + item_ids: item_ids.move_map(|x| folder.fold_item_id(x)), } } @@ -1048,7 +1020,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 +1143,7 @@ pub fn noop_fold_expr(Expr { id, node, span }: Expr, folder: &mut T) } }, span: folder.new_span(span), + attrs: attrs.map_thin_attrs(|attrs| fold_attrs(attrs, folder)), } } diff --git a/src/librustc_front/hir.rs b/src/librustc_front/hir.rs index 95d73daa632b1..beb7059d73cd2 100644 --- a/src/librustc_front/hir.rs +++ b/src/librustc_front/hir.rs @@ -41,6 +41,7 @@ use syntax::codemap::{self, Span, Spanned, DUMMY_SP, ExpnId}; use syntax::abi::Abi; use syntax::ast::{Name, Ident, NodeId, DUMMY_NODE_ID, TokenTree, AsmDialect}; use syntax::ast::{Attribute, Lit, StrStyle, FloatTy, IntTy, UintTy, CrateConfig}; +use syntax::attr::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/lib.rs b/src/librustc_front/lib.rs index 09ff7c8e58ebe..3bfa645afc7d9 100644 --- a/src/librustc_front/lib.rs +++ b/src/librustc_front/lib.rs @@ -35,7 +35,6 @@ #![feature(slice_patterns)] #![feature(staged_api)] #![feature(str_char)] -#![feature(filling_drop)] extern crate serialize; #[macro_use] diff --git a/src/librustc_front/lowering.rs b/src/librustc_front/lowering.rs index b984f23c4c02a..e8c4a6484e2a2 100644 --- a/src/librustc_front/lowering.rs +++ b/src/librustc_front/lowering.rs @@ -66,6 +66,7 @@ use hir; use std::collections::BTreeMap; use std::collections::HashMap; use syntax::ast::*; +use syntax::attr::{ThinAttributes, ThinAttributesExt}; use syntax::ptr::P; use syntax::codemap::{respan, Spanned, Span}; use syntax::owned_slice::OwnedSlice; @@ -331,6 +332,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(), }) } @@ -984,16 +986,16 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P { let make_call = |lctx: &LoweringContext, p, args| { let path = core_path(lctx, e.span, p); - let path = expr_path(lctx, path); - expr_call(lctx, e.span, path, args) + let path = expr_path(lctx, path, None); + expr_call(lctx, e.span, path, args, None) }; let mk_stmt_let = |lctx: &LoweringContext, bind, expr| { - stmt_let(lctx, e.span, false, bind, expr) + stmt_let(lctx, e.span, false, bind, expr, None) }; let mk_stmt_let_mut = |lctx: &LoweringContext, bind, expr| { - stmt_let(lctx, e.span, true, bind, expr) + stmt_let(lctx, e.span, true, bind, expr, None) }; // let placer = ; @@ -1002,21 +1004,22 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P { vec![], placer_expr, e.span, - hir::PopUnstableBlock); + hir::PopUnstableBlock, + None); mk_stmt_let(lctx, placer_ident, placer_expr) }; // let mut place = Placer::make_place(placer); let s2 = { - let placer = expr_ident(lctx, e.span, placer_ident); + let placer = expr_ident(lctx, e.span, placer_ident, None); let call = make_call(lctx, &make_place, vec![placer]); mk_stmt_let_mut(lctx, place_ident, call) }; // let p_ptr = Place::pointer(&mut place); let s3 = { - let agent = expr_ident(lctx, e.span, place_ident); - let args = vec![expr_mut_addr_of(lctx, e.span, agent)]; + let agent = expr_ident(lctx, e.span, place_ident, None); + let args = vec![expr_mut_addr_of(lctx, e.span, agent, None)]; let call = make_call(lctx, &place_pointer, args); mk_stmt_let(lctx, p_ptr_ident, call) }; @@ -1027,12 +1030,13 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P { vec![], value_expr, e.span, - hir::PopUnstableBlock); + hir::PopUnstableBlock, + None); signal_block_expr(lctx, vec![], value_expr, e.span, - hir::PopUnsafeBlock(hir::CompilerGenerated)) + hir::PopUnsafeBlock(hir::CompilerGenerated), None) }; // push_unsafe!({ @@ -1040,27 +1044,28 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P { // InPlace::finalize(place) // }) let expr = { - let ptr = expr_ident(lctx, e.span, p_ptr_ident); + let ptr = expr_ident(lctx, e.span, p_ptr_ident, None); let call_move_val_init = hir::StmtSemi( make_call(lctx, &move_val_init, vec![ptr, pop_unsafe_expr]), lctx.next_id()); let call_move_val_init = respan(e.span, call_move_val_init); - let place = expr_ident(lctx, e.span, place_ident); + let place = expr_ident(lctx, e.span, place_ident, None); let call = make_call(lctx, &inplace_finalize, vec![place]); signal_block_expr(lctx, vec![P(call_move_val_init)], call, e.span, - hir::PushUnsafeBlock(hir::CompilerGenerated)) + hir::PushUnsafeBlock(hir::CompilerGenerated), None) }; signal_block_expr(lctx, vec![s1, s2, s3], expr, e.span, - hir::PushUnstableBlock) + hir::PushUnstableBlock, + e.attrs.clone()) }); } @@ -1123,7 +1128,7 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P { rules: hir::DefaultBlock, span: span, }); - expr_block(lctx, blk) + expr_block(lctx, blk, None) }) } _ => lower_expr(lctx, els), @@ -1215,7 +1220,13 @@ 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| { + attrs.prepend(e.attrs.clone()) + }); + ex + }); } // Desugar ExprIfLet @@ -1233,7 +1244,7 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P { // ` => ` let pat_arm = { let body = lower_block(lctx, body); - let body_expr = expr_block(lctx, body); + let body_expr = expr_block(lctx, body, None); arm(vec![lower_pat(lctx, pat)], body_expr) }; @@ -1252,7 +1263,7 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P { attrs: vec![], pats: vec![pat_under], guard: Some(cond), - body: expr_block(lctx, then), + body: expr_block(lctx, then, None), }); else_opt.map(|else_opt| (else_opt, true)) } @@ -1284,7 +1295,7 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P { let pat_under = pat_wild(lctx, e.span); let else_expr = else_opt.unwrap_or_else( - || expr_tuple(lctx, e.span, vec![])); + || expr_tuple(lctx, e.span, vec![], None)); arm(vec![pat_under], else_expr) }; @@ -1294,13 +1305,15 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P { arms.push(else_arm); let sub_expr = lower_expr(lctx, sub_expr); + // add attributes to the outer returned expr node expr(lctx, e.span, hir::ExprMatch(sub_expr, arms, hir::MatchSource::IfLetDesugar { contains_else_clause: contains_else_clause, - })) + }), + e.attrs.clone()) }); } @@ -1320,14 +1333,14 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P { // ` => ` let pat_arm = { let body = lower_block(lctx, body); - let body_expr = expr_block(lctx, body); + let body_expr = expr_block(lctx, body, None); arm(vec![lower_pat(lctx, pat)], body_expr) }; // `_ => break` let break_arm = { let pat_under = pat_wild(lctx, e.span); - let break_expr = expr_break(lctx, e.span); + let break_expr = expr_break(lctx, e.span, None); arm(vec![pat_under], break_expr) }; @@ -1338,11 +1351,13 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P { e.span, hir::ExprMatch(sub_expr, arms, - hir::MatchSource::WhileLetDesugar)); + hir::MatchSource::WhileLetDesugar), + None); // `[opt_ident]: loop { ... }` let loop_block = block_expr(lctx, match_expr); - expr(lctx, e.span, hir::ExprLoop(loop_block, opt_ident)) + // add attributes to the outer returned expr node + expr(lctx, e.span, hir::ExprLoop(loop_block, opt_ident), e.attrs.clone()) }); } @@ -1379,6 +1394,7 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P { id: lctx.next_id(), node: hir::ExprBlock(body_block), span: body_span, + attrs: None, }); let pat = lower_pat(lctx, pat); let some_pat = pat_some(lctx, e.span, pat); @@ -1388,7 +1404,7 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P { // `::std::option::Option::None => break` let break_arm = { - let break_expr = expr_break(lctx, e.span); + let break_expr = expr_break(lctx, e.span, None); arm(vec![pat_none(lctx, e.span)], break_expr) }; @@ -1400,20 +1416,28 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P { path_global(e.span, strs) }; - let iter = expr_ident(lctx, e.span, iter); - let ref_mut_iter = expr_mut_addr_of(lctx, e.span, iter); - let next_path = expr_path(lctx, next_path); - let next_expr = expr_call(lctx, e.span, next_path, vec![ref_mut_iter]); + let iter = expr_ident(lctx, e.span, iter, None); + let ref_mut_iter = expr_mut_addr_of(lctx, e.span, iter, None); + let next_path = expr_path(lctx, next_path, None); + let next_expr = expr_call(lctx, + e.span, + next_path, + vec![ref_mut_iter], + None); let arms = vec![pat_arm, break_arm]; expr(lctx, e.span, - hir::ExprMatch(next_expr, arms, hir::MatchSource::ForLoopDesugar)) + hir::ExprMatch(next_expr, arms, hir::MatchSource::ForLoopDesugar), + None) }; // `[opt_ident]: loop { ... }` let loop_block = block_expr(lctx, match_expr); - let loop_expr = expr(lctx, e.span, hir::ExprLoop(loop_block, opt_ident)); + let loop_expr = expr(lctx, + e.span, + hir::ExprLoop(loop_block, opt_ident), + None); // `mut iter => { ... }` let iter_arm = { @@ -1432,28 +1456,31 @@ pub fn lower_expr(lctx: &LoweringContext, e: &Expr) -> P { path_global(e.span, strs) }; - let into_iter = expr_path(lctx, into_iter_path); - expr_call(lctx, e.span, into_iter, vec![head]) + let into_iter = expr_path(lctx, into_iter_path, None); + expr_call(lctx, e.span, into_iter, vec![head], None) }; let match_expr = expr_match(lctx, e.span, into_iter_expr, vec![iter_arm], - hir::MatchSource::ForLoopDesugar); + hir::MatchSource::ForLoopDesugar, + None); // `{ let result = ...; result }` let result_ident = lctx.str_to_ident("result"); - let let_stmt = stmt_let(lctx, e.span, false, result_ident, match_expr); - let result = expr_ident(lctx, e.span, result_ident); + let let_stmt = stmt_let(lctx, e.span, false, result_ident, match_expr, None); + let result = expr_ident(lctx, e.span, result_ident, None); let block = block_all(lctx, e.span, vec![let_stmt], Some(result)); - expr_block(lctx, block) + // add the attributes to the outer returned expr node + expr_block(lctx, block, e.attrs.clone()) }); } ExprMac(_) => panic!("Shouldn't exist here"), }, span: e.span, + attrs: e.attrs.clone(), }) } @@ -1552,52 +1579,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 +1642,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 +1656,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 +1794,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 +1805,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 2146dc8e9b94b..b5f8be496fb02 100644 --- a/src/librustc_lint/bad_style.rs +++ b/src/librustc_lint/bad_style.rs @@ -138,7 +138,7 @@ impl LateLintPass for NonCamelCaseTypes { declare_lint! { pub NON_SNAKE_CASE, Warn, - "methods, functions, lifetime parameters and modules should have snake case names" + "variables, methods, functions, lifetime parameters and modules should have snake case names" } #[derive(Copy, Clone)] diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index ab62c8d9421ae..f11291fc0f7e7 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -45,6 +45,7 @@ pub use self::ViewPath_::*; pub use self::Visibility::*; pub use self::PathParameters::*; +use attr::ThinAttributes; use codemap::{Span, Spanned, DUMMY_SP, ExpnId}; use abi::Abi; use ast_util; @@ -692,8 +693,21 @@ pub enum Stmt_ { /// Expr with trailing semi-colon (may have any type): StmtSemi(P, 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 +732,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 +754,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 +799,16 @@ pub struct Expr { pub id: NodeId, pub node: Expr_, pub span: Span, + pub attrs: ThinAttributes +} + +impl Expr { + pub fn attrs(&self) -> &[Attribute] { + match self.attrs { + Some(ref b) => b, + None => &[], + } + } } impl fmt::Debug for Expr { @@ -1792,6 +1835,12 @@ pub struct Item { pub span: Span, } +impl Item { + pub fn attrs(&self) -> &[Attribute] { + &self.attrs + } +} + #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] pub enum Item_ { /// An`extern crate` item, with optional original crate name, diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs index 153a81e6c5475..e828d8ae24874 100644 --- a/src/libsyntax/attr.rs +++ b/src/libsyntax/attr.rs @@ -16,10 +16,13 @@ pub use self::IntType::*; use ast; use ast::{AttrId, Attribute, Attribute_, MetaItem, MetaWord, MetaNameValue, MetaList}; +use ast::{Stmt, StmtDecl, StmtExpr, StmtMac, StmtSemi, DeclItem, DeclLocal}; +use ast::{Expr, Item, Local, Decl}; use codemap::{Span, Spanned, spanned, dummy_spanned}; use codemap::BytePos; +use config::CfgDiag; use diagnostic::SpanHandler; -use feature_gate::GatedCfg; +use feature_gate::{GatedCfg, GatedCfgAttr}; use parse::lexer::comments::{doc_comment_style, strip_doc_comment_decoration}; use parse::token::{InternedString, intern_and_get_ident}; use parse::token; @@ -356,26 +359,35 @@ pub fn requests_inline(attrs: &[Attribute]) -> bool { } /// Tests if a cfg-pattern matches the cfg set -pub fn cfg_matches(diagnostic: &SpanHandler, cfgs: &[P], cfg: &ast::MetaItem, - feature_gated_cfgs: &mut Vec) -> bool { +pub fn cfg_matches(cfgs: &[P], + cfg: &ast::MetaItem, + diag: &mut T) -> bool { match cfg.node { ast::MetaList(ref pred, ref mis) if &pred[..] == "any" => - mis.iter().any(|mi| cfg_matches(diagnostic, cfgs, &**mi, feature_gated_cfgs)), + mis.iter().any(|mi| cfg_matches(cfgs, &**mi, diag)), ast::MetaList(ref pred, ref mis) if &pred[..] == "all" => - mis.iter().all(|mi| cfg_matches(diagnostic, cfgs, &**mi, feature_gated_cfgs)), + mis.iter().all(|mi| cfg_matches(cfgs, &**mi, diag)), ast::MetaList(ref pred, ref mis) if &pred[..] == "not" => { if mis.len() != 1 { - diagnostic.span_err(cfg.span, "expected 1 cfg-pattern"); + diag.emit_error(|diagnostic| { + diagnostic.span_err(cfg.span, "expected 1 cfg-pattern"); + }); return false; } - !cfg_matches(diagnostic, cfgs, &*mis[0], feature_gated_cfgs) + !cfg_matches(cfgs, &*mis[0], diag) } ast::MetaList(ref pred, _) => { - diagnostic.span_err(cfg.span, &format!("invalid predicate `{}`", pred)); + diag.emit_error(|diagnostic| { + diagnostic.span_err(cfg.span, + &format!("invalid predicate `{}`", pred)); + }); false }, ast::MetaWord(_) | ast::MetaNameValue(..) => { - feature_gated_cfgs.extend(GatedCfg::gate(cfg)); + diag.flag_gated(|feature_gated_cfgs| { + feature_gated_cfgs.extend( + GatedCfg::gate(cfg).map(GatedCfgAttr::GatedCfg)); + }); contains(cfgs, cfg) } } @@ -720,3 +732,157 @@ impl IntType { } } } + +/// A list of attributes, behind a optional box as +/// a space optimization. +pub type ThinAttributes = Option>>; + +pub trait ThinAttributesExt { + fn map_thin_attrs(self, f: F) -> Self + where F: FnOnce(Vec) -> Vec; + fn prepend(mut self, attrs: Self) -> Self; + fn append(mut self, attrs: Self) -> Self; + fn update(&mut self, f: F) + where Self: Sized, + F: FnOnce(Self) -> Self; + fn as_attr_slice(&self) -> &[Attribute]; + fn into_attr_vec(self) -> Vec; +} + +impl ThinAttributesExt for ThinAttributes { + fn map_thin_attrs(self, f: F) -> Self + where F: FnOnce(Vec) -> Vec + { + f(self.map(|b| *b).unwrap_or(Vec::new())).into_thin_attrs() + } + + fn prepend(self, attrs: ThinAttributes) -> Self { + attrs.map_thin_attrs(|mut attrs| { + attrs.extend(self.into_attr_vec()); + attrs + }) + } + + fn append(self, attrs: ThinAttributes) -> Self { + self.map_thin_attrs(|mut self_| { + self_.extend(attrs.into_attr_vec()); + self_ + }) + } + + fn update(&mut self, f: F) + where Self: Sized, + F: FnOnce(ThinAttributes) -> ThinAttributes + { + let self_ = f(self.take()); + *self = self_; + } + + fn as_attr_slice(&self) -> &[Attribute] { + match *self { + Some(ref b) => b, + None => &[], + } + } + + fn into_attr_vec(self) -> Vec { + match self { + Some(b) => *b, + None => Vec::new(), + } + } +} + +pub trait AttributesExt { + fn into_thin_attrs(self) -> ThinAttributes; +} + +impl AttributesExt for Vec { + fn into_thin_attrs(self) -> ThinAttributes { + if self.len() == 0 { + None + } else { + Some(Box::new(self)) + } + } +} + +/// A cheap way to add Attributes to an AST node. +pub trait WithAttrs { + // FIXME: Could be extended to anything IntoIter + 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(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_attr_vec()); + 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(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(attrs)); + StmtMac(mac, style, ats) + } + }, + } + }) + } +} diff --git a/src/libsyntax/config.rs b/src/libsyntax/config.rs index 10731178c0610..1209c58fd5ed1 100644 --- a/src/libsyntax/config.rs +++ b/src/libsyntax/config.rs @@ -10,9 +10,10 @@ use attr::AttrMetaMethods; use diagnostic::SpanHandler; -use feature_gate::GatedCfg; +use feature_gate::GatedCfgAttr; use fold::Folder; use {ast, fold, attr}; +use visit; use codemap::{Spanned, respan}; use ptr::P; @@ -20,28 +21,34 @@ use util::small_vector::SmallVector; /// A folder that strips out items that do not belong in the current /// configuration. -struct Context where F: FnMut(&[ast::Attribute]) -> bool { +struct Context<'a, F> where F: FnMut(&[ast::Attribute]) -> bool { in_cfg: F, + diagnostic: &'a SpanHandler, } // Support conditional compilation by transforming the AST, stripping out // any items that do not belong in the current configuration pub fn strip_unconfigured_items(diagnostic: &SpanHandler, krate: ast::Crate, - feature_gated_cfgs: &mut Vec) + feature_gated_cfgs: &mut Vec) -> ast::Crate { + // Need to do this check here because cfg runs before feature_gates + check_for_gated_stmt_expr_attributes(&krate, feature_gated_cfgs); + let krate = process_cfg_attr(diagnostic, krate, feature_gated_cfgs); let config = krate.config.clone(); - strip_items(krate, |attrs| in_cfg(diagnostic, &config, attrs, feature_gated_cfgs)) + strip_items(diagnostic, + krate, + |attrs| { + let mut diag = CfgDiagReal { + diag: diagnostic, + feature_gated_cfgs: feature_gated_cfgs, + }; + in_cfg(&config, attrs, &mut diag) + }) } -impl fold::Folder for Context 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) - } +impl<'a, F> fold::Folder for Context<'a, F> where F: FnMut(&[ast::Attribute]) -> bool { fn fold_foreign_mod(&mut self, foreign_mod: ast::ForeignMod) -> ast::ForeignMod { fold_foreign_mod(self, foreign_mod) } @@ -49,8 +56,25 @@ impl fold::Folder for Context where F: FnMut(&[ast::Attribute]) -> bool { 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 is intentionally not part of the fold_expr() function + // in order for fold_opt_expr() to be able to avoid this check + if let Some(attr) = expr.attrs().iter().find(|a| is_cfg(a)) { + self.diagnostic.span_err(attr.span, + "removing an expression is not supported in this position"); + } fold_expr(self, expr) } + fn fold_opt_expr(&mut self, expr: P) -> 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,28 +83,17 @@ impl fold::Folder for Context where F: FnMut(&[ast::Attribute]) -> bool { } } -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) } -fn fold_mod(cx: &mut Context, - ast::Mod {inner, items}: ast::Mod) - -> ast::Mod where - F: FnMut(&[ast::Attribute]) -> bool -{ - ast::Mod { - inner: inner, - items: items.into_iter().flat_map(|a| { - cx.fold_item(a).into_iter() - }).collect() - } -} - fn filter_foreign_item(cx: &mut Context, item: P) -> Option> where @@ -182,45 +195,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 +219,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,44 +259,51 @@ 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], - feature_gated_cfgs: &mut Vec) -> bool { +fn in_cfg(cfg: &[P], + attrs: &[ast::Attribute], + diag: &mut T) -> bool { attrs.iter().all(|attr| { let mis = match attr.node.value.node { - ast::MetaList(_, ref mis) if attr.check_name("cfg") => mis, + ast::MetaList(_, ref mis) if is_cfg(&attr) => mis, _ => return true }; if mis.len() != 1 { - diagnostic.span_err(attr.span, "expected 1 cfg-pattern"); + diag.emit_error(|diagnostic| { + diagnostic.span_err(attr.span, "expected 1 cfg-pattern"); + }); return true; } - attr::cfg_matches(diagnostic, cfg, &mis[0], - feature_gated_cfgs) + attr::cfg_matches(cfg, &mis[0], diag) }) } -struct CfgAttrFolder<'a, 'b> { - diag: &'a SpanHandler, - config: ast::CrateConfig, - feature_gated_cfgs: &'b mut Vec +struct CfgAttrFolder<'a, T> { + diag: T, + config: &'a ast::CrateConfig, } // Process `#[cfg_attr]`. fn process_cfg_attr(diagnostic: &SpanHandler, krate: ast::Crate, - feature_gated_cfgs: &mut Vec) -> ast::Crate { + feature_gated_cfgs: &mut Vec) -> ast::Crate { let mut fld = CfgAttrFolder { - diag: diagnostic, - config: krate.config.clone(), - feature_gated_cfgs: feature_gated_cfgs, + diag: CfgDiagReal { + diag: diagnostic, + feature_gated_cfgs: feature_gated_cfgs, + }, + config: &krate.config.clone(), }; fld.fold_crate(krate) } -impl<'a,'b> fold::Folder for CfgAttrFolder<'a,'b> { +impl<'a, T: CfgDiag> fold::Folder for CfgAttrFolder<'a, T> { fn fold_attribute(&mut self, attr: ast::Attribute) -> Option { if !attr.check_name("cfg_attr") { return fold::noop_fold_attribute(attr, self); @@ -294,20 +312,25 @@ impl<'a,'b> fold::Folder for CfgAttrFolder<'a,'b> { let attr_list = match attr.meta_item_list() { Some(attr_list) => attr_list, None => { - self.diag.span_err(attr.span, "expected `#[cfg_attr(, )]`"); + self.diag.emit_error(|diag| { + diag.span_err(attr.span, + "expected `#[cfg_attr(, )]`"); + }); return None; } }; let (cfg, mi) = match (attr_list.len(), attr_list.get(0), attr_list.get(1)) { (2, Some(cfg), Some(mi)) => (cfg, mi), _ => { - self.diag.span_err(attr.span, "expected `#[cfg_attr(, )]`"); + self.diag.emit_error(|diag| { + diag.span_err(attr.span, + "expected `#[cfg_attr(, )]`"); + }); return None; } }; - if attr::cfg_matches(self.diag, &self.config[..], &cfg, - self.feature_gated_cfgs) { + if attr::cfg_matches(&self.config[..], &cfg, &mut self.diag) { Some(respan(mi.span, ast::Attribute_ { id: attr::mk_attr_id(), style: attr.node.style, @@ -324,3 +347,174 @@ impl<'a,'b> fold::Folder for CfgAttrFolder<'a,'b> { fold::noop_fold_mac(mac, self) } } + +fn check_for_gated_stmt_expr_attributes(krate: &ast::Crate, + discovered: &mut Vec) { + let mut v = StmtExprAttrFeatureVisitor { + config: &krate.config, + discovered: discovered, + }; + visit::walk_crate(&mut v, krate); +} + +/// To cover this feature, we need to discover all attributes +/// so we need to run before cfg. +struct StmtExprAttrFeatureVisitor<'a, 'b> { + config: &'a ast::CrateConfig, + discovered: &'b mut Vec, +} + +// Runs the cfg_attr and cfg folders locally in "silent" mode +// to discover attribute use on stmts or expressions ahead of time +impl<'v, 'a, 'b> visit::Visitor<'v> for StmtExprAttrFeatureVisitor<'a, 'b> { + fn visit_stmt(&mut self, s: &'v ast::Stmt) { + // check if there even are any attributes on this node + let stmt_attrs = s.node.attrs(); + if stmt_attrs.len() > 0 { + // attributes on items are fine + if let ast::StmtDecl(ref decl, _) = s.node { + if let ast::DeclItem(_) = decl.node { + visit::walk_stmt(self, s); + return; + } + } + + // flag the offending attributes + for attr in stmt_attrs { + self.discovered.push(GatedCfgAttr::GatedAttr(attr.span)); + } + + // if the node does not end up being cfg-d away, walk down + if node_survives_cfg(stmt_attrs, self.config) { + visit::walk_stmt(self, s); + } + } else { + visit::walk_stmt(self, s); + } + } + + fn visit_expr(&mut self, ex: &'v ast::Expr) { + // check if there even are any attributes on this node + let expr_attrs = ex.attrs(); + if expr_attrs.len() > 0 { + + // flag the offending attributes + for attr in expr_attrs { + self.discovered.push(GatedCfgAttr::GatedAttr(attr.span)); + } + + // if the node does not end up being cfg-d away, walk down + if node_survives_cfg(expr_attrs, self.config) { + visit::walk_expr(self, ex); + } + } else { + visit::walk_expr(self, ex); + } + } + + fn visit_foreign_item(&mut self, i: &'v ast::ForeignItem) { + if node_survives_cfg(&i.attrs, self.config) { + visit::walk_foreign_item(self, i); + } + } + + fn visit_item(&mut self, i: &'v ast::Item) { + if node_survives_cfg(&i.attrs, self.config) { + visit::walk_item(self, i); + } + } + + fn visit_impl_item(&mut self, ii: &'v ast::ImplItem) { + if node_survives_cfg(&ii.attrs, self.config) { + visit::walk_impl_item(self, ii); + } + } + + fn visit_trait_item(&mut self, ti: &'v ast::TraitItem) { + if node_survives_cfg(&ti.attrs, self.config) { + visit::walk_trait_item(self, ti); + } + } + + fn visit_struct_field(&mut self, s: &'v ast::StructField) { + if node_survives_cfg(&s.node.attrs, self.config) { + visit::walk_struct_field(self, s); + } + } + + fn visit_variant(&mut self, v: &'v ast::Variant, + g: &'v ast::Generics, item_id: ast::NodeId) { + if node_survives_cfg(&v.node.attrs, self.config) { + visit::walk_variant(self, v, g, item_id); + } + } + + fn visit_arm(&mut self, a: &'v ast::Arm) { + if node_survives_cfg(&a.attrs, self.config) { + visit::walk_arm(self, a); + } + } + + // This visitor runs pre expansion, so we need to prevent + // the default panic here + fn visit_mac(&mut self, mac: &'v ast::Mac) { + visit::walk_mac(self, mac) + } +} + +pub trait CfgDiag { + fn emit_error(&mut self, f: F) where F: FnMut(&SpanHandler); + fn flag_gated(&mut self, f: F) where F: FnMut(&mut Vec); +} + +pub struct CfgDiagReal<'a, 'b> { + pub diag: &'a SpanHandler, + pub feature_gated_cfgs: &'b mut Vec, +} + +impl<'a, 'b> CfgDiag for CfgDiagReal<'a, 'b> { + fn emit_error(&mut self, mut f: F) where F: FnMut(&SpanHandler) { + f(self.diag) + } + fn flag_gated(&mut self, mut f: F) where F: FnMut(&mut Vec) { + f(self.feature_gated_cfgs) + } +} + +struct CfgDiagSilent { + error: bool, +} + +impl CfgDiag for CfgDiagSilent { + fn emit_error(&mut self, _: F) where F: FnMut(&SpanHandler) { + self.error = true; + } + fn flag_gated(&mut self, _: F) where F: FnMut(&mut Vec) {} +} + +fn node_survives_cfg(attrs: &[ast::Attribute], + config: &ast::CrateConfig) -> bool { + let mut survives_cfg = true; + + for attr in attrs { + let mut fld = CfgAttrFolder { + diag: CfgDiagSilent { error: false }, + config: config, + }; + let attr = fld.fold_attribute(attr.clone()); + + // In case of error we can just return true, + // since the actual cfg folders will end compilation anyway. + + if fld.diag.error { return true; } + + survives_cfg &= attr.map(|attr| { + let mut diag = CfgDiagSilent { error: false }; + let r = in_cfg(config, &[attr], &mut diag); + if diag.error { return true; } + r + }).unwrap_or(true) + } + + survives_cfg +} diff --git a/src/libsyntax/ext/asm.rs b/src/libsyntax/ext/asm.rs index ac18b9c0e4940..d968858f634eb 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 55f0fa5675ac8..41a4fc9e1dfa4 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -17,7 +17,7 @@ use codemap::{CodeMap, Span, ExpnId, ExpnInfo, NO_EXPANSION}; use ext; use ext::expand; use ext::tt::macro_rules; -use feature_gate::GatedCfg; +use feature_gate::GatedCfgAttr; use parse; use parse::parser; use parse::token; @@ -349,6 +349,7 @@ impl DummyResult { id: ast::DUMMY_NODE_ID, node: ast::ExprLit(P(codemap::respan(sp, ast::LitBool(false)))), span: sp, + attrs: None, }) } @@ -571,7 +572,7 @@ pub struct ExtCtxt<'a> { pub backtrace: ExpnId, pub ecfg: expand::ExpansionConfig<'a>, pub crate_root: Option<&'static str>, - pub feature_gated_cfgs: &'a mut Vec, + pub feature_gated_cfgs: &'a mut Vec, pub mod_path: Vec , pub exported_macros: Vec, @@ -583,7 +584,7 @@ pub struct ExtCtxt<'a> { impl<'a> ExtCtxt<'a> { pub fn new(parse_sess: &'a parse::ParseSess, cfg: ast::CrateConfig, ecfg: expand::ExpansionConfig<'a>, - feature_gated_cfgs: &'a mut Vec) -> ExtCtxt<'a> { + feature_gated_cfgs: &'a mut Vec) -> ExtCtxt<'a> { let env = initial_syntax_expander_table(&ecfg); ExtCtxt { parse_sess: parse_sess, diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 4c10a7496835b..806f5a7ee22ed 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -525,6 +525,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> { init: Some(ex), id: ast::DUMMY_NODE_ID, span: sp, + attrs: None, }); let decl = respan(sp, ast::DeclLocal(local)); P(respan(sp, ast::StmtDecl(P(decl), ast::DUMMY_NODE_ID))) @@ -548,6 +549,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> { init: Some(ex), id: ast::DUMMY_NODE_ID, span: sp, + attrs: None, }); let decl = respan(sp, ast::DeclLocal(local)); P(respan(sp, ast::StmtDecl(P(decl), ast::DUMMY_NODE_ID))) @@ -584,6 +586,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> { id: ast::DUMMY_NODE_ID, node: node, span: span, + attrs: None, }) } diff --git a/src/libsyntax/ext/cfg.rs b/src/libsyntax/ext/cfg.rs index 48199026204c8..e100355e4f868 100644 --- a/src/libsyntax/ext/cfg.rs +++ b/src/libsyntax/ext/cfg.rs @@ -20,6 +20,7 @@ use ext::build::AstBuilder; use attr; use attr::*; use parse::token; +use config::CfgDiagReal; pub fn expand_cfg<'cx>(cx: &mut ExtCtxt, sp: Span, @@ -33,7 +34,12 @@ pub fn expand_cfg<'cx>(cx: &mut ExtCtxt, return DummyResult::expr(sp); } - let matches_cfg = attr::cfg_matches(&cx.parse_sess.span_diagnostic, &cx.cfg, &cfg, - cx.feature_gated_cfgs); + let matches_cfg = { + let mut diag = CfgDiagReal { + diag: &cx.parse_sess.span_diagnostic, + feature_gated_cfgs: cx.feature_gated_cfgs, + }; + attr::cfg_matches(&cx.cfg, &cfg, &mut diag) + }; MacEager::expr(cx.expr_bool(sp, matches_cfg)) } diff --git a/src/libsyntax/ext/concat_idents.rs b/src/libsyntax/ext/concat_idents.rs index e9e36546ad6db..c2233202b2f81 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 2b2e530988387..9488cfb86fc9f 100644 --- a/src/libsyntax/ext/deriving/debug.rs +++ b/src/libsyntax/ext/deriving/debug.rs @@ -148,6 +148,7 @@ fn stmt_let_undescore(cx: &mut ExtCtxt, init: Some(expr), id: ast::DUMMY_NODE_ID, span: sp, + attrs: None, }); let decl = respan(sp, ast::DeclLocal(local)); P(respan(sp, ast::StmtDecl(P(decl), ast::DUMMY_NODE_ID))) diff --git a/src/libsyntax/ext/deriving/generic/mod.rs b/src/libsyntax/ext/deriving/generic/mod.rs index 625abf706caa4..bd89430d81de0 100644 --- a/src/libsyntax/ext/deriving/generic/mod.rs +++ b/src/libsyntax/ext/deriving/generic/mod.rs @@ -204,7 +204,7 @@ use ext::build::AstBuilder; use codemap::{self, DUMMY_SP}; use codemap::Span; use diagnostic::SpanHandler; -use fold::MoveMap; +use util::move_map::MoveMap; use owned_slice::OwnedSlice; use parse::token::{intern, InternedString}; use parse::token::special_idents; diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 9b1a7a50201b0..a09bc24c13937 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -17,13 +17,14 @@ use ast; use ext::mtwt; use ext::build::AstBuilder; use attr; -use attr::AttrMetaMethods; +use attr::{AttrMetaMethods, WithAttrs}; use codemap; use codemap::{Span, Spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute}; use ext::base::*; -use feature_gate::{self, Features, GatedCfg}; +use feature_gate::{self, Features, GatedCfgAttr}; use fold; use fold::*; +use util::move_map::MoveMap; use parse; use parse::token::{fresh_mark, fresh_name, intern}; use ptr::P; @@ -37,11 +38,15 @@ use std::collections::HashSet; pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { let expr_span = e.span; - return e.and_then(|ast::Expr {id, node, span}| match node { + return e.and_then(|ast::Expr {id, node, span, attrs}| match node { // expr_mac should really be expr_ext or something; it's the // entry-point for all syntax extensions. ast::ExprMac(mac) => { + + // Assert that we drop any macro attributes on the floor here + drop(attrs); + let expanded_expr = match expand_mac_invoc(mac, span, |r| r.make_expr(), mark_expr, fld) { @@ -60,6 +65,7 @@ pub fn expand_expr(e: P, 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(fold_thin_attrs(attrs, fld)) } ast::ExprWhile(cond, body, opt_ident) => { let cond = fld.fold_expr(cond); let (body, opt_ident) = expand_loop_block(body, opt_ident, fld); fld.cx.expr(span, ast::ExprWhile(cond, body, opt_ident)) + .with_attrs(fold_thin_attrs(attrs, fld)) } ast::ExprWhileLet(pat, expr, body, opt_ident) => { @@ -96,11 +104,13 @@ pub fn expand_expr(e: P, 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(fold_thin_attrs(attrs, fld)) } ast::ExprLoop(loop_block, opt_ident) => { let (loop_block, opt_ident) = expand_loop_block(loop_block, opt_ident, fld); fld.cx.expr(span, ast::ExprLoop(loop_block, opt_ident)) + .with_attrs(fold_thin_attrs(attrs, fld)) } ast::ExprForLoop(pat, head, body, opt_ident) => { @@ -118,6 +128,7 @@ pub fn expand_expr(e: P, 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(fold_thin_attrs(attrs, fld)) } 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(fold_thin_attrs(attrs, fld)) } ast::ExprClosure(capture_clause, fn_decl, block) => { @@ -144,14 +156,16 @@ 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: fold_thin_attrs(attrs, fld)}) } _ => { P(noop_fold_expr(ast::Expr { id: id, node: node, - span: span + span: span, + attrs: attrs }, fld)) } }); @@ -486,11 +500,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) }; + // Assert that we drop any macro attributes on the floor here + drop(attrs); + let maybe_new_items = expand_mac_invoc(mac.and_then(|m| m), stmt.span, |r| r.make_stmts(), @@ -538,7 +555,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 +585,8 @@ fn expand_non_macro_stmt(Spanned {node, span: stmt_span}: Stmt, fld: &mut MacroE pat: rewritten_pat, // also, don't forget to expand the init: init: init.map(|e| fld.fold_expr(e)), - span: span + span: span, + attrs: fold::fold_thin_attrs(attrs, fld), } }); SmallVector::one(P(Spanned { @@ -1262,7 +1280,7 @@ pub fn expand_crate<'feat>(parse_sess: &parse::ParseSess, // these are the macros being imported to this crate: imported_macros: Vec, user_exts: Vec, - feature_gated_cfgs: &mut Vec, + feature_gated_cfgs: &mut Vec, c: Crate) -> (Crate, HashSet) { let mut cx = ExtCtxt::new(parse_sess, c.config.clone(), cfg, feature_gated_cfgs); diff --git a/src/libsyntax/ext/quote.rs b/src/libsyntax/ext/quote.rs index 5e5b815818161..fc6cacb40f1f3 100644 --- a/src/libsyntax/ext/quote.rs +++ b/src/libsyntax/ext/quote.rs @@ -242,6 +242,7 @@ pub mod rt { id: ast::DUMMY_NODE_ID, node: ast::ExprLit(P(self.clone())), span: DUMMY_SP, + attrs: None, }).to_tokens(cx) } } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index b450331d44036..2c1dc638fbc1c 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -224,6 +224,9 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Option, Status // Allows cfg(target_vendor = "..."). ("cfg_target_vendor", "1.5.0", Some(29718), Active), + + // Allow attributes on expressions and non-item statements + ("stmt_expr_attributes", "1.6.0", Some(15701), Active), ]; // (changing above list without updating src/doc/reference.md makes @cmr sad) @@ -404,25 +407,57 @@ const GATED_CFGS: &'static [(&'static str, &'static str, fn(&Features) -> bool)] ("target_vendor", "cfg_target_vendor", cfg_fn!(|x| x.cfg_target_vendor)), ]; +#[derive(Debug, Eq, PartialEq)] +pub enum GatedCfgAttr { + GatedCfg(GatedCfg), + GatedAttr(Span), +} + #[derive(Debug, Eq, PartialEq)] pub struct GatedCfg { span: Span, index: usize, } -impl Ord for GatedCfg { - fn cmp(&self, other: &GatedCfg) -> cmp::Ordering { - (self.span.lo.0, self.span.hi.0, self.index) - .cmp(&(other.span.lo.0, other.span.hi.0, other.index)) +impl Ord for GatedCfgAttr { + fn cmp(&self, other: &GatedCfgAttr) -> cmp::Ordering { + let to_tup = |s: &GatedCfgAttr| match *s { + GatedCfgAttr::GatedCfg(ref gated_cfg) => { + (gated_cfg.span.lo.0, gated_cfg.span.hi.0, gated_cfg.index) + } + GatedCfgAttr::GatedAttr(ref span) => { + (span.lo.0, span.hi.0, GATED_CFGS.len()) + } + }; + to_tup(self).cmp(&to_tup(other)) } } -impl PartialOrd for GatedCfg { - fn partial_cmp(&self, other: &GatedCfg) -> Option { +impl PartialOrd for GatedCfgAttr { + fn partial_cmp(&self, other: &GatedCfgAttr) -> Option { Some(self.cmp(other)) } } +impl GatedCfgAttr { + pub fn check_and_emit(&self, diagnostic: &SpanHandler, features: &Features) { + match *self { + GatedCfgAttr::GatedCfg(ref cfg) => { + cfg.check_and_emit(diagnostic, features); + } + GatedCfgAttr::GatedAttr(span) => { + if !features.stmt_expr_attributes { + emit_feature_err(diagnostic, + "stmt_expr_attributes", + span, + GateIssue::Language, + EXPLAIN_STMT_ATTR_SYNTAX); + } + } + } + } +} + impl GatedCfg { pub fn gate(cfg: &ast::MetaItem) -> Option { let name = cfg.name(); @@ -435,7 +470,7 @@ impl GatedCfg { } }) } - pub fn check_and_emit(&self, diagnostic: &SpanHandler, features: &Features) { + fn check_and_emit(&self, diagnostic: &SpanHandler, features: &Features) { let (cfg, feature, has_feature) = GATED_CFGS[self.index]; if !has_feature(features) { let explain = format!("`cfg({})` is experimental and subject to change", cfg); @@ -500,6 +535,7 @@ pub struct Features { pub augmented_assignments: bool, pub braced_empty_structs: bool, pub staged_api: bool, + pub stmt_expr_attributes: bool, } impl Features { @@ -532,6 +568,7 @@ impl Features { augmented_assignments: false, braced_empty_structs: false, staged_api: false, + stmt_expr_attributes: false, } } } @@ -545,6 +582,9 @@ const EXPLAIN_PLACEMENT_IN: &'static str = const EXPLAIN_PUSHPOP_UNSAFE: &'static str = "push/pop_unsafe macros are experimental and subject to change."; +const EXPLAIN_STMT_ATTR_SYNTAX: &'static str = + "attributes on non-item statements and expressions are experimental."; + pub fn check_for_box_syntax(f: Option<&Features>, diag: &SpanHandler, span: Span) { if let Some(&Features { allow_box: true, .. }) = f { return; @@ -1105,6 +1145,7 @@ fn check_crate_inner(cm: &CodeMap, span_handler: &SpanHandler, augmented_assignments: cx.has_feature("augmented_assignments"), braced_empty_structs: cx.has_feature("braced_empty_structs"), staged_api: cx.has_feature("staged_api"), + stmt_expr_attributes: cx.has_feature("stmt_expr_attributes"), } } diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 3dba6cbecbd1c..73f2c51b24622 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -20,39 +20,17 @@ use ast::*; use ast; +use attr::{ThinAttributes, ThinAttributesExt}; use ast_util; use codemap::{respan, Span, Spanned}; use owned_slice::OwnedSlice; use parse::token; use ptr::P; -use std::ptr; use util::small_vector::SmallVector; +use util::move_map::MoveMap; use std::rc::Rc; -// This could have a better place to live. -pub trait MoveMap { - fn move_map(self, f: F) -> Self where F: FnMut(T) -> T; -} - -impl MoveMap for Vec { - fn move_map(mut self, mut f: F) -> Vec where F: FnMut(T) -> T { - for p in &mut self { - unsafe { - // FIXME(#5016) this shouldn't need to zero to be safe. - ptr::write(p, f(ptr::read_and_drop(p))); - } - } - self - } -} - -impl MoveMap for OwnedSlice { - fn move_map(self, f: F) -> OwnedSlice where F: FnMut(T) -> T { - OwnedSlice::from_vec(self.into_vec().move_map(f)) - } -} - pub trait Folder : Sized { // Any additions to this trait should happen in form // of a call to a public `noop_*` function that only calls @@ -134,6 +112,14 @@ pub trait Folder : Sized { e.map(|e| noop_fold_expr(e, self)) } + fn fold_opt_expr(&mut self, e: P) -> 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) } @@ -353,7 +339,11 @@ pub fn noop_fold_view_path(view_path: P, fld: &mut T) -> P< } pub fn fold_attrs(attrs: Vec, fld: &mut T) -> Vec { - attrs.into_iter().flat_map(|x| fld.fold_attribute(x)).collect() + attrs.move_flat_map(|x| fld.fold_attribute(x)) +} + +pub fn fold_thin_attrs(attrs: ThinAttributes, fld: &mut T) -> ThinAttributes { + attrs.map_thin_attrs(|v| fold_attrs(v, fld)) } pub fn noop_fold_arm(Arm {attrs, pats, guard, body}: Arm, fld: &mut T) -> Arm { @@ -508,12 +498,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_thin_attrs(|v| fold_attrs(v, fld)), }) } @@ -609,6 +600,8 @@ pub fn noop_fold_tt(tt: &TokenTree, fld: &mut T) -> TokenTree { } pub fn noop_fold_tts(tts: &[TokenTree], fld: &mut T) -> Vec { + // FIXME: Does this have to take a tts slice? + // Could use move_map otherwise... tts.iter().map(|tt| fld.fold_tt(tt)).collect() } @@ -890,8 +883,8 @@ fn noop_fold_bounds(bounds: TyParamBounds, folder: &mut T) 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)), + stmts: stmts.move_flat_map(|s| folder.fold_stmt(s).into_iter()), + expr: expr.and_then(|x| folder.fold_opt_expr(x)), rules: rules, span: folder.new_span(span), }) @@ -939,9 +932,9 @@ pub fn noop_fold_item_underscore(i: Item_, folder: &mut T) -> Item_ { ItemDefaultImpl(unsafety, folder.fold_trait_ref((*trait_ref).clone())) } ItemImpl(unsafety, polarity, generics, ifce, ty, impl_items) => { - let new_impl_items = impl_items.into_iter().flat_map(|item| { - folder.fold_impl_item(item).into_iter() - }).collect(); + let new_impl_items = impl_items.move_flat_map(|item| { + folder.fold_impl_item(item) + }); let ifce = match ifce { None => None, Some(ref trait_ref) => { @@ -957,9 +950,9 @@ pub fn noop_fold_item_underscore(i: Item_, folder: &mut T) -> Item_ { } ItemTrait(unsafety, generics, bounds, items) => { let bounds = folder.fold_bounds(bounds); - let items = items.into_iter().flat_map(|item| { - folder.fold_trait_item(item).into_iter() - }).collect(); + let items = items.move_flat_map(|item| { + folder.fold_trait_item(item) + }); ItemTrait(unsafety, folder.fold_generics(generics), bounds, @@ -1018,7 +1011,7 @@ pub fn noop_fold_impl_item(i: P, folder: &mut T) pub fn noop_fold_mod(Mod {inner, items}: Mod, folder: &mut T) -> Mod { Mod { inner: folder.new_span(inner), - items: items.into_iter().flat_map(|x| folder.fold_item(x).into_iter()).collect(), + items: items.move_flat_map(|x| folder.fold_item(x)), } } @@ -1171,7 +1164,7 @@ pub fn noop_fold_pat(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 +1175,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 +1322,19 @@ 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_thin_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> { + es.move_flat_map(|e| folder.fold_opt_expr(e)) +} + pub fn noop_fold_stmt(Spanned {node, span}: Stmt, folder: &mut T) -> SmallVector> { let span = folder.new_span(span); @@ -1346,20 +1348,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_thin_attrs(|v| fold_attrs(v, folder))), span: span })) } diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs index 475408472ee61..da62c253d8fd4 100644 --- a/src/libsyntax/lib.rs +++ b/src/libsyntax/lib.rs @@ -70,6 +70,7 @@ pub mod util { #[cfg(test)] pub mod parser_testing; pub mod small_vector; + pub mod move_map; } pub mod diagnostics { diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index 7e2fd09a37316..e9c8173a4d980 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -699,7 +699,8 @@ mod tests { } ), }), - span: sp(0, 1) + span: sp(0, 1), + attrs: None, })) } @@ -721,7 +722,8 @@ mod tests { } ) }), - span: sp(0, 6) + span: sp(0, 6), + attrs: None, })) } @@ -848,9 +850,11 @@ mod tests { } ), }), - span:sp(7,8) + span:sp(7,8), + attrs: None, }))), - span:sp(0,8) + span:sp(0,8), + attrs: None, })) } @@ -869,7 +873,8 @@ mod tests { } ), }), - span: sp(0,1)}), + span: sp(0,1), + attrs: None}), ast::DUMMY_NODE_ID), span: sp(0,1)}))) @@ -963,7 +968,8 @@ mod tests { } ), }), - span: sp(17,18)}), + span: sp(17,18), + attrs: None,}), ast::DUMMY_NODE_ID), span: sp(17,19)})), expr: None, diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 56a06f70ed4b4..46bd68428ab54 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -56,6 +56,7 @@ use ast::TypeTraitItem; use ast::{UnnamedField, UnsafeBlock}; use ast::{ViewPath, ViewPathGlob, ViewPathList, ViewPathSimple}; use ast::{Visibility, WhereClause}; +use attr::{ThinAttributes, ThinAttributesExt, AttributesExt}; use ast; use ast_util::{self, ident_to_path}; use codemap::{self, Span, BytePos, Spanned, spanned, mk_sp, CodeMap}; @@ -140,7 +141,7 @@ macro_rules! maybe_whole_expr { _ => unreachable!() }; let span = $p.span; - Some($p.mk_expr(span.lo, span.hi, ExprPath(None, pt))) + Some($p.mk_expr(span.lo, span.hi, ExprPath(None, pt), None)) } token::Interpolated(token::NtBlock(_)) => { // FIXME: The following avoids an issue with lexical borrowck scopes, @@ -150,7 +151,7 @@ macro_rules! maybe_whole_expr { _ => unreachable!() }; let span = $p.span; - Some($p.mk_expr(span.lo, span.hi, ExprBlock(b))) + Some($p.mk_expr(span.lo, span.hi, ExprBlock(b), None)) } _ => None }; @@ -319,6 +320,27 @@ pub struct ModulePathError { pub help_msg: String, } +pub enum LhsExpr { + NotYetParsed, + AttributesParsed(ThinAttributes), + AlreadyParsed(P), +} + +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 @@ impl<'a> Parser<'a> { } /// 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 @@ impl<'a> Parser<'a> { }) } - 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 @@ impl<'a> Parser<'a> { 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 @@ impl<'a> Parser<'a> { id: ast::DUMMY_NODE_ID, node: ExprLit(lv_lit), span: *span, + attrs: attrs, }) } @@ -2002,9 +2028,20 @@ impl<'a> Parser<'a> { /// At the bottom (top?) of the precedence hierarchy, /// parse things like parenthesized exprs, /// macros, return, etc. - pub fn parse_bottom_expr(&mut self) -> PResult> { + /// + /// 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 @@ impl<'a> Parser<'a> { token::OpenDelim(token::Paren) => { try!(self.bump()); + let attrs = try!(self.parse_inner_attributes()) + .into_thin_attrs() + .prepend(attrs); + // (e) is parenthesized e // (e,) is a tuple with only one field, e let mut es = vec![]; @@ -2036,17 +2077,17 @@ impl<'a> Parser<'a> { hi = self.last_span.hi; return if es.len() == 1 && !trailing_comma { - Ok(self.mk_expr(lo, hi, ExprParen(es.into_iter().nth(0).unwrap()))) + Ok(self.mk_expr(lo, hi, ExprParen(es.into_iter().nth(0).unwrap()), attrs)) } else { - Ok(self.mk_expr(lo, hi, ExprTup(es))) + Ok(self.mk_expr(lo, hi, ExprTup(es), attrs)) } }, token::OpenDelim(token::Brace) => { - return self.parse_block_expr(lo, DefaultBlock); + return self.parse_block_expr(lo, DefaultBlock, attrs); }, token::BinOp(token::Or) | token::OrOr => { let lo = self.span.lo; - return self.parse_lambda_expr(lo, CaptureByRef); + return self.parse_lambda_expr(lo, CaptureByRef, attrs); }, token::Ident(id @ ast::Ident { name: token::SELF_KEYWORD_NAME, @@ -2060,6 +2101,10 @@ impl<'a> Parser<'a> { token::OpenDelim(token::Bracket) => { try!(self.bump()); + let inner_attrs = try!(self.parse_inner_attributes()) + .into_thin_attrs(); + attrs.update(|attrs| attrs.append(inner_attrs)); + if self.check(&token::CloseDelim(token::Bracket)) { // Empty vector. try!(self.bump()); @@ -2097,22 +2142,22 @@ impl<'a> Parser<'a> { let (qself, path) = try!(self.parse_qualified_path(LifetimeAndTypesWithColons)); hi = path.span.hi; - return Ok(self.mk_expr(lo, hi, ExprPath(Some(qself), path))); + return Ok(self.mk_expr(lo, hi, ExprPath(Some(qself), path), attrs)); } if try!(self.eat_keyword(keywords::Move) ){ let lo = self.last_span.lo; - return self.parse_lambda_expr(lo, CaptureByValue); + return self.parse_lambda_expr(lo, CaptureByValue, attrs); } if try!(self.eat_keyword(keywords::If)) { - return self.parse_if_expr(); + return self.parse_if_expr(attrs); } if try!(self.eat_keyword(keywords::For) ){ let lo = self.last_span.lo; - return self.parse_for_expr(None, lo); + return self.parse_for_expr(None, lo, attrs); } if try!(self.eat_keyword(keywords::While) ){ let lo = self.last_span.lo; - return self.parse_while_expr(None, lo); + return self.parse_while_expr(None, lo, attrs); } if self.token.is_lifetime() { let lifetime = self.get_lifetime(); @@ -2120,19 +2165,19 @@ impl<'a> Parser<'a> { try!(self.bump()); try!(self.expect(&token::Colon)); if try!(self.eat_keyword(keywords::While) ){ - return self.parse_while_expr(Some(lifetime), lo) + return self.parse_while_expr(Some(lifetime), lo, attrs) } if try!(self.eat_keyword(keywords::For) ){ - return self.parse_for_expr(Some(lifetime), lo) + return self.parse_for_expr(Some(lifetime), lo, attrs) } if try!(self.eat_keyword(keywords::Loop) ){ - return self.parse_loop_expr(Some(lifetime), lo) + return self.parse_loop_expr(Some(lifetime), lo, attrs) } return Err(self.fatal("expected `while`, `for`, or `loop` after a label")) } if try!(self.eat_keyword(keywords::Loop) ){ let lo = self.last_span.lo; - return self.parse_loop_expr(None, lo); + return self.parse_loop_expr(None, lo, attrs); } if try!(self.eat_keyword(keywords::Continue) ){ let ex = if self.token.is_lifetime() { @@ -2146,15 +2191,16 @@ impl<'a> Parser<'a> { ExprAgain(None) }; let hi = self.last_span.hi; - return Ok(self.mk_expr(lo, hi, ex)); + return Ok(self.mk_expr(lo, hi, ex, attrs)); } if try!(self.eat_keyword(keywords::Match) ){ - return self.parse_match_expr(); + return self.parse_match_expr(attrs); } if try!(self.eat_keyword(keywords::Unsafe) ){ return self.parse_block_expr( lo, - UnsafeBlock(ast::UserProvided)); + UnsafeBlock(ast::UserProvided), + attrs); } if try!(self.eat_keyword(keywords::Return) ){ if self.token.can_begin_expr() { @@ -2196,7 +2242,8 @@ impl<'a> Parser<'a> { return Ok(self.mk_mac_expr(lo, hi, - Mac_ { path: pth, tts: tts, ctxt: EMPTY_CTXT })); + Mac_ { path: pth, tts: tts, ctxt: EMPTY_CTXT }, + attrs)); } if self.check(&token::OpenDelim(token::Brace)) { // This is a struct literal, unless we're prohibited @@ -2210,6 +2257,10 @@ impl<'a> Parser<'a> { let mut fields = Vec::new(); let mut base = None; + let attrs = attrs.append( + try!(self.parse_inner_attributes()) + .into_thin_attrs()); + while self.token != token::CloseDelim(token::Brace) { if try!(self.eat(&token::DotDot) ){ base = Some(try!(self.parse_expr())); @@ -2225,7 +2276,7 @@ impl<'a> Parser<'a> { hi = self.span.hi; try!(self.expect(&token::CloseDelim(token::Brace))); ex = ExprStruct(pth, fields, base); - return Ok(self.mk_expr(lo, hi, ex)); + return Ok(self.mk_expr(lo, hi, ex, attrs)); } } @@ -2240,24 +2291,74 @@ impl<'a> Parser<'a> { } } - return Ok(self.mk_expr(lo, hi, ex)); + return Ok(self.mk_expr(lo, hi, ex, attrs)); + } + + fn parse_or_use_outer_attributes(&mut self, + already_parsed_attrs: Option) + -> PResult { + if let Some(attrs) = already_parsed_attrs { + Ok(attrs) + } else { + self.parse_outer_attributes().map(|a| a.into_thin_attrs()) + } } /// Parse a block or unsafe block - pub fn parse_block_expr(&mut self, lo: BytePos, blk_mode: BlockCheckMode) + pub fn parse_block_expr(&mut self, lo: BytePos, blk_mode: BlockCheckMode, + attrs: ThinAttributes) -> PResult> { + + let outer_attrs = attrs; try!(self.expect(&token::OpenDelim(token::Brace))); + + let inner_attrs = try!(self.parse_inner_attributes()).into_thin_attrs(); + let attrs = outer_attrs.append(inner_attrs); + let blk = try!(self.parse_block_tail(lo, blk_mode)); - return Ok(self.mk_expr(blk.span.lo, blk.span.hi, ExprBlock(blk))); + return Ok(self.mk_expr(blk.span.lo, blk.span.hi, ExprBlock(blk), attrs)); } /// parse a.b or a(13) or a[4] or just a - pub fn parse_dot_or_call_expr(&mut self) -> PResult> { + 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(attrs)); + match expr.node { + ExprIf(..) | ExprIfLet(..) => { + if !expr.attrs.as_attr_slice().is_empty() { + // Just point to the first attribute in there... + let span = expr.attrs.as_attr_slice()[0].span; + + self.span_err(span, + "attributes are not yet allowed on `if` \ + expressions"); + } + } + _ => {} + } + expr + }) + ) } - pub fn parse_dot_or_call_expr_with(&mut self, e0: P) -> 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 @@ impl<'a> Parser<'a> { es.insert(0, e); let id = spanned(dot, hi, i); let nd = self.mk_method_call(id, tys, es); - e = self.mk_expr(lo, hi, nd); + e = self.mk_expr(lo, hi, nd, None); } _ => { if !tys.is_empty() { @@ -2307,7 +2408,7 @@ impl<'a> Parser<'a> { let id = spanned(dot, hi, i); let field = self.mk_field(e, id); - e = self.mk_expr(lo, hi, field); + e = self.mk_expr(lo, hi, field, None); } } } @@ -2326,7 +2427,7 @@ impl<'a> Parser<'a> { Some(n) => { let id = spanned(dot, hi, n); let field = self.mk_tup_field(e, id); - e = self.mk_expr(lo, hi, field); + e = self.mk_expr(lo, hi, field, None); } None => { let last_span = self.last_span; @@ -2370,7 +2471,7 @@ impl<'a> Parser<'a> { hi = self.last_span.hi; let nd = self.mk_call(e, es); - e = self.mk_expr(lo, hi, nd); + e = self.mk_expr(lo, hi, nd, None); } // expr[...] @@ -2381,7 +2482,7 @@ impl<'a> Parser<'a> { hi = self.span.hi; try!(self.commit_expr_expecting(&*ix, token::CloseDelim(token::Bracket))); let index = self.mk_index(e, ix); - e = self.mk_expr(lo, hi, index) + e = self.mk_expr(lo, hi, index, None) } _ => return Ok(e) } @@ -2578,75 +2679,90 @@ impl<'a> Parser<'a> { } /// Parse a prefix-unary-operator expr - pub fn parse_prefix_expr(&mut self) -> PResult> { + 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 @@ impl<'a> Parser<'a> { // Special cases: if op == AssocOp::As { let rhs = try!(self.parse_ty()); - lhs = self.mk_expr(lhs.span.lo, rhs.span.hi, ExprCast(lhs, rhs)); + lhs = self.mk_expr(lhs.span.lo, rhs.span.hi, + ExprCast(lhs, rhs), None); continue } else if op == AssocOp::DotDot { // If we didn’t have to handle `x..`, it would be pretty easy to generalise @@ -2679,7 +2796,8 @@ impl<'a> Parser<'a> { // We have 2 alternatives here: `x..y` and `x..` The other two variants are // handled with `parse_prefix_range_expr` call above. let rhs = if self.is_at_start_of_range_notation_rhs() { - self.parse_assoc_expr_with(op.precedence() + 1, None).ok() + self.parse_assoc_expr_with(op.precedence() + 1, + LhsExpr::NotYetParsed).ok() } else { None }; @@ -2689,22 +2807,22 @@ impl<'a> Parser<'a> { cur_op_span }); let r = self.mk_range(Some(lhs), rhs); - lhs = self.mk_expr(lhs_span.lo, rhs_span.hi, r); + lhs = self.mk_expr(lhs_span.lo, rhs_span.hi, r, None); break } let rhs = try!(match op.fixity() { Fixity::Right => self.with_res(restrictions, |this|{ - this.parse_assoc_expr_with(op.precedence(), None) + this.parse_assoc_expr_with(op.precedence(), LhsExpr::NotYetParsed) }), Fixity::Left => self.with_res(restrictions, |this|{ - this.parse_assoc_expr_with(op.precedence() + 1, None) + this.parse_assoc_expr_with(op.precedence() + 1, LhsExpr::NotYetParsed) }), // We currently have no non-associative operators that are not handled above by // the special cases. The code is here only for future convenience. Fixity::None => self.with_res(restrictions, |this|{ - this.parse_assoc_expr_with(op.precedence() + 1, None) + this.parse_assoc_expr_with(op.precedence() + 1, LhsExpr::NotYetParsed) }), }); @@ -2717,12 +2835,12 @@ impl<'a> Parser<'a> { let ast_op = op.to_ast_binop().unwrap(); let (lhs_span, rhs_span) = (lhs.span, rhs.span); let binary = self.mk_binary(codemap::respan(cur_op_span, ast_op), lhs, rhs); - self.mk_expr(lhs_span.lo, rhs_span.hi, binary) + self.mk_expr(lhs_span.lo, rhs_span.hi, binary, None) } AssocOp::Assign => - self.mk_expr(lhs.span.lo, rhs.span.hi, ExprAssign(lhs, rhs)), + self.mk_expr(lhs.span.lo, rhs.span.hi, ExprAssign(lhs, rhs), None), AssocOp::Inplace => - self.mk_expr(lhs.span.lo, rhs.span.hi, ExprInPlace(lhs, rhs)), + self.mk_expr(lhs.span.lo, rhs.span.hi, ExprInPlace(lhs, rhs), None), AssocOp::AssignOp(k) => { let aop = match k { token::Plus => BiAdd, @@ -2738,7 +2856,7 @@ impl<'a> Parser<'a> { }; let (lhs_span, rhs_span) = (lhs.span, rhs.span); let aopexpr = self.mk_assign_op(codemap::respan(cur_op_span, aop), lhs, rhs); - self.mk_expr(lhs_span.lo, rhs_span.hi, aopexpr) + self.mk_expr(lhs_span.lo, rhs_span.hi, aopexpr, None) } AssocOp::As | AssocOp::DotDot => self.bug("As or DotDot branch reached") }; @@ -2769,15 +2887,20 @@ impl<'a> Parser<'a> { } /// Parse prefix-forms of range notation: `..expr` and `..` - fn parse_prefix_range_expr(&mut self) -> PResult> { + 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 @@ impl<'a> Parser<'a> { None }; let r = self.mk_range(None, opt_end); - Ok(self.mk_expr(lo, hi, r)) + Ok(self.mk_expr(lo, hi, r, attrs)) } fn is_at_start_of_range_notation_rhs(&self) -> bool { @@ -2801,12 +2924,12 @@ impl<'a> Parser<'a> { } /// Parse an 'if' or 'if let' expression ('if' token already eaten) - pub fn parse_if_expr(&mut self) -> PResult> { + 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 @@ impl<'a> Parser<'a> { hi = elexpr.span.hi; els = Some(elexpr); } - Ok(self.mk_expr(lo, hi, ExprIf(cond, thn, els))) + Ok(self.mk_expr(lo, hi, ExprIf(cond, thn, els), attrs)) } /// Parse an 'if let' expression ('if' token already eaten) - pub fn parse_if_let_expr(&mut self) -> PResult> { + 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 @@ impl<'a> Parser<'a> { } else { (thn.span.hi, None) }; - Ok(self.mk_expr(lo, hi, ExprIfLet(pat, expr, thn, els))) + Ok(self.mk_expr(lo, hi, ExprIfLet(pat, expr, thn, els), attrs)) } // `|args| expr` - pub fn parse_lambda_expr(&mut self, lo: BytePos, capture_clause: CaptureClause) + pub fn parse_lambda_expr(&mut self, lo: BytePos, + capture_clause: CaptureClause, + attrs: ThinAttributes) -> PResult> { let decl = try!(self.parse_fn_block_decl()); @@ -2863,80 +2989,98 @@ impl<'a> Parser<'a> { Ok(self.mk_expr( lo, body.span.hi, - ExprClosure(capture_clause, decl, body))) + ExprClosure(capture_clause, decl, body), attrs)) } + // `else` token already eaten pub fn parse_else_expr(&mut self) -> PResult> { 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(iattrs.into_thin_attrs()); + let hi = self.last_span.hi; - Ok(self.mk_expr(span_lo, hi, ExprForLoop(pat, expr, loop_block, opt_ident))) + Ok(self.mk_expr(span_lo, hi, + ExprForLoop(pat, expr, loop_block, opt_ident), + attrs)) } /// Parse a 'while' or 'while let' expression ('while' token already eaten) pub fn parse_while_expr(&mut self, opt_ident: Option, - 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(iattrs.into_thin_attrs()); let hi = body.span.hi; - return Ok(self.mk_expr(span_lo, hi, ExprWhile(cond, body, opt_ident))); + return Ok(self.mk_expr(span_lo, hi, ExprWhile(cond, body, opt_ident), + attrs)); } /// Parse a 'while let' expression ('while' token already eaten) pub fn parse_while_let_expr(&mut self, opt_ident: Option, - 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(iattrs.into_thin_attrs()); let hi = body.span.hi; - return Ok(self.mk_expr(span_lo, hi, ExprWhileLet(pat, expr, body, opt_ident))); + return Ok(self.mk_expr(span_lo, hi, ExprWhileLet(pat, expr, body, opt_ident), attrs)); } + // parse `loop {...}`, `loop` token already eaten pub fn parse_loop_expr(&mut self, opt_ident: Option, - 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(iattrs.into_thin_attrs()); let hi = body.span.hi; - Ok(self.mk_expr(span_lo, hi, ExprLoop(body, opt_ident))) + Ok(self.mk_expr(span_lo, hi, ExprLoop(body, opt_ident), attrs)) } - fn parse_match_expr(&mut self) -> PResult> { + // `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( + try!(self.parse_inner_attributes()).into_thin_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 @@ impl<'a> Parser<'a> { guard = Some(try!(self.parse_expr())); } try!(self.expect(&token::FatArrow)); - let expr = try!(self.parse_expr_res(Restrictions::RESTRICTION_STMT_EXPR)); + let expr = try!(self.parse_expr_res(Restrictions::RESTRICTION_STMT_EXPR, None)); let require_comma = !classify::expr_is_simple_block(&*expr) @@ -2971,7 +3115,7 @@ impl<'a> Parser<'a> { /// Parse an expression pub fn parse_expr(&mut self) -> PResult> { - self.parse_expr_res(Restrictions::empty()) + self.parse_expr_res(Restrictions::empty(), None) } /// Evaluate the closure with restrictions in place. @@ -2988,8 +3132,10 @@ impl<'a> Parser<'a> { } /// Parse an expression, subject to the given restrictions - pub fn parse_expr_res(&mut self, r: Restrictions) -> PResult> { - 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 @@ impl<'a> Parser<'a> { (None, try!(self.parse_path(LifetimeAndTypesWithColons))) }; let hi = self.last_span.hi; - Ok(self.mk_expr(lo, hi, ExprPath(qself, path))) + Ok(self.mk_expr(lo, hi, ExprPath(qself, path), None)) } else { - self.parse_literal_maybe_minus() + self.parse_pat_literal_maybe_minus() } } @@ -3274,7 +3420,7 @@ impl<'a> Parser<'a> { token::DotDotDot => { // Parse range let hi = self.last_span.hi; - let begin = self.mk_expr(lo, hi, ExprPath(qself, path)); + let begin = self.mk_expr(lo, hi, ExprPath(qself, path), None); try!(self.bump()); let end = try!(self.parse_pat_range_end()); pat = PatRange(begin, end); @@ -3321,7 +3467,7 @@ impl<'a> Parser<'a> { } } else { // Try to parse everything else as literal with optional minus - let begin = try!(self.parse_literal_maybe_minus()); + let begin = try!(self.parse_pat_literal_maybe_minus()); if try!(self.eat(&token::DotDotDot)) { let end = try!(self.parse_pat_range_end()); pat = PatRange(begin, end); @@ -3378,7 +3524,7 @@ impl<'a> Parser<'a> { } /// Parse a local variable declaration - fn parse_local(&mut self) -> PResult> { + fn parse_local(&mut self, attrs: ThinAttributes) -> PResult> { let lo = self.span.lo; let pat = try!(self.parse_pat()); @@ -3393,13 +3539,14 @@ impl<'a> Parser<'a> { init: init, id: ast::DUMMY_NODE_ID, span: mk_sp(lo, self.last_span.hi), + attrs: attrs, })) } /// Parse a "let" stmt - fn parse_let(&mut self) -> PResult> { + 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 @@ impl<'a> Parser<'a> { 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_thin_attrs())); + let hi = decl.span.hi; + let stmt = StmtDecl(decl, ast::DUMMY_NODE_ID); + spanned(lo, hi, stmt) } else if self.token.is_ident() && !self.token.is_any_keyword() && self.look_ahead(1, |t| *t == token::Not) { // it's a macro invocation: - check_expected_item(self, &attrs); - // Potential trouble: if we allow macros with paths instead of // idents, we'd need to look ahead past the whole path here... let pth = try!(self.parse_path(NoTypesAllowed)); @@ -3511,11 +3650,12 @@ impl<'a> Parser<'a> { }; if id.name == token::special_idents::invalid.name { - spanned(lo, hi, - StmtMac(P(spanned(lo, - hi, - Mac_ { path: pth, tts: tts, ctxt: EMPTY_CTXT })), - style)) + let stmt = StmtMac(P(spanned(lo, + hi, + Mac_ { path: pth, tts: tts, ctxt: EMPTY_CTXT })), + style, + attrs.into_thin_attrs()); + spanned(lo, hi, stmt) } else { // if it has a special ident, it's definitely an item // @@ -3535,30 +3675,43 @@ impl<'a> Parser<'a> { lo, hi, id /*id is good here*/, ItemMac(spanned(lo, hi, Mac_ { path: pth, tts: tts, ctxt: EMPTY_CTXT })), - Inherited, Vec::new(/*no attrs*/))))), + Inherited, attrs)))), ast::DUMMY_NODE_ID)) } } else { - match try!(self.parse_item_(attrs, false)) { + // FIXME: Bad copy of attrs + match try!(self.parse_item_(attrs.clone(), false, true)) { Some(i) => { let hi = i.span.hi; let decl = P(spanned(lo, hi, DeclItem(i))); spanned(lo, hi, StmtDecl(decl, ast::DUMMY_NODE_ID)) } None => { + let unused_attrs = |attrs: &[_], s: &mut Self| { + if attrs.len() > 0 { + s.span_err(s.span, + "expected statement after outer attribute"); + } + }; + // Do not attempt to parse an expression if we're done here. if self.token == token::Semi { + unused_attrs(&attrs, self); try!(self.bump()); return Ok(None); } if self.token == token::CloseDelim(token::Brace) { + unused_attrs(&attrs, self); return Ok(None); } // Remainder are line-expr stmts. - let e = try!(self.parse_expr_res(Restrictions::RESTRICTION_STMT_EXPR)); - spanned(lo, e.span.hi, StmtExpr(e, ast::DUMMY_NODE_ID)) + let e = try!(self.parse_expr_res( + Restrictions::RESTRICTION_STMT_EXPR, Some(attrs.into_thin_attrs()))); + let hi = e.span.hi; + let stmt = StmtExpr(e, ast::DUMMY_NODE_ID); + spanned(lo, hi, stmt) } } })) @@ -3614,22 +3767,23 @@ impl<'a> Parser<'a> { StmtExpr(e, _) => { try!(self.handle_expression_like_statement(e, span, &mut stmts, &mut expr)); } - StmtMac(mac, MacStmtWithoutBraces) => { + StmtMac(mac, MacStmtWithoutBraces, attrs) => { // statement macro without braces; might be an // expr depending on whether a semicolon follows match self.token { token::Semi => { stmts.push(P(Spanned { - node: StmtMac(mac, MacStmtWithSemicolon), + node: StmtMac(mac, MacStmtWithSemicolon, attrs), span: mk_sp(span.lo, self.span.hi), })); try!(self.bump()); } _ => { let e = self.mk_mac_expr(span.lo, span.hi, - mac.and_then(|m| m.node)); - let e = try!(self.parse_dot_or_call_expr_with(e)); - let e = try!(self.parse_assoc_expr_with(0, Some(e))); + mac.and_then(|m| m.node), + None); + let e = try!(self.parse_dot_or_call_expr_with(e, attrs)); + let e = try!(self.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(e))); try!(self.handle_expression_like_statement( e, span, @@ -3638,12 +3792,12 @@ impl<'a> Parser<'a> { } } } - StmtMac(m, style) => { + StmtMac(m, style, attrs) => { // statement macro; might be an expr match self.token { token::Semi => { stmts.push(P(Spanned { - node: StmtMac(m, MacStmtWithSemicolon), + node: StmtMac(m, MacStmtWithSemicolon, attrs), span: mk_sp(span.lo, self.span.hi), })); try!(self.bump()); @@ -3652,11 +3806,12 @@ impl<'a> Parser<'a> { // if a block ends in `m!(arg)` without // a `;`, it must be an expr expr = Some(self.mk_mac_expr(span.lo, span.hi, - m.and_then(|x| x.node))); + m.and_then(|x| x.node), + attrs)); } _ => { stmts.push(P(Spanned { - node: StmtMac(m, style), + node: StmtMac(m, style, attrs), span: span })); } @@ -5210,7 +5365,7 @@ impl<'a> Parser<'a> { /// NB: this function no longer parses the items inside an /// extern crate. fn parse_item_(&mut self, attrs: Vec, - 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 @@ impl<'a> Parser<'a> { maybe_append(attrs, extra_attrs)); return Ok(Some(item)); } - self.parse_macro_use_or_failure(attrs,macros_allowed,lo,visibility) + self.parse_macro_use_or_failure(attrs,macros_allowed,attributes_allowed,lo,visibility) } /// Parse a foreign item. @@ -5487,7 +5642,7 @@ impl<'a> Parser<'a> { } // FIXME #5668: this will occur for a macro invocation: - match try!(self.parse_macro_use_or_failure(attrs, true, lo, visibility)) { + match try!(self.parse_macro_use_or_failure(attrs, true, false, lo, visibility)) { Some(item) => { return Err(self.span_fatal(item.span, "macros cannot expand to foreign items")); } @@ -5500,6 +5655,7 @@ impl<'a> Parser<'a> { &mut self, attrs: Vec , macros_allowed: bool, + attributes_allowed: bool, lo: BytePos, visibility: Visibility ) -> PResult>> { @@ -5566,7 +5722,7 @@ impl<'a> Parser<'a> { } } - if !attrs.is_empty() { + if !attributes_allowed && !attrs.is_empty() { self.expected_item_err(&attrs); } Ok(None) @@ -5574,7 +5730,7 @@ impl<'a> Parser<'a> { pub fn parse_item(&mut self) -> PResult>> { 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 5e4449af60405..17b7d8dbaece9 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -202,6 +202,7 @@ impl Token { Interpolated(NtIdent(..)) => true, Interpolated(NtBlock(..)) => true, Interpolated(NtPath(..)) => true, + Pound => true, // for expression attributes _ => false, } } diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 6de86de9c54eb..aa55cb847faff 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -13,6 +13,8 @@ pub use self::AnnNode::*; use abi; use ast::{self, TokenTree}; use ast::{RegionTyParamBound, TraitTyParamBound, TraitBoundModifier}; +use ast::Attribute; +use attr::ThinAttributesExt; use ast_util; use util::parser::AssocOp; use attr; @@ -77,7 +79,7 @@ pub fn rust_printer<'a>(writer: Box) -> 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 +92,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 +170,7 @@ impl<'a> State<'a> { 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 +401,7 @@ pub fn fun_to_string(decl: &ast::FnDecl, pub fn block_to_string(blk: &ast::Block) -> String { to_string(|s| { // containing cbox, will be closed by print-block at } - try!(s.cbox(indent_unit)); + try!(s.cbox(INDENT_UNIT)); // head-ibox, will be closed by print-block after { try!(s.ibox(0)); s.print_block(blk) @@ -707,43 +707,61 @@ pub trait PrintState<'a> { } fn print_inner_attributes(&mut self, - attrs: &[ast::Attribute]) -> io::Result<()> { - let mut count = 0; - for attr in attrs { - match attr.node.style { - ast::AttrStyle::Inner => { - try!(self.print_attribute(attr)); - count += 1; - } - _ => {/* fallthrough */ } - } - } - if count > 0 { - try!(self.hardbreak_if_not_bol()); - } - Ok(()) + attrs: &[ast::Attribute]) -> io::Result<()> { + self.print_either_attributes(attrs, ast::AttrStyle::Inner, false, true) + } + + fn print_inner_attributes_no_trailing_hardbreak(&mut self, + attrs: &[ast::Attribute]) + -> io::Result<()> { + self.print_either_attributes(attrs, ast::AttrStyle::Inner, false, false) } fn print_outer_attributes(&mut self, attrs: &[ast::Attribute]) -> io::Result<()> { + self.print_either_attributes(attrs, ast::AttrStyle::Outer, false, true) + } + + fn print_inner_attributes_inline(&mut self, + attrs: &[ast::Attribute]) -> io::Result<()> { + self.print_either_attributes(attrs, ast::AttrStyle::Inner, true, true) + } + + fn print_outer_attributes_inline(&mut self, + attrs: &[ast::Attribute]) -> io::Result<()> { + self.print_either_attributes(attrs, ast::AttrStyle::Outer, true, true) + } + + fn print_either_attributes(&mut self, + attrs: &[ast::Attribute], + kind: ast::AttrStyle, + is_inline: bool, + trailing_hardbreak: bool) -> io::Result<()> { let mut count = 0; for attr in attrs { - match attr.node.style { - ast::AttrStyle::Outer => { - try!(self.print_attribute(attr)); + if attr.node.style == kind { + try!(self.print_attribute_inline(attr, is_inline)); + if is_inline { + try!(self.nbsp()); + } count += 1; - } - _ => {/* fallthrough */ } } } - if count > 0 { + if count > 0 && trailing_hardbreak && !is_inline { try!(self.hardbreak_if_not_bol()); } Ok(()) } fn print_attribute(&mut self, attr: &ast::Attribute) -> io::Result<()> { - try!(self.hardbreak_if_not_bol()); + self.print_attribute_inline(attr, false) + } + + fn print_attribute_inline(&mut self, attr: &ast::Attribute, + is_inline: bool) -> io::Result<()> { + if !is_inline { + try!(self.hardbreak_if_not_bol()); + } try!(self.maybe_print_comment(attr.span.lo)); if attr.node.is_sugared_doc { word(self.writer(), &attr.value_str().unwrap()) @@ -758,7 +776,7 @@ pub trait PrintState<'a> { } fn print_meta_item(&mut self, item: &ast::MetaItem) -> io::Result<()> { - try!(self.ibox(indent_unit)); + try!(self.ibox(INDENT_UNIT)); match item.node { ast::MetaWord(ref name) => { try!(word(self.writer(), &name)); @@ -779,6 +797,13 @@ pub trait PrintState<'a> { } self.end() } + + fn space_if_not_bol(&mut self) -> io::Result<()> { + if !self.is_bol() { try!(space(self.writer())); } + Ok(()) + } + + fn nbsp(&mut self) -> io::Result<()> { word(self.writer(), " ") } } impl<'a> PrintState<'a> for State<'a> { @@ -809,8 +834,6 @@ impl<'a> State<'a> { pp::cbox(&mut self.s, u) } - pub fn nbsp(&mut self) -> io::Result<()> { word(&mut self.s, " ") } - pub fn word_nbsp(&mut self, w: &str) -> io::Result<()> { try!(word(&mut self.s, w)); self.nbsp() @@ -818,7 +841,7 @@ impl<'a> State<'a> { pub fn head(&mut self, w: &str) -> io::Result<()> { // outer-box is consistent - try!(self.cbox(indent_unit)); + try!(self.cbox(INDENT_UNIT)); // head-box is inconsistent try!(self.ibox(w.len() + 1)); // keyword that starts the head @@ -848,7 +871,7 @@ impl<'a> State<'a> { Ok(()) } pub fn bclose(&mut self, span: codemap::Span) -> io::Result<()> { - self.bclose_(span, indent_unit) + self.bclose_(span, INDENT_UNIT) } pub fn in_cbox(&self) -> bool { @@ -858,10 +881,6 @@ impl<'a> State<'a> { } } - pub fn space_if_not_bol(&mut self) -> io::Result<()> { - if !self.is_bol() { try!(space(&mut self.s)); } - Ok(()) - } pub fn break_offset_if_not_bol(&mut self, n: usize, off: isize) -> io::Result<()> { if !self.is_bol() { @@ -1200,7 +1219,7 @@ impl<'a> State<'a> { try!(self.bclose(item.span)); } ast::ItemTy(ref ty, ref params) => { - try!(self.ibox(indent_unit)); + try!(self.ibox(INDENT_UNIT)); try!(self.ibox(0)); try!(self.word_nbsp(&visibility_qualified(item.vis, "type"))); try!(self.print_ident(item.ident)); @@ -1314,7 +1333,7 @@ impl<'a> State<'a> { try!(self.print_path(&node.path, false, 0)); try!(word(&mut self.s, "! ")); try!(self.print_ident(item.ident)); - try!(self.cbox(indent_unit)); + try!(self.cbox(INDENT_UNIT)); try!(self.popen()); try!(self.print_tts(&node.tts[..])); try!(self.pclose()); @@ -1370,7 +1389,7 @@ impl<'a> State<'a> { try!(self.space_if_not_bol()); try!(self.maybe_print_comment(v.span.lo)); try!(self.print_outer_attributes(&v.node.attrs)); - try!(self.ibox(indent_unit)); + try!(self.ibox(INDENT_UNIT)); try!(self.print_variant(&**v)); try!(word(&mut self.s, ",")); try!(self.end()); @@ -1592,7 +1611,7 @@ impl<'a> State<'a> { // code copied from ItemMac: try!(self.print_path(&node.path, false, 0)); try!(word(&mut self.s, "! ")); - try!(self.cbox(indent_unit)); + try!(self.cbox(INDENT_UNIT)); try!(self.popen()); try!(self.print_tts(&node.tts[..])); try!(self.pclose()); @@ -1611,15 +1630,16 @@ impl<'a> State<'a> { } ast::StmtExpr(ref expr, _) => { try!(self.space_if_not_bol()); - try!(self.print_expr(&**expr)); + try!(self.print_expr_outer_attr_style(&**expr, false)); } ast::StmtSemi(ref expr, _) => { try!(self.space_if_not_bol()); - try!(self.print_expr(&**expr)); + try!(self.print_expr_outer_attr_style(&**expr, false)); try!(word(&mut self.s, ";")); } - ast::StmtMac(ref mac, style) => { + ast::StmtMac(ref mac, style, ref attrs) => { try!(self.space_if_not_bol()); + try!(self.print_outer_attributes(attrs.as_attr_slice())); let delim = match style { ast::MacStmtWithBraces => token::Brace, _ => token::Paren @@ -1642,7 +1662,13 @@ impl<'a> State<'a> { } pub fn print_block_unclosed(&mut self, blk: &ast::Block) -> io::Result<()> { - self.print_block_unclosed_indent(blk, indent_unit) + self.print_block_unclosed_indent(blk, INDENT_UNIT) + } + + pub fn print_block_unclosed_with_attrs(&mut self, blk: &ast::Block, + attrs: &[ast::Attribute]) + -> io::Result<()> { + self.print_block_maybe_unclosed(blk, INDENT_UNIT, attrs, false) } pub fn print_block_unclosed_indent(&mut self, blk: &ast::Block, @@ -1653,7 +1679,7 @@ impl<'a> State<'a> { pub fn print_block_with_attrs(&mut self, blk: &ast::Block, attrs: &[ast::Attribute]) -> io::Result<()> { - self.print_block_maybe_unclosed(blk, indent_unit, attrs, true) + self.print_block_maybe_unclosed(blk, INDENT_UNIT, attrs, true) } pub fn print_block_maybe_unclosed(&mut self, @@ -1677,7 +1703,7 @@ impl<'a> State<'a> { match blk.expr { Some(ref expr) => { try!(self.space_if_not_bol()); - try!(self.print_expr(&**expr)); + try!(self.print_expr_outer_attr_style(&**expr, false)); try!(self.maybe_print_trailing_comment(expr.span, Some(blk.span.hi))); } _ => () @@ -1692,7 +1718,7 @@ impl<'a> State<'a> { match _else.node { // "another else-if" ast::ExprIf(ref i, ref then, ref e) => { - try!(self.cbox(indent_unit - 1)); + try!(self.cbox(INDENT_UNIT - 1)); try!(self.ibox(0)); try!(word(&mut self.s, " else if ")); try!(self.print_expr(&**i)); @@ -1702,7 +1728,7 @@ impl<'a> State<'a> { } // "another else-if-let" ast::ExprIfLet(ref pat, ref expr, ref then, ref e) => { - try!(self.cbox(indent_unit - 1)); + try!(self.cbox(INDENT_UNIT - 1)); try!(self.ibox(0)); try!(word(&mut self.s, " else if let ")); try!(self.print_pat(&**pat)); @@ -1715,7 +1741,7 @@ impl<'a> State<'a> { } // "final else" ast::ExprBlock(ref b) => { - try!(self.cbox(indent_unit - 1)); + try!(self.cbox(INDENT_UNIT - 1)); try!(self.ibox(0)); try!(word(&mut self.s, " else ")); self.print_block(&**b) @@ -1758,7 +1784,13 @@ impl<'a> State<'a> { match delim { token::Paren => try!(self.popen()), token::Bracket => try!(word(&mut self.s, "[")), - token::Brace => try!(self.bopen()), + token::Brace => { + // head-ibox, will be closed by bopen() + try!(self.ibox(0)); + // Don't ask me why the regular bopen() does + // more then just opening a brace... + try!(self.bopen()) + } } try!(self.print_tts(&m.node.tts)); match delim { @@ -1811,9 +1843,11 @@ impl<'a> State<'a> { self.print_expr_maybe_paren(expr) } - fn print_expr_vec(&mut self, exprs: &[P]) -> 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 +1855,11 @@ impl<'a> State<'a> { fn print_expr_repeat(&mut self, element: &ast::Expr, - count: &ast::Expr) -> io::Result<()> { - try!(self.ibox(indent_unit)); + count: &ast::Expr, + attrs: &[Attribute]) -> io::Result<()> { + try!(self.ibox(INDENT_UNIT)); try!(word(&mut self.s, "[")); + try!(self.print_inner_attributes_inline(attrs)); try!(self.print_expr(element)); try!(self.word_space(";")); try!(self.print_expr(count)); @@ -1834,14 +1870,16 @@ impl<'a> State<'a> { fn print_expr_struct(&mut self, path: &ast::Path, fields: &[ast::Field], - wth: &Option>) -> 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 +1888,7 @@ impl<'a> State<'a> { |f| f.span)); match *wth { Some(ref expr) => { - try!(self.ibox(indent_unit)); + try!(self.ibox(INDENT_UNIT)); if !fields.is_empty() { try!(word(&mut self.s, ",")); try!(space(&mut self.s)); @@ -1867,8 +1905,10 @@ impl<'a> State<'a> { Ok(()) } - fn print_expr_tup(&mut self, exprs: &[P]) -> 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 +1974,22 @@ impl<'a> State<'a> { } pub fn print_expr(&mut self, expr: &ast::Expr) -> io::Result<()> { + self.print_expr_outer_attr_style(expr, true) + } + + fn print_expr_outer_attr_style(&mut self, + expr: &ast::Expr, + is_inline: bool) -> io::Result<()> { try!(self.maybe_print_comment(expr.span.lo)); - try!(self.ibox(indent_unit)); + + let attrs = expr.attrs.as_attr_slice(); + if is_inline { + try!(self.print_outer_attributes_inline(attrs)); + } else { + try!(self.print_outer_attributes(attrs)); + } + + try!(self.ibox(INDENT_UNIT)); try!(self.ann.pre(self, NodeExpr(expr))); match expr.node { ast::ExprBox(ref expr) => { @@ -1946,16 +2000,16 @@ impl<'a> State<'a> { try!(self.print_expr_in_place(place, expr)); } ast::ExprVec(ref exprs) => { - try!(self.print_expr_vec(&exprs[..])); + try!(self.print_expr_vec(&exprs[..], attrs)); } ast::ExprRepeat(ref element, ref count) => { - try!(self.print_expr_repeat(&**element, &**count)); + try!(self.print_expr_repeat(&**element, &**count, attrs)); } ast::ExprStruct(ref path, ref fields, ref wth) => { - try!(self.print_expr_struct(path, &fields[..], wth)); + try!(self.print_expr_struct(path, &fields[..], wth, attrs)); } ast::ExprTup(ref exprs) => { - try!(self.print_expr_tup(&exprs[..])); + try!(self.print_expr_tup(&exprs[..], attrs)); } ast::ExprCall(ref func, ref args) => { try!(self.print_expr_call(&**func, &args[..])); @@ -1999,7 +2053,7 @@ impl<'a> State<'a> { try!(self.head("while")); try!(self.print_expr(&**test)); try!(space(&mut self.s)); - try!(self.print_block(&**blk)); + try!(self.print_block_with_attrs(&**blk, attrs)); } ast::ExprWhileLet(ref pat, ref expr, ref blk, opt_ident) => { if let Some(ident) = opt_ident { @@ -2012,7 +2066,7 @@ impl<'a> State<'a> { try!(self.word_space("=")); try!(self.print_expr(&**expr)); try!(space(&mut self.s)); - try!(self.print_block(&**blk)); + try!(self.print_block_with_attrs(&**blk, attrs)); } ast::ExprForLoop(ref pat, ref iter, ref blk, opt_ident) => { if let Some(ident) = opt_ident { @@ -2025,7 +2079,7 @@ impl<'a> State<'a> { try!(self.word_space("in")); try!(self.print_expr(&**iter)); try!(space(&mut self.s)); - try!(self.print_block(&**blk)); + try!(self.print_block_with_attrs(&**blk, attrs)); } ast::ExprLoop(ref blk, opt_ident) => { if let Some(ident) = opt_ident { @@ -2034,19 +2088,20 @@ impl<'a> State<'a> { } try!(self.head("loop")); try!(space(&mut self.s)); - try!(self.print_block(&**blk)); + try!(self.print_block_with_attrs(&**blk, attrs)); } ast::ExprMatch(ref expr, ref arms) => { - try!(self.cbox(indent_unit)); + try!(self.cbox(INDENT_UNIT)); try!(self.ibox(4)); try!(self.word_nbsp("match")); try!(self.print_expr(&**expr)); try!(space(&mut self.s)); try!(self.bopen()); + try!(self.print_inner_attributes_no_trailing_hardbreak(attrs)); for arm in arms { try!(self.print_arm(arm)); } - try!(self.bclose_(expr.span, indent_unit)); + try!(self.bclose_(expr.span, INDENT_UNIT)); } ast::ExprClosure(capture_clause, ref decl, ref body) => { try!(self.print_capture_clause(capture_clause)); @@ -2063,13 +2118,16 @@ impl<'a> State<'a> { try!(self.print_block_unclosed(&**body)); } else { // we extract the block, so as not to create another set of boxes - match body.expr.as_ref().unwrap().node { + let i_expr = body.expr.as_ref().unwrap(); + match i_expr.node { ast::ExprBlock(ref blk) => { - try!(self.print_block_unclosed(&**blk)); + try!(self.print_block_unclosed_with_attrs( + &**blk, + i_expr.attrs.as_attr_slice())); } _ => { // this is a bare expression - try!(self.print_expr(body.expr.as_ref().map(|e| &**e).unwrap())); + try!(self.print_expr(&**i_expr)); try!(self.end()); // need to close a box } } @@ -2081,10 +2139,10 @@ impl<'a> State<'a> { } ast::ExprBlock(ref blk) => { // containing cbox, will be closed by print-block at } - try!(self.cbox(indent_unit)); + try!(self.cbox(INDENT_UNIT)); // head-box, will be closed by print-block after { try!(self.ibox(0)); - try!(self.print_block(&**blk)); + try!(self.print_block_with_attrs(&**blk, attrs)); } ast::ExprAssign(ref lhs, ref rhs) => { try!(self.print_expr(&**lhs)); @@ -2222,6 +2280,7 @@ impl<'a> State<'a> { ast::ExprMac(ref m) => try!(self.print_mac(m, token::Paren)), ast::ExprParen(ref e) => { try!(self.popen()); + try!(self.print_inner_attributes_inline(attrs)); try!(self.print_expr(&**e)); try!(self.pclose()); } @@ -2243,11 +2302,12 @@ impl<'a> State<'a> { try!(self.maybe_print_comment(decl.span.lo)); match decl.node { ast::DeclLocal(ref loc) => { + try!(self.print_outer_attributes(loc.attrs.as_attr_slice())); try!(self.space_if_not_bol()); - try!(self.ibox(indent_unit)); + try!(self.ibox(INDENT_UNIT)); try!(self.word_nbsp("let")); - try!(self.ibox(indent_unit)); + try!(self.ibox(INDENT_UNIT)); try!(self.print_local_decl(&**loc)); try!(self.end()); if let Some(ref init) = loc.init { @@ -2452,7 +2512,7 @@ impl<'a> State<'a> { try!(self.commasep_cmnt( Consistent, &fields[..], |s, f| { - try!(s.cbox(indent_unit)); + try!(s.cbox(INDENT_UNIT)); if !f.node.is_shorthand { try!(s.print_ident(f.node.ident)); try!(s.word_nbsp(":")); @@ -2525,7 +2585,7 @@ impl<'a> State<'a> { if arm.attrs.is_empty() { try!(space(&mut self.s)); } - try!(self.cbox(indent_unit)); + try!(self.cbox(INDENT_UNIT)); try!(self.ibox(0)); try!(self.print_outer_attributes(&arm.attrs)); let mut first = true; @@ -2549,7 +2609,7 @@ impl<'a> State<'a> { match arm.body.node { ast::ExprBlock(ref blk) => { // the block will close the pattern's ibox - try!(self.print_block_unclosed_indent(&**blk, indent_unit)); + try!(self.print_block_unclosed_indent(&**blk, INDENT_UNIT)); // If it is a user-provided unsafe block, print a comma after it if let ast::UnsafeBlock(ast::UserProvided) = blk.rules { @@ -2907,7 +2967,7 @@ impl<'a> State<'a> { } pub fn print_arg(&mut self, input: &ast::Arg) -> io::Result<()> { - try!(self.ibox(indent_unit)); + try!(self.ibox(INDENT_UNIT)); match input.ty.node { ast::TyInfer => try!(self.print_pat(&*input.pat)), _ => { @@ -2935,7 +2995,7 @@ impl<'a> State<'a> { } try!(self.space_if_not_bol()); - try!(self.ibox(indent_unit)); + try!(self.ibox(INDENT_UNIT)); try!(self.word_space("->")); match decl.output { ast::NoReturn(_) => @@ -2960,7 +3020,7 @@ impl<'a> State<'a> { generics: &ast::Generics, opt_explicit_self: Option<&ast::ExplicitSelf_>) -> io::Result<()> { - try!(self.ibox(indent_unit)); + try!(self.ibox(INDENT_UNIT)); if !generics.lifetimes.is_empty() || !generics.ty_params.is_empty() { try!(word(&mut self.s, "for")); try!(self.print_generics(generics)); diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs index 3e02476443a99..6fd3833a3cd50 100644 --- a/src/libsyntax/test.rs +++ b/src/libsyntax/test.rs @@ -29,7 +29,8 @@ use entry::{self, EntryPointType}; use ext::base::ExtCtxt; use ext::build::AstBuilder; use ext::expand::ExpansionConfig; -use fold::{Folder, MoveMap}; +use fold::Folder; +use util::move_map::MoveMap; use fold; use owned_slice::OwnedSlice; use parse::token::{intern, InternedString}; @@ -88,7 +89,7 @@ pub fn modify_for_testing(sess: &ParseSess, if should_test { generate_test_harness(sess, reexport_test_harness_main, krate, cfg, span_diagnostic) } else { - strip_test_functions(krate) + strip_test_functions(span_diagnostic, krate) } } @@ -314,10 +315,11 @@ fn generate_test_harness(sess: &ParseSess, return res; } -fn strip_test_functions(krate: ast::Crate) -> ast::Crate { +fn strip_test_functions(diagnostic: &diagnostic::SpanHandler, krate: ast::Crate) + -> ast::Crate { // When not compiling with --test we should not compile the // #[test] functions - config::strip_items(krate, |attrs| { + config::strip_items(diagnostic, krate, |attrs| { !attr::contains_name(&attrs[..], "test") && !attr::contains_name(&attrs[..], "bench") }) @@ -619,8 +621,10 @@ fn mk_test_descs(cx: &TestCtxt) -> P { mk_test_desc_and_fn_rec(cx, test) }).collect()), span: DUMMY_SP, + attrs: None, })), span: DUMMY_SP, + attrs: None, }) } diff --git a/src/libsyntax/util/move_map.rs b/src/libsyntax/util/move_map.rs new file mode 100644 index 0000000000000..95c24c66630f0 --- /dev/null +++ b/src/libsyntax/util/move_map.rs @@ -0,0 +1,79 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use owned_slice::OwnedSlice; + +use std::ptr; + +pub trait MoveMap: Sized { + fn move_map(self, mut f: F) -> Self where F: FnMut(T) -> T { + self.move_flat_map(|e| Some(f(e))) + } + + fn move_flat_map(self, f: F) -> Self + where F: FnMut(T) -> I, + I: IntoIterator; +} + +impl MoveMap for Vec { + fn move_flat_map(mut self, mut f: F) -> Self + where F: FnMut(T) -> I, + I: IntoIterator + { + let mut read_i = 0; + let mut write_i = 0; + unsafe { + let mut old_len = self.len(); + self.set_len(0); // make sure we just leak elements in case of panic + + while read_i < old_len { + // move the read_i'th item out of the vector and map it + // to an iterator + let e = ptr::read(self.get_unchecked(read_i)); + let mut iter = f(e).into_iter(); + read_i += 1; + + while let Some(e) = iter.next() { + if write_i < read_i { + ptr::write(self.get_unchecked_mut(write_i), e); + write_i += 1; + } else { + // If this is reached we ran out of space + // in the middle of the vector. + // However, the vector is in a valid state here, + // so we just do a somewhat inefficient insert. + self.set_len(old_len); + self.insert(write_i, e); + + old_len = self.len(); + self.set_len(0); + + read_i += 1; + write_i += 1; + } + } + } + + // write_i tracks the number of actually written new items. + self.set_len(write_i); + } + + self + } +} + +impl MoveMap for OwnedSlice { + fn move_flat_map(self, f: F) -> Self + where F: FnMut(T) -> I, + I: IntoIterator + { + OwnedSlice::from_vec(self.into_vec().move_flat_map(f)) + } +} diff --git a/src/libsyntax/util/small_vector.rs b/src/libsyntax/util/small_vector.rs index dac3b0541651c..ee183d7f3e96a 100644 --- a/src/libsyntax/util/small_vector.rs +++ b/src/libsyntax/util/small_vector.rs @@ -16,7 +16,7 @@ use std::mem; use std::slice; use std::vec; -use fold::MoveMap; +use util::move_map::MoveMap; /// A vector type optimized for cases where the size is almost always 0 or 1 pub struct SmallVector { @@ -134,15 +134,6 @@ impl SmallVector { self.into_iter() } - pub fn into_iter(self) -> IntoIter { - let repr = match self.repr { - Zero => ZeroIterator, - One(v) => OneIterator(v), - Many(vs) => ManyIterator(vs.into_iter()) - }; - IntoIter { repr: repr } - } - pub fn len(&self) -> usize { match self.repr { Zero => 0, @@ -154,6 +145,19 @@ impl SmallVector { pub fn is_empty(&self) -> bool { self.len() == 0 } } +impl IntoIterator for SmallVector { + type Item = T; + type IntoIter = IntoIter; + fn into_iter(self) -> Self::IntoIter { + let repr = match self.repr { + Zero => ZeroIterator, + One(v) => OneIterator(v), + Many(vs) => ManyIterator(vs.into_iter()) + }; + IntoIter { repr: repr } + } +} + pub struct IntoIter { repr: IntoIterRepr, } @@ -192,13 +196,15 @@ impl Iterator for IntoIter { } impl MoveMap for SmallVector { - fn move_map(self, mut f: F) -> SmallVector where F: FnMut(T) -> T { - let repr = match self.repr { - Zero => Zero, - One(v) => One(f(v)), - Many(vs) => Many(vs.move_map(f)) - }; - SmallVector { repr: repr } + fn move_flat_map(self, mut f: F) -> Self + where F: FnMut(T) -> I, + I: IntoIterator + { + match self.repr { + Zero => Self::zero(), + One(v) => f(v).into_iter().collect(), + Many(vs) => SmallVector { repr: Many(vs.move_flat_map(f)) }, + } } } diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 5d4a462e84491..cdc11fb2c1cb0 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -25,6 +25,7 @@ use abi::Abi; use ast::*; +use attr::ThinAttributesExt; use codemap::Span; #[derive(Copy, Clone, PartialEq, Eq)] @@ -628,7 +629,12 @@ pub fn walk_stmt<'v, V: Visitor<'v>>(visitor: &mut V, statement: &'v Stmt) { StmtExpr(ref expression, _) | StmtSemi(ref expression, _) => { visitor.visit_expr(expression) } - StmtMac(ref mac, _) => visitor.visit_mac(mac), + StmtMac(ref mac, _, ref attrs) => { + visitor.visit_mac(mac); + for attr in attrs.as_attr_slice() { + visitor.visit_attribute(attr); + } + } } } diff --git a/src/test/parse-fail/attr-before-ext.rs b/src/test/compile-fail/cfg-non-opt-expr.rs similarity index 51% rename from src/test/parse-fail/attr-before-ext.rs rename to src/test/compile-fail/cfg-non-opt-expr.rs index e15350fcad79c..d9d379ddc7dd5 100644 --- a/src/test/parse-fail/attr-before-ext.rs +++ b/src/test/compile-fail/cfg-non-opt-expr.rs @@ -1,4 +1,4 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// 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. // @@ -8,9 +8,11 @@ // 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"); + let _ = #[cfg(unset)] (); + //~^ ERROR removing an expression is not supported in this position + let _ = 1 + 2 + #[cfg(unset)] 3; + //~^ ERROR removing an expression is not supported in this position + let _ = [1, 2, 3][#[cfg(unset)] 1]; + //~^ ERROR removing an expression is not supported in this position } diff --git a/src/test/compile-fail/expr_attr_paren_order.rs b/src/test/compile-fail/expr_attr_paren_order.rs new file mode 100644 index 0000000000000..49b2fa0e35022 --- /dev/null +++ b/src/test/compile-fail/expr_attr_paren_order.rs @@ -0,0 +1,34 @@ +// 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(stmt_expr_attributes)] + +fn main() { + + // Test that attributes on parens get concatenated + // in the expected order in the hir folder. + + #[deny(non_snake_case)] ( + #![allow(non_snake_case)] + { + let X = 0; + let _ = X; + } + ); + + #[allow(non_snake_case)] ( + #![deny(non_snake_case)] + { + let X = 0; //~ ERROR snake case name + let _ = X; + } + ); + +} diff --git a/src/test/compile-fail/stmt_expr_attrs_no_feature.rs b/src/test/compile-fail/stmt_expr_attrs_no_feature.rs new file mode 100644 index 0000000000000..68338b115950c --- /dev/null +++ b/src/test/compile-fail/stmt_expr_attrs_no_feature.rs @@ -0,0 +1,151 @@ +// 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(custom_attribute)] +#![feature(associated_consts)] + +macro_rules! stmt_mac { + () => { + fn b() {} + } +} + +fn main() { + #[attr] + fn a() {} + + #[attr] //~ ERROR 15701 + { + + } + + #[attr] //~ ERROR 15701 + 5; + + #[attr] //~ ERROR 15701 + stmt_mac!(); +} + +// Check that cfg works right + +#[cfg(unset)] +fn c() { + #[attr] + 5; +} + +#[cfg(not(unset))] +fn j() { + #[attr] //~ ERROR 15701 + 5; +} + +#[cfg_attr(not(unset), cfg(unset))] +fn d() { + #[attr] + 8; +} + +#[cfg_attr(not(unset), cfg(not(unset)))] +fn i() { + #[attr] //~ ERROR 15701 + 8; +} + +// check that macro expansion and cfg works right + +macro_rules! item_mac { + ($e:ident) => { + fn $e() { + #[attr] //~ ERROR 15701 + 42; + + #[cfg(unset)] + fn f() { + #[attr] + 5; + } + + #[cfg(not(unset))] + fn k() { + #[attr] //~ ERROR 15701 + 5; + } + + #[cfg_attr(not(unset), cfg(unset))] + fn g() { + #[attr] + 8; + } + + #[cfg_attr(not(unset), cfg(not(unset)))] + fn h() { + #[attr] //~ ERROR 15701 + 8; + } + + } + } +} + +item_mac!(e); + +// check that the gate visitor works right: + +extern { + #[cfg(unset)] + fn x(a: [u8; #[attr] 5]); + fn y(a: [u8; #[attr] 5]); //~ ERROR 15701 +} + +struct Foo; +impl Foo { + #[cfg(unset)] + const X: u8 = #[attr] 5; + const Y: u8 = #[attr] 5; //~ ERROR 15701 +} + +trait Bar { + #[cfg(unset)] + const X: [u8; #[attr] 5]; + const Y: [u8; #[attr] 5]; //~ ERROR 15701 +} + +struct Joyce { + #[cfg(unset)] + field: [u8; #[attr] 5], + field2: [u8; #[attr] 5] //~ ERROR 15701 +} + +struct Walky( + #[cfg(unset)] [u8; #[attr] 5], + [u8; #[attr] 5] //~ ERROR 15701 +); + +enum Mike { + Happy( + #[cfg(unset)] [u8; #[attr] 5], + [u8; #[attr] 5] //~ ERROR 15701 + ), + Angry { + #[cfg(unset)] + field: [u8; #[attr] 5], + field2: [u8; #[attr] 5] //~ ERROR 15701 + } +} + +fn pat() { + match 5 { + #[cfg(unset)] + 5 => #[attr] (), + 6 => #[attr] (), //~ ERROR 15701 + _ => (), + } +} 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 03dabb980f20d..0000000000000 --- 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 bc306048cdc4a..0000000000000 --- 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 f2f4ecadd7a1d..7b731b6d6de60 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 44435bde03c0d..0000000000000 --- 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 8b69c385378f8..295d5ae432ecb 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 42c58af76d8c5..6a8906953be09 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 0000000000000..e52932cd7befa --- /dev/null +++ b/src/test/pretty/stmt_expr_attributes.rs @@ -0,0 +1,282 @@ +// 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)] +#![feature(stmt_expr_attributes)] + +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 0000000000000..e40abe0502333 --- /dev/null +++ b/src/test/run-pass-fulldeps/ast_stmt_expr_attr.rs @@ -0,0 +1,308 @@ +// 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. + +// ignore-cross-compile + +#![feature(rustc_private)] + +extern crate syntax; + +use syntax::ast::*; +use syntax::attr::*; +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() + }) +} + +fn stmt(s: &str) -> PResult> { + with_error_checking_parse(s.to_string(), |p| { + p.parse_stmt().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_attr_slice(), + 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 0000000000000..fcc93557665bf --- /dev/null +++ b/src/test/run-pass/cfg_stmt_expr.rs @@ -0,0 +1,98 @@ +// 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)] +#![feature(stmt_expr_attributes)] + +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() {} + } +} diff --git a/src/test/run-pass/stmt_expr_attr_macro_parse.rs b/src/test/run-pass/stmt_expr_attr_macro_parse.rs new file mode 100644 index 0000000000000..23559e8789bf5 --- /dev/null +++ b/src/test/run-pass/stmt_expr_attr_macro_parse.rs @@ -0,0 +1,32 @@ +// 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. + +macro_rules! m { + ($e:expr) => { + "expr includes attr" + }; + (#[$attr:meta] $e:expr) => { + "expr excludes attr" + } +} + +macro_rules! n { + (#[$attr:meta] $e:expr) => { + "expr excludes attr" + }; + ($e:expr) => { + "expr includes attr" + } +} + +fn main() { + assert_eq!(m!(#[attr] 1), "expr includes attr"); + assert_eq!(n!(#[attr] 1), "expr excludes attr"); +}