diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 1f3df1ff6f2d8..48b86d862f575 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -720,16 +720,7 @@ pub fn phase_2_configure_and_expand(sess: &Session, ret }); - // JBC: make CFG processing part of expansion to avoid this problem: - - // strip again, in case expansion added anything with a #[cfg]. krate = sess.track_errors(|| { - let krate = time(time_passes, "configuration 2", || { - syntax::config::strip_unconfigured_items(sess.diagnostic(), - krate, - &mut feature_gated_cfgs) - }); - time(time_passes, "gated configuration checking", || { let features = sess.features.borrow(); feature_gated_cfgs.sort(); diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index d9409d3bbd921..b641600fb448a 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -15,7 +15,7 @@ pub use self::UnsafeSource::*; pub use self::ViewPath_::*; pub use self::PathParameters::*; -use attr::ThinAttributes; +use attr::{ThinAttributes, HasAttrs}; use codemap::{mk_sp, respan, Span, Spanned, DUMMY_SP, ExpnId}; use abi::Abi; use errors; @@ -829,13 +829,7 @@ impl StmtKind { } pub fn attrs(&self) -> &[Attribute] { - match *self { - StmtKind::Decl(ref d, _) => d.attrs(), - StmtKind::Expr(ref e, _) | - StmtKind::Semi(ref e, _) => e.attrs(), - StmtKind::Mac(_, _, Some(ref b)) => b, - StmtKind::Mac(_, _, None) => &[], - } + HasAttrs::attrs(self) } } @@ -868,10 +862,7 @@ pub struct Local { impl Local { pub fn attrs(&self) -> &[Attribute] { - match self.attrs { - Some(ref b) => b, - None => &[], - } + HasAttrs::attrs(self) } } @@ -887,10 +878,7 @@ pub enum DeclKind { impl Decl { pub fn attrs(&self) -> &[Attribute] { - match self.node { - DeclKind::Local(ref l) => l.attrs(), - DeclKind::Item(ref i) => i.attrs(), - } + HasAttrs::attrs(self) } } @@ -935,10 +923,7 @@ pub struct Expr { impl Expr { pub fn attrs(&self) -> &[Attribute] { - match self.attrs { - Some(ref b) => b, - None => &[], - } + HasAttrs::attrs(self) } } diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs index 8761ca3717895..c3c3deea1877f 100644 --- a/src/libsyntax/attr.rs +++ b/src/libsyntax/attr.rs @@ -884,82 +884,109 @@ impl AttributesExt for Vec { } } +pub trait HasAttrs: Sized { + fn attrs(&self) -> &[ast::Attribute]; + fn map_attrs) -> Vec>(self, f: F) -> 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 { +impl WithAttrs for T { fn with_attrs(self, attrs: ThinAttributes) -> Self { - self.map(|mut e| { - e.attrs.update(|a| a.append(attrs)); - e + self.map_attrs(|mut orig_attrs| { + orig_attrs.extend(attrs.into_attr_vec()); + orig_attrs }) } } -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 HasAttrs for Vec { + fn attrs(&self) -> &[Attribute] { + &self + } + fn map_attrs) -> Vec>(self, f: F) -> Self { + f(self) } } -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 HasAttrs for ThinAttributes { + fn attrs(&self) -> &[Attribute] { + self.as_attr_slice() + } + fn map_attrs) -> Vec>(self, f: F) -> Self { + self.map_thin_attrs(f) } } -impl WithAttrs for P { - fn with_attrs(self, attrs: ThinAttributes) -> Self { - self.map(|Spanned { span, node }| { - Spanned { - span: span, - node: match node { - DeclKind::Local(local) => DeclKind::Local(local.with_attrs(attrs)), - DeclKind::Item(item) => DeclKind::Item(item.with_attrs(attrs)), - } - } - }) +impl HasAttrs for P { + fn attrs(&self) -> &[Attribute] { + (**self).attrs() + } + fn map_attrs) -> Vec>(self, f: F) -> Self { + self.map(|t| t.map_attrs(f)) } } -impl WithAttrs for P { - fn with_attrs(self, attrs: ThinAttributes) -> Self { - self.map(|Spanned { span, node }| { - Spanned { - span: span, - node: match node { - StmtKind::Decl(decl, id) => StmtKind::Decl(decl.with_attrs(attrs), id), - StmtKind::Expr(expr, id) => StmtKind::Expr(expr.with_attrs(attrs), id), - StmtKind::Semi(expr, id) => StmtKind::Semi(expr.with_attrs(attrs), id), - StmtKind::Mac(mac, style, mut ats) => { - ats.update(|a| a.append(attrs)); - StmtKind::Mac(mac, style, ats) - } - }, - } - }) +impl HasAttrs for DeclKind { + fn attrs(&self) -> &[Attribute] { + match *self { + DeclKind::Local(ref local) => local.attrs(), + DeclKind::Item(ref item) => item.attrs(), + } + } + + fn map_attrs) -> Vec>(self, f: F) -> Self { + match self { + DeclKind::Local(local) => DeclKind::Local(local.map_attrs(f)), + DeclKind::Item(item) => DeclKind::Item(item.map_attrs(f)), + } } } + +impl HasAttrs for StmtKind { + fn attrs(&self) -> &[Attribute] { + match *self { + StmtKind::Decl(ref decl, _) => decl.attrs(), + StmtKind::Expr(ref expr, _) | StmtKind::Semi(ref expr, _) => expr.attrs(), + StmtKind::Mac(_, _, ref attrs) => attrs.attrs(), + } + } + + fn map_attrs) -> Vec>(self, f: F) -> Self { + match self { + StmtKind::Decl(decl, id) => StmtKind::Decl(decl.map_attrs(f), id), + StmtKind::Expr(expr, id) => StmtKind::Expr(expr.map_attrs(f), id), + StmtKind::Semi(expr, id) => StmtKind::Semi(expr.map_attrs(f), id), + StmtKind::Mac(mac, style, attrs) => + StmtKind::Mac(mac, style, attrs.map_attrs(f)), + } + } +} + +macro_rules! derive_has_attrs_from_field { + ($($ty:path),*) => { derive_has_attrs_from_field!($($ty: .attrs),*); }; + ($($ty:path : $(.$field:ident)*),*) => { $( + impl HasAttrs for $ty { + fn attrs(&self) -> &[Attribute] { + self $(.$field)* .attrs() + } + + fn map_attrs(mut self, f: F) -> Self + where F: FnOnce(Vec) -> Vec, + { + self $(.$field)* = self $(.$field)* .map_attrs(f); + self + } + } + )* } +} + +derive_has_attrs_from_field! { + Item, Expr, Local, ast::ForeignItem, ast::StructField, ast::ImplItem, ast::TraitItem, ast::Arm +} + +derive_has_attrs_from_field! { Decl: .node, Stmt: .node, ast::Variant: .node.attrs } diff --git a/src/libsyntax/codemap.rs b/src/libsyntax/codemap.rs index ca8708fdc8326..14ab232b92c7e 100644 --- a/src/libsyntax/codemap.rs +++ b/src/libsyntax/codemap.rs @@ -1258,31 +1258,6 @@ impl CodeMap { return a; } - /// Check if the backtrace `subtrace` contains `suptrace` as a prefix. - pub fn more_specific_trace(&self, - mut subtrace: ExpnId, - suptrace: ExpnId) - -> bool { - loop { - if subtrace == suptrace { - return true; - } - - let stop = self.with_expn_info(subtrace, |opt_expn_info| { - if let Some(expn_info) = opt_expn_info { - subtrace = expn_info.call_site.expn_id; - false - } else { - true - } - }); - - if stop { - return false; - } - } - } - pub fn record_expansion(&self, expn_info: ExpnInfo) -> ExpnId { let mut expansions = self.expansions.borrow_mut(); expansions.push(expn_info); diff --git a/src/libsyntax/config.rs b/src/libsyntax/config.rs index 4554a280e5f19..14035d8d116a3 100644 --- a/src/libsyntax/config.rs +++ b/src/libsyntax/config.rs @@ -8,326 +8,76 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use attr::AttrMetaMethods; +use attr::{AttrMetaMethods, HasAttrs}; use errors::Handler; use feature_gate::GatedCfgAttr; use fold::Folder; use {ast, fold, attr}; -use visit; use codemap::{Spanned, respan}; use ptr::P; use util::small_vector::SmallVector; -/// A folder that strips out items that do not belong in the current -/// configuration. -struct Context<'a, F> where F: FnMut(&[ast::Attribute]) -> bool { - in_cfg: F, - diagnostic: &'a Handler, -} +pub trait CfgFolder: fold::Folder { + // Check if a node with the given attributes is in this configuration. + fn in_cfg(&mut self, attrs: &[ast::Attribute]) -> bool; -// 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: &Handler, krate: ast::Crate, - 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(diagnostic, - krate, - |attrs| { - let mut diag = CfgDiagReal { - diag: diagnostic, - feature_gated_cfgs: feature_gated_cfgs, - }; - in_cfg(&config, attrs, &mut diag) - }) -} + // Update a node before checking if it is in this configuration (used to implement `cfg_attr`). + fn process_attrs(&mut self, node: T) -> T { node } -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) - } - fn fold_item_kind(&mut self, item: ast::ItemKind) -> ast::ItemKind { - fold_item_kind(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: ast::Stmt) -> SmallVector { - fold_stmt(self, stmt) - } - fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac { - fold::noop_fold_mac(mac, self) - } - fn fold_item(&mut self, item: P) -> SmallVector> { - fold_item(self, item) - } -} + // Visit attributes on expression and statements (but not attributes on items in blocks). + fn visit_stmt_or_expr_attrs(&mut self, _attrs: &[ast::Attribute]) {} -pub fn strip_items<'a, F>(diagnostic: &'a Handler, - 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) -} + // Visit unremovable (non-optional) expressions -- c.f. `fold_expr` vs `fold_opt_expr`. + fn visit_unremovable_expr(&mut self, _expr: &ast::Expr) {} -fn filter_foreign_item(cx: &mut Context, - item: ast::ForeignItem) - -> Option where - F: FnMut(&[ast::Attribute]) -> bool -{ - if foreign_item_in_cfg(cx, &item) { - Some(item) - } else { - None + fn configure(&mut self, node: T) -> Option { + let node = self.process_attrs(node); + if self.in_cfg(node.attrs()) { Some(node) } else { None } } } -fn fold_foreign_mod(cx: &mut Context, - ast::ForeignMod {abi, items}: ast::ForeignMod) - -> ast::ForeignMod where - F: FnMut(&[ast::Attribute]) -> bool -{ - ast::ForeignMod { - abi: abi, - items: items.into_iter() - .filter_map(|a| filter_foreign_item(cx, a)) - .collect() - } -} - -fn fold_item(cx: &mut Context, item: P) -> SmallVector> where - F: FnMut(&[ast::Attribute]) -> bool -{ - if item_in_cfg(cx, &item) { - SmallVector::one(item.map(|i| cx.fold_item_simple(i))) - } else { - SmallVector::zero() - } -} - -fn fold_item_kind(cx: &mut Context, item: ast::ItemKind) -> ast::ItemKind where - F: FnMut(&[ast::Attribute]) -> bool -{ - let item = match item { - ast::ItemKind::Impl(u, o, a, b, c, impl_items) => { - let impl_items = impl_items.into_iter() - .filter(|ii| (cx.in_cfg)(&ii.attrs)) - .collect(); - ast::ItemKind::Impl(u, o, a, b, c, impl_items) - } - ast::ItemKind::Trait(u, a, b, methods) => { - let methods = methods.into_iter() - .filter(|ti| (cx.in_cfg)(&ti.attrs)) - .collect(); - ast::ItemKind::Trait(u, a, b, methods) - } - ast::ItemKind::Struct(def, generics) => { - ast::ItemKind::Struct(fold_struct(cx, def), generics) - } - ast::ItemKind::Enum(def, generics) => { - let variants = def.variants.into_iter().filter_map(|v| { - if !(cx.in_cfg)(&v.node.attrs) { - None - } else { - Some(Spanned { - node: ast::Variant_ { - name: v.node.name, - attrs: v.node.attrs, - data: fold_struct(cx, v.node.data), - disr_expr: v.node.disr_expr, - }, - span: v.span - }) - } - }); - ast::ItemKind::Enum(ast::EnumDef { - variants: variants.collect(), - }, generics) - } - item => item, - }; - - fold::noop_fold_item_kind(item, cx) +/// A folder that strips out items that do not belong in the current +/// configuration. +pub struct StripUnconfigured<'a> { + diag: CfgDiagReal<'a, 'a>, + config: &'a ast::CrateConfig, } -fn fold_struct(cx: &mut Context, vdata: ast::VariantData) -> ast::VariantData where - F: FnMut(&[ast::Attribute]) -> bool -{ - match vdata { - ast::VariantData::Struct(fields, id) => { - ast::VariantData::Struct(fields.into_iter().filter(|m| { - (cx.in_cfg)(&m.attrs) - }).collect(), id) - } - ast::VariantData::Tuple(fields, id) => { - ast::VariantData::Tuple(fields.into_iter().filter(|m| { - (cx.in_cfg)(&m.attrs) - }).collect(), id) +impl<'a> StripUnconfigured<'a> { + pub fn new(config: &'a ast::CrateConfig, + diagnostic: &'a Handler, + feature_gated_cfgs: &'a mut Vec) + -> Self { + StripUnconfigured { + config: config, + diag: CfgDiagReal { diag: diagnostic, feature_gated_cfgs: feature_gated_cfgs }, } - ast::VariantData::Unit(id) => ast::VariantData::Unit(id) - } -} - -fn fold_opt_expr(cx: &mut Context, expr: P) -> Option> - where F: FnMut(&[ast::Attribute]) -> bool -{ - if expr_in_cfg(cx, &expr) { - Some(fold_expr(cx, expr)) - } else { - None - } -} - -fn fold_expr(cx: &mut Context, expr: P) -> P where - F: FnMut(&[ast::Attribute]) -> bool -{ - expr.map(|ast::Expr {id, span, node, attrs}| { - fold::noop_fold_expr(ast::Expr { - id: id, - node: match node { - ast::ExprKind::Match(m, arms) => { - ast::ExprKind::Match(m, arms.into_iter() - .filter(|a| (cx.in_cfg)(&a.attrs)) - .collect()) - } - _ => node - }, - span: span, - attrs: attrs, - }, cx) - }) -} - -fn fold_stmt(cx: &mut Context, stmt: ast::Stmt) -> SmallVector - where F: FnMut(&[ast::Attribute]) -> bool -{ - if stmt_in_cfg(cx, &stmt) { - fold::noop_fold_stmt(stmt, 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 -{ - return (cx.in_cfg)(&item.attrs); -} - -fn foreign_item_in_cfg(cx: &mut Context, item: &ast::ForeignItem) -> bool where - F: FnMut(&[ast::Attribute]) -> 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(cfg: &[P], - attrs: &[ast::Attribute], - diag: &mut T) -> bool { - attrs.iter().all(|attr| { - let mis = match attr.node.value.node { - ast::MetaItemKind::List(_, ref mis) if is_cfg(&attr) => mis, - _ => return true - }; - - if mis.len() != 1 { - diag.emit_error(|diagnostic| { - diagnostic.span_err(attr.span, "expected 1 cfg-pattern"); - }); - return true; - } - - attr::cfg_matches(cfg, &mis[0], diag) - }) -} -struct CfgAttrFolder<'a, T> { - diag: T, - config: &'a ast::CrateConfig, -} - -// Process `#[cfg_attr]`. -fn process_cfg_attr(diagnostic: &Handler, krate: ast::Crate, - feature_gated_cfgs: &mut Vec) -> ast::Crate { - let mut fld = CfgAttrFolder { - diag: CfgDiagReal { - diag: diagnostic, - feature_gated_cfgs: feature_gated_cfgs, - }, - config: &krate.config.clone(), - }; - fld.fold_crate(krate) -} - -impl<'a, T: CfgDiag> fold::Folder for CfgAttrFolder<'a, T> { - fn fold_attribute(&mut self, attr: ast::Attribute) -> Option { + fn process_cfg_attr(&mut self, attr: ast::Attribute) -> Option { if !attr.check_name("cfg_attr") { - return fold::noop_fold_attribute(attr, self); + return Some(attr); } let attr_list = match attr.meta_item_list() { Some(attr_list) => attr_list, None => { - self.diag.emit_error(|diag| { - diag.span_err(attr.span, - "expected `#[cfg_attr(, )]`"); - }); + let msg = "expected `#[cfg_attr(, )]`"; + self.diag.diag.span_err(attr.span, msg); 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.emit_error(|diag| { - diag.span_err(attr.span, - "expected `#[cfg_attr(, )]`"); - }); + let msg = "expected `#[cfg_attr(, )]`"; + self.diag.diag.span_err(attr.span, msg); return None; } }; - if attr::cfg_matches(&self.config[..], &cfg, &mut self.diag) { + 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, @@ -338,127 +88,187 @@ impl<'a, T: CfgDiag> fold::Folder for CfgAttrFolder<'a, T> { None } } - - // Need the ability to run pre-expansion. - fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac { - 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::StmtKind::Decl(ref decl, _) = s.node { - if let ast::DeclKind::Item(_) = decl.node { - visit::walk_stmt(self, s); - return; - } - } - - // flag the offending attributes - for attr in stmt_attrs { - self.discovered.push(GatedCfgAttr::GatedAttr(attr.span)); +impl<'a> CfgFolder for StripUnconfigured<'a> { + // Determine if an item should be translated in the current crate + // configuration based on the item's attributes + fn in_cfg(&mut self, attrs: &[ast::Attribute]) -> bool { + attrs.iter().all(|attr| { + let mis = match attr.node.value.node { + ast::MetaItemKind::List(_, ref mis) if is_cfg(&attr) => mis, + _ => return true + }; + + if mis.len() != 1 { + self.diag.emit_error(|diagnostic| { + diagnostic.span_err(attr.span, "expected 1 cfg-pattern"); + }); + return true; } - // 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); - } + attr::cfg_matches(self.config, &mis[0], &mut self.diag) + }) } - 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)); - } + fn process_attrs(&mut self, node: T) -> T { + node.map_attrs(|attrs| { + attrs.into_iter().filter_map(|attr| self.process_cfg_attr(attr)).collect() + }) + } - // 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_stmt_or_expr_attrs(&mut self, attrs: &[ast::Attribute]) { + // flag the offending attributes + for attr in attrs.iter() { + self.diag.feature_gated_cfgs.push(GatedCfgAttr::GatedAttr(attr.span)); } } - 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_unremovable_expr(&mut self, expr: &ast::Expr) { + if let Some(attr) = expr.attrs().iter().find(|a| is_cfg(a)) { + let msg = "removing an expression is not supported in this position"; + self.diag.diag.span_err(attr.span, msg); } } +} + +// 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: &Handler, krate: ast::Crate, + feature_gated_cfgs: &mut Vec) + -> ast::Crate +{ + let config = &krate.config.clone(); + StripUnconfigured::new(config, diagnostic, feature_gated_cfgs).fold_crate(krate) +} - fn visit_item(&mut self, i: &'v ast::Item) { - if node_survives_cfg(&i.attrs, self.config) { - visit::walk_item(self, i); +impl fold::Folder for T { + fn fold_foreign_mod(&mut self, foreign_mod: ast::ForeignMod) -> ast::ForeignMod { + ast::ForeignMod { + abi: foreign_mod.abi, + items: foreign_mod.items.into_iter().filter_map(|item| { + self.configure(item).map(|item| fold::noop_fold_foreign_item(item, self)) + }).collect(), } } - 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 fold_item_kind(&mut self, item: ast::ItemKind) -> ast::ItemKind { + let fold_struct = |this: &mut Self, vdata| match vdata { + ast::VariantData::Struct(fields, id) => { + let fields = fields.into_iter().filter_map(|field| this.configure(field)); + ast::VariantData::Struct(fields.collect(), id) + } + ast::VariantData::Tuple(fields, id) => { + let fields = fields.into_iter().filter_map(|field| this.configure(field)); + ast::VariantData::Tuple(fields.collect(), id) + } + ast::VariantData::Unit(id) => ast::VariantData::Unit(id) + }; + + let item = match item { + ast::ItemKind::Impl(u, o, a, b, c, items) => { + let items = items.into_iter().filter_map(|item| self.configure(item)).collect(); + ast::ItemKind::Impl(u, o, a, b, c, items) + } + ast::ItemKind::Trait(u, a, b, items) => { + let items = items.into_iter().filter_map(|item| self.configure(item)).collect(); + ast::ItemKind::Trait(u, a, b, items) + } + ast::ItemKind::Struct(def, generics) => { + ast::ItemKind::Struct(fold_struct(self, def), generics) + } + ast::ItemKind::Enum(def, generics) => { + let variants = def.variants.into_iter().filter_map(|v| { + self.configure(v).map(|v| { + Spanned { + node: ast::Variant_ { + name: v.node.name, + attrs: v.node.attrs, + data: fold_struct(self, v.node.data), + disr_expr: v.node.disr_expr, + }, + span: v.span + } + }) + }); + ast::ItemKind::Enum(ast::EnumDef { + variants: variants.collect(), + }, generics) + } + item => item, + }; + + fold::noop_fold_item_kind(item, self) } - 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 fold_expr(&mut self, expr: P) -> P { + self.visit_stmt_or_expr_attrs(expr.attrs()); + // 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 + self.visit_unremovable_expr(&expr); + let expr = self.process_attrs(expr); + fold_expr(self, expr) } - fn visit_struct_field(&mut self, s: &'v ast::StructField) { - if node_survives_cfg(&s.attrs, self.config) { - visit::walk_struct_field(self, s); - } + fn fold_opt_expr(&mut self, expr: P) -> Option> { + self.configure(expr).map(|expr| fold_expr(self, expr)) } - 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 fold_stmt(&mut self, stmt: ast::Stmt) -> SmallVector { + let is_item = match stmt.node { + ast::StmtKind::Decl(ref decl, _) => match decl.node { + ast::DeclKind::Item(_) => true, + _ => false, + }, + _ => false, + }; + + // avoid calling `visit_stmt_or_expr_attrs` on items + if !is_item { + self.visit_stmt_or_expr_attrs(stmt.attrs()); } + + self.configure(stmt).map(|stmt| fold::noop_fold_stmt(stmt, self)) + .unwrap_or(SmallVector::zero()) } - fn visit_arm(&mut self, a: &'v ast::Arm) { - if node_survives_cfg(&a.attrs, self.config) { - visit::walk_arm(self, a); - } + fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac { + fold::noop_fold_mac(mac, self) } - // 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) + fn fold_item(&mut self, item: P) -> SmallVector> { + self.configure(item).map(|item| SmallVector::one(item.map(|i| self.fold_item_simple(i)))) + .unwrap_or(SmallVector::zero()) } } +fn fold_expr(folder: &mut F, expr: P) -> P { + expr.map(|ast::Expr {id, span, node, attrs}| { + fold::noop_fold_expr(ast::Expr { + id: id, + node: match node { + ast::ExprKind::Match(m, arms) => { + ast::ExprKind::Match(m, arms.into_iter() + .filter_map(|a| folder.configure(a)) + .collect()) + } + _ => node + }, + span: span, + attrs: attrs, + }, folder) + }) +} + +fn is_cfg(attr: &ast::Attribute) -> bool { + attr.check_name("cfg") +} + pub trait CfgDiag { fn emit_error(&mut self, f: F) where F: FnMut(&Handler); fn flag_gated(&mut self, f: F) where F: FnMut(&mut Vec); @@ -477,41 +287,3 @@ impl<'a, 'b> CfgDiag for CfgDiagReal<'a, 'b> { f(self.feature_gated_cfgs) } } - -struct CfgDiagSilent { - error: bool, -} - -impl CfgDiag for CfgDiagSilent { - fn emit_error(&mut self, _: F) where F: FnMut(&Handler) { - self.error = true; - } - fn flag_gated(&mut self, _: F) where F: FnMut(&mut Vec) {} -} - -fn node_survives_cfg(attrs: &[ast::Attribute], - config: &ast::CrateConfig) -> bool { - let mut survives_cfg = true; - - for attr in attrs { - let mut fld = CfgAttrFolder { - diag: CfgDiagSilent { error: false }, - config: config, - }; - let attr = fld.fold_attribute(attr.clone()); - - // In case of error we can just return true, - // since the actual cfg folders will end compilation anyway. - - if fld.diag.error { return true; } - - survives_cfg &= attr.map(|attr| { - let mut diag = CfgDiagSilent { error: false }; - let r = in_cfg(config, &[attr], &mut diag); - if diag.error { return true; } - r - }).unwrap_or(true) - } - - survives_cfg -} diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 596faac35882a..6cd5da9bc4963 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -18,7 +18,8 @@ use ext::build::AstBuilder; use attr; use attr::{AttrMetaMethods, WithAttrs, ThinAttributesExt}; use codemap; -use codemap::{Span, Spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute}; +use codemap::{Span, Spanned, ExpnInfo, ExpnId, NameAndSpan, MacroBang, MacroAttribute}; +use config::StripUnconfigured; use ext::base::*; use feature_gate::{self, Features}; use fold; @@ -33,7 +34,6 @@ use visit::Visitor; use std_inject; use std::collections::HashSet; -use std::env; // A trait for AST nodes and AST node lists into which macro invocations may expand. trait MacroGenerable: Sized { @@ -77,25 +77,35 @@ impl_macro_generable! { "statement", .make_stmts, lift .fold_stmt, |_span| SmallVector::zero(); } -pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { - return e.and_then(|ast::Expr {id, node, span, attrs}| match node { +impl MacroGenerable for Option> { + fn kind_name() -> &'static str { "expression" } + fn dummy(_span: Span) -> Self { None } + fn make_with<'a>(result: Box) -> Option { + result.make_expr().map(Some) + } + fn fold_with(self, folder: &mut F) -> Self { + self.and_then(|expr| folder.fold_opt_expr(expr)) + } +} +pub fn expand_expr(expr: ast::Expr, fld: &mut MacroExpander) -> P { + match expr.node { // expr_mac should really be expr_ext or something; it's the // entry-point for all syntax extensions. ast::ExprKind::Mac(mac) => { - expand_mac_invoc(mac, None, attrs.into_attr_vec(), span, fld) + expand_mac_invoc(mac, None, expr.attrs.into_attr_vec(), expr.span, fld) } ast::ExprKind::While(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::ExprKind::While(cond, body, opt_ident)) - .with_attrs(fold_thin_attrs(attrs, fld)) + fld.cx.expr(expr.span, ast::ExprKind::While(cond, body, opt_ident)) + .with_attrs(fold_thin_attrs(expr.attrs, fld)) } - ast::ExprKind::WhileLet(pat, expr, body, opt_ident) => { + ast::ExprKind::WhileLet(pat, cond, body, opt_ident) => { let pat = fld.fold_pat(pat); - let expr = fld.fold_expr(expr); + let cond = fld.fold_expr(cond); // Hygienic renaming of the body. let ((body, opt_ident), mut rewritten_pats) = @@ -107,14 +117,14 @@ pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { }); assert!(rewritten_pats.len() == 1); - let wl = ast::ExprKind::WhileLet(rewritten_pats.remove(0), expr, body, opt_ident); - fld.cx.expr(span, wl).with_attrs(fold_thin_attrs(attrs, fld)) + let wl = ast::ExprKind::WhileLet(rewritten_pats.remove(0), cond, body, opt_ident); + fld.cx.expr(expr.span, wl).with_attrs(fold_thin_attrs(expr.attrs, fld)) } ast::ExprKind::Loop(loop_block, opt_ident) => { let (loop_block, opt_ident) = expand_loop_block(loop_block, opt_ident, fld); - fld.cx.expr(span, ast::ExprKind::Loop(loop_block, opt_ident)) - .with_attrs(fold_thin_attrs(attrs, fld)) + fld.cx.expr(expr.span, ast::ExprKind::Loop(loop_block, opt_ident)) + .with_attrs(fold_thin_attrs(expr.attrs, fld)) } ast::ExprKind::ForLoop(pat, head, body, opt_ident) => { @@ -132,7 +142,7 @@ pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { let head = fld.fold_expr(head); let fl = ast::ExprKind::ForLoop(rewritten_pats.remove(0), head, body, opt_ident); - fld.cx.expr(span, fl).with_attrs(fold_thin_attrs(attrs, fld)) + fld.cx.expr(expr.span, fl).with_attrs(fold_thin_attrs(expr.attrs, fld)) } ast::ExprKind::IfLet(pat, sub_expr, body, else_opt) => { @@ -151,7 +161,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); let il = ast::ExprKind::IfLet(rewritten_pats.remove(0), sub_expr, body, else_opt); - fld.cx.expr(span, il).with_attrs(fold_thin_attrs(attrs, fld)) + fld.cx.expr(expr.span, il).with_attrs(fold_thin_attrs(expr.attrs, fld)) } ast::ExprKind::Closure(capture_clause, fn_decl, block, fn_decl_span) => { @@ -160,22 +170,15 @@ pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { let new_node = ast::ExprKind::Closure(capture_clause, rewritten_fn_decl, rewritten_block, - fld.new_span(fn_decl_span)); - P(ast::Expr{ id:id, + fn_decl_span); + P(ast::Expr{ id: expr.id, node: new_node, - span: fld.new_span(span), - attrs: fold_thin_attrs(attrs, fld) }) + span: expr.span, + attrs: fold_thin_attrs(expr.attrs, fld) }) } - _ => { - P(noop_fold_expr(ast::Expr { - id: id, - node: node, - span: span, - attrs: attrs - }, fld)) - } - }); + _ => P(noop_fold_expr(expr, fld)), + } } /// Expand a macro invocation. Returns the result of expansion. @@ -322,8 +325,9 @@ fn expand_mac_invoc(mac: ast::Mac, ident: Option, attrs: Vec Folder for PatIdentRenamer<'a> { mtwt::apply_renames(self.renames, ident.ctxt)); let new_node = PatKind::Ident(binding_mode, - Spanned{span: self.new_span(sp), node: new_ident}, + Spanned{span: sp, node: new_ident}, sub.map(|p| self.fold_pat(p))); ast::Pat { id: id, node: new_node, - span: self.new_span(span) + span: span, } }, _ => unreachable!() @@ -774,7 +778,7 @@ fn expand_annotatable(a: Annotatable, } _ => unreachable!() }, - span: fld.new_span(ti.span) + span: ti.span, }) } _ => fold::noop_fold_trait_item(it.unwrap(), fld) @@ -914,7 +918,7 @@ fn expand_impl_item(ii: ast::ImplItem, fld: &mut MacroExpander) } _ => unreachable!() }, - span: fld.new_span(ii.span) + span: ii.span, }), ast::ImplItemKind::Macro(mac) => { expand_mac_invoc(mac, None, ii.attrs, ii.span, fld) @@ -988,6 +992,12 @@ impl<'a, 'b> MacroExpander<'a, 'b> { pub fn new(cx: &'a mut ExtCtxt<'b>) -> MacroExpander<'a, 'b> { MacroExpander { cx: cx } } + + fn strip_unconfigured(&mut self) -> StripUnconfigured { + StripUnconfigured::new(&self.cx.cfg, + &self.cx.parse_sess.span_diagnostic, + self.cx.feature_gated_cfgs) + } } impl<'a, 'b> Folder for MacroExpander<'a, 'b> { @@ -997,7 +1007,15 @@ impl<'a, 'b> Folder for MacroExpander<'a, 'b> { } fn fold_expr(&mut self, expr: P) -> P { - expand_expr(expr, self) + expr.and_then(|expr| expand_expr(expr, self)) + } + + fn fold_opt_expr(&mut self, expr: P) -> Option> { + expr.and_then(|expr| match expr.node { + ast::ExprKind::Mac(mac) => + expand_mac_invoc(mac, None, expr.attrs.into_attr_vec(), expr.span, self), + _ => Some(expand_expr(expr, self)), + }) } fn fold_pat(&mut self, pat: P) -> P { @@ -1060,10 +1078,6 @@ impl<'a, 'b> Folder for MacroExpander<'a, 'b> { fn fold_ty(&mut self, ty: P) -> P { expand_type(ty, self) } - - fn new_span(&mut self, span: Span) -> Span { - new_span(self.cx, span) - } } impl<'a, 'b> MacroExpander<'a, 'b> { @@ -1081,45 +1095,6 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } } -fn new_span(cx: &ExtCtxt, sp: Span) -> Span { - debug!("new_span(sp={:?})", sp); - - if cx.codemap().more_specific_trace(sp.expn_id, cx.backtrace()) { - // If the span we are looking at has a backtrace that has more - // detail than our current backtrace, then we keep that - // backtrace. Honestly, I have no idea if this makes sense, - // because I have no idea why we are stripping the backtrace - // below. But the reason I made this change is because, in - // deriving, we were generating attributes with a specific - // backtrace, which was essential for `#[structural_match]` to - // be properly supported, but these backtraces were being - // stripped and replaced with a null backtrace. Sort of - // unclear why this is the case. --nmatsakis - debug!("new_span: keeping trace from {:?} because it is more specific", - sp.expn_id); - sp - } else { - // This discards information in the case of macro-defining macros. - // - // The comment above was originally added in - // b7ec2488ff2f29681fe28691d20fd2c260a9e454 in Feb 2012. I - // *THINK* the reason we are doing this is because we want to - // replace the backtrace of the macro contents with the - // backtrace that contains the macro use. But it's pretty - // unclear to me. --nmatsakis - let sp1 = Span { - lo: sp.lo, - hi: sp.hi, - expn_id: cx.backtrace(), - }; - debug!("new_span({:?}) = {:?}", sp, sp1); - if sp.expn_id.into_u32() == 0 && env::var_os("NDM").is_some() { - panic!("NDM"); - } - sp1 - } -} - pub struct ExpansionConfig<'feat> { pub crate_name: String, pub features: Option<&'feat Features>, @@ -1206,8 +1181,9 @@ pub fn expand_crate(mut cx: ExtCtxt, // the ones defined here include: // Marker - add a mark to a context -// A Marker adds the given mark to the syntax context -struct Marker { mark: Mrk } +// A Marker adds the given mark to the syntax context and +// sets spans' `expn_id` to the given expn_id (unless it is `None`). +struct Marker { mark: Mrk, expn_id: Option } impl Folder for Marker { fn fold_ident(&mut self, id: Ident) -> Ident { @@ -1220,14 +1196,21 @@ impl Folder for Marker { tts: self.fold_tts(&node.tts), ctxt: mtwt::apply_mark(self.mark, node.ctxt), }, - span: span, + span: self.new_span(span), + } + } + + fn new_span(&mut self, mut span: Span) -> Span { + if let Some(expn_id) = self.expn_id { + span.expn_id = expn_id; } + span } } // apply a given mark to the given token trees. Used prior to expansion of a macro. fn mark_tts(tts: &[TokenTree], m: Mrk) -> Vec { - noop_fold_tts(tts, &mut Marker{mark:m}) + noop_fold_tts(tts, &mut Marker{mark:m, expn_id: None}) } /// Check that there are no macro invocations left in the AST: diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs index 8eeb61e0de46c..45f349eff31ea 100644 --- a/src/libsyntax/test.rs +++ b/src/libsyntax/test.rs @@ -87,7 +87,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(span_diagnostic, krate) + strip_test_functions(krate) } } @@ -312,14 +312,17 @@ fn generate_test_harness(sess: &ParseSess, return res; } -fn strip_test_functions(diagnostic: &errors::Handler, krate: ast::Crate) - -> ast::Crate { +fn strip_test_functions(krate: ast::Crate) -> ast::Crate { // When not compiling with --test we should not compile the // #[test] functions - config::strip_items(diagnostic, krate, |attrs| { - !attr::contains_name(&attrs[..], "test") && - !attr::contains_name(&attrs[..], "bench") - }) + struct StripTests; + impl config::CfgFolder for StripTests { + fn in_cfg(&mut self, attrs: &[ast::Attribute]) -> bool { + !attr::contains_name(attrs, "test") && !attr::contains_name(attrs, "bench") + } + } + + StripTests.fold_crate(krate) } /// Craft a span that will be ignored by the stability lint's diff --git a/src/test/compile-fail/expanded-cfg.rs b/src/test/compile-fail/expanded-cfg.rs index 2f74aeba9eb43..77351f6e4f171 100644 --- a/src/test/compile-fail/expanded-cfg.rs +++ b/src/test/compile-fail/expanded-cfg.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(rustc_attrs)] +#![feature(custom_attribute, rustc_attrs)] macro_rules! mac { {} => { @@ -16,7 +16,13 @@ macro_rules! mac { mod m { #[lang_item] fn f() {} + + #[cfg_attr(target_thread_local, custom)] + fn g() {} } + + #[cfg(attr)] + unconfigured_invocation!(); } }