rustc_attr_parsing/attributes/
cfg_select.rs1use rustc_ast::token::Token;
2use rustc_ast::tokenstream::TokenStream;
3use rustc_ast::{AttrStyle, NodeId, token};
4use rustc_feature::{AttributeTemplate, Features};
5use rustc_hir::AttrPath;
6use rustc_hir::attrs::CfgEntry;
7use rustc_parse::exp;
8use rustc_parse::parser::Parser;
9use rustc_session::Session;
10use rustc_span::{ErrorGuaranteed, Ident, Span};
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(Default)]
21pub struct CfgSelectBranches {
22 pub reachable: Vec<(CfgEntry, TokenStream, Span)>,
24 pub wildcard: Option<(Token, TokenStream, Span)>,
26 pub unreachable: Vec<(CfgSelectPredicate, TokenStream, Span)>,
29}
30
31impl CfgSelectBranches {
32 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 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(exp!(Underscore)) {
68 let underscore = p.prev_token;
69 p.expect(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 {
90 segments: vec![Ident::from_str("cfg_select")].into_boxed_slice(),
91 span: cfg_span,
92 },
93 None,
94 ParsedDescription::Macro,
95 cfg_span,
96 lint_node_id,
97 features,
98 ShouldEmit::ErrorsAndLints,
99 &meta,
100 parse_cfg_entry,
101 &AttributeTemplate::default(),
102 )?;
103
104 p.expect(exp!(FatArrow)).map_err(|e| e.emit())?;
105
106 let tts = p.parse_delimited_token_tree().map_err(|e| e.emit())?;
107 let span = cfg_span.to(p.token.span);
108
109 match branches.wildcard {
110 None => branches.reachable.push((cfg, tts, span)),
111 Some(_) => branches.unreachable.push((CfgSelectPredicate::Cfg(cfg), tts, span)),
112 }
113 }
114 }
115
116 Ok(branches)
117}