rustc_attr_parsing/attributes/
cfg_select.rs1use rustc_ast::token::Token;
2use rustc_ast::tokenstream::TokenStream;
3use rustc_ast::{AttrStyle, NodeId, token};
4use rustc_data_structures::fx::FxHashMap;
5use rustc_feature::{AttributeTemplate, Features};
6use rustc_hir::attrs::CfgEntry;
7use rustc_hir::{AttrPath, Target};
8use rustc_parse::exp;
9use rustc_parse::parser::{Parser, Recovery};
10use rustc_session::Session;
11use rustc_session::lint::BuiltinLintDiag;
12use rustc_session::lint::builtin::UNREACHABLE_CFG_SELECT_PREDICATES;
13use rustc_span::{ErrorGuaranteed, Span, Symbol, sym};
14
15use crate::parser::MetaItemOrLitParser;
16use crate::{AttributeParser, ParsedDescription, ShouldEmit, parse_cfg_entry};
17
18#[derive(#[automatically_derived]
impl ::core::clone::Clone for CfgSelectPredicate {
#[inline]
fn clone(&self) -> CfgSelectPredicate {
match self {
CfgSelectPredicate::Cfg(__self_0) =>
CfgSelectPredicate::Cfg(::core::clone::Clone::clone(__self_0)),
CfgSelectPredicate::Wildcard(__self_0) =>
CfgSelectPredicate::Wildcard(::core::clone::Clone::clone(__self_0)),
}
}
}Clone)]
19pub enum CfgSelectPredicate {
20 Cfg(CfgEntry),
21 Wildcard(Token),
22}
23
24impl CfgSelectPredicate {
25 fn span(&self) -> Span {
26 match self {
27 CfgSelectPredicate::Cfg(cfg_entry) => cfg_entry.span(),
28 CfgSelectPredicate::Wildcard(token) => token.span,
29 }
30 }
31}
32
33#[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)]
34pub struct CfgSelectBranches {
35 pub reachable: Vec<(CfgEntry, TokenStream, Span)>,
37 pub wildcard: Option<(Token, TokenStream, Span)>,
39 pub unreachable: Vec<(CfgSelectPredicate, TokenStream, Span)>,
42}
43
44impl CfgSelectBranches {
45 pub fn pop_first_match<F>(&mut self, predicate: F) -> Option<(TokenStream, Span)>
48 where
49 F: Fn(&CfgEntry) -> bool,
50 {
51 for (index, (cfg, _, _)) in self.reachable.iter().enumerate() {
52 if predicate(cfg) {
53 let matched = self.reachable.remove(index);
54 return Some((matched.1, matched.2));
55 }
56 }
57
58 self.wildcard.take().map(|(_, tts, span)| (tts, span))
59 }
60
61 pub fn into_iter_tts(self) -> impl Iterator<Item = (TokenStream, Span)> {
63 let it1 = self.reachable.into_iter().map(|(_, tts, span)| (tts, span));
64 let it2 = self.wildcard.into_iter().map(|(_, tts, span)| (tts, span));
65 let it3 = self.unreachable.into_iter().map(|(_, tts, span)| (tts, span));
66
67 it1.chain(it2).chain(it3)
68 }
69}
70
71pub fn parse_cfg_select(
72 p: &mut Parser<'_>,
73 sess: &Session,
74 features: Option<&Features>,
75 lint_node_id: NodeId,
76) -> Result<CfgSelectBranches, ErrorGuaranteed> {
77 let mut branches = CfgSelectBranches::default();
78
79 while p.token != token::Eof {
80 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)) {
81 let underscore = p.prev_token;
82 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())?;
83
84 let tts = p.parse_delimited_token_tree().map_err(|e| e.emit())?;
85 let span = underscore.span.to(p.token.span);
86
87 match branches.wildcard {
88 None => branches.wildcard = Some((underscore, tts, span)),
89 Some(_) => {
90 branches.unreachable.push((CfgSelectPredicate::Wildcard(underscore), tts, span))
91 }
92 }
93 } else {
94 let meta = MetaItemOrLitParser::parse_single(
95 p,
96 ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed },
97 )
98 .map_err(|diag| diag.emit())?;
99 let cfg_span = meta.span();
100 let cfg = AttributeParser::parse_single_args(
101 sess,
102 cfg_span,
103 cfg_span,
104 AttrStyle::Inner,
105 AttrPath { segments: ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[sym::cfg_select]))vec![sym::cfg_select].into_boxed_slice(), span: cfg_span },
106 None,
107 ParsedDescription::Macro,
108 cfg_span,
109 lint_node_id,
110 Target::Crate,
112 features,
113 ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed },
114 &meta,
115 parse_cfg_entry,
116 &AttributeTemplate::default(),
117 )?;
118
119 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())?;
120
121 let tts = p.parse_delimited_token_tree().map_err(|e| e.emit())?;
122 let span = cfg_span.to(p.token.span);
123
124 match branches.wildcard {
125 None => branches.reachable.push((cfg, tts, span)),
126 Some(_) => branches.unreachable.push((CfgSelectPredicate::Cfg(cfg), tts, span)),
127 }
128 }
129 }
130
131 if let Some(features) = features
132 && features.enabled(sym::cfg_select)
133 {
134 let it = branches
135 .reachable
136 .iter()
137 .map(|(entry, _, _)| CfgSelectPredicate::Cfg(entry.clone()))
138 .chain(branches.wildcard.as_ref().map(|(t, _, _)| CfgSelectPredicate::Wildcard(*t)))
139 .chain(
140 branches.unreachable.iter().map(|(entry, _, _)| CfgSelectPredicate::clone(entry)),
141 );
142
143 lint_unreachable(p, it, lint_node_id);
144 }
145
146 Ok(branches)
147}
148
149fn lint_unreachable(
150 p: &mut Parser<'_>,
151 predicates: impl Iterator<Item = CfgSelectPredicate>,
152 lint_node_id: NodeId,
153) {
154 let mut known = FxHashMap::<Symbol, bool>::default();
156 let mut wildcard_span = None;
157 let mut it = predicates;
158
159 let branch_is_unreachable = |predicate: CfgSelectPredicate, wildcard_span| {
160 let span = predicate.span();
161 p.psess.buffer_lint(
162 UNREACHABLE_CFG_SELECT_PREDICATES,
163 span,
164 lint_node_id,
165 BuiltinLintDiag::UnreachableCfg { span, wildcard_span },
166 );
167 };
168
169 for predicate in &mut it {
170 let CfgSelectPredicate::Cfg(ref cfg_entry) = predicate else {
171 wildcard_span = Some(predicate.span());
172 break;
173 };
174
175 match cfg_entry {
176 CfgEntry::Bool(true, _) => {
177 wildcard_span = Some(predicate.span());
178 break;
179 }
180 CfgEntry::Bool(false, _) => continue,
181 CfgEntry::NameValue { name, value, .. } => match value {
182 None => {
183 let current = known.insert(*name, false);
185
186 match current {
187 None => continue,
188 Some(false) => {
189 branch_is_unreachable(predicate, None);
190 break;
191 }
192 Some(true) => {
193 break;
195 }
196 }
197 }
198 Some(_) => { }
199 },
200 CfgEntry::Not(inner, _) => match &**inner {
201 CfgEntry::NameValue { name, value: None, .. } => {
202 let current = known.insert(*name, true);
204
205 match current {
206 None => continue,
207 Some(true) => {
208 branch_is_unreachable(predicate, None);
209 break;
210 }
211 Some(false) => {
212 break;
214 }
215 }
216 }
217 _ => { }
218 },
219 CfgEntry::All(_, _) | CfgEntry::Any(_, _) => {
220 }
222 CfgEntry::Version(..) => { }
223 }
224 }
225
226 for predicate in it {
227 branch_is_unreachable(predicate, wildcard_span)
228 }
229}