From 29c105964bc303448611b6329ecb36a7470b796b Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Wed, 18 May 2016 07:25:44 +0000 Subject: [PATCH 01/13] Add and use `HasAttrs` trait --- src/libsyntax/ast.rs | 25 ++------ src/libsyntax/attr.rs | 141 +++++++++++++++++++++++++----------------- 2 files changed, 89 insertions(+), 77 deletions(-) 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 } From 7a42e46eec5590314b64bb82027a22d66545e627 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Sun, 15 May 2016 02:34:32 +0000 Subject: [PATCH 02/13] Refactor the `syntax::config::fold_*` functions into methods --- src/libsyntax/config.rs | 228 ++++++++++++++-------------------------- 1 file changed, 81 insertions(+), 147 deletions(-) diff --git a/src/libsyntax/config.rs b/src/libsyntax/config.rs index 4554a280e5f19..0491a8c549427 100644 --- a/src/libsyntax/config.rs +++ b/src/libsyntax/config.rs @@ -50,11 +50,71 @@ pub fn strip_unconfigured_items(diagnostic: &Handler, krate: ast::Crate, 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) + ast::ForeignMod { + abi: foreign_mod.abi, + items: foreign_mod.items.into_iter().filter(|item| { + (self.in_cfg)(&item.attrs) + }).collect(), + } } + fn fold_item_kind(&mut self, item: ast::ItemKind) -> ast::ItemKind { - fold_item_kind(self, item) + let fold_struct = |this: &mut Self, vdata| match vdata { + ast::VariantData::Struct(fields, id) => { + ast::VariantData::Struct(fields.into_iter().filter(|m| { + (this.in_cfg)(&m.attrs) + }).collect(), id) + } + ast::VariantData::Tuple(fields, id) => { + ast::VariantData::Tuple(fields.into_iter().filter(|m| { + (this.in_cfg)(&m.attrs) + }).collect(), id) + } + ast::VariantData::Unit(id) => ast::VariantData::Unit(id) + }; + + let item = match item { + ast::ItemKind::Impl(u, o, a, b, c, impl_items) => { + let impl_items = impl_items.into_iter() + .filter(|ii| (self.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| (self.in_cfg)(&ti.attrs)) + .collect(); + ast::ItemKind::Trait(u, a, b, methods) + } + 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| { + if !(self.in_cfg)(&v.node.attrs) { + None + } else { + Some(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 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. @@ -69,17 +129,33 @@ impl<'a, F> fold::Folder for Context<'a, F> where F: FnMut(&[ast::Attribute]) -> } fold_expr(self, expr) } + fn fold_opt_expr(&mut self, expr: P) -> Option> { - fold_opt_expr(self, expr) + if (self.in_cfg)(expr.attrs()) { + Some(fold_expr(self, expr)) + } else { + None + } } + fn fold_stmt(&mut self, stmt: ast::Stmt) -> SmallVector { - fold_stmt(self, stmt) + if (self.in_cfg)(stmt.node.attrs()) { + fold::noop_fold_stmt(stmt, self) + } else { + SmallVector::zero() + } } + 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) + if (self.in_cfg)(&item.attrs) { + SmallVector::one(item.map(|i| self.fold_item_simple(i))) + } else { + SmallVector::zero() + } } } @@ -94,114 +170,6 @@ pub fn strip_items<'a, F>(diagnostic: &'a Handler, ctxt.fold_crate(krate) } -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 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) -} - -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) - } - 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 { @@ -222,40 +190,6 @@ fn fold_expr(cx: &mut Context, expr: P) -> P where }) } -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") } From 79854395caef3a5cda5ff6b0015d49c303eec93e Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Sun, 15 May 2016 09:15:02 +0000 Subject: [PATCH 03/13] Introduce `CfgFolder` trait --- src/libsyntax/config.rs | 51 ++++++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/src/libsyntax/config.rs b/src/libsyntax/config.rs index 0491a8c549427..54f3feee96ca7 100644 --- a/src/libsyntax/config.rs +++ b/src/libsyntax/config.rs @@ -19,6 +19,11 @@ use ptr::P; use util::small_vector::SmallVector; +pub trait CfgFolder: fold::Folder { + fn in_cfg(&mut self, attrs: &[ast::Attribute]) -> bool; + fn visit_unconfigurable_expr(&mut self, _expr: &ast::Expr) {} +} + /// A folder that strips out items that do not belong in the current /// configuration. struct Context<'a, F> where F: FnMut(&[ast::Attribute]) -> bool { @@ -26,6 +31,19 @@ struct Context<'a, F> where F: FnMut(&[ast::Attribute]) -> bool { diagnostic: &'a Handler, } +impl<'a, F: FnMut(&[ast::Attribute]) -> bool> CfgFolder for Context<'a, F> { + fn in_cfg(&mut self, attrs: &[ast::Attribute]) -> bool { + (self.in_cfg)(attrs) + } + + fn visit_unconfigurable_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.diagnostic.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, @@ -48,12 +66,12 @@ pub fn strip_unconfigured_items(diagnostic: &Handler, krate: ast::Crate, }) } -impl<'a, F> fold::Folder for Context<'a, F> where F: FnMut(&[ast::Attribute]) -> bool { +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(|item| { - (self.in_cfg)(&item.attrs) + self.in_cfg(&item.attrs) }).collect(), } } @@ -62,12 +80,12 @@ impl<'a, F> fold::Folder for Context<'a, F> where F: FnMut(&[ast::Attribute]) -> let fold_struct = |this: &mut Self, vdata| match vdata { ast::VariantData::Struct(fields, id) => { ast::VariantData::Struct(fields.into_iter().filter(|m| { - (this.in_cfg)(&m.attrs) + this.in_cfg(&m.attrs) }).collect(), id) } ast::VariantData::Tuple(fields, id) => { ast::VariantData::Tuple(fields.into_iter().filter(|m| { - (this.in_cfg)(&m.attrs) + this.in_cfg(&m.attrs) }).collect(), id) } ast::VariantData::Unit(id) => ast::VariantData::Unit(id) @@ -76,13 +94,13 @@ impl<'a, F> fold::Folder for Context<'a, F> where F: FnMut(&[ast::Attribute]) -> let item = match item { ast::ItemKind::Impl(u, o, a, b, c, impl_items) => { let impl_items = impl_items.into_iter() - .filter(|ii| (self.in_cfg)(&ii.attrs)) + .filter(|ii| self.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| (self.in_cfg)(&ti.attrs)) + .filter(|ti| self.in_cfg(&ti.attrs)) .collect(); ast::ItemKind::Trait(u, a, b, methods) } @@ -91,7 +109,7 @@ impl<'a, F> fold::Folder for Context<'a, F> where F: FnMut(&[ast::Attribute]) -> } ast::ItemKind::Enum(def, generics) => { let variants = def.variants.into_iter().filter_map(|v| { - if !(self.in_cfg)(&v.node.attrs) { + if !self.in_cfg(&v.node.attrs) { None } else { Some(Spanned { @@ -123,15 +141,12 @@ impl<'a, F> fold::Folder for Context<'a, F> where F: FnMut(&[ast::Attribute]) -> // // 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"); - } + self.visit_unconfigurable_expr(&expr); fold_expr(self, expr) } fn fold_opt_expr(&mut self, expr: P) -> Option> { - if (self.in_cfg)(expr.attrs()) { + if self.in_cfg(expr.attrs()) { Some(fold_expr(self, expr)) } else { None @@ -139,7 +154,7 @@ impl<'a, F> fold::Folder for Context<'a, F> where F: FnMut(&[ast::Attribute]) -> } fn fold_stmt(&mut self, stmt: ast::Stmt) -> SmallVector { - if (self.in_cfg)(stmt.node.attrs()) { + if self.in_cfg(stmt.node.attrs()) { fold::noop_fold_stmt(stmt, self) } else { SmallVector::zero() @@ -151,7 +166,7 @@ impl<'a, F> fold::Folder for Context<'a, F> where F: FnMut(&[ast::Attribute]) -> } fn fold_item(&mut self, item: P) -> SmallVector> { - if (self.in_cfg)(&item.attrs) { + if self.in_cfg(&item.attrs) { SmallVector::one(item.map(|i| self.fold_item_simple(i))) } else { SmallVector::zero() @@ -170,23 +185,21 @@ pub fn strip_items<'a, F>(diagnostic: &'a Handler, ctxt.fold_crate(krate) } -fn fold_expr(cx: &mut Context, expr: P) -> P where - F: FnMut(&[ast::Attribute]) -> bool -{ +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(|a| (cx.in_cfg)(&a.attrs)) + .filter(|a| folder.in_cfg(&a.attrs)) .collect()) } _ => node }, span: span, attrs: attrs, - }, cx) + }, folder) }) } From f3e80760e9e7ceaa40661e07e0c89e1739f68d9a Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Mon, 16 May 2016 02:42:24 +0000 Subject: [PATCH 04/13] Refactor `CfgFolder::in_cfg` -> `CfgFolder::configure` --- src/libsyntax/config.rs | 74 ++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 45 deletions(-) diff --git a/src/libsyntax/config.rs b/src/libsyntax/config.rs index 54f3feee96ca7..a22e08d427571 100644 --- a/src/libsyntax/config.rs +++ b/src/libsyntax/config.rs @@ -8,7 +8,7 @@ // 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; @@ -20,7 +20,7 @@ use ptr::P; use util::small_vector::SmallVector; pub trait CfgFolder: fold::Folder { - fn in_cfg(&mut self, attrs: &[ast::Attribute]) -> bool; + fn configure(&mut self, node: T) -> Option; fn visit_unconfigurable_expr(&mut self, _expr: &ast::Expr) {} } @@ -32,8 +32,12 @@ struct Context<'a, F> where F: FnMut(&[ast::Attribute]) -> bool { } impl<'a, F: FnMut(&[ast::Attribute]) -> bool> CfgFolder for Context<'a, F> { - fn in_cfg(&mut self, attrs: &[ast::Attribute]) -> bool { - (self.in_cfg)(attrs) + fn configure(&mut self, node: T) -> Option { + if (self.in_cfg)(node.attrs()) { + Some(node) + } else { + None + } } fn visit_unconfigurable_expr(&mut self, expr: &ast::Expr) { @@ -70,49 +74,39 @@ 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(|item| { - self.in_cfg(&item.attrs) - }).collect(), + items: foreign_mod.items.into_iter().filter_map(|item| self.configure(item)).collect(), } } 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) => { - ast::VariantData::Struct(fields.into_iter().filter(|m| { - this.in_cfg(&m.attrs) - }).collect(), id) + let fields = fields.into_iter().filter_map(|field| this.configure(field)); + ast::VariantData::Struct(fields.collect(), id) } ast::VariantData::Tuple(fields, id) => { - ast::VariantData::Tuple(fields.into_iter().filter(|m| { - this.in_cfg(&m.attrs) - }).collect(), 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, impl_items) => { - let impl_items = impl_items.into_iter() - .filter(|ii| self.in_cfg(&ii.attrs)) - .collect(); - ast::ItemKind::Impl(u, o, a, b, c, impl_items) + 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, methods) => { - let methods = methods.into_iter() - .filter(|ti| self.in_cfg(&ti.attrs)) - .collect(); - ast::ItemKind::Trait(u, a, b, methods) + 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| { - if !self.in_cfg(&v.node.attrs) { - None - } else { - Some(Spanned { + self.configure(v).map(|v| { + Spanned { node: ast::Variant_ { name: v.node.name, attrs: v.node.attrs, @@ -120,8 +114,8 @@ impl fold::Folder for T { disr_expr: v.node.disr_expr, }, span: v.span - }) - } + } + }) }); ast::ItemKind::Enum(ast::EnumDef { variants: variants.collect(), @@ -146,19 +140,12 @@ impl fold::Folder for T { } fn fold_opt_expr(&mut self, expr: P) -> Option> { - if self.in_cfg(expr.attrs()) { - Some(fold_expr(self, expr)) - } else { - None - } + self.configure(expr).map(|expr| fold_expr(self, expr)) } fn fold_stmt(&mut self, stmt: ast::Stmt) -> SmallVector { - if self.in_cfg(stmt.node.attrs()) { - fold::noop_fold_stmt(stmt, self) - } else { - SmallVector::zero() - } + self.configure(stmt).map(|stmt| fold::noop_fold_stmt(stmt, self)) + .unwrap_or(SmallVector::zero()) } fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac { @@ -166,11 +153,8 @@ impl fold::Folder for T { } fn fold_item(&mut self, item: P) -> SmallVector> { - if self.in_cfg(&item.attrs) { - SmallVector::one(item.map(|i| self.fold_item_simple(i))) - } else { - SmallVector::zero() - } + self.configure(item).map(|item| SmallVector::one(item.map(|i| self.fold_item_simple(i)))) + .unwrap_or(SmallVector::zero()) } } @@ -192,7 +176,7 @@ fn fold_expr(folder: &mut F, expr: P) -> P { node: match node { ast::ExprKind::Match(m, arms) => { ast::ExprKind::Match(m, arms.into_iter() - .filter(|a| folder.in_cfg(&a.attrs)) + .filter_map(|a| folder.configure(a)) .collect()) } _ => node From a306f85df9f75c35c88395be756dc09c73cab891 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Sun, 15 May 2016 09:22:58 +0000 Subject: [PATCH 05/13] Implement `CfgFolder` directly instead of passing a closure to `strip_items` --- src/libsyntax/config.rs | 41 ++++++++++++++--------------------------- src/libsyntax/test.rs | 22 +++++++++++++++------- 2 files changed, 29 insertions(+), 34 deletions(-) diff --git a/src/libsyntax/config.rs b/src/libsyntax/config.rs index a22e08d427571..1948c3afdbd05 100644 --- a/src/libsyntax/config.rs +++ b/src/libsyntax/config.rs @@ -26,14 +26,14 @@ pub trait CfgFolder: fold::Folder { /// 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 struct StripUnconfigured<'a> { + diag: CfgDiagReal<'a, 'a>, + config: &'a ast::CrateConfig, } -impl<'a, F: FnMut(&[ast::Attribute]) -> bool> CfgFolder for Context<'a, F> { +impl<'a> CfgFolder for StripUnconfigured<'a> { fn configure(&mut self, node: T) -> Option { - if (self.in_cfg)(node.attrs()) { + if in_cfg(self.config, node.attrs(), &mut self.diag) { Some(node) } else { None @@ -43,7 +43,7 @@ impl<'a, F: FnMut(&[ast::Attribute]) -> bool> CfgFolder for Context<'a, F> { fn visit_unconfigurable_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.diagnostic.span_err(attr.span, msg); + self.diag.diag.span_err(attr.span, msg); } } } @@ -58,16 +58,14 @@ pub fn strip_unconfigured_items(diagnostic: &Handler, krate: ast::Crate, 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) - }) + + StripUnconfigured { + config: &krate.config.clone(), + diag: CfgDiagReal { + diag: diagnostic, + feature_gated_cfgs: feature_gated_cfgs, + }, + }.fold_crate(krate) } impl fold::Folder for T { @@ -158,17 +156,6 @@ impl fold::Folder for T { } } -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) -} - fn fold_expr(folder: &mut F, expr: P) -> P { expr.map(|ast::Expr {id, span, node, attrs}| { fold::noop_fold_expr(ast::Expr { diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs index 8eeb61e0de46c..84c7250fac6e5 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,22 @@ 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 configure(&mut self, node: T) -> Option { + let strip_node = { + let attrs = node.attrs(); + attr::contains_name(attrs, "test") || attr::contains_name(attrs, "bench") + }; + + if strip_node { None } else { Some(node) } + } + } + + StripTests.fold_crate(krate) } /// Craft a span that will be ignored by the stability lint's From d3a0e1783c8e3ef336a684908fb08a0e8c0364d8 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Mon, 16 May 2016 04:42:57 +0000 Subject: [PATCH 06/13] Move cfg_attr processing and stmt/expr attribute gated feature checking into `StripUnconfigured` --- src/libsyntax/config.rs | 334 +++++++++++----------------------------- 1 file changed, 86 insertions(+), 248 deletions(-) diff --git a/src/libsyntax/config.rs b/src/libsyntax/config.rs index 1948c3afdbd05..62c922a4dbfdf 100644 --- a/src/libsyntax/config.rs +++ b/src/libsyntax/config.rs @@ -13,7 +13,6 @@ use errors::Handler; use feature_gate::GatedCfgAttr; use fold::Folder; use {ast, fold, attr}; -use visit; use codemap::{Spanned, respan}; use ptr::P; @@ -21,6 +20,7 @@ use util::small_vector::SmallVector; pub trait CfgFolder: fold::Folder { fn configure(&mut self, node: T) -> Option; + fn visit_stmt_or_expr_attrs(&mut self, _attrs: &[ast::Attribute]) {} fn visit_unconfigurable_expr(&mut self, _expr: &ast::Expr) {} } @@ -31,14 +31,78 @@ pub struct StripUnconfigured<'a> { config: &'a ast::CrateConfig, } -impl<'a> CfgFolder for StripUnconfigured<'a> { - fn configure(&mut self, node: T) -> Option { - if in_cfg(self.config, node.attrs(), &mut self.diag) { - Some(node) +impl<'a> 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; + } + + attr::cfg_matches(self.config, &mis[0], &mut self.diag) + }) + } + + fn process_cfg_attrs(&mut self, attrs: Vec) -> Vec { + attrs.into_iter().filter_map(|attr| self.process_cfg_attr(attr)).collect() + } + + fn process_cfg_attr(&mut self, attr: ast::Attribute) -> Option { + if !attr.check_name("cfg_attr") { + return Some(attr); + } + + let attr_list = match attr.meta_item_list() { + Some(attr_list) => attr_list, + None => { + 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), + _ => { + 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) { + Some(respan(mi.span, ast::Attribute_ { + id: attr::mk_attr_id(), + style: attr.node.style, + value: mi.clone(), + is_sugared_doc: false, + })) } else { None } } +} + +impl<'a> CfgFolder for StripUnconfigured<'a> { + fn configure(&mut self, node: T) -> Option { + let node = node.map_attrs(|attrs| self.process_cfg_attrs(attrs)); + if self.in_cfg(node.attrs()) { Some(node) } else { None } + } + + 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_unconfigurable_expr(&mut self, expr: &ast::Expr) { if let Some(attr) = expr.attrs().iter().find(|a| is_cfg(a)) { @@ -54,11 +118,6 @@ 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); - StripUnconfigured { config: &krate.config.clone(), diag: CfgDiagReal { @@ -72,7 +131,9 @@ 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)).collect(), + items: foreign_mod.items.into_iter().filter_map(|item| { + self.configure(item).map(|item| fold::noop_fold_foreign_item(item, self)) + }).collect(), } } @@ -126,6 +187,7 @@ impl fold::Folder for T { } 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 @@ -142,6 +204,19 @@ impl fold::Folder for T { } 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()) } @@ -178,205 +253,6 @@ 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 { - if !attr.check_name("cfg_attr") { - return fold::noop_fold_attribute(attr, self); - } - - 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(, )]`"); - }); - 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(, )]`"); - }); - return None; - } - }; - - 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, - value: mi.clone(), - is_sugared_doc: false, - })) - } else { - 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)); - } - - // 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.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(&Handler); fn flag_gated(&mut self, f: F) where F: FnMut(&mut Vec); @@ -395,41 +271,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 -} From 15d5074a34976ad562acab0bc9c4a10754525b0a Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Thu, 26 May 2016 23:56:25 +0000 Subject: [PATCH 07/13] Process `cfg_attr` attributes on non-optional expressions --- src/libsyntax/config.rs | 66 ++++++++++++++++++++++------------------- src/libsyntax/test.rs | 9 ++---- 2 files changed, 37 insertions(+), 38 deletions(-) diff --git a/src/libsyntax/config.rs b/src/libsyntax/config.rs index 62c922a4dbfdf..e1c17ca43d3ab 100644 --- a/src/libsyntax/config.rs +++ b/src/libsyntax/config.rs @@ -19,9 +19,15 @@ use ptr::P; use util::small_vector::SmallVector; pub trait CfgFolder: fold::Folder { - fn configure(&mut self, node: T) -> Option; + fn in_cfg(&mut self, attrs: &[ast::Attribute]) -> bool; + fn process_attrs(&mut self, node: T) -> T { node } fn visit_stmt_or_expr_attrs(&mut self, _attrs: &[ast::Attribute]) {} - fn visit_unconfigurable_expr(&mut self, _expr: &ast::Expr) {} + fn visit_unremovable_expr(&mut self, _expr: &ast::Expr) {} + + fn configure(&mut self, node: T) -> Option { + let node = self.process_attrs(node); + if self.in_cfg(node.attrs()) { Some(node) } else { None } + } } /// A folder that strips out items that do not belong in the current @@ -32,30 +38,6 @@ pub struct StripUnconfigured<'a> { } impl<'a> 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; - } - - attr::cfg_matches(self.config, &mis[0], &mut self.diag) - }) - } - - fn process_cfg_attrs(&mut self, attrs: Vec) -> Vec { - attrs.into_iter().filter_map(|attr| self.process_cfg_attr(attr)).collect() - } - fn process_cfg_attr(&mut self, attr: ast::Attribute) -> Option { if !attr.check_name("cfg_attr") { return Some(attr); @@ -92,9 +74,30 @@ impl<'a> StripUnconfigured<'a> { } impl<'a> CfgFolder for StripUnconfigured<'a> { - fn configure(&mut self, node: T) -> Option { - let node = node.map_attrs(|attrs| self.process_cfg_attrs(attrs)); - if self.in_cfg(node.attrs()) { Some(node) } else { None } + // 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; + } + + attr::cfg_matches(self.config, &mis[0], &mut self.diag) + }) + } + + fn process_attrs(&mut self, node: T) -> T { + node.map_attrs(|attrs| { + attrs.into_iter().filter_map(|attr| self.process_cfg_attr(attr)).collect() + }) } fn visit_stmt_or_expr_attrs(&mut self, attrs: &[ast::Attribute]) { @@ -104,7 +107,7 @@ impl<'a> CfgFolder for StripUnconfigured<'a> { } } - fn visit_unconfigurable_expr(&mut self, expr: &ast::Expr) { + 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); @@ -195,7 +198,8 @@ impl fold::Folder for T { // // 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_unconfigurable_expr(&expr); + self.visit_unremovable_expr(&expr); + let expr = self.process_attrs(expr); fold_expr(self, expr) } diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs index 84c7250fac6e5..45f349eff31ea 100644 --- a/src/libsyntax/test.rs +++ b/src/libsyntax/test.rs @@ -317,13 +317,8 @@ fn strip_test_functions(krate: ast::Crate) -> ast::Crate { // #[test] functions struct StripTests; impl config::CfgFolder for StripTests { - fn configure(&mut self, node: T) -> Option { - let strip_node = { - let attrs = node.attrs(); - attr::contains_name(attrs, "test") || attr::contains_name(attrs, "bench") - }; - - if strip_node { None } else { Some(node) } + fn in_cfg(&mut self, attrs: &[ast::Attribute]) -> bool { + !attr::contains_name(attrs, "test") && !attr::contains_name(attrs, "bench") } } From 3636ce7875dc71d907789a779c1c1cb5973e021f Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Wed, 18 May 2016 01:50:29 +0000 Subject: [PATCH 08/13] Test that a feature gated cfg variable in a `cfg_attr` on an unconfigured item is allowed --- src/test/compile-fail/expanded-cfg.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/test/compile-fail/expanded-cfg.rs b/src/test/compile-fail/expanded-cfg.rs index 2f74aeba9eb43..a15e0548a8681 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,6 +16,9 @@ macro_rules! mac { mod m { #[lang_item] fn f() {} + + #[cfg_attr(target_thread_local, custom)] + fn g() {} } } } From 25c733360b1cddfc0b129680a9220d1d5097483d Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Tue, 17 May 2016 21:20:09 +0000 Subject: [PATCH 09/13] Update spans' `expn_id` during the marking fold --- src/libsyntax/codemap.rs | 25 ------------ src/libsyntax/ext/expand.rs | 76 ++++++++++--------------------------- 2 files changed, 20 insertions(+), 81 deletions(-) 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/ext/expand.rs b/src/libsyntax/ext/expand.rs index 596faac35882a..d66515ed7d68e 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -18,7 +18,7 @@ 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 ext::base::*; use feature_gate::{self, Features}; use fold; @@ -33,7 +33,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 { @@ -160,10 +159,10 @@ 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)); + fn_decl_span); P(ast::Expr{ id:id, node: new_node, - span: fld.new_span(span), + span: span, attrs: fold_thin_attrs(attrs, fld) }) } @@ -322,7 +321,7 @@ 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 +773,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 +913,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) @@ -1060,10 +1059,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 +1076,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 +1162,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 +1177,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: From 1aa34e0b5fbdfcd8b176f97c748ee9be0e3c8eeb Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Mon, 16 May 2016 10:09:23 +0000 Subject: [PATCH 10/13] Strip unconfigured items during macro expansion --- src/librustc_driver/driver.rs | 9 --------- src/libsyntax/config.rs | 19 ++++++++++++------- src/libsyntax/ext/expand.rs | 34 +++++++++++++++++++++++++++++++++- 3 files changed, 45 insertions(+), 17 deletions(-) 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/config.rs b/src/libsyntax/config.rs index e1c17ca43d3ab..77f20934d805b 100644 --- a/src/libsyntax/config.rs +++ b/src/libsyntax/config.rs @@ -38,6 +38,16 @@ pub struct StripUnconfigured<'a> { } 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 }, + } + } + fn process_cfg_attr(&mut self, attr: ast::Attribute) -> Option { if !attr.check_name("cfg_attr") { return Some(attr); @@ -121,13 +131,8 @@ pub fn strip_unconfigured_items(diagnostic: &Handler, krate: ast::Crate, feature_gated_cfgs: &mut Vec) -> ast::Crate { - StripUnconfigured { - config: &krate.config.clone(), - diag: CfgDiagReal { - diag: diagnostic, - feature_gated_cfgs: feature_gated_cfgs, - }, - }.fold_crate(krate) + let config = &krate.config.clone(); + StripUnconfigured::new(config, diagnostic, feature_gated_cfgs).fold_crate(krate) } impl fold::Folder for T { diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index d66515ed7d68e..95d03b5018daa 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -19,6 +19,7 @@ use attr; use attr::{AttrMetaMethods, WithAttrs, ThinAttributesExt}; use codemap; use codemap::{Span, Spanned, ExpnInfo, ExpnId, NameAndSpan, MacroBang, MacroAttribute}; +use config::StripUnconfigured; use ext::base::*; use feature_gate::{self, Features}; use fold; @@ -76,6 +77,17 @@ impl_macro_generable! { "statement", .make_stmts, lift .fold_stmt, |_span| SmallVector::zero(); } +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(e: P, fld: &mut MacroExpander) -> P { return e.and_then(|ast::Expr {id, node, span, attrs}| match node { @@ -322,7 +334,8 @@ fn expand_mac_invoc(mac: ast::Mac, ident: Option, attrs: Vec 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> { @@ -999,6 +1018,19 @@ impl<'a, 'b> Folder for MacroExpander<'a, 'b> { expand_expr(expr, self) } + fn fold_opt_expr(&mut self, expr: P) -> Option> { + match expr.node { + ast::ExprKind::Mac(_) => {} + _ => return Some(expand_expr(expr, self)), + } + + expr.and_then(|ast::Expr {node, span, attrs, ..}| match node { + ast::ExprKind::Mac(mac) => + expand_mac_invoc(mac, None, attrs.into_attr_vec(), span, self), + _ => unreachable!(), + }) + } + fn fold_pat(&mut self, pat: P) -> P { expand_pat(pat, self) } From 0558df24af9d99894a8305a57303824d8b0c15ba Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Thu, 26 May 2016 23:48:45 +0000 Subject: [PATCH 11/13] Refactor `expand_expr` --- src/libsyntax/ext/expand.rs | 57 ++++++++++++++----------------------- 1 file changed, 22 insertions(+), 35 deletions(-) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 95d03b5018daa..6cd5da9bc4963 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -88,25 +88,24 @@ impl MacroGenerable for Option> { } } -pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { - return e.and_then(|ast::Expr {id, node, span, attrs}| match node { - +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) = @@ -118,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) => { @@ -143,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) => { @@ -162,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) => { @@ -172,21 +171,14 @@ pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { rewritten_fn_decl, rewritten_block, fn_decl_span); - P(ast::Expr{ id:id, + P(ast::Expr{ id: expr.id, node: new_node, - 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. @@ -1015,19 +1007,14 @@ 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> { - match expr.node { - ast::ExprKind::Mac(_) => {} - _ => return Some(expand_expr(expr, self)), - } - - expr.and_then(|ast::Expr {node, span, attrs, ..}| match node { + expr.and_then(|expr| match expr.node { ast::ExprKind::Mac(mac) => - expand_mac_invoc(mac, None, attrs.into_attr_vec(), span, self), - _ => unreachable!(), + expand_mac_invoc(mac, None, expr.attrs.into_attr_vec(), expr.span, self), + _ => Some(expand_expr(expr, self)), }) } From 6b3edc2f8933a987ed956b4d720be1be9b02e036 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Wed, 18 May 2016 02:02:04 +0000 Subject: [PATCH 12/13] Test that unconfigured macro-expanded macro invocations are not expanded. --- src/test/compile-fail/expanded-cfg.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/test/compile-fail/expanded-cfg.rs b/src/test/compile-fail/expanded-cfg.rs index a15e0548a8681..77351f6e4f171 100644 --- a/src/test/compile-fail/expanded-cfg.rs +++ b/src/test/compile-fail/expanded-cfg.rs @@ -20,6 +20,9 @@ macro_rules! mac { #[cfg_attr(target_thread_local, custom)] fn g() {} } + + #[cfg(attr)] + unconfigured_invocation!(); } } From 53ab1378419d48a96e3ae923462f4ba8b921ad53 Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Tue, 24 May 2016 05:15:07 +0000 Subject: [PATCH 13/13] Comment methods in `CfgFolder` --- src/libsyntax/config.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/libsyntax/config.rs b/src/libsyntax/config.rs index 77f20934d805b..14035d8d116a3 100644 --- a/src/libsyntax/config.rs +++ b/src/libsyntax/config.rs @@ -19,9 +19,16 @@ use ptr::P; use util::small_vector::SmallVector; 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; + + // 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 } + + // Visit attributes on expression and statements (but not attributes on items in blocks). fn visit_stmt_or_expr_attrs(&mut self, _attrs: &[ast::Attribute]) {} + + // Visit unremovable (non-optional) expressions -- c.f. `fold_expr` vs `fold_opt_expr`. fn visit_unremovable_expr(&mut self, _expr: &ast::Expr) {} fn configure(&mut self, node: T) -> Option {