1use crate::{
2 error::{ParseError, Reason},
3 expr::{
4 lexer::{Lexer, Token},
5 ExprNode, Expression, Func, InnerPredicate,
6 },
7};
8use smallvec::SmallVec;
9
10impl Expression {
11 /// Given a `cfg()` expression (the cfg( and ) are optional), attempts to
12 /// parse it into a form where it can be evaluated
13 ///
14 /// ```
15 /// assert!(cfg_expr::Expression::parse(r#"cfg(all(unix, target_arch = "x86_64"))"#).is_ok());
16 /// ```
17 pub fn parse(original: &str) -> Result<Self, ParseError> {
18 let lexer = Lexer::new(original);
19
20 // The lexer automatically trims any cfg( ), so reacquire
21 // the string before we start walking tokens
22 let original = lexer.inner;
23
24 #[derive(Debug)]
25 struct FuncAndSpan {
26 func: Func,
27 parens_index: usize,
28 span: std::ops::Range<usize>,
29 predicates: SmallVec<[InnerPredicate; 5]>,
30 nest_level: u8,
31 }
32
33 let mut func_stack = SmallVec::<[FuncAndSpan; 5]>::new();
34 let mut expr_queue = SmallVec::<[ExprNode; 5]>::new();
35
36 // Keep track of the last token to simplify validation of the token stream
37 let mut last_token: Option<Token<'_>> = None;
38
39 let parse_predicate = |key: (&str, std::ops::Range<usize>),
40 val: Option<(&str, std::ops::Range<usize>)>|
41 -> Result<InnerPredicate, ParseError> {
42 // Warning: It is possible for arbitrarily-set configuration
43 // options to have the same value as compiler-set configuration
44 // options. For example, it is possible to do rustc --cfg "unix" program.rs
45 // while compiling to a Windows target, and have both unix and windows
46 // configuration options set at the same time. It is unwise to actually
47 // do this.
48 //
49 // rustc is very permissive in this regard, but I'd rather be really
50 // strict, as it's much easier to loosen restrictions over time than add
51 // new ones
52 macro_rules! err_if_val {
53 () => {
54 if let Some((_, vspan)) = val {
55 return Err(ParseError {
56 original: original.to_owned(),
57 span: vspan,
58 reason: Reason::Unexpected(&[]),
59 });
60 }
61 };
62 }
63
64 let span = key.1;
65 let key = key.0;
66
67 use super::{InnerTarget, Which};
68
69 Ok(match key {
70 // These are special cases in the cfg language that are
71 // semantically the same as `target_family = "<family>"`,
72 // so we just make them not special
73 // NOTE: other target families like "wasm" are NOT allowed
74 // as naked predicates; they must be specified through
75 // `target_family`
76 "unix" | "windows" => {
77 err_if_val!();
78
79 InnerPredicate::Target(InnerTarget {
80 which: Which::Family,
81 span: Some(span),
82 })
83 }
84 "test" => {
85 err_if_val!();
86 InnerPredicate::Test
87 }
88 "debug_assertions" => {
89 err_if_val!();
90 InnerPredicate::DebugAssertions
91 }
92 "proc_macro" => {
93 err_if_val!();
94 InnerPredicate::ProcMacro
95 }
96 "feature" => {
97 // rustc allows bare feature without a value, but the only way
98 // such a predicate would ever evaluate to true would be if they
99 // explicitly set --cfg feature, which would be terrible, so we
100 // just error instead
101 match val {
102 Some((_, span)) => InnerPredicate::Feature(span),
103 None => {
104 return Err(ParseError {
105 original: original.to_owned(),
106 span,
107 reason: Reason::Unexpected(&["= \"<feature_name>\""]),
108 });
109 }
110 }
111 }
112 "panic" => match val {
113 Some((_, vspan)) => InnerPredicate::Target(InnerTarget {
114 which: Which::Panic,
115 span: Some(vspan),
116 }),
117 None => {
118 return Err(ParseError {
119 original: original.to_owned(),
120 span,
121 reason: Reason::Unexpected(&["= \"<panic_strategy>\""]),
122 });
123 }
124 },
125 target_key if key.starts_with("target_") => {
126 let (val, vspan) = match val {
127 None => {
128 return Err(ParseError {
129 original: original.to_owned(),
130 span,
131 reason: Reason::Unexpected(&["= \"<target_cfg_value>\""]),
132 });
133 }
134 Some((val, vspan)) => (val, vspan),
135 };
136
137 macro_rules! tp {
138 ($which:ident) => {
139 InnerTarget {
140 which: Which::$which,
141 span: Some(vspan),
142 }
143 };
144 }
145
146 let tp = match &target_key[7..] {
147 "abi" => tp!(Abi),
148 "arch" => tp!(Arch),
149 "feature" => {
150 if val.is_empty() {
151 return Err(ParseError {
152 original: original.to_owned(),
153 span: vspan,
154 reason: Reason::Unexpected(&["<feature>"]),
155 });
156 }
157
158 return Ok(InnerPredicate::TargetFeature(vspan));
159 }
160 "os" => tp!(Os),
161 "family" => tp!(Family),
162 "env" => tp!(Env),
163 "endian" => InnerTarget {
164 which: Which::Endian(val.parse().map_err(|_err| ParseError {
165 original: original.to_owned(),
166 span: vspan,
167 reason: Reason::InvalidInteger,
168 })?),
169 span: None,
170 },
171 "has_atomic" => InnerTarget {
172 which: Which::HasAtomic(val.parse().map_err(|_err| ParseError {
173 original: original.to_owned(),
174 span: vspan,
175 reason: Reason::InvalidHasAtomic,
176 })?),
177 span: None,
178 },
179 "pointer_width" => InnerTarget {
180 which: Which::PointerWidth(val.parse().map_err(|_err| ParseError {
181 original: original.to_owned(),
182 span: vspan,
183 reason: Reason::InvalidInteger,
184 })?),
185 span: None,
186 },
187 "vendor" => tp!(Vendor),
188 _ => {
189 return Err(ParseError {
190 original: original.to_owned(),
191 span,
192 reason: Reason::Unexpected(&[
193 "target_arch",
194 "target_feature",
195 "target_os",
196 "target_family",
197 "target_env",
198 "target_endian",
199 "target_has_atomic",
200 "target_pointer_width",
201 "target_vendor",
202 ]),
203 })
204 }
205 };
206
207 InnerPredicate::Target(tp)
208 }
209 _other => InnerPredicate::Other {
210 identifier: span,
211 value: val.map(|(_, span)| span),
212 },
213 })
214 };
215
216 macro_rules! token_err {
217 ($span:expr) => {{
218 let expected: &[&str] = match last_token {
219 None => &["<key>", "all", "any", "not"],
220 Some(Token::All | Token::Any | Token::Not) => &["("],
221 Some(Token::CloseParen) => &[")", ","],
222 Some(Token::Comma) => &[")", "<key>"],
223 Some(Token::Equals) => &["\""],
224 Some(Token::Key(_)) => &["=", ",", ")"],
225 Some(Token::Value(_)) => &[",", ")"],
226 Some(Token::OpenParen) => &["<key>", ")", "all", "any", "not"],
227 };
228
229 return Err(ParseError {
230 original: original.to_owned(),
231 span: $span,
232 reason: Reason::Unexpected(&expected),
233 });
234 }};
235 }
236
237 let mut pred_key: Option<(&str, _)> = None;
238 let mut pred_val: Option<(&str, _)> = None;
239
240 let mut root_predicate_count = 0;
241
242 // Basic implementation of the https://en.wikipedia.org/wiki/Shunting-yard_algorithm
243 'outer: for lt in lexer {
244 let lt = lt?;
245 match &lt.token {
246 Token::Key(k) => {
247 if matches!(last_token, None | Some(Token::OpenParen | Token::Comma)) {
248 pred_key = Some((k, lt.span.clone()));
249 } else {
250 token_err!(lt.span)
251 }
252 }
253 Token::Value(v) => {
254 if matches!(last_token, Some(Token::Equals)) {
255 // We only record the span for keys and values
256 // so that the expression doesn't need a lifetime
257 // but in the value case we need to strip off
258 // the quotes so that the proper raw string is
259 // provided to callers when evaluating the expression
260 pred_val = Some((v, lt.span.start + 1..lt.span.end - 1));
261 } else {
262 token_err!(lt.span)
263 }
264 }
265 Token::Equals => {
266 if !matches!(last_token, Some(Token::Key(_))) {
267 token_err!(lt.span)
268 }
269 }
270 Token::All | Token::Any | Token::Not => {
271 if matches!(last_token, None | Some(Token::OpenParen | Token::Comma)) {
272 let new_fn = match lt.token {
273 // the 0 is a dummy value -- it will be substituted for the real
274 // number of predicates in the `CloseParen` branch below.
275 Token::All => Func::All(0),
276 Token::Any => Func::Any(0),
277 Token::Not => Func::Not,
278 _ => unreachable!(),
279 };
280
281 if let Some(fs) = func_stack.last_mut() {
282 fs.nest_level += 1;
283 }
284
285 func_stack.push(FuncAndSpan {
286 func: new_fn,
287 span: lt.span,
288 parens_index: 0,
289 predicates: SmallVec::new(),
290 nest_level: 0,
291 });
292 } else {
293 token_err!(lt.span)
294 }
295 }
296 Token::OpenParen => {
297 if matches!(last_token, Some(Token::All | Token::Any | Token::Not)) {
298 if let Some(ref mut fs) = func_stack.last_mut() {
299 fs.parens_index = lt.span.start;
300 }
301 } else {
302 token_err!(lt.span)
303 }
304 }
305 Token::CloseParen => {
306 if matches!(
307 last_token,
308 None | Some(Token::All | Token::Any | Token::Not | Token::Equals)
309 ) {
310 token_err!(lt.span)
311 } else {
312 if let Some(top) = func_stack.pop() {
313 let key = pred_key.take();
314 let val = pred_val.take();
315
316 // In this context, the boolean to int conversion is confusing.
317 #[allow(clippy::bool_to_int_with_if)]
318 let num_predicates = top.predicates.len()
319 + if key.is_some() { 1 } else { 0 }
320 + top.nest_level as usize;
321
322 let func = match top.func {
323 Func::All(_) => Func::All(num_predicates),
324 Func::Any(_) => Func::Any(num_predicates),
325 Func::Not => {
326 // not() doesn't take a predicate list, but only a single predicate,
327 // so ensure we have exactly 1
328 if num_predicates != 1 {
329 return Err(ParseError {
330 original: original.to_owned(),
331 span: top.span.start..lt.span.end,
332 reason: Reason::InvalidNot(num_predicates),
333 });
334 }
335
336 Func::Not
337 }
338 };
339
340 for pred in top.predicates {
341 expr_queue.push(ExprNode::Predicate(pred));
342 }
343
344 if let Some(key) = key {
345 let inner_pred = parse_predicate(key, val)?;
346 expr_queue.push(ExprNode::Predicate(inner_pred));
347 }
348
349 expr_queue.push(ExprNode::Fn(func));
350
351 // This is the only place we go back to the top of the outer loop,
352 // so make sure we correctly record this token
353 last_token = Some(Token::CloseParen);
354 continue 'outer;
355 }
356
357 // We didn't have an opening parentheses if we get here
358 return Err(ParseError {
359 original: original.to_owned(),
360 span: lt.span,
361 reason: Reason::UnopenedParens,
362 });
363 }
364 }
365 Token::Comma => {
366 if matches!(
367 last_token,
368 None | Some(
369 Token::OpenParen | Token::All | Token::Any | Token::Not | Token::Equals
370 )
371 ) {
372 token_err!(lt.span)
373 } else {
374 let key = pred_key.take();
375 let val = pred_val.take();
376
377 let inner_pred = key.map(|key| parse_predicate(key, val)).transpose()?;
378
379 match (inner_pred, func_stack.last_mut()) {
380 (Some(pred), Some(func)) => {
381 func.predicates.push(pred);
382 }
383 (Some(pred), None) => {
384 root_predicate_count += 1;
385
386 expr_queue.push(ExprNode::Predicate(pred));
387 }
388 _ => {}
389 }
390 }
391 }
392 }
393
394 last_token = Some(lt.token);
395 }
396
397 if let Some(Token::Equals) = last_token {
398 return Err(ParseError {
399 original: original.to_owned(),
400 span: original.len()..original.len(),
401 reason: Reason::Unexpected(&["\"<value>\""]),
402 });
403 }
404
405 // If we still have functions on the stack, it means we have an unclosed parens
406 if let Some(top) = func_stack.pop() {
407 if top.parens_index != 0 {
408 Err(ParseError {
409 original: original.to_owned(),
410 span: top.parens_index..original.len(),
411 reason: Reason::UnclosedParens,
412 })
413 } else {
414 Err(ParseError {
415 original: original.to_owned(),
416 span: top.span,
417 reason: Reason::Unexpected(&["("]),
418 })
419 }
420 } else {
421 let key = pred_key.take();
422 let val = pred_val.take();
423
424 if let Some(key) = key {
425 root_predicate_count += 1;
426 expr_queue.push(ExprNode::Predicate(parse_predicate(key, val)?));
427 }
428
429 if expr_queue.is_empty() {
430 Err(ParseError {
431 original: original.to_owned(),
432 span: 0..original.len(),
433 reason: Reason::Empty,
434 })
435 } else if root_predicate_count > 1 {
436 Err(ParseError {
437 original: original.to_owned(),
438 span: 0..original.len(),
439 reason: Reason::MultipleRootPredicates,
440 })
441 } else {
442 Ok(Expression {
443 original: original.to_owned(),
444 expr: expr_queue,
445 })
446 }
447 }
448 }
449}
450