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
14struct 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
63fn 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 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 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}