rustfmt_nightly/parse/macros/
cfg_select.rs1use std::panic::{AssertUnwindSafe, catch_unwind};
6
7use rustc_ast::ast;
8use rustc_ast::token;
9use rustc_ast::token::{Token, TokenKind};
10use rustc_ast::tokenstream::TokenStream;
11use rustc_parse::exp;
12use rustc_parse::parser::{AllowConstBlockItems, ForceCollect};
13use rustc_span::Span;
14use tracing::debug;
15
16use crate::parse::macros::build_stream_parser;
17use crate::parse::session::ParseSess;
18use crate::spanned::Spanned;
19
20pub(crate) fn parse_items_from_cfg_select<'a>(
21 psess: &'a ParseSess,
22 mac: &'a ast::MacCall,
23) -> Result<Vec<ast::Item>, &'static str> {
24 match catch_unwind(AssertUnwindSafe(|| {
25 parse_items_from_cfg_select_inner(psess, mac)
26 })) {
27 Ok(Ok(items)) => Ok(items),
28 Ok(err @ Err(_)) => err,
29 Err(..) => Err("failed to parse cfg_select!"),
30 }
31}
32
33fn parse_items_from_cfg_select_inner<'a>(
34 psess: &'a ParseSess,
35 mac: &'a ast::MacCall,
36) -> Result<Vec<ast::Item>, &'static str> {
37 let ts = mac.args.tokens.clone();
38 let mut parser = build_stream_parser(psess.inner(), ts);
39
40 if parser.token == TokenKind::OpenBrace {
41 return Err("Expression position cfg_select! not yet supported");
42 }
43
44 let mut items = vec![];
45
46 while parser.token.kind != TokenKind::Eof {
47 if !parser.eat_keyword(exp!(Underscore)) {
48 parser.parse_attr_item(ForceCollect::No).map_err(|e| {
49 e.cancel();
50 "Failed to parse attr item"
51 })?;
52 }
53
54 if !parser.eat(exp!(FatArrow)) {
55 return Err("Expected a fat arrow");
56 }
57
58 if !parser.eat(exp!(OpenBrace)) {
59 return Err("Expected an opening brace");
60 }
61
62 while parser.token != TokenKind::CloseBrace && parser.token.kind != TokenKind::Eof {
63 let item = match parser
64 .parse_item(ForceCollect::No, AllowConstBlockItems::DoesNotMatter)
65 {
66 Ok(Some(item_ptr)) => *item_ptr,
67 Ok(None) => continue,
68 Err(err) => {
69 err.cancel();
70 parser.psess.dcx().reset_err_count();
71 return Err(
72 "Expected item inside cfg_select block, but failed to parse it as an item",
73 );
74 }
75 };
76 if let ast::ItemKind::Mod(..) = item.kind {
77 items.push(item);
78 }
79 }
80
81 if !parser.eat(exp!(CloseBrace)) {
82 return Err("Expected a closing brace");
83 }
84
85 if parser.eat(exp!(Eof)) {
86 break;
87 }
88 }
89
90 Ok(items)
91}
92
93pub(crate) enum CfgSelectFormatPredicate {
95 Cfg(ast::MetaItemInner),
97 Wildcard(Span),
99}
100
101impl Spanned for CfgSelectFormatPredicate {
102 fn span(&self) -> rustc_span::Span {
103 match self {
104 Self::Cfg(meta_item_inner) => meta_item_inner.span(),
105 Self::Wildcard(span) => *span,
106 }
107 }
108}
109
110pub(crate) struct CfgSelectArm {
112 pub(crate) predicate: CfgSelectFormatPredicate,
114 pub(crate) arrow: Token,
116 pub(crate) expr: Box<ast::Expr>,
118 pub(crate) trailing_comma: Option<Span>,
121}
122
123impl PartialEq for &CfgSelectArm {
124 fn eq(&self, other: &Self) -> bool {
125 self.span() == other.span()
127 }
128}
129
130impl Spanned for CfgSelectArm {
131 fn span(&self) -> Span {
132 self.predicate
133 .span()
134 .with_hi(if let Some(comma) = self.trailing_comma {
135 comma.hi()
136 } else {
137 self.expr.span.hi()
138 })
139 }
140}
141
142impl std::fmt::Debug for CfgSelectArm {
143 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
144 match &self.predicate {
145 CfgSelectFormatPredicate::Cfg(cfg_entry) => cfg_entry.fmt(f)?,
146 CfgSelectFormatPredicate::Wildcard(t) => t.fmt(f)?,
147 };
148 write!(f, "=> {:?}", self.expr)
149 }
150}
151
152pub(crate) fn parse_cfg_select_arms(
155 psess: &ParseSess,
156 ts: TokenStream,
157) -> Option<Vec<CfgSelectArm>> {
158 let mut cfg_select_predicates = vec![];
159 let mut parser = build_stream_parser(psess.inner(), ts);
160
161 while parser.token != token::Eof {
162 let predicate = if parser.eat_keyword(exp!(Underscore)) {
163 CfgSelectFormatPredicate::Wildcard(parser.prev_token.span)
164 } else {
165 let Ok(meta_item) = parser.parse_meta_item_inner().map_err(|e| e.cancel()) else {
166 debug!("Failed to parse cfg entry in cfg_select! predicate");
167 return None;
168 };
169 CfgSelectFormatPredicate::Cfg(meta_item)
170 };
171
172 if let Err(e) = parser.expect(exp!(FatArrow)) {
173 e.cancel();
174 debug!("Expected to find a `=>` after cfg_selec! predicate.");
175 return None;
176 };
177
178 let arrow = parser.prev_token;
179
180 let Ok(expr) = parser.parse_expr().map_err(|e| e.cancel()) else {
181 debug!("Couldn't parse cfg_select! arm body after `=>`.");
182 return None;
183 };
184
185 let trailing_comma = if parser.eat(exp!(Comma)) {
186 Some(parser.prev_token.span)
187 } else {
188 None
189 };
190
191 let arm = CfgSelectArm {
192 predicate,
193 arrow,
194 expr,
195 trailing_comma,
196 };
197
198 cfg_select_predicates.push(arm);
199 }
200 Some(cfg_select_predicates)
201}