| 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) |
| } |
| }) |
| } |