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