Skip to main content

rustc_builtin_macros/
cfg_select.rs

1use rustc_ast::tokenstream::TokenStream;
2use rustc_ast::{Expr, ast};
3use rustc_attr_parsing as attr;
4use rustc_attr_parsing::{CfgSelectBranches, EvalConfigResult, parse_cfg_select};
5use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacResult, MacroExpanderResult};
6use rustc_span::{Ident, Span, sym};
7use smallvec::SmallVec;
8
9use crate::errors::CfgSelectNoMatches;
10
11/// This intermediate structure is used to emit parse errors for the branches that are not chosen.
12/// The `MacResult` instance below parses all branches, emitting any errors it encounters, but only
13/// keeps the parse result for the selected branch.
14struct CfgSelectResult<'cx, 'sess> {
15    ecx: &'cx mut ExtCtxt<'sess>,
16    site_span: Span,
17    selected_tts: TokenStream,
18    selected_span: Span,
19    other_branches: CfgSelectBranches,
20}
21
22fn tts_to_mac_result<'cx, 'sess>(
23    ecx: &'cx mut ExtCtxt<'sess>,
24    site_span: Span,
25    tts: TokenStream,
26    span: Span,
27) -> Box<dyn MacResult + 'cx> {
28    match ExpandResult::from_tts(ecx, tts, site_span, span, Ident::with_dummy_span(sym::cfg_select))
29    {
30        ExpandResult::Ready(x) => x,
31        _ => {
    ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
            format_args!("from_tts always returns Ready")));
}unreachable!("from_tts always returns Ready"),
32    }
33}
34
35macro_rules! forward_to_parser_any_macro {
36    ($method_name:ident, $ret_ty:ty) => {
37        fn $method_name(self: Box<Self>) -> Option<$ret_ty> {
38            let CfgSelectResult { ecx, site_span, selected_tts, selected_span, .. } = *self;
39
40            for (tts, span) in self.other_branches.into_iter_tts() {
41                let _ = tts_to_mac_result(ecx, site_span, tts, span).$method_name();
42            }
43
44            tts_to_mac_result(ecx, site_span, selected_tts, selected_span).$method_name()
45        }
46    };
47}
48
49impl<'cx, 'sess> MacResult for CfgSelectResult<'cx, 'sess> {
50    fn make_expr(self: Box<Self>) -> Option<Box<Expr>> {
    let CfgSelectResult { ecx, site_span, selected_tts, selected_span, .. } =
        *self;
    for (tts, span) in self.other_branches.into_iter_tts() {
        let _ = tts_to_mac_result(ecx, site_span, tts, span).make_expr();
    }
    tts_to_mac_result(ecx, site_span, selected_tts, selected_span).make_expr()
}forward_to_parser_any_macro!(make_expr, Box<Expr>);
51    fn make_stmts(self: Box<Self>) -> Option<SmallVec<[ast::Stmt; 1]>> {
    let CfgSelectResult { ecx, site_span, selected_tts, selected_span, .. } =
        *self;
    for (tts, span) in self.other_branches.into_iter_tts() {
        let _ = tts_to_mac_result(ecx, site_span, tts, span).make_stmts();
    }
    tts_to_mac_result(ecx, site_span, selected_tts,
            selected_span).make_stmts()
}forward_to_parser_any_macro!(make_stmts, SmallVec<[ast::Stmt; 1]>);
52    fn make_items(self: Box<Self>) -> Option<SmallVec<[Box<ast::Item>; 1]>> {
    let CfgSelectResult { ecx, site_span, selected_tts, selected_span, .. } =
        *self;
    for (tts, span) in self.other_branches.into_iter_tts() {
        let _ = tts_to_mac_result(ecx, site_span, tts, span).make_items();
    }
    tts_to_mac_result(ecx, site_span, selected_tts,
            selected_span).make_items()
}forward_to_parser_any_macro!(make_items, SmallVec<[Box<ast::Item>; 1]>);
53
54    fn make_impl_items(self: Box<Self>)
    -> Option<SmallVec<[Box<ast::AssocItem>; 1]>> {
    let CfgSelectResult { ecx, site_span, selected_tts, selected_span, .. } =
        *self;
    for (tts, span) in self.other_branches.into_iter_tts() {
        let _ =
            tts_to_mac_result(ecx, site_span, tts, span).make_impl_items();
    }
    tts_to_mac_result(ecx, site_span, selected_tts,
            selected_span).make_impl_items()
}forward_to_parser_any_macro!(make_impl_items, SmallVec<[Box<ast::AssocItem>; 1]>);
55    fn make_trait_impl_items(self: Box<Self>)
    -> Option<SmallVec<[Box<ast::AssocItem>; 1]>> {
    let CfgSelectResult { ecx, site_span, selected_tts, selected_span, .. } =
        *self;
    for (tts, span) in self.other_branches.into_iter_tts() {
        let _ =
            tts_to_mac_result(ecx, site_span, tts,
                    span).make_trait_impl_items();
    }
    tts_to_mac_result(ecx, site_span, selected_tts,
            selected_span).make_trait_impl_items()
}forward_to_parser_any_macro!(make_trait_impl_items, SmallVec<[Box<ast::AssocItem>; 1]>);
56    fn make_trait_items(self: Box<Self>)
    -> Option<SmallVec<[Box<ast::AssocItem>; 1]>> {
    let CfgSelectResult { ecx, site_span, selected_tts, selected_span, .. } =
        *self;
    for (tts, span) in self.other_branches.into_iter_tts() {
        let _ =
            tts_to_mac_result(ecx, site_span, tts, span).make_trait_items();
    }
    tts_to_mac_result(ecx, site_span, selected_tts,
            selected_span).make_trait_items()
}forward_to_parser_any_macro!(make_trait_items, SmallVec<[Box<ast::AssocItem>; 1]>);
57    fn make_foreign_items(self: Box<Self>)
    -> Option<SmallVec<[Box<ast::ForeignItem>; 1]>> {
    let CfgSelectResult { ecx, site_span, selected_tts, selected_span, .. } =
        *self;
    for (tts, span) in self.other_branches.into_iter_tts() {
        let _ =
            tts_to_mac_result(ecx, site_span, tts, span).make_foreign_items();
    }
    tts_to_mac_result(ecx, site_span, selected_tts,
            selected_span).make_foreign_items()
}forward_to_parser_any_macro!(make_foreign_items, SmallVec<[Box<ast::ForeignItem>; 1]>);
58
59    fn make_ty(self: Box<Self>) -> Option<Box<ast::Ty>> {
    let CfgSelectResult { ecx, site_span, selected_tts, selected_span, .. } =
        *self;
    for (tts, span) in self.other_branches.into_iter_tts() {
        let _ = tts_to_mac_result(ecx, site_span, tts, span).make_ty();
    }
    tts_to_mac_result(ecx, site_span, selected_tts, selected_span).make_ty()
}forward_to_parser_any_macro!(make_ty, Box<ast::Ty>);
60    fn make_pat(self: Box<Self>) -> Option<Box<ast::Pat>> {
    let CfgSelectResult { ecx, site_span, selected_tts, selected_span, .. } =
        *self;
    for (tts, span) in self.other_branches.into_iter_tts() {
        let _ = tts_to_mac_result(ecx, site_span, tts, span).make_pat();
    }
    tts_to_mac_result(ecx, site_span, selected_tts, selected_span).make_pat()
}forward_to_parser_any_macro!(make_pat, Box<ast::Pat>);
61}
62
63pub(super) fn expand_cfg_select<'cx>(
64    ecx: &'cx mut ExtCtxt<'_>,
65    sp: Span,
66    tts: TokenStream,
67) -> MacroExpanderResult<'cx> {
68    ExpandResult::Ready(
69        match parse_cfg_select(
70            &mut ecx.new_parser_from_tts(tts),
71            ecx.sess,
72            Some(ecx.ecfg.features),
73            ecx.current_expansion.lint_node_id,
74        ) {
75            Ok(mut branches) => {
76                if let Some((selected_tts, selected_span)) = branches.pop_first_match(|cfg| {
77                    #[allow(non_exhaustive_omitted_patterns)] match attr::eval_config_entry(&ecx.sess,
        cfg) {
    EvalConfigResult::True => true,
    _ => false,
}matches!(attr::eval_config_entry(&ecx.sess, cfg), EvalConfigResult::True)
78                }) {
79                    let mac = CfgSelectResult {
80                        ecx,
81                        selected_tts,
82                        selected_span,
83                        other_branches: branches,
84                        site_span: sp,
85                    };
86                    return ExpandResult::Ready(Box::new(mac));
87                } else {
88                    // Emit a compiler error when none of the predicates matched.
89                    let guar = ecx.dcx().emit_err(CfgSelectNoMatches { span: sp });
90                    DummyResult::any(sp, guar)
91                }
92            }
93            Err(guar) => DummyResult::any(sp, guar),
94        },
95    )
96}