rustc_parse/parser/
cfg_select.rs

1use rustc_ast::token::Token;
2use rustc_ast::tokenstream::{TokenStream, TokenTree};
3use rustc_ast::{MetaItemInner, token};
4use rustc_errors::PResult;
5use rustc_span::Span;
6
7use crate::exp;
8use crate::parser::Parser;
9
10pub enum CfgSelectPredicate {
11    Cfg(MetaItemInner),
12    Wildcard(Token),
13}
14
15#[derive(Default)]
16pub struct CfgSelectBranches {
17    /// All the conditional branches.
18    pub reachable: Vec<(MetaItemInner, TokenStream, Span)>,
19    /// The first wildcard `_ => { ... }` branch.
20    pub wildcard: Option<(Token, TokenStream, Span)>,
21    /// All branches after the first wildcard, including further wildcards.
22    /// These branches are kept for formatting.
23    pub unreachable: Vec<(CfgSelectPredicate, TokenStream, Span)>,
24}
25
26/// Parses a `TokenTree` that must be of the form `{ /* ... */ }`, and returns a `TokenStream` where
27/// the surrounding braces are stripped.
28fn parse_token_tree<'a>(p: &mut Parser<'a>) -> PResult<'a, TokenStream> {
29    // Generate an error if the `=>` is not followed by `{`.
30    if p.token != token::OpenBrace {
31        p.expect(exp!(OpenBrace))?;
32    }
33
34    // Strip the outer '{' and '}'.
35    match p.parse_token_tree() {
36        TokenTree::Token(..) => unreachable!("because of the expect above"),
37        TokenTree::Delimited(.., tts) => Ok(tts),
38    }
39}
40
41pub fn parse_cfg_select<'a>(p: &mut Parser<'a>) -> PResult<'a, CfgSelectBranches> {
42    let mut branches = CfgSelectBranches::default();
43
44    while p.token != token::Eof {
45        if p.eat_keyword(exp!(Underscore)) {
46            let underscore = p.prev_token;
47            p.expect(exp!(FatArrow))?;
48
49            let tts = parse_token_tree(p)?;
50            let span = underscore.span.to(p.token.span);
51
52            match branches.wildcard {
53                None => branches.wildcard = Some((underscore, tts, span)),
54                Some(_) => {
55                    branches.unreachable.push((CfgSelectPredicate::Wildcard(underscore), tts, span))
56                }
57            }
58        } else {
59            let meta_item = p.parse_meta_item_inner()?;
60            p.expect(exp!(FatArrow))?;
61
62            let tts = parse_token_tree(p)?;
63            let span = meta_item.span().to(p.token.span);
64
65            match branches.wildcard {
66                None => branches.reachable.push((meta_item, tts, span)),
67                Some(_) => {
68                    branches.unreachable.push((CfgSelectPredicate::Cfg(meta_item), tts, span))
69                }
70            }
71        }
72    }
73
74    Ok(branches)
75}