From 4e90ed6113d5aa8a7f50904708163d87a757ee66 Mon Sep 17 00:00:00 2001 From: Erick Tryzelaar Date: Sun, 6 Dec 2015 15:24:23 -0800 Subject: [PATCH] Update to rust (64c21f9) --- syntex_syntax/src/ast.rs | 55 ++- syntex_syntax/src/ast_util.rs | 42 +- syntex_syntax/src/attr.rs | 194 +++++++- syntex_syntax/src/config.rs | 348 ++++++++++--- syntex_syntax/src/diagnostic.rs | 16 +- syntex_syntax/src/ext/asm.rs | 3 +- syntex_syntax/src/ext/base.rs | 28 +- syntex_syntax/src/ext/build.rs | 3 + syntex_syntax/src/ext/cfg.rs | 10 +- syntex_syntax/src/ext/concat_idents.rs | 1 + syntex_syntax/src/ext/deriving/debug.rs | 1 + syntex_syntax/src/ext/deriving/generic/mod.rs | 2 +- syntex_syntax/src/ext/expand.rs | 39 +- syntex_syntax/src/ext/quote.rs | 3 +- syntex_syntax/src/ext/tt/macro_parser.rs | 35 +- syntex_syntax/src/ext/tt/macro_rules.rs | 24 +- syntex_syntax/src/feature_gate.rs | 73 ++- syntex_syntax/src/fold.rs | 116 +++-- syntex_syntax/src/lib.rs | 8 +- syntex_syntax/src/parse/mod.rs | 18 +- syntex_syntax/src/parse/parser.rs | 462 ++++++++++++------ syntex_syntax/src/parse/token.rs | 1 + syntex_syntax/src/print/pprust.rs | 246 ++++++---- syntex_syntax/src/test.rs | 12 +- syntex_syntax/src/util/small_vector.rs | 42 +- syntex_syntax/src/visit.rs | 8 +- 26 files changed, 1277 insertions(+), 513 deletions(-) diff --git a/syntex_syntax/src/ast.rs b/syntex_syntax/src/ast.rs index e4697a7f..f11291fc 100644 --- a/syntex_syntax/src/ast.rs +++ b/syntex_syntax/src/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 { @@ -1258,7 +1301,7 @@ impl fmt::Debug for IntTy { impl fmt::Display for IntTy { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", ast_util::int_ty_to_string(*self, None)) + write!(f, "{}", ast_util::int_ty_to_string(*self)) } } @@ -1303,7 +1346,7 @@ impl fmt::Debug for UintTy { impl fmt::Display for UintTy { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", ast_util::uint_ty_to_string(*self, None)) + write!(f, "{}", ast_util::uint_ty_to_string(*self)) } } @@ -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/syntex_syntax/src/ast_util.rs b/syntex_syntax/src/ast_util.rs index 44334762..489c61b8 100644 --- a/syntex_syntax/src/ast_util.rs +++ b/syntex_syntax/src/ast_util.rs @@ -111,26 +111,23 @@ pub fn is_path(e: P) -> bool { match e.node { ExprPath(..) => true, _ => false } } -/// Get a string representation of a signed int type, with its value. -/// We want to avoid "45int" and "-3int" in favor of "45" and "-3" -pub fn int_ty_to_string(t: IntTy, val: Option) -> String { - let s = match t { +pub fn int_ty_to_string(t: IntTy) -> &'static str { + match t { TyIs => "isize", TyI8 => "i8", TyI16 => "i16", TyI32 => "i32", TyI64 => "i64" - }; - - match val { - // cast to a u64 so we can correctly print INT64_MIN. All integral types - // are parsed as u64, so we wouldn't want to print an extra negative - // sign. - Some(n) => format!("{}{}", n as u64, s), - None => s.to_string() } } +pub fn int_val_to_string(t: IntTy, val: i64) -> String { + // cast to a u64 so we can correctly print INT64_MIN. All integral types + // are parsed as u64, so we wouldn't want to print an extra negative + // sign. + format!("{}{}", val as u64, int_ty_to_string(t)) +} + pub fn int_ty_max(t: IntTy) -> u64 { match t { TyI8 => 0x80, @@ -140,23 +137,20 @@ pub fn int_ty_max(t: IntTy) -> u64 { } } -/// Get a string representation of an unsigned int type, with its value. -/// We want to avoid "42u" in favor of "42us". "42uint" is right out. -pub fn uint_ty_to_string(t: UintTy, val: Option) -> String { - let s = match t { +pub fn uint_ty_to_string(t: UintTy) -> &'static str { + match t { TyUs => "usize", TyU8 => "u8", TyU16 => "u16", TyU32 => "u32", TyU64 => "u64" - }; - - match val { - Some(n) => format!("{}{}", n, s), - None => s.to_string() } } +pub fn uint_val_to_string(t: UintTy, val: u64) -> String { + format!("{}{}", val, uint_ty_to_string(t)) +} + pub fn uint_ty_max(t: UintTy) -> u64 { match t { TyU8 => 0xff, @@ -166,10 +160,10 @@ pub fn uint_ty_max(t: UintTy) -> u64 { } } -pub fn float_ty_to_string(t: FloatTy) -> String { +pub fn float_ty_to_string(t: FloatTy) -> &'static str { match t { - TyF32 => "f32".to_string(), - TyF64 => "f64".to_string(), + TyF32 => "f32", + TyF64 => "f64", } } diff --git a/syntex_syntax/src/attr.rs b/syntex_syntax/src/attr.rs index 571f9506..e828d8ae 100644 --- a/syntex_syntax/src/attr.rs +++ b/syntex_syntax/src/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,32 +359,41 @@ 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) } } } -/// Represents the #[stable], #[unstable] and #[deprecated] attributes. +/// Represents the #[stable], #[unstable] and #[rustc_deprecated] attributes. #[derive(RustcEncodable, RustcDecodable, Clone, Debug, PartialEq, Eq, Hash)] pub struct Stability { pub level: StabilityLevel, @@ -420,7 +432,7 @@ fn find_stability_generic<'a, I>(diagnostic: &SpanHandler, 'outer: for attr in attrs_iter { let tag = attr.name(); let tag = &*tag; - if tag != "deprecated" && tag != "unstable" && tag != "stable" { + if tag != "rustc_deprecated" && tag != "unstable" && tag != "stable" { continue // not a stability level } @@ -443,9 +455,9 @@ fn find_stability_generic<'a, I>(diagnostic: &SpanHandler, }; match tag { - "deprecated" => { + "rustc_deprecated" => { if depr.is_some() { - diagnostic.span_err(item_sp, "multiple deprecated attributes"); + diagnostic.span_err(item_sp, "multiple rustc_deprecated attributes"); break } @@ -586,7 +598,7 @@ fn find_stability_generic<'a, I>(diagnostic: &SpanHandler, } stab.depr = Some(depr); } else { - diagnostic.span_err(item_sp, "deprecated attribute must be paired with \ + diagnostic.span_err(item_sp, "rustc_deprecated attribute must be paired with \ either stable or unstable attribute"); } } @@ -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/syntex_syntax/src/config.rs b/syntex_syntax/src/config.rs index 10731178..1209c58f 100644 --- a/syntex_syntax/src/config.rs +++ b/syntex_syntax/src/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/syntex_syntax/src/diagnostic.rs b/syntex_syntax/src/diagnostic.rs index 870dea02..b854a2f2 100644 --- a/syntex_syntax/src/diagnostic.rs +++ b/syntex_syntax/src/diagnostic.rs @@ -20,7 +20,7 @@ use std::cell::{RefCell, Cell}; use std::{cmp, error, fmt}; use std::io::prelude::*; use std::io; -use term::{self, WriterWrapper}; +use term; /// maximum number of lines we will print for each error; arbitrary. const MAX_LINES: usize = 6; @@ -318,7 +318,7 @@ pub struct EmitterWriter { } enum Destination { - Terminal(Box + Send>), + Terminal(Box), Raw(Box), } @@ -365,7 +365,7 @@ impl EmitterWriter { fn print_maybe_styled(&mut self, args: fmt::Arguments, - color: term::attr::Attr, + color: term::Attr, print_newline_at_end: bool) -> io::Result<()> { match self.dst { Terminal(ref mut t) => { @@ -408,13 +408,13 @@ impl EmitterWriter { try!(write!(&mut self.dst, "{} ", topic)); } - try!(print_maybe_styled!(self, term::attr::ForegroundColor(lvl.color()), + try!(print_maybe_styled!(self, term::Attr::ForegroundColor(lvl.color()), "{}: ", lvl.to_string())); - try!(print_maybe_styled!(self, term::attr::Bold, "{}", msg)); + try!(print_maybe_styled!(self, term::Attr::Bold, "{}", msg)); match code { Some(code) => { - let style = term::attr::ForegroundColor(term::color::BRIGHT_MAGENTA); + let style = term::Attr::ForegroundColor(term::color::BRIGHT_MAGENTA); try!(print_maybe_styled!(self, style, " [{}]", code.clone())); } None => () @@ -646,7 +646,7 @@ impl EmitterWriter { s.pop(); } - try!(println_maybe_styled!(self, term::attr::ForegroundColor(lvl.color()), + try!(println_maybe_styled!(self, term::Attr::ForegroundColor(lvl.color()), "{}", s)); } } @@ -719,7 +719,7 @@ impl EmitterWriter { } } s.push('^'); - println_maybe_styled!(self, term::attr::ForegroundColor(lvl.color()), + println_maybe_styled!(self, term::Attr::ForegroundColor(lvl.color()), "{}", s) } diff --git a/syntex_syntax/src/ext/asm.rs b/syntex_syntax/src/ext/asm.rs index ac18b9c0..d968858f 100644 --- a/syntex_syntax/src/ext/asm.rs +++ b/syntex_syntax/src/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/syntex_syntax/src/ext/base.rs b/syntex_syntax/src/ext/base.rs index 0dba1576..3b613922 100644 --- a/syntex_syntax/src/ext/base.rs +++ b/syntex_syntax/src/ext/base.rs @@ -17,13 +17,14 @@ 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; use parse::token::{InternedString, intern, str_to_ident}; use ptr::P; use util::small_vector::SmallVector; +use util::lev_distance::{lev_distance, max_suggestion_distance}; use ext::mtwt; use fold::Folder; @@ -349,6 +350,7 @@ impl DummyResult { id: ast::DUMMY_NODE_ID, node: ast::ExprLit(P(codemap::respan(sp, ast::LitBool(false)))), span: sp, + attrs: None, }) } @@ -571,7 +573,7 @@ pub struct ExtCtxt<'a> { pub backtrace: ExpnId, pub ecfg: expand::ExpansionConfig<'a>, pub crate_root: Option<&'static str>, - pub feature_gated_cfgs: &'a mut Vec, + pub feature_gated_cfgs: &'a mut Vec, pub mod_path: Vec , pub exported_macros: Vec, @@ -583,7 +585,7 @@ pub struct ExtCtxt<'a> { impl<'a> ExtCtxt<'a> { pub fn new(parse_sess: &'a parse::ParseSess, cfg: ast::CrateConfig, ecfg: expand::ExpansionConfig<'a>, - feature_gated_cfgs: &'a mut Vec) -> ExtCtxt<'a> { + feature_gated_cfgs: &'a mut Vec) -> ExtCtxt<'a> { let env = initial_syntax_expander_table(&ecfg); ExtCtxt { parse_sess: parse_sess, @@ -600,7 +602,7 @@ impl<'a> ExtCtxt<'a> { } #[unstable(feature = "rustc_private", issue = "0")] - #[deprecated(since = "1.0.0", + #[rustc_deprecated(since = "1.0.0", reason = "Replaced with `expander().fold_expr()`")] pub fn expand_expr(&mut self, e: P) -> P { self.expander().fold_expr(e) @@ -678,9 +680,9 @@ impl<'a> ExtCtxt<'a> { pub fn bt_push(&mut self, ei: ExpnInfo) { self.recursion_count += 1; if self.recursion_count > self.ecfg.recursion_limit { - panic!(self.span_fatal(ei.call_site, + self.span_fatal(ei.call_site, &format!("recursion limit reached while expanding the macro `{}`", - ei.callee.name()))); + ei.callee.name())); } let mut call_site = ei.call_site; @@ -776,6 +778,20 @@ impl<'a> ExtCtxt<'a> { pub fn name_of(&self, st: &str) -> ast::Name { token::intern(st) } + + pub fn suggest_macro_name(&mut self, name: &str, span: Span) { + let mut min: Option<(Name, usize)> = None; + let max_dist = max_suggestion_distance(name); + for macro_name in self.syntax_env.names.iter() { + let dist = lev_distance(name, ¯o_name.as_str()); + if dist <= max_dist && (min.is_none() || min.unwrap().1 > dist) { + min = Some((*macro_name, dist)); + } + } + if let Some((suggestion, _)) = min { + self.fileline_help(span, &format!("did you mean `{}!`?", suggestion)); + } + } } /// Extract a string literal from the macro expanded version of `expr`, diff --git a/syntex_syntax/src/ext/build.rs b/syntex_syntax/src/ext/build.rs index 4c10a749..806f5a7e 100644 --- a/syntex_syntax/src/ext/build.rs +++ b/syntex_syntax/src/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/syntex_syntax/src/ext/cfg.rs b/syntex_syntax/src/ext/cfg.rs index 48199026..e100355e 100644 --- a/syntex_syntax/src/ext/cfg.rs +++ b/syntex_syntax/src/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/syntex_syntax/src/ext/concat_idents.rs b/syntex_syntax/src/ext/concat_idents.rs index e9e36546..c2233202 100644 --- a/syntex_syntax/src/ext/concat_idents.rs +++ b/syntex_syntax/src/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/syntex_syntax/src/ext/deriving/debug.rs b/syntex_syntax/src/ext/deriving/debug.rs index 2b2e5309..9488cfb8 100644 --- a/syntex_syntax/src/ext/deriving/debug.rs +++ b/syntex_syntax/src/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/syntex_syntax/src/ext/deriving/generic/mod.rs b/syntex_syntax/src/ext/deriving/generic/mod.rs index 625abf70..bd89430d 100644 --- a/syntex_syntax/src/ext/deriving/generic/mod.rs +++ b/syntex_syntax/src/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/syntex_syntax/src/ext/expand.rs b/syntex_syntax/src/ext/expand.rs index 9b1a7a50..573f4cfe 100644 --- a/syntex_syntax/src/ext/expand.rs +++ b/syntex_syntax/src/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)) } }); @@ -191,6 +205,7 @@ fn expand_mac_invoc(mac: ast::Mac, pth.span, &format!("macro undefined: '{}!'", &extname)); + fld.cx.suggest_macro_name(&extname.as_str(), pth.span); // let compilation continue None @@ -486,11 +501,14 @@ pub fn expand_item_mac(it: P, /// Expand a stmt fn expand_stmt(stmt: P, fld: &mut MacroExpander) -> SmallVector> { let stmt = stmt.and_then(|stmt| stmt); - let (mac, style) = match stmt.node { - StmtMac(mac, style) => (mac, style), + let (mac, style, attrs) = match stmt.node { + StmtMac(mac, style, attrs) => (mac, style, attrs), _ => return expand_non_macro_stmt(stmt, fld) }; + // 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 +556,7 @@ fn expand_non_macro_stmt(Spanned {node, span: stmt_span}: Stmt, fld: &mut MacroE StmtDecl(decl, node_id) => decl.and_then(|Spanned {node: decl, span}| match decl { DeclLocal(local) => { // take it apart: - let rewritten_local = local.map(|Local {id, pat, ty, init, span}| { + let rewritten_local = local.map(|Local {id, pat, ty, init, span, attrs}| { // expand the ty since TyFixedLengthVec contains an Expr // and thus may have a macro use let expanded_ty = ty.map(|t| fld.fold_ty(t)); @@ -568,7 +586,8 @@ fn expand_non_macro_stmt(Spanned {node, span: stmt_span}: Stmt, fld: &mut MacroE pat: rewritten_pat, // also, don't forget to expand the init: init: init.map(|e| fld.fold_expr(e)), - span: span + span: span, + attrs: fold::fold_thin_attrs(attrs, fld), } }); SmallVector::one(P(Spanned { @@ -1262,7 +1281,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/syntex_syntax/src/ext/quote.rs b/syntex_syntax/src/ext/quote.rs index 5e5b8158..496f6a42 100644 --- a/syntex_syntax/src/ext/quote.rs +++ b/syntex_syntax/src/ext/quote.rs @@ -178,7 +178,7 @@ pub mod rt { let mut v = vec![]; for (i, x) in self.iter().enumerate() { if i > 0 { - v.push_all(&$sep); + v.extend_from_slice(&$sep); } v.extend(x.to_tokens(cx)); } @@ -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/syntex_syntax/src/ext/tt/macro_parser.rs b/syntex_syntax/src/ext/tt/macro_parser.rs index 675482fd..5b8307eb 100644 --- a/syntex_syntax/src/ext/tt/macro_parser.rs +++ b/syntex_syntax/src/ext/tt/macro_parser.rs @@ -200,18 +200,19 @@ pub enum NamedMatch { } pub fn nameize(p_s: &ParseSess, ms: &[TokenTree], res: &[Rc]) - -> HashMap> { + -> ParseResult>> { fn n_rec(p_s: &ParseSess, m: &TokenTree, res: &[Rc], - ret_val: &mut HashMap>, idx: &mut usize) { + ret_val: &mut HashMap>, idx: &mut usize) + -> Result<(), (codemap::Span, String)> { match *m { TokenTree::Sequence(_, ref seq) => { for next_m in &seq.tts { - n_rec(p_s, next_m, res, ret_val, idx) + try!(n_rec(p_s, next_m, res, ret_val, idx)) } } TokenTree::Delimited(_, ref delim) => { for next_m in &delim.tts { - n_rec(p_s, next_m, res, ret_val, idx) + try!(n_rec(p_s, next_m, res, ret_val, idx)); } } TokenTree::Token(sp, MatchNt(bind_name, _, _, _)) => { @@ -221,26 +222,36 @@ pub fn nameize(p_s: &ParseSess, ms: &[TokenTree], res: &[Rc]) *idx += 1; } Occupied(..) => { - panic!(p_s.span_diagnostic - .span_fatal(sp, - &format!("duplicated bind name: {}", - bind_name))) + return Err((sp, format!("duplicated bind name: {}", bind_name))) } } } - TokenTree::Token(_, SubstNt(..)) => panic!("Cannot fill in a NT"), + TokenTree::Token(sp, SubstNt(..)) => { + return Err((sp, "missing fragment specifier".to_string())) + } TokenTree::Token(_, _) => (), } + + Ok(()) } + let mut ret_val = HashMap::new(); let mut idx = 0; - for m in ms { n_rec(p_s, m, res, &mut ret_val, &mut idx) } - ret_val + for m in ms { + match n_rec(p_s, m, res, &mut ret_val, &mut idx) { + Ok(_) => {}, + Err((sp, msg)) => return Error(sp, msg), + } + } + + Success(ret_val) } pub enum ParseResult { Success(T), + /// Arm failed to match Failure(codemap::Span, String), + /// Fatal error (malformed macro?). Abort compilation. Error(codemap::Span, String) } @@ -429,7 +440,7 @@ pub fn parse(sess: &ParseSess, for dv in &mut (&mut eof_eis[0]).matches { v.push(dv.pop().unwrap()); } - return Success(nameize(sess, ms, &v[..])); + return nameize(sess, ms, &v[..]); } else if eof_eis.len() > 1 { return Error(sp, "ambiguity: multiple successful parses".to_string()); } else { diff --git a/syntex_syntax/src/ext/tt/macro_rules.rs b/syntex_syntax/src/ext/tt/macro_rules.rs index 43647ea4..fae60d28 100644 --- a/syntex_syntax/src/ext/tt/macro_rules.rs +++ b/syntex_syntax/src/ext/tt/macro_rules.rs @@ -44,7 +44,7 @@ impl<'a> ParserAnyMacro<'a> { /// about e.g. the semicolon in `macro_rules! kapow { () => { /// panic!(); } }` doesn't get picked up by .parse_expr(), but it's /// allowed to be there. - fn ensure_complete_parse(&self, allow_semi: bool) { + fn ensure_complete_parse(&self, allow_semi: bool, context: &str) { let mut parser = self.parser.borrow_mut(); if allow_semi && parser.token == token::Semi { panictry!(parser.bump()) @@ -58,8 +58,8 @@ impl<'a> ParserAnyMacro<'a> { parser.span_err(span, &msg[..]); let msg = format!("caused by the macro expansion here; the usage \ - of `{}` is likely invalid in this context", - self.macro_ident); + of `{}!` is likely invalid in {} context", + self.macro_ident, context); parser.span_note(self.site_span, &msg[..]); } } @@ -68,12 +68,12 @@ impl<'a> ParserAnyMacro<'a> { impl<'a> MacResult for ParserAnyMacro<'a> { fn make_expr(self: Box>) -> Option> { let ret = panictry!(self.parser.borrow_mut().parse_expr()); - self.ensure_complete_parse(true); + self.ensure_complete_parse(true, "expression"); Some(ret) } fn make_pat(self: Box>) -> Option> { let ret = panictry!(self.parser.borrow_mut().parse_pat()); - self.ensure_complete_parse(false); + self.ensure_complete_parse(false, "pattern"); Some(ret) } fn make_items(self: Box>) -> Option>> { @@ -81,7 +81,7 @@ impl<'a> MacResult for ParserAnyMacro<'a> { while let Some(item) = panictry!(self.parser.borrow_mut().parse_item()) { ret.push(item); } - self.ensure_complete_parse(false); + self.ensure_complete_parse(false, "item"); Some(ret) } @@ -95,7 +95,7 @@ impl<'a> MacResult for ParserAnyMacro<'a> { _ => ret.push(panictry!(parser.parse_impl_item())) } } - self.ensure_complete_parse(false); + self.ensure_complete_parse(false, "item"); Some(ret) } @@ -115,13 +115,13 @@ impl<'a> MacResult for ParserAnyMacro<'a> { } } } - self.ensure_complete_parse(false); + self.ensure_complete_parse(false, "statement"); Some(ret) } fn make_ty(self: Box>) -> Option> { let ret = panictry!(self.parser.borrow_mut().parse_ty()); - self.ensure_complete_parse(true); + self.ensure_complete_parse(false, "type"); Some(ret) } } @@ -209,12 +209,12 @@ fn generic_extension<'cx>(cx: &'cx ExtCtxt, best_fail_msg = (*msg).clone(); }, Error(err_sp, ref msg) => { - panic!(cx.span_fatal(err_sp.substitute_dummy(sp), &msg[..])) + cx.span_fatal(err_sp.substitute_dummy(sp), &msg[..]) } } } - panic!(cx.span_fatal(best_fail_spot.substitute_dummy(sp), &best_fail_msg[..])); + cx.span_fatal(best_fail_spot.substitute_dummy(sp), &best_fail_msg[..]); } // Note that macro-by-example's input is also matched against a token tree: @@ -327,7 +327,7 @@ fn check_lhs_nt_follows(cx: &mut ExtCtxt, lhs: &TokenTree, sp: Span) { tt @ &TokenTree::Sequence(..) => { check_matcher(cx, Some(tt).into_iter(), &Eof); }, - _ => cx.span_err(sp, "Invalid macro matcher; matchers must be contained \ + _ => cx.span_err(sp, "invalid macro matcher; matchers must be contained \ in balanced delimiters or a repetition indicator") }; // we don't abort on errors on rejection, the driver will do that for us diff --git a/syntex_syntax/src/feature_gate.rs b/syntex_syntax/src/feature_gate.rs index ca232963..c456b7dc 100644 --- a/syntex_syntax/src/feature_gate.rs +++ b/syntex_syntax/src/feature_gate.rs @@ -132,7 +132,7 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Option, Status ("unmarked_api", "1.0.0", None, Active), // Allows using #![no_std] - ("no_std", "1.0.0", Some(27701), Active), + ("no_std", "1.0.0", None, Accepted), // Allows using #![no_core] ("no_core", "1.3.0", Some(29639), Active), @@ -179,6 +179,9 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Option, Status // Allows the definition of `const fn` functions. ("const_fn", "1.2.0", Some(24111), Active), + // Allows indexing into constant arrays. + ("const_indexing", "1.4.0", Some(29947), Active), + // Allows using #[prelude_import] on glob `use` items. // // rustc internal @@ -224,6 +227,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) @@ -277,13 +283,10 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeGat // Not used any more, but we can't feature gate it ("no_stack_check", Normal, Ungated), - ("staged_api", CrateLevel, Gated("staged_api", - "staged_api is for use by rustc only")), ("plugin", CrateLevel, Gated("plugin", "compiler plugins are experimental \ and possibly buggy")), - ("no_std", CrateLevel, Gated("no_std", - "no_std is experimental")), + ("no_std", CrateLevel, Ungated), ("no_core", CrateLevel, Gated("no_core", "no_core is experimental")), ("lang", Normal, Gated("lang_items", @@ -370,7 +373,7 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeGat // FIXME: #14407 these are only looked at on-demand so we can't // guarantee they'll have already been checked - ("deprecated", Whitelisted, Ungated), + ("rustc_deprecated", Whitelisted, Ungated), ("must_use", Whitelisted, Ungated), ("stable", Whitelisted, Ungated), ("unstable", Whitelisted, Ungated), @@ -406,25 +409,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(); @@ -437,7 +472,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); @@ -494,6 +529,7 @@ pub struct Features { /// #![feature] attrs for non-language (library) features pub declared_lib_features: Vec<(InternedString, Span)>, pub const_fn: bool, + pub const_indexing: bool, pub static_recursion: bool, pub default_type_parameter_fallback: bool, pub type_macros: bool, @@ -501,6 +537,8 @@ pub struct Features { pub cfg_target_vendor: bool, pub augmented_assignments: bool, pub braced_empty_structs: bool, + pub staged_api: bool, + pub stmt_expr_attributes: bool, } impl Features { @@ -525,6 +563,7 @@ impl Features { declared_stable_lang_features: Vec::new(), declared_lib_features: Vec::new(), const_fn: false, + const_indexing: false, static_recursion: false, default_type_parameter_fallback: false, type_macros: false, @@ -532,6 +571,8 @@ impl Features { cfg_target_vendor: false, augmented_assignments: false, braced_empty_structs: false, + staged_api: false, + stmt_expr_attributes: false, } } } @@ -545,6 +586,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; @@ -1097,6 +1141,7 @@ fn check_crate_inner(cm: &CodeMap, span_handler: &SpanHandler, declared_stable_lang_features: accepted_features, declared_lib_features: unknown_features, const_fn: cx.has_feature("const_fn"), + const_indexing: cx.has_feature("const_indexing"), static_recursion: cx.has_feature("static_recursion"), default_type_parameter_fallback: cx.has_feature("default_type_parameter_fallback"), type_macros: cx.has_feature("type_macros"), @@ -1104,6 +1149,8 @@ fn check_crate_inner(cm: &CodeMap, span_handler: &SpanHandler, cfg_target_vendor: cx.has_feature("cfg_target_vendor"), 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/syntex_syntax/src/fold.rs b/syntex_syntax/src/fold.rs index 3dba6cbe..73f2c51b 100644 --- a/syntex_syntax/src/fold.rs +++ b/syntex_syntax/src/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/syntex_syntax/src/lib.rs b/syntex_syntax/src/lib.rs index 0cd27d1b..4498120a 100644 --- a/syntex_syntax/src/lib.rs +++ b/syntex_syntax/src/lib.rs @@ -18,7 +18,7 @@ #![cfg_attr(stage0, feature(custom_attribute))] #![crate_name = "syntax"] #![unstable(feature = "rustc_private", issue = "27812")] -#![staged_api] +#![cfg_attr(stage0, staged_api)] #![crate_type = "dylib"] #![crate_type = "rlib"] #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", @@ -26,8 +26,9 @@ html_root_url = "https://doc.rust-lang.org/nightly/", test(attr(deny(warnings))))] +#![cfg_attr(stage0, feature(rustc_attrs))] +#![cfg_attr(stage0, allow(unused_attributes))] #![feature(associated_consts)] -#![feature(drain)] #![feature(filling_drop)] #![feature(libc)] #![feature(rustc_private)] @@ -35,7 +36,6 @@ #![feature(str_char)] #![feature(str_escape)] #![feature(unicode)] -#![feature(vec_push_all)] extern crate fmt_macros; extern crate serialize; @@ -63,11 +63,13 @@ macro_rules! panictry { pub mod util { pub mod interner; + pub mod lev_distance; pub mod node_count; pub mod parser; #[cfg(test)] pub mod parser_testing; pub mod small_vector; + pub mod move_map; } pub mod diagnostics { diff --git a/syntex_syntax/src/parse/mod.rs b/syntex_syntax/src/parse/mod.rs index 7e2fd09a..e9c8173a 100644 --- a/syntex_syntax/src/parse/mod.rs +++ b/syntex_syntax/src/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/syntex_syntax/src/parse/parser.rs b/syntex_syntax/src/parse/parser.rs index 56a06f70..7502a8cb 100644 --- a/syntex_syntax/src/parse/parser.rs +++ b/syntex_syntax/src/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() } } @@ -3253,7 +3399,8 @@ impl<'a> Parser<'a> { let tts = try!(self.parse_seq_to_end(&token::CloseDelim(delim), seq_sep_none(), |p| p.parse_token_tree())); let mac = Mac_ { path: path, tts: tts, ctxt: EMPTY_CTXT }; - pat = PatMac(codemap::Spanned {node: mac, span: self.span}); + pat = PatMac(codemap::Spanned {node: mac, + span: mk_sp(lo, self.last_span.hi)}); } else { // Parse ident @ pat // This can give false positives and parse nullary enums, @@ -3274,7 +3421,7 @@ impl<'a> Parser<'a> { token::DotDotDot => { // Parse range let hi = self.last_span.hi; - let begin = self.mk_expr(lo, hi, ExprPath(qself, path)); + let begin = self.mk_expr(lo, hi, ExprPath(qself, path), None); try!(self.bump()); let end = try!(self.parse_pat_range_end()); pat = PatRange(begin, end); @@ -3321,7 +3468,7 @@ impl<'a> Parser<'a> { } } else { // Try to parse everything else as literal with optional minus - let begin = try!(self.parse_literal_maybe_minus()); + let begin = try!(self.parse_pat_literal_maybe_minus()); if try!(self.eat(&token::DotDotDot)) { let end = try!(self.parse_pat_range_end()); pat = PatRange(begin, end); @@ -3378,7 +3525,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 +3540,14 @@ impl<'a> Parser<'a> { init: init, id: ast::DUMMY_NODE_ID, span: mk_sp(lo, self.last_span.hi), + attrs: attrs, })) } /// Parse a "let" stmt - fn parse_let(&mut self) -> PResult> { + 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 +3592,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 +3651,12 @@ impl<'a> Parser<'a> { }; if id.name == token::special_idents::invalid.name { - spanned(lo, hi, - StmtMac(P(spanned(lo, - hi, - Mac_ { path: pth, tts: tts, ctxt: EMPTY_CTXT })), - style)) + let stmt = StmtMac(P(spanned(lo, + hi, + Mac_ { path: pth, tts: tts, ctxt: EMPTY_CTXT })), + style, + attrs.into_thin_attrs()); + spanned(lo, hi, stmt) } else { // if it has a special ident, it's definitely an item // @@ -3535,30 +3676,43 @@ impl<'a> Parser<'a> { lo, hi, id /*id is good here*/, ItemMac(spanned(lo, hi, Mac_ { path: pth, tts: tts, ctxt: EMPTY_CTXT })), - Inherited, Vec::new(/*no attrs*/))))), + Inherited, attrs)))), ast::DUMMY_NODE_ID)) } } else { - match try!(self.parse_item_(attrs, false)) { + // FIXME: Bad copy of attrs + match try!(self.parse_item_(attrs.clone(), false, true)) { Some(i) => { let hi = i.span.hi; let decl = P(spanned(lo, hi, DeclItem(i))); spanned(lo, hi, StmtDecl(decl, ast::DUMMY_NODE_ID)) } None => { + let unused_attrs = |attrs: &[_], s: &mut Self| { + if attrs.len() > 0 { + s.span_err(s.span, + "expected statement after outer attribute"); + } + }; + // Do not attempt to parse an expression if we're done here. if self.token == token::Semi { + unused_attrs(&attrs, self); try!(self.bump()); return Ok(None); } if self.token == token::CloseDelim(token::Brace) { + unused_attrs(&attrs, self); return Ok(None); } // Remainder are line-expr stmts. - let e = try!(self.parse_expr_res(Restrictions::RESTRICTION_STMT_EXPR)); - spanned(lo, e.span.hi, StmtExpr(e, ast::DUMMY_NODE_ID)) + let e = try!(self.parse_expr_res( + Restrictions::RESTRICTION_STMT_EXPR, Some(attrs.into_thin_attrs()))); + let hi = e.span.hi; + let stmt = StmtExpr(e, ast::DUMMY_NODE_ID); + spanned(lo, hi, stmt) } } })) @@ -3614,22 +3768,23 @@ impl<'a> Parser<'a> { StmtExpr(e, _) => { try!(self.handle_expression_like_statement(e, span, &mut stmts, &mut expr)); } - StmtMac(mac, MacStmtWithoutBraces) => { + StmtMac(mac, MacStmtWithoutBraces, attrs) => { // statement macro without braces; might be an // expr depending on whether a semicolon follows match self.token { token::Semi => { stmts.push(P(Spanned { - node: StmtMac(mac, MacStmtWithSemicolon), + node: StmtMac(mac, MacStmtWithSemicolon, attrs), span: mk_sp(span.lo, self.span.hi), })); try!(self.bump()); } _ => { let e = self.mk_mac_expr(span.lo, span.hi, - mac.and_then(|m| m.node)); - let e = try!(self.parse_dot_or_call_expr_with(e)); - let e = try!(self.parse_assoc_expr_with(0, Some(e))); + mac.and_then(|m| m.node), + None); + let e = try!(self.parse_dot_or_call_expr_with(e, attrs)); + let e = try!(self.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(e))); try!(self.handle_expression_like_statement( e, span, @@ -3638,12 +3793,12 @@ impl<'a> Parser<'a> { } } } - StmtMac(m, style) => { + StmtMac(m, style, attrs) => { // statement macro; might be an expr match self.token { token::Semi => { stmts.push(P(Spanned { - node: StmtMac(m, MacStmtWithSemicolon), + node: StmtMac(m, MacStmtWithSemicolon, attrs), span: mk_sp(span.lo, self.span.hi), })); try!(self.bump()); @@ -3652,11 +3807,12 @@ impl<'a> Parser<'a> { // if a block ends in `m!(arg)` without // a `;`, it must be an expr expr = Some(self.mk_mac_expr(span.lo, span.hi, - m.and_then(|x| x.node))); + m.and_then(|x| x.node), + attrs)); } _ => { stmts.push(P(Spanned { - node: StmtMac(m, style), + node: StmtMac(m, style, attrs), span: span })); } @@ -4475,6 +4631,7 @@ impl<'a> Parser<'a> { let last_span = self.last_span; self.complain_if_pub_macro(vis, last_span); + let lo = self.span.lo; let pth = try!(self.parse_path(NoTypesAllowed)); try!(self.expect(&token::Not)); @@ -4485,8 +4642,8 @@ impl<'a> Parser<'a> { |p| p.parse_token_tree())); let m_ = Mac_ { path: pth, tts: tts, ctxt: EMPTY_CTXT }; let m: ast::Mac = codemap::Spanned { node: m_, - span: mk_sp(self.span.lo, - self.span.hi) }; + span: mk_sp(lo, + self.last_span.hi) }; if delim != token::Brace { try!(self.expect(&token::Semi)) } @@ -5210,7 +5367,7 @@ impl<'a> Parser<'a> { /// NB: this function no longer parses the items inside an /// extern crate. fn parse_item_(&mut self, attrs: Vec, - 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 +5625,7 @@ impl<'a> Parser<'a> { maybe_append(attrs, extra_attrs)); return Ok(Some(item)); } - self.parse_macro_use_or_failure(attrs,macros_allowed,lo,visibility) + self.parse_macro_use_or_failure(attrs,macros_allowed,attributes_allowed,lo,visibility) } /// Parse a foreign item. @@ -5487,7 +5644,7 @@ impl<'a> Parser<'a> { } // FIXME #5668: this will occur for a macro invocation: - match try!(self.parse_macro_use_or_failure(attrs, true, lo, visibility)) { + match try!(self.parse_macro_use_or_failure(attrs, true, false, lo, visibility)) { Some(item) => { return Err(self.span_fatal(item.span, "macros cannot expand to foreign items")); } @@ -5500,6 +5657,7 @@ impl<'a> Parser<'a> { &mut self, attrs: Vec , macros_allowed: bool, + attributes_allowed: bool, lo: BytePos, visibility: Visibility ) -> PResult>> { @@ -5513,6 +5671,8 @@ impl<'a> Parser<'a> { let last_span = self.last_span; self.complain_if_pub_macro(visibility, last_span); + let mac_lo = self.span.lo; + // item macro. let pth = try!(self.parse_path(NoTypesAllowed)); try!(self.expect(&token::Not)); @@ -5533,8 +5693,8 @@ impl<'a> Parser<'a> { // single-variant-enum... : let m = Mac_ { path: pth, tts: tts, ctxt: EMPTY_CTXT }; let m: ast::Mac = codemap::Spanned { node: m, - span: mk_sp(self.span.lo, - self.span.hi) }; + span: mk_sp(mac_lo, + self.last_span.hi) }; if delim != token::Brace { if !try!(self.eat(&token::Semi) ){ @@ -5566,7 +5726,7 @@ impl<'a> Parser<'a> { } } - if !attrs.is_empty() { + if !attributes_allowed && !attrs.is_empty() { self.expected_item_err(&attrs); } Ok(None) @@ -5574,7 +5734,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/syntex_syntax/src/parse/token.rs b/syntex_syntax/src/parse/token.rs index 5e4449af..17b7d8db 100644 --- a/syntex_syntax/src/parse/token.rs +++ b/syntex_syntax/src/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/syntex_syntax/src/print/pprust.rs b/syntex_syntax/src/print/pprust.rs index e9c71601..f7105951 100644 --- a/syntex_syntax/src/print/pprust.rs +++ b/syntex_syntax/src/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 @@ -120,17 +120,15 @@ pub fn print_crate<'a>(cm: &'a CodeMap, // However we don't want these attributes in the AST because // of the feature gate, so we fake them up here. - let no_std_meta = attr::mk_word_item(InternedString::new("no_std")); + // #![feature(prelude_import)] let prelude_import_meta = attr::mk_word_item(InternedString::new("prelude_import")); - - // #![feature(no_std)] - let fake_attr = attr::mk_attr_inner(attr::mk_attr_id(), - attr::mk_list_item(InternedString::new("feature"), - vec![no_std_meta.clone(), - prelude_import_meta])); + let list = attr::mk_list_item(InternedString::new("feature"), + vec![prelude_import_meta]); + let fake_attr = attr::mk_attr_inner(attr::mk_attr_id(), list); try!(s.print_attribute(&fake_attr)); // #![no_std] + let no_std_meta = attr::mk_word_item(InternedString::new("no_std")); let fake_attr = attr::mk_attr_inner(attr::mk_attr_id(), no_std_meta); try!(s.print_attribute(&fake_attr)); } @@ -170,7 +168,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 +399,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) @@ -651,15 +649,15 @@ pub trait PrintState<'a> { match t { ast::SignedIntLit(st, ast::Plus) => { word(self.writer(), - &ast_util::int_ty_to_string(st, Some(i as i64))) + &ast_util::int_val_to_string(st, i as i64)) } ast::SignedIntLit(st, ast::Minus) => { - let istr = ast_util::int_ty_to_string(st, Some(-(i as i64))); + let istr = ast_util::int_val_to_string(st, -(i as i64)); word(self.writer(), &format!("-{}", istr)) } ast::UnsignedIntLit(ut) => { - word(self.writer(), &ast_util::uint_ty_to_string(ut, Some(i))) + word(self.writer(), &ast_util::uint_val_to_string(ut, i)) } ast::UnsuffixedIntLit(ast::Plus) => { word(self.writer(), &format!("{}", i)) @@ -707,43 +705,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 +774,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 +795,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 +832,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 +839,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 +869,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 +879,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 +1217,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 +1331,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 +1387,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 +1609,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 +1628,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 +1660,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 +1677,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 +1701,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 +1716,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 +1726,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 +1739,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 +1782,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 +1841,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 +1853,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 +1868,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 +1886,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 +1903,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 +1972,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 +1998,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 +2051,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 +2064,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 +2077,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 +2086,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 +2116,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 +2137,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 +2278,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 +2300,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 +2510,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 +2583,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 +2607,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 +2965,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 +2993,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 +3018,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/syntex_syntax/src/test.rs b/syntex_syntax/src/test.rs index 3e024764..6fd3833a 100644 --- a/syntex_syntax/src/test.rs +++ b/syntex_syntax/src/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/syntex_syntax/src/util/small_vector.rs b/syntex_syntax/src/util/small_vector.rs index 9d53cb96..ee183d7f 100644 --- a/syntex_syntax/src/util/small_vector.rs +++ b/syntex_syntax/src/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 { @@ -129,20 +129,11 @@ impl SmallVector { /// Deprecated: use `into_iter`. #[unstable(feature = "rustc_private", issue = "0")] - #[deprecated(since = "1.0.0", reason = "use into_iter")] + #[rustc_deprecated(since = "1.0.0", reason = "use into_iter")] pub fn move_iter(self) -> IntoIter { 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/syntex_syntax/src/visit.rs b/syntex_syntax/src/visit.rs index 5d4a462e..cdc11fb2 100644 --- a/syntex_syntax/src/visit.rs +++ b/syntex_syntax/src/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); + } + } } }