|  | use core::ops::ControlFlow; | 
|  |  | 
|  | use rustc_ast as ast; | 
|  | use rustc_ast::mut_visit::MutVisitor; | 
|  | use rustc_ast::visit::{AssocCtxt, Visitor}; | 
|  | use rustc_ast::{Attribute, HasAttrs, HasTokens, NodeId, mut_visit, visit}; | 
|  | use rustc_errors::PResult; | 
|  | use rustc_expand::base::{Annotatable, ExtCtxt}; | 
|  | use rustc_expand::config::StripUnconfigured; | 
|  | use rustc_expand::configure; | 
|  | use rustc_feature::Features; | 
|  | use rustc_parse::parser::{ForceCollect, Parser}; | 
|  | use rustc_session::Session; | 
|  | use rustc_span::{Span, sym}; | 
|  | use smallvec::SmallVec; | 
|  | use tracing::instrument; | 
|  |  | 
|  | use crate::util::{check_builtin_macro_attribute, warn_on_duplicate_attribute}; | 
|  |  | 
|  | pub(crate) fn expand( | 
|  | ecx: &mut ExtCtxt<'_>, | 
|  | _span: Span, | 
|  | meta_item: &ast::MetaItem, | 
|  | annotatable: Annotatable, | 
|  | ) -> Vec<Annotatable> { | 
|  | check_builtin_macro_attribute(ecx, meta_item, sym::cfg_eval); | 
|  | warn_on_duplicate_attribute(ecx, &annotatable, sym::cfg_eval); | 
|  | vec![cfg_eval(ecx.sess, ecx.ecfg.features, annotatable, ecx.current_expansion.lint_node_id)] | 
|  | } | 
|  |  | 
|  | pub(crate) fn cfg_eval( | 
|  | sess: &Session, | 
|  | features: &Features, | 
|  | annotatable: Annotatable, | 
|  | lint_node_id: NodeId, | 
|  | ) -> Annotatable { | 
|  | let features = Some(features); | 
|  | CfgEval(StripUnconfigured { sess, features, config_tokens: true, lint_node_id }) | 
|  | .configure_annotatable(annotatable) | 
|  | } | 
|  |  | 
|  | struct CfgEval<'a>(StripUnconfigured<'a>); | 
|  |  | 
|  | fn has_cfg_or_cfg_attr(annotatable: &Annotatable) -> bool { | 
|  | struct CfgFinder; | 
|  |  | 
|  | impl<'ast> visit::Visitor<'ast> for CfgFinder { | 
|  | type Result = ControlFlow<()>; | 
|  | fn visit_attribute(&mut self, attr: &'ast Attribute) -> ControlFlow<()> { | 
|  | if attr | 
|  | .ident() | 
|  | .is_some_and(|ident| ident.name == sym::cfg || ident.name == sym::cfg_attr) | 
|  | { | 
|  | ControlFlow::Break(()) | 
|  | } else { | 
|  | ControlFlow::Continue(()) | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | let res = match annotatable { | 
|  | Annotatable::Item(item) => CfgFinder.visit_item(item), | 
|  | Annotatable::AssocItem(item, ctxt) => CfgFinder.visit_assoc_item(item, *ctxt), | 
|  | Annotatable::ForeignItem(item) => CfgFinder.visit_foreign_item(item), | 
|  | Annotatable::Stmt(stmt) => CfgFinder.visit_stmt(stmt), | 
|  | Annotatable::Expr(expr) => CfgFinder.visit_expr(expr), | 
|  | _ => unreachable!(), | 
|  | }; | 
|  | res.is_break() | 
|  | } | 
|  |  | 
|  | impl CfgEval<'_> { | 
|  | fn configure<T: HasAttrs + HasTokens>(&mut self, node: T) -> Option<T> { | 
|  | self.0.configure(node) | 
|  | } | 
|  |  | 
|  | fn configure_annotatable(mut self, annotatable: Annotatable) -> Annotatable { | 
|  | // Tokenizing and re-parsing the `Annotatable` can have a significant | 
|  | // performance impact, so try to avoid it if possible | 
|  | if !has_cfg_or_cfg_attr(&annotatable) { | 
|  | return annotatable; | 
|  | } | 
|  |  | 
|  | // The majority of parsed attribute targets will never need to have early cfg-expansion | 
|  | // run (e.g. they are not part of a `#[derive]` or `#[cfg_eval]` macro input). | 
|  | // Therefore, we normally do not capture the necessary information about `#[cfg]` | 
|  | // and `#[cfg_attr]` attributes during parsing. | 
|  | // | 
|  | // Therefore, when we actually *do* run early cfg-expansion, we need to tokenize | 
|  | // and re-parse the attribute target, this time capturing information about | 
|  | // the location of `#[cfg]` and `#[cfg_attr]` in the token stream. The tokenization | 
|  | // process is lossless, so this process is invisible to proc-macros. | 
|  |  | 
|  | // Interesting cases: | 
|  | // | 
|  | // ```rust | 
|  | // #[cfg_eval] #[cfg] $item | 
|  | //``` | 
|  | // | 
|  | // where `$item` is `#[cfg_attr] struct Foo {}`. We want to make | 
|  | // sure to evaluate *all* `#[cfg]` and `#[cfg_attr]` attributes - the simplest | 
|  | // way to do this is to do a single parse of the token stream. | 
|  | let orig_tokens = annotatable.to_tokens(); | 
|  |  | 
|  | // Re-parse the tokens, setting the `capture_cfg` flag to save extra information | 
|  | // to the captured `AttrTokenStream` (specifically, we capture | 
|  | // `AttrTokenTree::AttrsTarget` for all occurrences of `#[cfg]` and `#[cfg_attr]`) | 
|  | // | 
|  | // After that we have our re-parsed `AttrTokenStream`, recursively configuring | 
|  | // our attribute target will correctly configure the tokens as well. | 
|  | let mut parser = Parser::new(&self.0.sess.psess, orig_tokens, None); | 
|  | parser.capture_cfg = true; | 
|  | let res: PResult<'_, Annotatable> = try { | 
|  | match annotatable { | 
|  | Annotatable::Item(_) => { | 
|  | let item = parser.parse_item(ForceCollect::Yes)?.unwrap(); | 
|  | Annotatable::Item(self.flat_map_item(item).pop().unwrap()) | 
|  | } | 
|  | Annotatable::AssocItem(_, ctxt) => { | 
|  | let item = parser.parse_trait_item(ForceCollect::Yes)?.unwrap().unwrap(); | 
|  | Annotatable::AssocItem( | 
|  | self.flat_map_assoc_item(item, ctxt).pop().unwrap(), | 
|  | ctxt, | 
|  | ) | 
|  | } | 
|  | Annotatable::ForeignItem(_) => { | 
|  | let item = parser.parse_foreign_item(ForceCollect::Yes)?.unwrap().unwrap(); | 
|  | Annotatable::ForeignItem(self.flat_map_foreign_item(item).pop().unwrap()) | 
|  | } | 
|  | Annotatable::Stmt(_) => { | 
|  | let stmt = parser | 
|  | .parse_stmt_without_recovery(false, ForceCollect::Yes, false)? | 
|  | .unwrap(); | 
|  | Annotatable::Stmt(Box::new(self.flat_map_stmt(stmt).pop().unwrap())) | 
|  | } | 
|  | Annotatable::Expr(_) => { | 
|  | let mut expr = parser.parse_expr_force_collect()?; | 
|  | self.visit_expr(&mut expr); | 
|  | Annotatable::Expr(expr) | 
|  | } | 
|  | _ => unreachable!(), | 
|  | } | 
|  | }; | 
|  |  | 
|  | match res { | 
|  | Ok(ann) => ann, | 
|  | Err(err) => { | 
|  | err.emit(); | 
|  | annotatable | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | impl MutVisitor for CfgEval<'_> { | 
|  | #[instrument(level = "trace", skip(self))] | 
|  | fn visit_expr(&mut self, expr: &mut ast::Expr) { | 
|  | self.0.configure_expr(expr, false); | 
|  | mut_visit::walk_expr(self, expr); | 
|  | } | 
|  |  | 
|  | #[instrument(level = "trace", skip(self))] | 
|  | fn visit_method_receiver_expr(&mut self, expr: &mut ast::Expr) { | 
|  | self.0.configure_expr(expr, true); | 
|  | mut_visit::walk_expr(self, expr); | 
|  | } | 
|  |  | 
|  | fn filter_map_expr(&mut self, expr: Box<ast::Expr>) -> Option<Box<ast::Expr>> { | 
|  | let mut expr = configure!(self, expr); | 
|  | mut_visit::walk_expr(self, &mut expr); | 
|  | Some(expr) | 
|  | } | 
|  |  | 
|  | fn flat_map_generic_param( | 
|  | &mut self, | 
|  | param: ast::GenericParam, | 
|  | ) -> SmallVec<[ast::GenericParam; 1]> { | 
|  | let param = configure!(self, param); | 
|  | mut_visit::walk_flat_map_generic_param(self, param) | 
|  | } | 
|  |  | 
|  | fn flat_map_stmt(&mut self, stmt: ast::Stmt) -> SmallVec<[ast::Stmt; 1]> { | 
|  | let stmt = configure!(self, stmt); | 
|  | mut_visit::walk_flat_map_stmt(self, stmt) | 
|  | } | 
|  |  | 
|  | fn flat_map_item(&mut self, item: Box<ast::Item>) -> SmallVec<[Box<ast::Item>; 1]> { | 
|  | let item = configure!(self, item); | 
|  | mut_visit::walk_flat_map_item(self, item) | 
|  | } | 
|  |  | 
|  | fn flat_map_assoc_item( | 
|  | &mut self, | 
|  | item: Box<ast::AssocItem>, | 
|  | ctxt: AssocCtxt, | 
|  | ) -> SmallVec<[Box<ast::AssocItem>; 1]> { | 
|  | let item = configure!(self, item); | 
|  | mut_visit::walk_flat_map_assoc_item(self, item, ctxt) | 
|  | } | 
|  |  | 
|  | fn flat_map_foreign_item( | 
|  | &mut self, | 
|  | foreign_item: Box<ast::ForeignItem>, | 
|  | ) -> SmallVec<[Box<ast::ForeignItem>; 1]> { | 
|  | let foreign_item = configure!(self, foreign_item); | 
|  | mut_visit::walk_flat_map_foreign_item(self, foreign_item) | 
|  | } | 
|  |  | 
|  | fn flat_map_arm(&mut self, arm: ast::Arm) -> SmallVec<[ast::Arm; 1]> { | 
|  | let arm = configure!(self, arm); | 
|  | mut_visit::walk_flat_map_arm(self, arm) | 
|  | } | 
|  |  | 
|  | fn flat_map_expr_field(&mut self, field: ast::ExprField) -> SmallVec<[ast::ExprField; 1]> { | 
|  | let field = configure!(self, field); | 
|  | mut_visit::walk_flat_map_expr_field(self, field) | 
|  | } | 
|  |  | 
|  | fn flat_map_pat_field(&mut self, fp: ast::PatField) -> SmallVec<[ast::PatField; 1]> { | 
|  | let fp = configure!(self, fp); | 
|  | mut_visit::walk_flat_map_pat_field(self, fp) | 
|  | } | 
|  |  | 
|  | fn flat_map_param(&mut self, p: ast::Param) -> SmallVec<[ast::Param; 1]> { | 
|  | let p = configure!(self, p); | 
|  | mut_visit::walk_flat_map_param(self, p) | 
|  | } | 
|  |  | 
|  | fn flat_map_field_def(&mut self, sf: ast::FieldDef) -> SmallVec<[ast::FieldDef; 1]> { | 
|  | let sf = configure!(self, sf); | 
|  | mut_visit::walk_flat_map_field_def(self, sf) | 
|  | } | 
|  |  | 
|  | fn flat_map_variant(&mut self, variant: ast::Variant) -> SmallVec<[ast::Variant; 1]> { | 
|  | let variant = configure!(self, variant); | 
|  | mut_visit::walk_flat_map_variant(self, variant) | 
|  | } | 
|  | } |