diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index de44a2031ab82..99196210e004b 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -915,16 +915,6 @@ impl Stmt { } } - pub fn tokens_mut(&mut self) -> Option<&mut LazyTokenStream> { - match self.kind { - StmtKind::Local(ref mut local) => local.tokens.as_mut(), - StmtKind::Item(ref mut item) => item.tokens.as_mut(), - StmtKind::Expr(ref mut expr) | StmtKind::Semi(ref mut expr) => expr.tokens.as_mut(), - StmtKind::Empty => None, - StmtKind::MacCall(ref mut mac) => mac.tokens.as_mut(), - } - } - pub fn has_trailing_semicolon(&self) -> bool { match &self.kind { StmtKind::Semi(_) => true, diff --git a/compiler/rustc_ast/src/ast_like.rs b/compiler/rustc_ast/src/ast_like.rs index 6649cda69a0b0..a71f2ac981501 100644 --- a/compiler/rustc_ast/src/ast_like.rs +++ b/compiler/rustc_ast/src/ast_like.rs @@ -11,13 +11,7 @@ use super::{AttrVec, Attribute, Stmt, StmtKind}; pub trait AstLike: Sized { fn attrs(&self) -> &[Attribute]; fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec)); - /// Called by `Parser::collect_tokens` to store the collected - /// tokens inside an AST node - fn finalize_tokens(&mut self, _tokens: LazyTokenStream) { - // This default impl makes this trait easier to implement - // in tools like `rust-analyzer` - panic!("`finalize_tokens` is not supported!") - } + fn tokens_mut(&mut self) -> Option<&mut Option>; } impl AstLike for P { @@ -27,8 +21,8 @@ impl AstLike for P { fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec)) { (**self).visit_attrs(f); } - fn finalize_tokens(&mut self, tokens: LazyTokenStream) { - (**self).finalize_tokens(tokens) + fn tokens_mut(&mut self) -> Option<&mut Option> { + (**self).tokens_mut() } } @@ -42,12 +36,12 @@ fn visit_attrvec(attrs: &mut AttrVec, f: impl FnOnce(&mut Vec)) { impl AstLike for StmtKind { fn attrs(&self) -> &[Attribute] { - match *self { - StmtKind::Local(ref local) => local.attrs(), - StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => expr.attrs(), - StmtKind::Item(ref item) => item.attrs(), + match self { + StmtKind::Local(local) => local.attrs(), + StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr.attrs(), + StmtKind::Item(item) => item.attrs(), StmtKind::Empty => &[], - StmtKind::MacCall(ref mac) => &*mac.attrs, + StmtKind::MacCall(mac) => &mac.attrs, } } @@ -60,17 +54,14 @@ impl AstLike for StmtKind { StmtKind::MacCall(mac) => visit_attrvec(&mut mac.attrs, f), } } - fn finalize_tokens(&mut self, tokens: LazyTokenStream) { - let stmt_tokens = match self { - StmtKind::Local(ref mut local) => &mut local.tokens, - StmtKind::Item(ref mut item) => &mut item.tokens, - StmtKind::Expr(ref mut expr) | StmtKind::Semi(ref mut expr) => &mut expr.tokens, - StmtKind::Empty => return, - StmtKind::MacCall(ref mut mac) => &mut mac.tokens, - }; - if stmt_tokens.is_none() { - *stmt_tokens = Some(tokens); - } + fn tokens_mut(&mut self) -> Option<&mut Option> { + Some(match self { + StmtKind::Local(local) => &mut local.tokens, + StmtKind::Item(item) => &mut item.tokens, + StmtKind::Expr(expr) | StmtKind::Semi(expr) => &mut expr.tokens, + StmtKind::Empty => return None, + StmtKind::MacCall(mac) => &mut mac.tokens, + }) } } @@ -82,8 +73,8 @@ impl AstLike for Stmt { fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec)) { self.kind.visit_attrs(f); } - fn finalize_tokens(&mut self, tokens: LazyTokenStream) { - self.kind.finalize_tokens(tokens) + fn tokens_mut(&mut self) -> Option<&mut Option> { + self.kind.tokens_mut() } } @@ -92,17 +83,13 @@ impl AstLike for Attribute { &[] } fn visit_attrs(&mut self, _f: impl FnOnce(&mut Vec)) {} - fn finalize_tokens(&mut self, tokens: LazyTokenStream) { - match &mut self.kind { - AttrKind::Normal(_, attr_tokens) => { - if attr_tokens.is_none() { - *attr_tokens = Some(tokens); - } + fn tokens_mut(&mut self) -> Option<&mut Option> { + Some(match &mut self.kind { + AttrKind::Normal(_, tokens) => tokens, + kind @ AttrKind::DocComment(..) => { + panic!("Called tokens_mut on doc comment attr {:?}", kind) } - AttrKind::DocComment(..) => { - panic!("Called finalize_tokens on doc comment attr {:?}", self) - } - } + }) } } @@ -115,10 +102,8 @@ impl AstLike for Option { inner.visit_attrs(f); } } - fn finalize_tokens(&mut self, tokens: LazyTokenStream) { - if let Some(inner) = self { - inner.finalize_tokens(tokens); - } + fn tokens_mut(&mut self) -> Option<&mut Option> { + self.as_mut().and_then(|inner| inner.tokens_mut()) } } @@ -152,11 +137,8 @@ macro_rules! derive_has_tokens_and_attrs { VecOrAttrVec::visit(&mut self.attrs, f) } - fn finalize_tokens(&mut self, tokens: LazyTokenStream) { - if self.tokens.is_none() { - self.tokens = Some(tokens); - } - + fn tokens_mut(&mut self) -> Option<&mut Option> { + Some(&mut self.tokens) } } )* } @@ -173,7 +155,9 @@ macro_rules! derive_has_attrs_no_tokens { VecOrAttrVec::visit(&mut self.attrs, f) } - fn finalize_tokens(&mut self, _tokens: LazyTokenStream) {} + fn tokens_mut(&mut self) -> Option<&mut Option> { + None + } } )* } } @@ -185,14 +169,10 @@ macro_rules! derive_has_tokens_no_attrs { &[] } - fn visit_attrs(&mut self, _f: impl FnOnce(&mut Vec)) { - } - - fn finalize_tokens(&mut self, tokens: LazyTokenStream) { - if self.tokens.is_none() { - self.tokens = Some(tokens); - } + fn visit_attrs(&mut self, _f: impl FnOnce(&mut Vec)) {} + fn tokens_mut(&mut self) -> Option<&mut Option> { + Some(&mut self.tokens) } } )* } diff --git a/compiler/rustc_builtin_macros/src/cfg_eval.rs b/compiler/rustc_builtin_macros/src/cfg_eval.rs new file mode 100644 index 0000000000000..eea4d785dee06 --- /dev/null +++ b/compiler/rustc_builtin_macros/src/cfg_eval.rs @@ -0,0 +1,157 @@ +use crate::util::check_builtin_macro_attribute; + +use rustc_ast::mut_visit::{self, MutVisitor}; +use rustc_ast::ptr::P; +use rustc_ast::{self as ast, AstLike}; +use rustc_expand::base::{Annotatable, ExtCtxt}; +use rustc_expand::config::StripUnconfigured; +use rustc_expand::configure; +use rustc_span::symbol::sym; +use rustc_span::Span; +use smallvec::SmallVec; + +crate fn expand( + ecx: &mut ExtCtxt<'_>, + _span: Span, + meta_item: &ast::MetaItem, + annotatable: Annotatable, +) -> Vec { + check_builtin_macro_attribute(ecx, meta_item, sym::cfg_eval); + cfg_eval(ecx, annotatable) +} + +crate fn cfg_eval(ecx: &ExtCtxt<'_>, annotatable: Annotatable) -> Vec { + let mut visitor = CfgEval { + cfg: StripUnconfigured { sess: ecx.sess, features: ecx.ecfg.features, modified: false }, + }; + let mut annotatable = visitor.configure_annotatable(annotatable); + if visitor.cfg.modified { + // Erase the tokens if cfg-stripping modified the item + // This will cause us to synthesize fake tokens + // when `nt_to_tokenstream` is called on this item. + if let Some(tokens) = annotatable.tokens_mut() { + *tokens = None; + } + } + vec![annotatable] +} + +struct CfgEval<'a> { + cfg: StripUnconfigured<'a>, +} + +impl CfgEval<'_> { + fn configure(&mut self, node: T) -> Option { + self.cfg.configure(node) + } + + fn configure_annotatable(&mut self, annotatable: Annotatable) -> Annotatable { + // Since the item itself has already been configured by the InvocationCollector, + // we know that fold result vector will contain exactly one element + match annotatable { + Annotatable::Item(item) => Annotatable::Item(self.flat_map_item(item).pop().unwrap()), + Annotatable::TraitItem(item) => { + Annotatable::TraitItem(self.flat_map_trait_item(item).pop().unwrap()) + } + Annotatable::ImplItem(item) => { + Annotatable::ImplItem(self.flat_map_impl_item(item).pop().unwrap()) + } + Annotatable::ForeignItem(item) => { + Annotatable::ForeignItem(self.flat_map_foreign_item(item).pop().unwrap()) + } + Annotatable::Stmt(stmt) => { + Annotatable::Stmt(stmt.map(|stmt| self.flat_map_stmt(stmt).pop().unwrap())) + } + Annotatable::Expr(mut expr) => Annotatable::Expr({ + self.visit_expr(&mut expr); + expr + }), + Annotatable::Arm(arm) => Annotatable::Arm(self.flat_map_arm(arm).pop().unwrap()), + Annotatable::Field(field) => { + Annotatable::Field(self.flat_map_field(field).pop().unwrap()) + } + Annotatable::FieldPat(fp) => { + Annotatable::FieldPat(self.flat_map_field_pattern(fp).pop().unwrap()) + } + Annotatable::GenericParam(param) => { + Annotatable::GenericParam(self.flat_map_generic_param(param).pop().unwrap()) + } + Annotatable::Param(param) => { + Annotatable::Param(self.flat_map_param(param).pop().unwrap()) + } + Annotatable::StructField(sf) => { + Annotatable::StructField(self.flat_map_struct_field(sf).pop().unwrap()) + } + Annotatable::Variant(v) => { + Annotatable::Variant(self.flat_map_variant(v).pop().unwrap()) + } + } + } +} + +impl MutVisitor for CfgEval<'_> { + fn visit_expr(&mut self, expr: &mut P) { + self.cfg.configure_expr(expr); + mut_visit::noop_visit_expr(expr, self); + } + + fn filter_map_expr(&mut self, expr: P) -> Option> { + let mut expr = configure!(self, expr); + mut_visit::noop_visit_expr(&mut expr, self); + Some(expr) + } + + fn flat_map_generic_param( + &mut self, + param: ast::GenericParam, + ) -> SmallVec<[ast::GenericParam; 1]> { + mut_visit::noop_flat_map_generic_param(configure!(self, param), self) + } + + fn flat_map_stmt(&mut self, stmt: ast::Stmt) -> SmallVec<[ast::Stmt; 1]> { + mut_visit::noop_flat_map_stmt(configure!(self, stmt), self) + } + + fn flat_map_item(&mut self, item: P) -> SmallVec<[P; 1]> { + mut_visit::noop_flat_map_item(configure!(self, item), self) + } + + fn flat_map_impl_item(&mut self, item: P) -> SmallVec<[P; 1]> { + mut_visit::noop_flat_map_assoc_item(configure!(self, item), self) + } + + fn flat_map_trait_item(&mut self, item: P) -> SmallVec<[P; 1]> { + mut_visit::noop_flat_map_assoc_item(configure!(self, item), self) + } + + fn flat_map_foreign_item( + &mut self, + foreign_item: P, + ) -> SmallVec<[P; 1]> { + mut_visit::noop_flat_map_foreign_item(configure!(self, foreign_item), self) + } + + fn flat_map_arm(&mut self, arm: ast::Arm) -> SmallVec<[ast::Arm; 1]> { + mut_visit::noop_flat_map_arm(configure!(self, arm), self) + } + + fn flat_map_field(&mut self, field: ast::Field) -> SmallVec<[ast::Field; 1]> { + mut_visit::noop_flat_map_field(configure!(self, field), self) + } + + fn flat_map_field_pattern(&mut self, fp: ast::FieldPat) -> SmallVec<[ast::FieldPat; 1]> { + mut_visit::noop_flat_map_field_pattern(configure!(self, fp), self) + } + + fn flat_map_param(&mut self, p: ast::Param) -> SmallVec<[ast::Param; 1]> { + mut_visit::noop_flat_map_param(configure!(self, p), self) + } + + fn flat_map_struct_field(&mut self, sf: ast::StructField) -> SmallVec<[ast::StructField; 1]> { + mut_visit::noop_flat_map_struct_field(configure!(self, sf), self) + } + + fn flat_map_variant(&mut self, variant: ast::Variant) -> SmallVec<[ast::Variant; 1]> { + mut_visit::noop_flat_map_variant(configure!(self, variant), self) + } +} diff --git a/compiler/rustc_builtin_macros/src/derive.rs b/compiler/rustc_builtin_macros/src/derive.rs index fad64858ce3ff..0da2c1c1021f3 100644 --- a/compiler/rustc_builtin_macros/src/derive.rs +++ b/compiler/rustc_builtin_macros/src/derive.rs @@ -1,7 +1,8 @@ +use crate::cfg_eval::cfg_eval; + use rustc_ast::{self as ast, token, ItemKind, MetaItemKind, NestedMetaItem, StmtKind}; use rustc_errors::{struct_span_err, Applicability}; use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, Indeterminate, MultiItemModifier}; -use rustc_expand::config::StripUnconfigured; use rustc_feature::AttributeTemplate; use rustc_parse::validate_attr; use rustc_session::Session; @@ -51,26 +52,7 @@ impl MultiItemModifier for Expander { // FIXME: Try to cache intermediate results to avoid collecting same paths multiple times. match ecx.resolver.resolve_derives(ecx.current_expansion.id, derives, ecx.force_mode) { - Ok(()) => { - let mut visitor = - StripUnconfigured { sess, features: ecx.ecfg.features, modified: false }; - let mut item = visitor.fully_configure(item); - if visitor.modified { - // Erase the tokens if cfg-stripping modified the item - // This will cause us to synthesize fake tokens - // when `nt_to_tokenstream` is called on this item. - match &mut item { - Annotatable::Item(item) => item, - Annotatable::Stmt(stmt) => match &mut stmt.kind { - StmtKind::Item(item) => item, - _ => unreachable!(), - }, - _ => unreachable!(), - } - .tokens = None; - } - ExpandResult::Ready(vec![item]) - } + Ok(()) => ExpandResult::Ready(cfg_eval(ecx, item)), Err(Indeterminate) => ExpandResult::Retry(item), } } diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 9a3c914337ca2..1017b23e5675a 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -24,6 +24,7 @@ mod asm; mod assert; mod cfg; mod cfg_accessible; +mod cfg_eval; mod compile_error; mod concat; mod concat_idents; @@ -89,6 +90,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) { register_attr! { bench: test::expand_bench, cfg_accessible: cfg_accessible::Expander, + cfg_eval: cfg_eval::expand, derive: derive::Expander, global_allocator: global_allocator::expand, test: test::expand_test, diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 5bd06e8e7d115..666065efdfb43 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -81,8 +81,22 @@ impl AstLike for Annotatable { } } - fn finalize_tokens(&mut self, tokens: LazyTokenStream) { - panic!("Called finalize_tokens on an Annotatable: {:?}", tokens); + fn tokens_mut(&mut self) -> Option<&mut Option> { + match self { + Annotatable::Item(item) => item.tokens_mut(), + Annotatable::TraitItem(trait_item) => trait_item.tokens_mut(), + Annotatable::ImplItem(impl_item) => impl_item.tokens_mut(), + Annotatable::ForeignItem(foreign_item) => foreign_item.tokens_mut(), + Annotatable::Stmt(stmt) => stmt.tokens_mut(), + Annotatable::Expr(expr) => expr.tokens_mut(), + Annotatable::Arm(arm) => arm.tokens_mut(), + Annotatable::Field(field) => field.tokens_mut(), + Annotatable::FieldPat(fp) => fp.tokens_mut(), + Annotatable::GenericParam(gp) => gp.tokens_mut(), + Annotatable::Param(p) => p.tokens_mut(), + Annotatable::StructField(sf) => sf.tokens_mut(), + Annotatable::Variant(v) => v.tokens_mut(), + } } } diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 7d0becf1f5d85..a23731cf309a4 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -1,8 +1,5 @@ //! Conditional compilation stripping. -use crate::base::Annotatable; - -use rustc_ast::mut_visit::*; use rustc_ast::ptr::P; use rustc_ast::token::{DelimToken, Token, TokenKind}; use rustc_ast::tokenstream::{DelimSpan, LazyTokenStream, Spacing, TokenStream, TokenTree}; @@ -22,8 +19,6 @@ use rustc_span::edition::{Edition, ALL_EDITIONS}; use rustc_span::symbol::{sym, Symbol}; use rustc_span::{Span, DUMMY_SP}; -use smallvec::SmallVec; - /// A folder that strips out items that do not belong in the current configuration. pub struct StripUnconfigured<'a> { pub sess: &'a Session, @@ -272,7 +267,7 @@ impl<'a> StripUnconfigured<'a> { /// Gives compiler warnings if any `cfg_attr` does not contain any /// attributes and is in the original source code. Gives compiler errors if /// the syntax of any `cfg_attr` is incorrect. - pub fn process_cfg_attrs(&mut self, node: &mut T) { + fn process_cfg_attrs(&mut self, node: &mut T) { node.visit_attrs(|attrs| { attrs.flat_map_in_place(|attr| self.process_cfg_attr(attr)); }); @@ -387,7 +382,7 @@ impl<'a> StripUnconfigured<'a> { } /// Determines if a node with the given attributes should be included in this configuration. - pub fn in_cfg(&self, attrs: &[Attribute]) -> bool { + fn in_cfg(&self, attrs: &[Attribute]) -> bool { attrs.iter().all(|attr| { if !is_cfg(self.sess, attr) { return true; @@ -427,16 +422,8 @@ impl<'a> StripUnconfigured<'a> { }) } - /// Visit attributes on expression and statements (but not attributes on items in blocks). - fn visit_expr_attrs(&mut self, attrs: &[Attribute]) { - // flag the offending attributes - for attr in attrs.iter() { - self.maybe_emit_expr_attr_err(attr); - } - } - /// If attributes are not allowed on expressions, emit an error for `attr` - pub fn maybe_emit_expr_attr_err(&self, attr: &Attribute) { + crate fn maybe_emit_expr_attr_err(&self, attr: &Attribute) { if !self.features.map_or(true, |features| features.stmt_expr_attributes) { let mut err = feature_err( &self.sess.parse_sess, @@ -453,49 +440,10 @@ impl<'a> StripUnconfigured<'a> { } } - pub fn configure_foreign_mod(&mut self, foreign_mod: &mut ast::ForeignMod) { - let ast::ForeignMod { unsafety: _, abi: _, items } = foreign_mod; - items.flat_map_in_place(|item| self.configure(item)); - } - - fn configure_variant_data(&mut self, vdata: &mut ast::VariantData) { - match vdata { - ast::VariantData::Struct(fields, ..) | ast::VariantData::Tuple(fields, _) => { - fields.flat_map_in_place(|field| self.configure(field)) - } - ast::VariantData::Unit(_) => {} - } - } - - pub fn configure_item_kind(&mut self, item: &mut ast::ItemKind) { - match item { - ast::ItemKind::Struct(def, _generics) | ast::ItemKind::Union(def, _generics) => { - self.configure_variant_data(def) - } - ast::ItemKind::Enum(ast::EnumDef { variants }, _generics) => { - variants.flat_map_in_place(|variant| self.configure(variant)); - for variant in variants { - self.configure_variant_data(&mut variant.data); - } - } - _ => {} - } - } - - pub fn configure_expr_kind(&mut self, expr_kind: &mut ast::ExprKind) { - match expr_kind { - ast::ExprKind::Match(_m, arms) => { - arms.flat_map_in_place(|arm| self.configure(arm)); - } - ast::ExprKind::Struct(_path, fields, _base) => { - fields.flat_map_in_place(|field| self.configure(field)); - } - _ => {} - } - } - pub fn configure_expr(&mut self, expr: &mut P) { - self.visit_expr_attrs(expr.attrs()); + for attr in expr.attrs.iter() { + self.maybe_emit_expr_attr_err(attr); + } // If an expr is valid to cfg away it will have been removed by the // outer stmt or expression folder before descending in here. @@ -511,117 +459,6 @@ impl<'a> StripUnconfigured<'a> { self.process_cfg_attrs(expr) } - - pub fn configure_pat(&mut self, pat: &mut P) { - if let ast::PatKind::Struct(_path, fields, _etc) = &mut pat.kind { - fields.flat_map_in_place(|field| self.configure(field)); - } - } - - pub fn configure_fn_decl(&mut self, fn_decl: &mut ast::FnDecl) { - fn_decl.inputs.flat_map_in_place(|arg| self.configure(arg)); - } - - pub fn fully_configure(&mut self, item: Annotatable) -> Annotatable { - // Since the item itself has already been configured by the InvocationCollector, - // we know that fold result vector will contain exactly one element - match item { - Annotatable::Item(item) => Annotatable::Item(self.flat_map_item(item).pop().unwrap()), - Annotatable::TraitItem(item) => { - Annotatable::TraitItem(self.flat_map_trait_item(item).pop().unwrap()) - } - Annotatable::ImplItem(item) => { - Annotatable::ImplItem(self.flat_map_impl_item(item).pop().unwrap()) - } - Annotatable::ForeignItem(item) => { - Annotatable::ForeignItem(self.flat_map_foreign_item(item).pop().unwrap()) - } - Annotatable::Stmt(stmt) => { - Annotatable::Stmt(stmt.map(|stmt| self.flat_map_stmt(stmt).pop().unwrap())) - } - Annotatable::Expr(mut expr) => Annotatable::Expr({ - self.visit_expr(&mut expr); - expr - }), - Annotatable::Arm(arm) => Annotatable::Arm(self.flat_map_arm(arm).pop().unwrap()), - Annotatable::Field(field) => { - Annotatable::Field(self.flat_map_field(field).pop().unwrap()) - } - Annotatable::FieldPat(fp) => { - Annotatable::FieldPat(self.flat_map_field_pattern(fp).pop().unwrap()) - } - Annotatable::GenericParam(param) => { - Annotatable::GenericParam(self.flat_map_generic_param(param).pop().unwrap()) - } - Annotatable::Param(param) => { - Annotatable::Param(self.flat_map_param(param).pop().unwrap()) - } - Annotatable::StructField(sf) => { - Annotatable::StructField(self.flat_map_struct_field(sf).pop().unwrap()) - } - Annotatable::Variant(v) => { - Annotatable::Variant(self.flat_map_variant(v).pop().unwrap()) - } - } - } -} - -impl<'a> MutVisitor for StripUnconfigured<'a> { - fn visit_foreign_mod(&mut self, foreign_mod: &mut ast::ForeignMod) { - self.configure_foreign_mod(foreign_mod); - noop_visit_foreign_mod(foreign_mod, self); - } - - fn visit_item_kind(&mut self, item: &mut ast::ItemKind) { - self.configure_item_kind(item); - noop_visit_item_kind(item, self); - } - - fn visit_expr(&mut self, expr: &mut P) { - self.configure_expr(expr); - self.configure_expr_kind(&mut expr.kind); - noop_visit_expr(expr, self); - } - - fn filter_map_expr(&mut self, expr: P) -> Option> { - let mut expr = configure!(self, expr); - self.configure_expr_kind(&mut expr.kind); - noop_visit_expr(&mut expr, self); - Some(expr) - } - - fn flat_map_generic_param( - &mut self, - param: ast::GenericParam, - ) -> SmallVec<[ast::GenericParam; 1]> { - noop_flat_map_generic_param(configure!(self, param), self) - } - - fn flat_map_stmt(&mut self, stmt: ast::Stmt) -> SmallVec<[ast::Stmt; 1]> { - noop_flat_map_stmt(configure!(self, stmt), self) - } - - fn flat_map_item(&mut self, item: P) -> SmallVec<[P; 1]> { - noop_flat_map_item(configure!(self, item), self) - } - - fn flat_map_impl_item(&mut self, item: P) -> SmallVec<[P; 1]> { - noop_flat_map_assoc_item(configure!(self, item), self) - } - - fn flat_map_trait_item(&mut self, item: P) -> SmallVec<[P; 1]> { - noop_flat_map_assoc_item(configure!(self, item), self) - } - - fn visit_pat(&mut self, pat: &mut P) { - self.configure_pat(pat); - noop_visit_pat(pat, self) - } - - fn visit_fn_decl(&mut self, mut fn_decl: &mut P) { - self.configure_fn_decl(&mut fn_decl); - noop_visit_fn_decl(fn_decl, self); - } } fn is_cfg(sess: &Session, attr: &Attribute) -> bool { diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index f45d8d6c7a00e..7512f46988c91 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -72,6 +72,10 @@ impl<'a> Parser<'a> { let cursor_snapshot = self.token_cursor.clone(); let (mut ret, trailing_token) = f(self, attrs.attrs)?; + let tokens = match ret.tokens_mut() { + Some(tokens) if tokens.is_none() => tokens, + _ => return Ok(ret), + }; // Produces a `TokenStream` on-demand. Using `cursor_snapshot` // and `num_calls`, we can reconstruct the `TokenStream` seen @@ -128,14 +132,14 @@ impl<'a> Parser<'a> { } } - let lazy_impl = LazyTokenStreamImpl { + *tokens = Some(LazyTokenStream::new(LazyTokenStreamImpl { start_token, num_calls, cursor_snapshot, desugar_doc_comments: self.desugar_doc_comments, append_unglued_token: self.token_cursor.append_unglued_token.clone(), - }; - ret.finalize_tokens(LazyTokenStream::new(lazy_impl)); + })); + Ok(ret) } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 9663760cba189..507eb1e1cbe89 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -344,6 +344,7 @@ symbols! { cfg_attr, cfg_attr_multi, cfg_doctest, + cfg_eval, cfg_panic, cfg_sanitize, cfg_target_feature, diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 3e70ba81d4997..28fed9b8a14cd 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -1452,6 +1452,18 @@ pub(crate) mod builtin { /* compiler built-in */ } + /// Expands all `#[cfg]` and `#[cfg_attr]` attributes in the code fragment it's applied to. + #[cfg(not(bootstrap))] + #[unstable( + feature = "cfg_eval", + issue = "82679", + reason = "`cfg_eval` is a recently implemented feature" + )] + #[rustc_builtin_macro] + pub macro cfg_eval($($tt:tt)*) { + /* compiler built-in */ + } + /// Unstable implementation detail of the `rustc` compiler, do not use. #[rustc_builtin_macro] #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/core/src/prelude/v1.rs b/library/core/src/prelude/v1.rs index a1fbd8dec7505..5e8a8d252a238 100644 --- a/library/core/src/prelude/v1.rs +++ b/library/core/src/prelude/v1.rs @@ -81,3 +81,12 @@ pub use crate::macros::builtin::derive; )] #[doc(no_inline)] pub use crate::macros::builtin::cfg_accessible; + +#[cfg(not(bootstrap))] +#[unstable( + feature = "cfg_eval", + issue = "82679", + reason = "`cfg_eval` is a recently implemented feature" +)] +#[doc(no_inline)] +pub use crate::macros::builtin::cfg_eval; diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 72b86338d2c97..acdf7550fe71d 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -234,6 +234,7 @@ #![feature(box_syntax)] #![feature(c_variadic)] #![feature(cfg_accessible)] +#![cfg_attr(not(bootstrap), feature(cfg_eval))] #![feature(cfg_target_has_atomic)] #![feature(cfg_target_thread_local)] #![feature(char_error_internals)] diff --git a/library/std/src/prelude/v1.rs b/library/std/src/prelude/v1.rs index ef9aec54a4ca2..7181dc6e710e4 100644 --- a/library/std/src/prelude/v1.rs +++ b/library/std/src/prelude/v1.rs @@ -67,6 +67,15 @@ pub use core::prelude::v1::derive; #[doc(hidden)] pub use core::prelude::v1::cfg_accessible; +#[cfg(not(bootstrap))] +#[unstable( + feature = "cfg_eval", + issue = "82679", + reason = "`cfg_eval` is a recently implemented feature" +)] +#[doc(hidden)] +pub use core::prelude::v1::cfg_eval; + // The file so far is equivalent to src/libcore/prelude/v1.rs, // and below to src/liballoc/prelude.rs. // Those files are duplicated rather than using glob imports diff --git a/src/test/ui/proc-macro/cfg-eval-fail.rs b/src/test/ui/proc-macro/cfg-eval-fail.rs new file mode 100644 index 0000000000000..379491f3126b0 --- /dev/null +++ b/src/test/ui/proc-macro/cfg-eval-fail.rs @@ -0,0 +1,9 @@ +#![feature(cfg_eval)] +#![feature(stmt_expr_attributes)] + +fn main() { + let _ = #[cfg_eval] #[cfg(FALSE)] 0; + //~^ ERROR removing an expression is not supported in this position + //~| ERROR removing an expression is not supported in this position + //~| ERROR removing an expression is not supported in this position +} diff --git a/src/test/ui/proc-macro/cfg-eval-fail.stderr b/src/test/ui/proc-macro/cfg-eval-fail.stderr new file mode 100644 index 0000000000000..010ac006b0bee --- /dev/null +++ b/src/test/ui/proc-macro/cfg-eval-fail.stderr @@ -0,0 +1,20 @@ +error: removing an expression is not supported in this position + --> $DIR/cfg-eval-fail.rs:5:25 + | +LL | let _ = #[cfg_eval] #[cfg(FALSE)] 0; + | ^^^^^^^^^^^^^ + +error: removing an expression is not supported in this position + --> $DIR/cfg-eval-fail.rs:5:25 + | +LL | let _ = #[cfg_eval] #[cfg(FALSE)] 0; + | ^^^^^^^^^^^^^ + +error: removing an expression is not supported in this position + --> $DIR/cfg-eval-fail.rs:5:25 + | +LL | let _ = #[cfg_eval] #[cfg(FALSE)] 0; + | ^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/proc-macro/cfg-eval.rs b/src/test/ui/proc-macro/cfg-eval.rs new file mode 100644 index 0000000000000..ea397df545265 --- /dev/null +++ b/src/test/ui/proc-macro/cfg-eval.rs @@ -0,0 +1,32 @@ +// check-pass +// compile-flags: -Z span-debug +// aux-build:test-macros.rs + +#![feature(cfg_eval)] +#![feature(proc_macro_hygiene)] +#![feature(stmt_expr_attributes)] + +#![no_std] // Don't load unnecessary hygiene information from std +extern crate std; + +#[macro_use] +extern crate test_macros; + +#[cfg_eval] +#[print_attr] +struct S1 { + #[cfg(FALSE)] + field_false: u8, + #[cfg(all(/*true*/))] + #[cfg_attr(FALSE, unknown_attr)] + #[cfg_attr(all(/*true*/), allow())] + field_true: u8, +} + +#[cfg_eval] +#[cfg(FALSE)] +struct S2 {} + +fn main() { + let _ = #[cfg_eval] #[print_attr](#[cfg(FALSE)] 0, #[cfg(all(/*true*/))] 1); +} diff --git a/src/test/ui/proc-macro/cfg-eval.stdout b/src/test/ui/proc-macro/cfg-eval.stdout new file mode 100644 index 0000000000000..b98e8961bfea7 --- /dev/null +++ b/src/test/ui/proc-macro/cfg-eval.stdout @@ -0,0 +1,135 @@ +PRINT-ATTR INPUT (DISPLAY): struct S1 { #[cfg(all())] #[allow()] field_true : u8, } +PRINT-ATTR INPUT (DEBUG): TokenStream [ + Ident { + ident: "struct", + span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + }, + Ident { + ident: "S1", + span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + }, + Group { + delimiter: Brace, + stream: TokenStream [ + Punct { + ch: '#', + spacing: Alone, + span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "cfg", + span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "all", + span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [], + span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + }, + ], + span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + }, + ], + span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + }, + Punct { + ch: '#', + spacing: Alone, + span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "allow", + span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [], + span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + }, + ], + span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + }, + Ident { + ident: "field_true", + span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + }, + Punct { + ch: ':', + spacing: Alone, + span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + }, + Ident { + ident: "u8", + span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + }, + Punct { + ch: ',', + spacing: Alone, + span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + }, + ], + span: $DIR/cfg-eval.rs:17:1: 24:2 (#0), + }, +] +PRINT-ATTR INPUT (DISPLAY): (#[cfg(all())] 1,) +PRINT-ATTR INPUT (DEBUG): TokenStream [ + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Punct { + ch: '#', + spacing: Alone, + span: $DIR/cfg-eval.rs:31:38: 31:80 (#0), + }, + Group { + delimiter: Bracket, + stream: TokenStream [ + Ident { + ident: "cfg", + span: $DIR/cfg-eval.rs:31:38: 31:80 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [ + Ident { + ident: "all", + span: $DIR/cfg-eval.rs:31:38: 31:80 (#0), + }, + Group { + delimiter: Parenthesis, + stream: TokenStream [], + span: $DIR/cfg-eval.rs:31:38: 31:80 (#0), + }, + ], + span: $DIR/cfg-eval.rs:31:38: 31:80 (#0), + }, + ], + span: $DIR/cfg-eval.rs:31:38: 31:80 (#0), + }, + Literal { + kind: Integer, + symbol: "1", + suffix: None, + span: $DIR/cfg-eval.rs:31:38: 31:80 (#0), + }, + Punct { + ch: ',', + spacing: Alone, + span: $DIR/cfg-eval.rs:31:38: 31:80 (#0), + }, + ], + span: $DIR/cfg-eval.rs:31:38: 31:80 (#0), + }, +]