diff --git a/compiler/rustc_builtin_macros/src/cfg_eval.rs b/compiler/rustc_builtin_macros/src/cfg_eval.rs index d7b46f282151f..307730f7f5f14 100644 --- a/compiler/rustc_builtin_macros/src/cfg_eval.rs +++ b/compiler/rustc_builtin_macros/src/cfg_eval.rs @@ -2,6 +2,7 @@ use crate::util::check_builtin_macro_attribute; use rustc_ast as ast; use rustc_ast::mut_visit::MutVisitor; +use rustc_ast::ptr::P; use rustc_ast::tokenstream::CanSynthesizeMissingTokens; use rustc_ast::visit::Visitor; use rustc_ast::{mut_visit, visit}; @@ -9,10 +10,10 @@ use rustc_ast::{AstLike, Attribute}; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_expand::config::StripUnconfigured; use rustc_expand::configure; +use rustc_feature::Features; use rustc_parse::parser::ForceCollect; use rustc_session::utils::FlattenNonterminals; - -use rustc_ast::ptr::P; +use rustc_session::Session; use rustc_span::symbol::sym; use rustc_span::Span; use smallvec::SmallVec; @@ -24,21 +25,19 @@ crate fn expand( annotatable: Annotatable, ) -> Vec { check_builtin_macro_attribute(ecx, meta_item, sym::cfg_eval); - vec![cfg_eval(ecx, annotatable)] + vec![cfg_eval(ecx.sess, ecx.ecfg.features, annotatable)] } -crate fn cfg_eval(ecx: &ExtCtxt<'_>, annotatable: Annotatable) -> Annotatable { - CfgEval { - cfg: &mut StripUnconfigured { - sess: ecx.sess, - features: ecx.ecfg.features, - config_tokens: true, - }, - } - .configure_annotatable(annotatable) - // Since the item itself has already been configured by the `InvocationCollector`, - // we know that fold result vector will contain exactly one element. - .unwrap() +crate fn cfg_eval( + sess: &Session, + features: Option<&Features>, + annotatable: Annotatable, +) -> Annotatable { + CfgEval { cfg: &mut StripUnconfigured { sess, features, config_tokens: true } } + .configure_annotatable(annotatable) + // Since the item itself has already been configured by the `InvocationCollector`, + // we know that fold result vector will contain exactly one element. + .unwrap() } struct CfgEval<'a, 'b> { diff --git a/compiler/rustc_builtin_macros/src/derive.rs b/compiler/rustc_builtin_macros/src/derive.rs index 824776b45e21a..241c90c157125 100644 --- a/compiler/rustc_builtin_macros/src/derive.rs +++ b/compiler/rustc_builtin_macros/src/derive.rs @@ -1,12 +1,13 @@ use crate::cfg_eval::cfg_eval; -use rustc_ast::{self as ast, attr, token, ItemKind, MetaItemKind, NestedMetaItem, StmtKind}; +use rustc_ast as ast; +use rustc_ast::{attr, token, GenericParamKind, ItemKind, MetaItemKind, NestedMetaItem, StmtKind}; use rustc_errors::{struct_span_err, Applicability}; use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, Indeterminate, MultiItemModifier}; use rustc_feature::AttributeTemplate; use rustc_parse::validate_attr; use rustc_session::Session; -use rustc_span::symbol::sym; +use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; crate struct Expander; @@ -26,8 +27,7 @@ impl MultiItemModifier for Expander { return ExpandResult::Ready(vec![item]); } - let configured_item = cfg_eval(ecx, item.clone()); - + let (sess, features) = (ecx.sess, ecx.ecfg.features); let result = ecx.resolver.resolve_derives(ecx.current_expansion.id, ecx.force_mode, &|| { let template = @@ -40,7 +40,8 @@ impl MultiItemModifier for Expander { template, ); - attr.meta_item_list() + let mut resolutions: Vec<_> = attr + .meta_item_list() .unwrap_or_default() .into_iter() .filter_map(|nested_meta| match nested_meta { @@ -56,8 +57,21 @@ impl MultiItemModifier for Expander { report_path_args(sess, &meta); meta.path }) - .map(|path| (path, configured_item.clone(), None)) - .collect() + .map(|path| (path, dummy_annotatable(), None)) + .collect(); + + // Do not configure or clone items unless necessary. + match &mut resolutions[..] { + [] => {} + [(_, first_item, _), others @ ..] => { + *first_item = cfg_eval(sess, features, item.clone()); + for (_, item, _) in others { + *item = first_item.clone(); + } + } + } + + resolutions }); match result { @@ -67,6 +81,18 @@ impl MultiItemModifier for Expander { } } +// The cheapest `Annotatable` to construct. +fn dummy_annotatable() -> Annotatable { + Annotatable::GenericParam(ast::GenericParam { + id: ast::DUMMY_NODE_ID, + ident: Ident::invalid(), + attrs: Default::default(), + bounds: Default::default(), + is_placeholder: false, + kind: GenericParamKind::Lifetime, + }) +} + fn report_bad_target(sess: &Session, item: &Annotatable, span: Span) -> bool { let item_kind = match item { Annotatable::Item(item) => Some(&item.kind),