use rustc_ast::token::Token; use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_ast::util::classify; use rustc_ast::{MetaItemInner, token}; use rustc_errors::PResult; use rustc_span::Span; use crate::exp; use crate::parser::{AttrWrapper, ForceCollect, Parser, Restrictions, Trailing, UsePreAttrPos}; pub enum CfgSelectPredicate { Cfg(MetaItemInner), Wildcard(Token), } #[derive(Default)] pub struct CfgSelectBranches { /// All the conditional branches. pub reachable: Vec<(MetaItemInner, TokenStream, Span)>, /// The first wildcard `_ => { ... }` branch. pub wildcard: Option<(Token, TokenStream, Span)>, /// All branches after the first wildcard, including further wildcards. /// These branches are kept for formatting. pub unreachable: Vec<(CfgSelectPredicate, TokenStream, Span)>, } /// Parses a `TokenTree` consisting either of `{ /* ... */ }` (and strip the braces) or an /// expression followed by a comma (and strip the comma). fn parse_token_tree<'a>(p: &mut Parser<'a>) -> PResult<'a, TokenStream> { if p.token == token::OpenBrace { // Strip the outer '{' and '}'. match p.parse_token_tree() { TokenTree::Token(..) => unreachable!("because of the expect above"), TokenTree::Delimited(.., tts) => return Ok(tts), } } let expr = p.collect_tokens(None, AttrWrapper::empty(), ForceCollect::Yes, |p, _| { p.parse_expr_res(Restrictions::STMT_EXPR, AttrWrapper::empty()) .map(|(expr, _)| (expr, Trailing::No, UsePreAttrPos::No)) })?; if !classify::expr_is_complete(&expr) && p.token != token::CloseBrace && p.token != token::Eof { p.expect(exp!(Comma))?; } else { let _ = p.eat(exp!(Comma)); } Ok(TokenStream::from_ast(&expr)) } pub fn parse_cfg_select<'a>(p: &mut Parser<'a>) -> PResult<'a, CfgSelectBranches> { let mut branches = CfgSelectBranches::default(); while p.token != token::Eof { if p.eat_keyword(exp!(Underscore)) { let underscore = p.prev_token; p.expect(exp!(FatArrow))?; let tts = parse_token_tree(p)?; let span = underscore.span.to(p.token.span); match branches.wildcard { None => branches.wildcard = Some((underscore, tts, span)), Some(_) => { branches.unreachable.push((CfgSelectPredicate::Wildcard(underscore), tts, span)) } } } else { let meta_item = p.parse_meta_item_inner()?; p.expect(exp!(FatArrow))?; let tts = parse_token_tree(p)?; let span = meta_item.span().to(p.token.span); match branches.wildcard { None => branches.reachable.push((meta_item, tts, span)), Some(_) => { branches.unreachable.push((CfgSelectPredicate::Cfg(meta_item), tts, span)) } } } } Ok(branches) }