rustc_attr_parsing/attributes/
cfg_select.rs

1use rustc_ast::token::Token;
2use rustc_ast::tokenstream::TokenStream;
3use rustc_ast::{AttrStyle, NodeId, token};
4use rustc_feature::{AttributeTemplate, Features};
5use rustc_hir::attrs::CfgEntry;
6use rustc_hir::{AttrPath, Target};
7use rustc_parse::exp;
8use rustc_parse::parser::Parser;
9use rustc_session::Session;
10use rustc_span::{ErrorGuaranteed, Span, sym};
11
12use crate::parser::MetaItemOrLitParser;
13use crate::{AttributeParser, ParsedDescription, ShouldEmit, parse_cfg_entry};
14
15pub enum CfgSelectPredicate {
16    Cfg(CfgEntry),
17    Wildcard(Token),
18}
19
20#[derive(#[automatically_derived]
impl ::core::default::Default for CfgSelectBranches {
    #[inline]
    fn default() -> CfgSelectBranches {
        CfgSelectBranches {
            reachable: ::core::default::Default::default(),
            wildcard: ::core::default::Default::default(),
            unreachable: ::core::default::Default::default(),
        }
    }
}Default)]
21pub struct CfgSelectBranches {
22    /// All the conditional branches.
23    pub reachable: Vec<(CfgEntry, TokenStream, Span)>,
24    /// The first wildcard `_ => { ... }` branch.
25    pub wildcard: Option<(Token, TokenStream, Span)>,
26    /// All branches after the first wildcard, including further wildcards.
27    /// These branches are kept for formatting.
28    pub unreachable: Vec<(CfgSelectPredicate, TokenStream, Span)>,
29}
30
31impl CfgSelectBranches {
32    /// Removes the top-most branch for which `predicate` returns `true`,
33    /// or the wildcard if none of the reachable branches satisfied the predicate.
34    pub fn pop_first_match<F>(&mut self, predicate: F) -> Option<(TokenStream, Span)>
35    where
36        F: Fn(&CfgEntry) -> bool,
37    {
38        for (index, (cfg, _, _)) in self.reachable.iter().enumerate() {
39            if predicate(cfg) {
40                let matched = self.reachable.remove(index);
41                return Some((matched.1, matched.2));
42            }
43        }
44
45        self.wildcard.take().map(|(_, tts, span)| (tts, span))
46    }
47
48    /// Consume this value and iterate over all the `TokenStream`s that it stores.
49    pub fn into_iter_tts(self) -> impl Iterator<Item = (TokenStream, Span)> {
50        let it1 = self.reachable.into_iter().map(|(_, tts, span)| (tts, span));
51        let it2 = self.wildcard.into_iter().map(|(_, tts, span)| (tts, span));
52        let it3 = self.unreachable.into_iter().map(|(_, tts, span)| (tts, span));
53
54        it1.chain(it2).chain(it3)
55    }
56}
57
58pub fn parse_cfg_select(
59    p: &mut Parser<'_>,
60    sess: &Session,
61    features: Option<&Features>,
62    lint_node_id: NodeId,
63) -> Result<CfgSelectBranches, ErrorGuaranteed> {
64    let mut branches = CfgSelectBranches::default();
65
66    while p.token != token::Eof {
67        if p.eat_keyword(::rustc_parse::parser::token_type::ExpKeywordPair {
    kw: rustc_span::symbol::kw::Underscore,
    token_type: ::rustc_parse::parser::token_type::TokenType::KwUnderscore,
}exp!(Underscore)) {
68            let underscore = p.prev_token;
69            p.expect(::rustc_parse::parser::token_type::ExpTokenPair {
    tok: rustc_ast::token::FatArrow,
    token_type: ::rustc_parse::parser::token_type::TokenType::FatArrow,
}exp!(FatArrow)).map_err(|e| e.emit())?;
70
71            let tts = p.parse_delimited_token_tree().map_err(|e| e.emit())?;
72            let span = underscore.span.to(p.token.span);
73
74            match branches.wildcard {
75                None => branches.wildcard = Some((underscore, tts, span)),
76                Some(_) => {
77                    branches.unreachable.push((CfgSelectPredicate::Wildcard(underscore), tts, span))
78                }
79            }
80        } else {
81            let meta = MetaItemOrLitParser::parse_single(p, ShouldEmit::ErrorsAndLints)
82                .map_err(|diag| diag.emit())?;
83            let cfg_span = meta.span();
84            let cfg = AttributeParser::parse_single_args(
85                sess,
86                cfg_span,
87                cfg_span,
88                AttrStyle::Inner,
89                AttrPath { segments: <[_]>::into_vec(::alloc::boxed::box_new([sym::cfg_select]))vec![sym::cfg_select].into_boxed_slice(), span: cfg_span },
90                None,
91                ParsedDescription::Macro,
92                cfg_span,
93                lint_node_id,
94                // Doesn't matter what the target actually is here.
95                Target::Crate,
96                features,
97                ShouldEmit::ErrorsAndLints,
98                &meta,
99                parse_cfg_entry,
100                &AttributeTemplate::default(),
101            )?;
102
103            p.expect(::rustc_parse::parser::token_type::ExpTokenPair {
    tok: rustc_ast::token::FatArrow,
    token_type: ::rustc_parse::parser::token_type::TokenType::FatArrow,
}exp!(FatArrow)).map_err(|e| e.emit())?;
104
105            let tts = p.parse_delimited_token_tree().map_err(|e| e.emit())?;
106            let span = cfg_span.to(p.token.span);
107
108            match branches.wildcard {
109                None => branches.reachable.push((cfg, tts, span)),
110                Some(_) => branches.unreachable.push((CfgSelectPredicate::Cfg(cfg), tts, span)),
111            }
112        }
113    }
114
115    Ok(branches)
116}