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_errors::{Diagnostic, MultiSpan};
6use rustc_feature::Features;
7use rustc_hir::attrs::CfgEntry;
8use rustc_hir::{AttrPath, Target};
9use rustc_parse::exp;
10use rustc_parse::parser::{Parser, Recovery};
11use rustc_session::Session;
12use rustc_session::lint::builtin::UNREACHABLE_CFG_SELECT_PREDICATES;
13use rustc_span::{ErrorGuaranteed, Span, Symbol, sym};
14
15use crate::attributes::AttributeSafety;
16use crate::parser::{AllowExprMetavar, MetaItemOrLitParser};
17use crate::{
18 AttributeParser, AttributeTemplate, ParsedDescription, ShouldEmit, diagnostics, parse_cfg_entry,
19};
20
21#[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)]
22pub enum CfgSelectPredicate {
23 Cfg(CfgEntry),
24 Wildcard(Token),
25}
26
27impl CfgSelectPredicate {
28 fn span(&self) -> Span {
29 match self {
30 CfgSelectPredicate::Cfg(cfg_entry) => cfg_entry.span(),
31 CfgSelectPredicate::Wildcard(token) => token.span,
32 }
33 }
34}
35
36#[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)]
37pub struct CfgSelectBranches {
38 pub reachable: Vec<(CfgEntry, TokenStream, Span)>,
40 pub wildcard: Option<(Token, TokenStream, Span)>,
42 pub unreachable: Vec<(CfgSelectPredicate, TokenStream, Span)>,
45}
46
47impl CfgSelectBranches {
48 pub fn pop_first_match<F>(&mut self, predicate: F) -> Option<(TokenStream, Span)>
51 where
52 F: Fn(&CfgEntry) -> bool,
53 {
54 for (index, (cfg, _, _)) in self.reachable.iter().enumerate() {
55 if predicate(cfg) {
56 let matched = self.reachable.remove(index);
57 return Some((matched.1, matched.2));
58 }
59 }
60
61 self.wildcard.take().map(|(_, tts, span)| (tts, span))
62 }
63
64 pub fn into_iter_tts(self) -> impl Iterator<Item = (TokenStream, Span)> {
66 let it1 = self.reachable.into_iter().map(|(_, tts, span)| (tts, span));
67 let it2 = self.wildcard.into_iter().map(|(_, tts, span)| (tts, span));
68 let it3 = self.unreachable.into_iter().map(|(_, tts, span)| (tts, span));
69
70 it1.chain(it2).chain(it3)
71 }
72}
73
74pub fn parse_cfg_select(
75 p: &mut Parser<'_>,
76 sess: &Session,
77 features: Option<&Features>,
78 lint_node_id: NodeId,
79) -> Result<CfgSelectBranches, ErrorGuaranteed> {
80 let mut branches = CfgSelectBranches::default();
81 let mut branch_attr_error: Option<ErrorGuaranteed> = None;
82
83 while p.token != token::Eof {
84 reject_branch_outer_attrs(p, &mut branch_attr_error)?;
85
86 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)) {
87 let underscore = p.prev_token;
88 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())?;
89
90 let tts = p.parse_delimited_token_tree().map_err(|e| e.emit())?;
91 let span = underscore.span.to(p.token.span);
92
93 match branches.wildcard {
94 None => branches.wildcard = Some((underscore, tts, span)),
95 Some(_) => {
96 branches.unreachable.push((CfgSelectPredicate::Wildcard(underscore), tts, span))
97 }
98 }
99 } else {
100 let meta = MetaItemOrLitParser::parse_single(
101 p,
102 ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed },
103 AllowExprMetavar::Yes,
104 )
105 .map_err(|diag| diag.emit())?;
106 let cfg_span = meta.span();
107 let cfg = AttributeParser::parse_single_args(
108 sess,
109 cfg_span,
110 cfg_span,
111 AttrStyle::Inner,
112 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 },
113 None,
114 AttributeSafety::Normal,
115 ParsedDescription::Macro,
116 cfg_span,
117 lint_node_id,
118 Target::Crate,
120 features,
121 ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed },
122 &meta,
123 parse_cfg_entry,
124 &AttributeTemplate::default(),
125 )?;
126
127 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())?;
128
129 let tts = p.parse_delimited_token_tree().map_err(|e| e.emit())?;
130 let span = cfg_span.to(p.token.span);
131
132 match branches.wildcard {
133 None => branches.reachable.push((cfg, tts, span)),
134 Some(_) => branches.unreachable.push((CfgSelectPredicate::Cfg(cfg), tts, span)),
135 }
136 }
137 }
138
139 if let Some(guar) = branch_attr_error {
140 return Err(guar);
141 }
142
143 let it = branches
144 .reachable
145 .iter()
146 .map(|(entry, _, _)| CfgSelectPredicate::Cfg(entry.clone()))
147 .chain(branches.wildcard.as_ref().map(|(t, _, _)| CfgSelectPredicate::Wildcard(*t)))
148 .chain(branches.unreachable.iter().map(|(entry, _, _)| CfgSelectPredicate::clone(entry)));
149
150 lint_unreachable(p, it, lint_node_id);
151
152 Ok(branches)
153}
154
155fn reject_branch_outer_attrs(
156 p: &mut Parser<'_>,
157 branch_attr_error: &mut Option<ErrorGuaranteed>,
158) -> Result<(), ErrorGuaranteed> {
159 let Some(spans) = p.parse_cfg_select_branch_outer_attrs().map_err(|e| e.emit())? else {
160 return Ok(());
161 };
162
163 for (spans, msg) in [
164 (spans.doc_comments, "doc comments are not allowed on `cfg_select` branches"),
165 (spans.attrs, "attributes are not allowed on `cfg_select` branches"),
166 ] {
167 if !spans.is_empty() {
168 branch_attr_error
169 .get_or_insert(p.dcx().struct_span_err(MultiSpan::from_spans(spans), msg).emit());
170 }
171 }
172
173 Ok(())
174}
175
176fn lint_unreachable(
177 p: &mut Parser<'_>,
178 predicates: impl Iterator<Item = CfgSelectPredicate>,
179 lint_node_id: NodeId,
180) {
181 let mut known = FxHashMap::<Symbol, bool>::default();
183 let mut wildcard_span = None;
184 let mut it = predicates;
185
186 let branch_is_unreachable = |predicate: CfgSelectPredicate, wildcard_span| {
187 let span = predicate.span();
188 p.psess.dyn_buffer_lint(
189 UNREACHABLE_CFG_SELECT_PREDICATES,
190 span,
191 lint_node_id,
192 move |dcx, level| match wildcard_span {
193 Some(wildcard_span) => {
194 diagnostics::UnreachableCfgSelectPredicateWildcard { span, wildcard_span }
195 .into_diag(dcx, level)
196 }
197 None => diagnostics::UnreachableCfgSelectPredicate { span }.into_diag(dcx, level),
198 },
199 );
200 };
201
202 for predicate in &mut it {
203 let CfgSelectPredicate::Cfg(ref cfg_entry) = predicate else {
204 wildcard_span = Some(predicate.span());
205 break;
206 };
207
208 match cfg_entry {
209 CfgEntry::Bool(true, _) => {
210 wildcard_span = Some(predicate.span());
211 break;
212 }
213 CfgEntry::Bool(false, _) => continue,
214 CfgEntry::NameValue { name, value, .. } => match value {
215 None => {
216 let current = known.insert(*name, false);
218
219 match current {
220 None => continue,
221 Some(false) => {
222 branch_is_unreachable(predicate, None);
223 break;
224 }
225 Some(true) => {
226 break;
228 }
229 }
230 }
231 Some(_) => { }
232 },
233 CfgEntry::Not(inner, _) => match &**inner {
234 CfgEntry::NameValue { name, value: None, .. } => {
235 let current = known.insert(*name, true);
237
238 match current {
239 None => continue,
240 Some(true) => {
241 branch_is_unreachable(predicate, None);
242 break;
243 }
244 Some(false) => {
245 break;
247 }
248 }
249 }
250 _ => { }
251 },
252 CfgEntry::All(_, _) | CfgEntry::Any(_, _) => {
253 }
255 CfgEntry::Version(..) => { }
256 }
257 }
258
259 for predicate in it {
260 branch_is_unreachable(predicate, wildcard_span)
261 }
262}