Skip to main content

rustc_builtin_macros/
cfg_select.rs

1use rustc_ast::attr::AttrIdGenerator;
2use rustc_ast::tokenstream::TokenStream;
3use rustc_ast::{AttrKind, Expr, SyntheticAttr, ast};
4use rustc_attr_ir::CfgEntry;
5use rustc_attr_parsing as attr;
6use rustc_attr_parsing::{CfgSelectBranches, EvalConfigResult, parse_cfg_select};
7use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacResult, MacroExpanderResult};
8use rustc_expand::expand::DeclaredIdents;
9use rustc_span::{Ident, Span, sym};
10use smallvec::SmallVec;
11
12use crate::diagnostics::CfgSelectNoMatches;
13
14/// This intermediate structure is used to emit parse errors for the branches that are not chosen.
15/// The `MacResult` instance below parses all branches, emitting any errors it encounters, but only
16/// keeps the parse result for the selected branch.
17struct CfgSelectResult<'cx, 'sess> {
18    ecx: &'cx mut ExtCtxt<'sess>,
19    site_span: Span,
20    selected_tts: TokenStream,
21    selected_span: Span,
22    other_branches: CfgSelectBranches,
23    cfg_entry: CfgEntry,
24}
25
26fn tts_to_mac_result<'cx, 'sess>(
27    ecx: &'cx mut ExtCtxt<'sess>,
28    site_span: Span,
29    tts: TokenStream,
30    span: Span,
31) -> Box<dyn MacResult + 'cx> {
32    match ExpandResult::from_tts(ecx, tts, site_span, span, Ident::with_dummy_span(sym::cfg_select))
33    {
34        ExpandResult::Ready(x) => x,
35        _ => {
    ::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"),
36    }
37}
38
39macro_rules! forward_to_parser_any_macro {
40    ($method_name:ident, $ret_ty:ty, $other:expr, $selected:expr) => {
41        fn $method_name(self: Box<Self>) -> Option<$ret_ty> {
42            let CfgSelectResult { ecx, site_span, selected_tts, selected_span, cfg_entry, .. } =
43                *self;
44
45            for (cfg_entry, tts, span) in self.other_branches.into_iter_tts() {
46                let result = tts_to_mac_result(ecx, site_span, tts, span).$method_name();
47                $other(&mut *ecx, cfg_entry, span, result);
48            }
49
50            tts_to_mac_result(ecx, site_span, selected_tts, selected_span)
51                .$method_name()
52                .map(|elements| $selected(&mut *ecx, cfg_entry, elements))
53        }
54    };
55
56    ($method_name:ident, $ret_ty:ty) => {
57        forward_to_parser_any_macro!($method_name, $ret_ty, |_, _, _, _| {}, |_, _, elements| {
58            elements
59        });
60    };
61}
62
63/// Construct a `#[<cfg_trace>]` attribute from a `CfgEntry`. This allows us to keep track of items
64/// that were behind a `cfg_select!`, which is relevant for some diagnostics.
65fn mk_attr(g: &AttrIdGenerator, cfg_entry: CfgEntry) -> ast::Attribute {
66    let cfg_span = cfg_entry.span();
67    ast::Attribute {
68        kind: AttrKind::Synthetic(Box::new(SyntheticAttr::CfgAttrTrace(cfg_entry))),
69        id: g.mk_attr_id(),
70        style: ast::AttrStyle::Outer,
71        span: cfg_span,
72    }
73}
74
75impl<'cx, 'sess> MacResult for CfgSelectResult<'cx, 'sess> {
76    fn make_expr(self: Box<Self>) -> Option<Box<Expr>> {
    let CfgSelectResult {
            ecx, site_span, selected_tts, selected_span, cfg_entry, .. } =
        *self;
    for (cfg_entry, tts, span) in self.other_branches.into_iter_tts() {
        let result = tts_to_mac_result(ecx, site_span, tts, span).make_expr();
        (|_, _, _, _| {})(&mut *ecx, cfg_entry, span, result);
    }
    tts_to_mac_result(ecx, site_span, selected_tts,
                selected_span).make_expr().map(|elements|
            (|_, _, elements| { elements })(&mut *ecx, cfg_entry, elements))
}forward_to_parser_any_macro!(make_expr, Box<Expr>);
77    fn make_stmts(self: Box<Self>) -> Option<SmallVec<[ast::Stmt; 1]>> {
    let CfgSelectResult {
            ecx, site_span, selected_tts, selected_span, cfg_entry, .. } =
        *self;
    for (cfg_entry, tts, span) in self.other_branches.into_iter_tts() {
        let result =
            tts_to_mac_result(ecx, site_span, tts, span).make_stmts();
        (|_, _, _, _| {})(&mut *ecx, cfg_entry, span, result);
    }
    tts_to_mac_result(ecx, site_span, selected_tts,
                selected_span).make_stmts().map(|elements|
            (|_, _, elements| { elements })(&mut *ecx, cfg_entry, elements))
}forward_to_parser_any_macro!(make_stmts, SmallVec<[ast::Stmt; 1]>);
78    fn make_items(self: Box<Self>) -> Option<SmallVec<[Box<ast::Item>; 1]>> {
    let CfgSelectResult {
            ecx, site_span, selected_tts, selected_span, cfg_entry, .. } =
        *self;
    for (cfg_entry, tts, span) in self.other_branches.into_iter_tts() {
        let result =
            tts_to_mac_result(ecx, site_span, tts, span).make_items();
        (|ecx: &mut ExtCtxt<'_>, cfg_entry: CfgEntry, _span: Span,
                items: Option<SmallVec<[Box<ast::Item>; 1]>>|
                if let Some(items) = items {
                    for item in items {
                        for name in item.declared_idents() {
                            ecx.resolver.append_stripped_cfg_item(ecx.current_expansion.lint_node_id,
                                name, cfg_entry.clone(), cfg_entry.span());
                        }
                    }
                })(&mut *ecx, cfg_entry, span, result);
    }
    tts_to_mac_result(ecx, site_span, selected_tts,
                selected_span).make_items().map(|elements|
            (|ecx: &mut ExtCtxt<'_>, cfg_entry: CfgEntry,
                    items: SmallVec<[Box<ast::Item>; 1]>|
                    {
                        items.into_iter().map(|mut item|
                                    {
                                        item.attrs.push(mk_attr(&ecx.sess.psess.attr_id_generator,
                                                cfg_entry.clone()));
                                        item
                                    }).collect()
                    })(&mut *ecx, cfg_entry, elements))
}forward_to_parser_any_macro!(
79        make_items,
80        SmallVec<[Box<ast::Item>; 1]>,
81        |ecx: &mut ExtCtxt<'_>,
82         cfg_entry: CfgEntry,
83         _span: Span,
84         items: Option<SmallVec<[Box<ast::Item>; 1]>>| if let Some(items) = items {
85            // Register item names that were not selected for error reporting. We do this
86            // for `#[cfg]` too.
87            for item in items {
88                for name in item.declared_idents() {
89                    ecx.resolver.append_stripped_cfg_item(
90                        ecx.current_expansion.lint_node_id,
91                        name,
92                        cfg_entry.clone(),
93                        cfg_entry.span(),
94                    );
95                }
96            }
97        },
98        |ecx: &mut ExtCtxt<'_>, cfg_entry: CfgEntry, items: SmallVec<[Box<ast::Item>; 1]>| {
99            items
100                .into_iter()
101                .map(|mut item| {
102                    item.attrs.push(mk_attr(&ecx.sess.psess.attr_id_generator, cfg_entry.clone()));
103                    item
104                })
105                .collect()
106        }
107    );
108
109    fn make_impl_items(self: Box<Self>)
    -> Option<SmallVec<[Box<ast::AssocItem>; 1]>> {
    let CfgSelectResult {
            ecx, site_span, selected_tts, selected_span, cfg_entry, .. } =
        *self;
    for (cfg_entry, tts, span) in self.other_branches.into_iter_tts() {
        let result =
            tts_to_mac_result(ecx, site_span, tts, span).make_impl_items();
        (|_, _, _, _| {})(&mut *ecx, cfg_entry, span, result);
    }
    tts_to_mac_result(ecx, site_span, selected_tts,
                selected_span).make_impl_items().map(|elements|
            (|_, _, elements| { elements })(&mut *ecx, cfg_entry, elements))
}forward_to_parser_any_macro!(make_impl_items, SmallVec<[Box<ast::AssocItem>; 1]>);
110    fn make_trait_impl_items(self: Box<Self>)
    -> Option<SmallVec<[Box<ast::AssocItem>; 1]>> {
    let CfgSelectResult {
            ecx, site_span, selected_tts, selected_span, cfg_entry, .. } =
        *self;
    for (cfg_entry, tts, span) in self.other_branches.into_iter_tts() {
        let result =
            tts_to_mac_result(ecx, site_span, tts,
                    span).make_trait_impl_items();
        (|_, _, _, _| {})(&mut *ecx, cfg_entry, span, result);
    }
    tts_to_mac_result(ecx, site_span, selected_tts,
                selected_span).make_trait_impl_items().map(|elements|
            (|_, _, elements| { elements })(&mut *ecx, cfg_entry, elements))
}forward_to_parser_any_macro!(make_trait_impl_items, SmallVec<[Box<ast::AssocItem>; 1]>);
111    fn make_trait_items(self: Box<Self>)
    -> Option<SmallVec<[Box<ast::AssocItem>; 1]>> {
    let CfgSelectResult {
            ecx, site_span, selected_tts, selected_span, cfg_entry, .. } =
        *self;
    for (cfg_entry, tts, span) in self.other_branches.into_iter_tts() {
        let result =
            tts_to_mac_result(ecx, site_span, tts, span).make_trait_items();
        (|_, _, _, _| {})(&mut *ecx, cfg_entry, span, result);
    }
    tts_to_mac_result(ecx, site_span, selected_tts,
                selected_span).make_trait_items().map(|elements|
            (|_, _, elements| { elements })(&mut *ecx, cfg_entry, elements))
}forward_to_parser_any_macro!(make_trait_items, SmallVec<[Box<ast::AssocItem>; 1]>);
112    fn make_foreign_items(self: Box<Self>)
    -> Option<SmallVec<[Box<ast::ForeignItem>; 1]>> {
    let CfgSelectResult {
            ecx, site_span, selected_tts, selected_span, cfg_entry, .. } =
        *self;
    for (cfg_entry, tts, span) in self.other_branches.into_iter_tts() {
        let result =
            tts_to_mac_result(ecx, site_span, tts, span).make_foreign_items();
        (|_, _, _, _| {})(&mut *ecx, cfg_entry, span, result);
    }
    tts_to_mac_result(ecx, site_span, selected_tts,
                selected_span).make_foreign_items().map(|elements|
            (|_, _, elements| { elements })(&mut *ecx, cfg_entry, elements))
}forward_to_parser_any_macro!(make_foreign_items, SmallVec<[Box<ast::ForeignItem>; 1]>);
113
114    fn make_ty(self: Box<Self>) -> Option<Box<ast::Ty>> {
    let CfgSelectResult {
            ecx, site_span, selected_tts, selected_span, cfg_entry, .. } =
        *self;
    for (cfg_entry, tts, span) in self.other_branches.into_iter_tts() {
        let result = tts_to_mac_result(ecx, site_span, tts, span).make_ty();
        (|_, _, _, _| {})(&mut *ecx, cfg_entry, span, result);
    }
    tts_to_mac_result(ecx, site_span, selected_tts,
                selected_span).make_ty().map(|elements|
            (|_, _, elements| { elements })(&mut *ecx, cfg_entry, elements))
}forward_to_parser_any_macro!(make_ty, Box<ast::Ty>);
115    fn make_pat(self: Box<Self>) -> Option<Box<ast::Pat>> {
    let CfgSelectResult {
            ecx, site_span, selected_tts, selected_span, cfg_entry, .. } =
        *self;
    for (cfg_entry, tts, span) in self.other_branches.into_iter_tts() {
        let result = tts_to_mac_result(ecx, site_span, tts, span).make_pat();
        (|_, _, _, _| {})(&mut *ecx, cfg_entry, span, result);
    }
    tts_to_mac_result(ecx, site_span, selected_tts,
                selected_span).make_pat().map(|elements|
            (|_, _, elements| { elements })(&mut *ecx, cfg_entry, elements))
}forward_to_parser_any_macro!(make_pat, Box<ast::Pat>);
116}
117
118pub(super) fn expand_cfg_select<'cx>(
119    ecx: &'cx mut ExtCtxt<'_>,
120    sp: Span,
121    tts: TokenStream,
122) -> MacroExpanderResult<'cx> {
123    ExpandResult::Ready(
124        match parse_cfg_select(
125            &mut ecx.new_parser_from_tts(tts),
126            ecx.sess,
127            Some(ecx.ecfg.features),
128            ecx.current_expansion.lint_node_id,
129        ) {
130            Ok(mut branches) => {
131                if let Some((cfg_entry, selected_tts, selected_span)) =
132                    branches.pop_first_match(|cfg| {
133                        #[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)
134                    })
135                {
136                    let mac = CfgSelectResult {
137                        ecx,
138                        selected_tts,
139                        selected_span,
140                        other_branches: branches,
141                        site_span: sp,
142                        cfg_entry,
143                    };
144                    return ExpandResult::Ready(Box::new(mac));
145                } else {
146                    // Emit a compiler error when none of the predicates matched.
147                    let guar = ecx.dcx().emit_err(CfgSelectNoMatches { span: sp });
148                    DummyResult::any(sp, guar)
149                }
150            }
151            Err(guar) => DummyResult::any(sp, guar),
152        },
153    )
154}