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