|
1 | 1 | use rustc_ast::tokenstream::TokenStream; |
2 | | -use rustc_ast::{Expr, ast, token}; |
3 | | -use rustc_ast_pretty::pprust; |
| 2 | +use rustc_ast::{Expr, ast}; |
4 | 3 | use rustc_attr_parsing as attr; |
5 | 4 | use rustc_attr_parsing::{ |
6 | 5 | CfgSelectBranches, CfgSelectPredicate, EvalConfigResult, parse_cfg_select, |
7 | 6 | }; |
8 | 7 | use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacResult, MacroExpanderResult}; |
9 | | -use rustc_parse::parser::ForceCollect; |
10 | | -use rustc_span::Span; |
| 8 | +use rustc_span::{Ident, Span, sym}; |
11 | 9 | use smallvec::SmallVec; |
12 | 10 |
|
13 | | -use crate::errors; |
14 | 11 | use crate::errors::{CfgSelectNoMatches, CfgSelectUnreachable}; |
15 | 12 |
|
16 | 13 | /// This intermediate structure is used to emit parse errors for the branches that are not chosen. |
17 | 14 | /// The `MacResult` instance below parses all branches, emitting any errors it encounters, but only |
18 | 15 | /// keeps the parse result for the selected branch. |
19 | 16 | struct CfgSelectResult<'cx, 'sess> { |
20 | 17 | ecx: &'cx mut ExtCtxt<'sess>, |
| 18 | + site_span: Span, |
21 | 19 | selected_tts: TokenStream, |
| 20 | + selected_span: Span, |
22 | 21 | other_branches: CfgSelectBranches, |
23 | 22 | } |
24 | 23 |
|
| 24 | +fn 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 | + |
25 | 37 | impl<'cx, 'sess> MacResult for CfgSelectResult<'cx, 'sess> { |
26 | 38 | fn make_expr(self: Box<Self>) -> Option<Box<Expr>> { |
27 | | - for tts in self.other_branches.into_iter_tts() { |
28 | | - let mut p = self.ecx.new_parser_from_tts(tts); |
29 | | - if let Err(diag) = p.parse_expr() { |
30 | | - diag.emit(); |
31 | | - return None; |
32 | | - } |
33 | | - } |
| 39 | + let CfgSelectResult { ecx, site_span, selected_tts, selected_span, .. } = *self; |
34 | 40 |
|
35 | | - let mut p = self.ecx.new_parser_from_tts(self.selected_tts); |
36 | | - p.parse_expr().ok() |
37 | | - } |
38 | | - |
39 | | - fn make_items(self: Box<Self>) -> Option<SmallVec<[Box<ast::Item>; 1]>> { |
40 | | - for tts in self.other_branches.into_iter_tts() { |
41 | | - let _ = make_items(self.ecx, tts, false); |
| 41 | + for (tts, span) in self.other_branches.into_iter_tts() { |
| 42 | + let _ = tts_to_mac_result(ecx, site_span, tts, span).make_expr(); |
42 | 43 | } |
43 | 44 |
|
44 | | - make_items(self.ecx, self.selected_tts, true) |
| 45 | + tts_to_mac_result(ecx, site_span, selected_tts, selected_span).make_expr() |
45 | 46 | } |
46 | | -} |
47 | 47 |
|
48 | | -fn make_items<'cx>( |
49 | | - ecx: &'cx mut ExtCtxt<'_>, |
50 | | - tts: TokenStream, |
51 | | - keep: bool, |
52 | | -) -> Option<SmallVec<[Box<ast::Item>; 1]>> { |
53 | | - let mut p = ecx.new_parser_from_tts(tts); |
54 | | - let mut ret = SmallVec::new(); |
55 | | - loop { |
56 | | - match p.parse_item(ForceCollect::No) { |
57 | | - Err(err) => { |
58 | | - err.emit(); |
59 | | - break; |
60 | | - } |
61 | | - Ok(Some(item)) => { |
62 | | - if keep { |
63 | | - ret.push(item) |
64 | | - } |
65 | | - } |
66 | | - Ok(None) => { |
67 | | - if p.token != token::Eof { |
68 | | - p.dcx().emit_err(errors::ExpectedItem { |
69 | | - span: p.token.span, |
70 | | - token: &pprust::token_to_string(&p.token), |
71 | | - }); |
72 | | - } |
| 48 | + fn make_items(self: Box<Self>) -> Option<SmallVec<[Box<ast::Item>; 1]>> { |
| 49 | + let CfgSelectResult { ecx, site_span, selected_tts, selected_span, .. } = *self; |
73 | 50 |
|
74 | | - break; |
75 | | - } |
| 51 | + for (tts, span) in self.other_branches.into_iter_tts() { |
| 52 | + let _ = tts_to_mac_result(ecx, site_span, tts, span).make_items(); |
76 | 53 | } |
| 54 | + |
| 55 | + tts_to_mac_result(ecx, site_span, selected_tts, selected_span).make_items() |
77 | 56 | } |
78 | | - Some(ret) |
79 | 57 | } |
80 | 58 |
|
81 | 59 | pub(super) fn expand_cfg_select<'cx>( |
@@ -103,10 +81,16 @@ pub(super) fn expand_cfg_select<'cx>( |
103 | 81 | } |
104 | 82 | } |
105 | 83 |
|
106 | | - if let Some(selected_tts) = branches.pop_first_match(|cfg| { |
| 84 | + if let Some((selected_tts, selected_span)) = branches.pop_first_match(|cfg| { |
107 | 85 | matches!(attr::eval_config_entry(&ecx.sess, cfg), EvalConfigResult::True) |
108 | 86 | }) { |
109 | | - let mac = CfgSelectResult { ecx, selected_tts, other_branches: branches }; |
| 87 | + let mac = CfgSelectResult { |
| 88 | + ecx, |
| 89 | + selected_tts, |
| 90 | + selected_span, |
| 91 | + other_branches: branches, |
| 92 | + site_span: sp, |
| 93 | + }; |
110 | 94 | return ExpandResult::Ready(Box::new(mac)); |
111 | 95 | } else { |
112 | 96 | // Emit a compiler error when none of the predicates matched. |
|
0 commit comments