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::{
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
13/// This intermediate structure is used to emit parse errors for the branches that are not chosen.
14/// The `MacResult` instance below parses all branches, emitting any errors it encounters, but only
15/// keeps the parse result for the selected branch.
16struct 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        _ => {
    ::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"),
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    Box<Self>
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>);
53    Box<Self>
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]>);
54    Box<Self>
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]>);
55
56    Box<Self>
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]>);
57    Box<Self>
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]>);
58    Box<Self>
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]>);
59    Box<Self>
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]>);
60
61    Box<Self>
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>);
62    Box<Self>
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>);
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                    // Warn for every unreachable predicate. We store the fully parsed branch for rustfmt.
80                    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                    #[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)
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                    // Emit a compiler error when none of the predicates matched.
103                    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}