rustc_builtin_macros/
cfg_select.rs1use rustc_ast::tokenstream::TokenStream;
2use rustc_ast::{Expr, ast};
3use rustc_attr_parsing as attr;
4use rustc_attr_parsing::{
5 CfgSelectBranches, CfgSelectPredicate, EvalConfigResult, parse_cfg_select,
6};
7use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacResult, MacroExpanderResult};
8use rustc_span::{Ident, Span, sym};
9use smallvec::SmallVec;
10
11use crate::errors::{CfgSelectNoMatches, CfgSelectUnreachable};
12
13struct CfgSelectResult<'cx, 'sess> {
17 ecx: &'cx mut ExtCtxt<'sess>,
18 site_span: Span,
19 selected_tts: TokenStream,
20 selected_span: Span,
21 other_branches: CfgSelectBranches,
22}
23
24fn tts_to_mac_result<'cx, 'sess>(
25 ecx: &'cx mut ExtCtxt<'sess>,
26 site_span: Span,
27 tts: TokenStream,
28 span: Span,
29) -> Box<dyn MacResult + 'cx> {
30 match ExpandResult::from_tts(ecx, tts, site_span, span, Ident::with_dummy_span(sym::cfg_select))
31 {
32 ExpandResult::Ready(x) => x,
33 _ => unreachable!("from_tts always returns Ready"),
34 }
35}
36
37macro_rules! forward_to_parser_any_macro {
38 ($method_name:ident, $ret_ty:ty) => {
39 fn $method_name(self: Box<Self>) -> Option<$ret_ty> {
40 let CfgSelectResult { ecx, site_span, selected_tts, selected_span, .. } = *self;
41
42 for (tts, span) in self.other_branches.into_iter_tts() {
43 let _ = tts_to_mac_result(ecx, site_span, tts, span).$method_name();
44 }
45
46 tts_to_mac_result(ecx, site_span, selected_tts, selected_span).$method_name()
47 }
48 };
49}
50
51impl<'cx, 'sess> MacResult for CfgSelectResult<'cx, 'sess> {
52 forward_to_parser_any_macro!(make_expr, Box<Expr>);
53 forward_to_parser_any_macro!(make_stmts, SmallVec<[ast::Stmt; 1]>);
54 forward_to_parser_any_macro!(make_items, SmallVec<[Box<ast::Item>; 1]>);
55
56 forward_to_parser_any_macro!(make_impl_items, SmallVec<[Box<ast::AssocItem>; 1]>);
57 forward_to_parser_any_macro!(make_trait_impl_items, SmallVec<[Box<ast::AssocItem>; 1]>);
58 forward_to_parser_any_macro!(make_trait_items, SmallVec<[Box<ast::AssocItem>; 1]>);
59 forward_to_parser_any_macro!(make_foreign_items, SmallVec<[Box<ast::ForeignItem>; 1]>);
60
61 forward_to_parser_any_macro!(make_ty, Box<ast::Ty>);
62 forward_to_parser_any_macro!(make_pat, Box<ast::Pat>);
63}
64
65pub(super) fn expand_cfg_select<'cx>(
66 ecx: &'cx mut ExtCtxt<'_>,
67 sp: Span,
68 tts: TokenStream,
69) -> MacroExpanderResult<'cx> {
70 ExpandResult::Ready(
71 match parse_cfg_select(
72 &mut ecx.new_parser_from_tts(tts),
73 ecx.sess,
74 Some(ecx.ecfg.features),
75 ecx.current_expansion.lint_node_id,
76 ) {
77 Ok(mut branches) => {
78 if let Some((underscore, _, _)) = branches.wildcard {
79 for (predicate, _, _) in &branches.unreachable {
81 let span = match predicate {
82 CfgSelectPredicate::Wildcard(underscore) => underscore.span,
83 CfgSelectPredicate::Cfg(cfg) => cfg.span(),
84 };
85 let err = CfgSelectUnreachable { span, wildcard_span: underscore.span };
86 ecx.dcx().emit_warn(err);
87 }
88 }
89
90 if let Some((selected_tts, selected_span)) = branches.pop_first_match(|cfg| {
91 matches!(attr::eval_config_entry(&ecx.sess, cfg), EvalConfigResult::True)
92 }) {
93 let mac = CfgSelectResult {
94 ecx,
95 selected_tts,
96 selected_span,
97 other_branches: branches,
98 site_span: sp,
99 };
100 return ExpandResult::Ready(Box::new(mac));
101 } else {
102 let guar = ecx.dcx().emit_err(CfgSelectNoMatches { span: sp });
104 DummyResult::any(sp, guar)
105 }
106 }
107 Err(guar) => DummyResult::any(sp, guar),
108 },
109 )
110}