use rustc_ast::tokenstream::TokenStream; use rustc_attr_parsing as attr; use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacroExpanderResult}; use rustc_parse::parser::cfg_select::{CfgSelectBranches, CfgSelectPredicate, parse_cfg_select}; use rustc_span::{Ident, Span, sym}; use crate::errors::{CfgSelectNoMatches, CfgSelectUnreachable}; /// Selects the first arm whose predicate evaluates to true. fn select_arm(ecx: &ExtCtxt<'_>, branches: CfgSelectBranches) -> Option<(TokenStream, Span)> { for (cfg, tt, arm_span) in branches.reachable { if attr::cfg_matches( &cfg, &ecx.sess, ecx.current_expansion.lint_node_id, Some(ecx.ecfg.features), ) { return Some((tt, arm_span)); } } branches.wildcard.map(|(_, tt, span)| (tt, span)) } pub(super) fn expand_cfg_select<'cx>( ecx: &'cx mut ExtCtxt<'_>, sp: Span, tts: TokenStream, ) -> MacroExpanderResult<'cx> { ExpandResult::Ready(match parse_cfg_select(&mut ecx.new_parser_from_tts(tts)) { Ok(branches) => { if let Some((underscore, _, _)) = branches.wildcard { // Warn for every unreachable predicate. We store the fully parsed branch for rustfmt. for (predicate, _, _) in &branches.unreachable { let span = match predicate { CfgSelectPredicate::Wildcard(underscore) => underscore.span, CfgSelectPredicate::Cfg(cfg) => cfg.span(), }; let err = CfgSelectUnreachable { span, wildcard_span: underscore.span }; ecx.dcx().emit_warn(err); } } if let Some((tts, arm_span)) = select_arm(ecx, branches) { return ExpandResult::from_tts( ecx, tts, sp, arm_span, Ident::with_dummy_span(sym::cfg_select), ); } else { // Emit a compiler error when none of the predicates matched. let guar = ecx.dcx().emit_err(CfgSelectNoMatches { span: sp }); DummyResult::any(sp, guar) } } Err(err) => { let guar = err.emit(); DummyResult::any(sp, guar) } }) }