rustc_parse/parser/
cfg_select.rs

1use rustc_ast::token::Token;
2use rustc_ast::tokenstream::{TokenStream, TokenTree};
3use rustc_ast::util::classify;
4use rustc_ast::{MetaItemInner, token};
5use rustc_errors::PResult;
6use rustc_span::Span;
7
8use crate::exp;
9use crate::parser::{AttrWrapper, ForceCollect, Parser, Restrictions, Trailing, UsePreAttrPos};
10
11pub enum CfgSelectPredicate {
12    Cfg(MetaItemInner),
13    Wildcard(Token),
14}
15
16#[derive(Default)]
17pub struct CfgSelectBranches {
18    /// All the conditional branches.
19    pub reachable: Vec<(MetaItemInner, TokenStream, Span)>,
20    /// The first wildcard `_ => { ... }` branch.
21    pub wildcard: Option<(Token, TokenStream, Span)>,
22    /// All branches after the first wildcard, including further wildcards.
23    /// These branches are kept for formatting.
24    pub unreachable: Vec<(CfgSelectPredicate, TokenStream, Span)>,
25}
26
27/// Parses a `TokenTree` consisting either of `{ /* ... */ }` (and strip the braces) or an
28/// expression followed by a comma (and strip the comma).
29fn parse_token_tree<'a>(p: &mut Parser<'a>) -> PResult<'a, TokenStream> {
30    if p.token == token::OpenBrace {
31        // Strip the outer '{' and '}'.
32        match p.parse_token_tree() {
33            TokenTree::Token(..) => unreachable!("because of the expect above"),
34            TokenTree::Delimited(.., tts) => return Ok(tts),
35        }
36    }
37    let expr = p.collect_tokens(None, AttrWrapper::empty(), ForceCollect::Yes, |p, _| {
38        p.parse_expr_res(Restrictions::STMT_EXPR, AttrWrapper::empty())
39            .map(|(expr, _)| (expr, Trailing::No, UsePreAttrPos::No))
40    })?;
41    if !classify::expr_is_complete(&expr) && p.token != token::CloseBrace && p.token != token::Eof {
42        p.expect(exp!(Comma))?;
43    } else {
44        let _ = p.eat(exp!(Comma));
45    }
46    Ok(TokenStream::from_ast(&expr))
47}
48
49pub fn parse_cfg_select<'a>(p: &mut Parser<'a>) -> PResult<'a, CfgSelectBranches> {
50    let mut branches = CfgSelectBranches::default();
51
52    while p.token != token::Eof {
53        if p.eat_keyword(exp!(Underscore)) {
54            let underscore = p.prev_token;
55            p.expect(exp!(FatArrow))?;
56
57            let tts = parse_token_tree(p)?;
58            let span = underscore.span.to(p.token.span);
59
60            match branches.wildcard {
61                None => branches.wildcard = Some((underscore, tts, span)),
62                Some(_) => {
63                    branches.unreachable.push((CfgSelectPredicate::Wildcard(underscore), tts, span))
64                }
65            }
66        } else {
67            let meta_item = p.parse_meta_item_inner()?;
68            p.expect(exp!(FatArrow))?;
69
70            let tts = parse_token_tree(p)?;
71            let span = meta_item.span().to(p.token.span);
72
73            match branches.wildcard {
74                None => branches.reachable.push((meta_item, tts, span)),
75                Some(_) => {
76                    branches.unreachable.push((CfgSelectPredicate::Cfg(meta_item), tts, span))
77                }
78            }
79        }
80    }
81
82    Ok(branches)
83}