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) => {
68 parser.bump();
70 continue;
71 }
72 Err(err) => {
73 err.cancel();
74 parser.psess.dcx().reset_err_count();
75 return Err(
76 "Expected item inside cfg_select block, but failed to parse it as an item",
77 );
78 }
79 };
80 if let ast::ItemKind::Mod(..) = item.kind {
81 items.push(item);
82 }
83 }
84
85 if !parser.eat(exp!(CloseBrace)) {
86 return Err("Expected a closing brace");
87 }
88
89 if parser.eat(exp!(Eof)) {
90 break;
91 }
92 }
93
94 Ok(items)
95}
96
97pub(crate) enum CfgSelectFormatPredicate {
99 Cfg(ast::MetaItemInner),
101 Wildcard(Span),
103}
104
105impl Spanned for CfgSelectFormatPredicate {
106 fn span(&self) -> rustc_span::Span {
107 match self {
108 Self::Cfg(meta_item_inner) => meta_item_inner.span(),
109 Self::Wildcard(span) => *span,
110 }
111 }
112}
113
114pub(crate) struct CfgSelectArm {
116 pub(crate) predicate: CfgSelectFormatPredicate,
118 pub(crate) arrow: Token,
120 pub(crate) expr: Box<ast::Expr>,
122 pub(crate) trailing_comma: Option<Span>,
125}
126
127impl PartialEq for &CfgSelectArm {
128 fn eq(&self, other: &Self) -> bool {
129 self.span() == other.span()
131 }
132}
133
134impl Spanned for CfgSelectArm {
135 fn span(&self) -> Span {
136 self.predicate
137 .span()
138 .with_hi(if let Some(comma) = self.trailing_comma {
139 comma.hi()
140 } else {
141 self.expr.span.hi()
142 })
143 }
144}
145
146impl std::fmt::Debug for CfgSelectArm {
147 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
148 match &self.predicate {
149 CfgSelectFormatPredicate::Cfg(cfg_entry) => cfg_entry.fmt(f)?,
150 CfgSelectFormatPredicate::Wildcard(t) => t.fmt(f)?,
151 };
152 write!(f, "=> {:?}", self.expr)
153 }
154}
155
156pub(crate) fn parse_cfg_select_arms(
159 psess: &ParseSess,
160 ts: TokenStream,
161) -> Option<Vec<CfgSelectArm>> {
162 let mut cfg_select_predicates = vec![];
163 let mut parser = build_stream_parser(psess.inner(), ts);
164
165 while parser.token != token::Eof {
166 let predicate = if parser.eat_keyword(exp!(Underscore)) {
167 CfgSelectFormatPredicate::Wildcard(parser.prev_token.span)
168 } else {
169 let Ok(meta_item) = parser.parse_meta_item_inner().map_err(|e| e.cancel()) else {
170 debug!("Failed to parse cfg entry in cfg_select! predicate");
171 return None;
172 };
173 CfgSelectFormatPredicate::Cfg(meta_item)
174 };
175
176 if let Err(e) = parser.expect(exp!(FatArrow)) {
177 e.cancel();
178 debug!("Expected to find a `=>` after cfg_selec! predicate.");
179 return None;
180 };
181
182 let arrow = parser.prev_token;
183
184 let Ok(expr) = parser.parse_expr().map_err(|e| e.cancel()) else {
185 debug!("Couldn't parse cfg_select! arm body after `=>`.");
186 return None;
187 };
188
189 let trailing_comma = if parser.eat(exp!(Comma)) {
190 Some(parser.prev_token.span)
191 } else {
192 None
193 };
194
195 let arm = CfgSelectArm {
196 predicate,
197 arrow,
198 expr,
199 trailing_comma,
200 };
201
202 cfg_select_predicates.push(arm);
203 }
204 Some(cfg_select_predicates)
205}