Skip to main content

nextest_filtering/
parsing.rs

1// Copyright (c) The nextest Contributors
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4//! Parsing for filtersets.
5//!
6//! The parsing strategy is based on the following blog post:
7//! `<https://eyalkalderon.com/blog/nom-error-recovery/>`
8//!
9//! All high level parsing functions should:
10//! - always return Ok(_)
11//! - on error:
12//!     - consume as much input as it makes sense so that we can try to resume parsing
13//!     - return an error/none variant of the expected result type
14//!     - push an error in the parsing state (in span.state)
15
16use guppy::graph::cargo::BuildPlatform;
17use miette::SourceSpan;
18use recursion::CollapsibleExt;
19use std::fmt;
20use winnow::{
21    LocatingSlice, ModalParser, Parser,
22    ascii::line_ending,
23    combinator::{alt, delimited, eof, not, peek, preceded, repeat, terminated, trace},
24    stream::{AsChar, Location, SliceLen, Stream},
25    token::{literal, one_of, take_till},
26};
27
28mod glob;
29mod unicode_string;
30use crate::{
31    NameMatcher,
32    errors::*,
33    expression::{ExprFrame, Wrapped},
34};
35pub(crate) use glob::GenericGlob;
36pub(crate) use unicode_string::DisplayParsedString;
37
38pub(crate) type Span<'a> = winnow::Stateful<LocatingSlice<&'a str>, State<'a>>;
39type Error = ();
40type PResult<T> = winnow::ModalResult<T, Error>;
41
42pub(crate) fn new_span<'a>(input: &'a str, errors: &'a mut Vec<ParseSingleError>) -> Span<'a> {
43    Span {
44        input: LocatingSlice::new(input),
45        state: State::new(errors),
46    }
47}
48
49/// A filterset that has been parsed but not yet compiled against a package
50/// graph.
51#[derive(Clone, Debug, PartialEq, Eq)]
52pub enum ParsedLeaf<S = SourceSpan> {
53    Package(NameMatcher, S),
54    Deps(NameMatcher, S),
55    Rdeps(NameMatcher, S),
56    Kind(NameMatcher, S),
57    Binary(NameMatcher, S),
58    BinaryId(NameMatcher, S),
59    Platform(BuildPlatform, S),
60    Test(NameMatcher, S),
61    /// All tests in the named test group.
62    Group(NameMatcher, S),
63    Default(S),
64    All,
65    None,
66}
67
68impl ParsedLeaf {
69    /// Returns true if this leaf can only be evaluated at runtime, i.e. it
70    /// requires test names to be available.
71    ///
72    /// Currently, this also returns true (conservatively) for the `Default`
73    /// leaf, which is used to represent the default set of tests to run.
74    pub fn is_runtime_only(&self) -> bool {
75        matches!(
76            self,
77            Self::Test(_, _) | Self::Group(_, _) | Self::Default(_)
78        )
79    }
80
81    #[cfg(test)]
82    fn drop_source_span(self) -> ParsedLeaf<()> {
83        match self {
84            Self::Package(matcher, _) => ParsedLeaf::Package(matcher, ()),
85            Self::Deps(matcher, _) => ParsedLeaf::Deps(matcher, ()),
86            Self::Rdeps(matcher, _) => ParsedLeaf::Rdeps(matcher, ()),
87            Self::Kind(matcher, _) => ParsedLeaf::Kind(matcher, ()),
88            Self::Binary(matcher, _) => ParsedLeaf::Binary(matcher, ()),
89            Self::BinaryId(matcher, _) => ParsedLeaf::BinaryId(matcher, ()),
90            Self::Platform(platform, _) => ParsedLeaf::Platform(platform, ()),
91            Self::Test(matcher, _) => ParsedLeaf::Test(matcher, ()),
92            Self::Group(matcher, _) => ParsedLeaf::Group(matcher, ()),
93            Self::Default(_) => ParsedLeaf::Default(()),
94            Self::All => ParsedLeaf::All,
95            Self::None => ParsedLeaf::None,
96        }
97    }
98}
99
100impl<S> fmt::Display for ParsedLeaf<S> {
101    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102        match self {
103            Self::Package(matcher, _) => write!(f, "package({matcher})"),
104            Self::Deps(matcher, _) => write!(f, "deps({matcher})"),
105            Self::Rdeps(matcher, _) => write!(f, "rdeps({matcher})"),
106            Self::Kind(matcher, _) => write!(f, "kind({matcher})"),
107            Self::Binary(matcher, _) => write!(f, "binary({matcher})"),
108            Self::BinaryId(matcher, _) => write!(f, "binary_id({matcher})"),
109            Self::Platform(platform, _) => write!(f, "platform({platform})"),
110            Self::Test(matcher, _) => write!(f, "test({matcher})"),
111            Self::Group(matcher, _) => write!(f, "group({matcher})"),
112            Self::Default(_) => write!(f, "default()"),
113            Self::All => write!(f, "all()"),
114            Self::None => write!(f, "none()"),
115        }
116    }
117}
118
119/// A filterset that hasn't been compiled against a package graph.
120///
121/// XXX: explain why `S` is required (for equality checking w/tests), or replace it with its own
122/// structure.
123#[derive(Clone, Debug, PartialEq, Eq)]
124pub enum ParsedExpr<S = SourceSpan> {
125    Not(NotOperator, Box<ParsedExpr<S>>),
126    Union(OrOperator, Box<ParsedExpr<S>>, Box<ParsedExpr<S>>),
127    Intersection(AndOperator, Box<ParsedExpr<S>>, Box<ParsedExpr<S>>),
128    Difference(DifferenceOperator, Box<ParsedExpr<S>>, Box<ParsedExpr<S>>),
129    Parens(Box<ParsedExpr<S>>),
130    Set(ParsedLeaf<S>),
131}
132
133impl ParsedExpr {
134    pub fn parse(input: &str) -> Result<Self, Vec<ParseSingleError>> {
135        let mut errors = Vec::new();
136        let span = new_span(input, &mut errors);
137        match parse(span).unwrap() {
138            ExprResult::Valid(expr) => Ok(expr),
139            ExprResult::Error => Err(errors),
140        }
141    }
142
143    /// Returns runtime-only leaves, i.e. ones that require test names to be
144    /// available.
145    pub fn runtime_only_leaves(&self) -> Vec<&ParsedLeaf> {
146        use ExprFrame::*;
147
148        let mut leaves = Vec::new();
149        Wrapped(self).collapse_frames(|layer: ExprFrame<&ParsedLeaf, ()>| match layer {
150            Set(leaf) => {
151                if leaf.is_runtime_only() {
152                    leaves.push(leaf);
153                }
154            }
155            Not(_) | Union(_, _) | Intersection(_, _) | Difference(_, _) | Parens(_) => (),
156        });
157
158        leaves
159    }
160
161    fn boxed(self) -> Box<Self> {
162        Box::new(self)
163    }
164
165    fn not(self, op: NotOperator) -> Self {
166        ParsedExpr::Not(op, self.boxed())
167    }
168
169    fn union(op: OrOperator, expr_1: Self, expr_2: Self) -> Self {
170        ParsedExpr::Union(op, expr_1.boxed(), expr_2.boxed())
171    }
172
173    fn intersection(op: AndOperator, expr_1: Self, expr_2: Self) -> Self {
174        ParsedExpr::Intersection(op, expr_1.boxed(), expr_2.boxed())
175    }
176
177    fn difference(op: DifferenceOperator, expr_1: Self, expr_2: Self) -> Self {
178        ParsedExpr::Difference(op, expr_1.boxed(), expr_2.boxed())
179    }
180
181    fn parens(self) -> Self {
182        ParsedExpr::Parens(self.boxed())
183    }
184
185    #[cfg(test)]
186    fn all() -> ParsedExpr {
187        ParsedExpr::Set(ParsedLeaf::All)
188    }
189
190    #[cfg(test)]
191    fn none() -> ParsedExpr {
192        ParsedExpr::Set(ParsedLeaf::None)
193    }
194
195    #[cfg(test)]
196    fn drop_source_span(self) -> ParsedExpr<()> {
197        match self {
198            Self::Not(op, expr) => ParsedExpr::Not(op, Box::new(expr.drop_source_span())),
199            Self::Union(op, a, b) => ParsedExpr::Union(
200                op,
201                Box::new(a.drop_source_span()),
202                Box::new(b.drop_source_span()),
203            ),
204            Self::Intersection(op, a, b) => ParsedExpr::Intersection(
205                op,
206                Box::new(a.drop_source_span()),
207                Box::new(b.drop_source_span()),
208            ),
209            Self::Difference(op, a, b) => ParsedExpr::Difference(
210                op,
211                Box::new(a.drop_source_span()),
212                Box::new(b.drop_source_span()),
213            ),
214            Self::Parens(a) => ParsedExpr::Parens(Box::new(a.drop_source_span())),
215            Self::Set(set) => ParsedExpr::Set(set.drop_source_span()),
216        }
217    }
218}
219
220impl<S> fmt::Display for ParsedExpr<S> {
221    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
222        match self {
223            Self::Not(op, expr) => write!(f, "{op} {expr}"),
224            Self::Union(op, expr_1, expr_2) => write!(f, "{expr_1} {op} {expr_2}"),
225            Self::Intersection(op, expr_1, expr_2) => write!(f, "{expr_1} {op} {expr_2}"),
226            Self::Difference(op, expr_1, expr_2) => write!(f, "{expr_1} {op} {expr_2}"),
227            Self::Parens(expr) => write!(f, "({expr})"),
228            Self::Set(set) => write!(f, "{set}"),
229        }
230    }
231}
232
233pub(crate) enum ExprResult {
234    Valid(ParsedExpr),
235    Error,
236}
237
238impl ExprResult {
239    fn combine(self, op: impl FnOnce(ParsedExpr, ParsedExpr) -> ParsedExpr, other: Self) -> Self {
240        match (self, other) {
241            (Self::Valid(expr_1), Self::Valid(expr_2)) => Self::Valid(op(expr_1, expr_2)),
242            _ => Self::Error,
243        }
244    }
245
246    fn negate(self, op: NotOperator) -> Self {
247        match self {
248            Self::Valid(expr) => Self::Valid(expr.not(op)),
249            _ => Self::Error,
250        }
251    }
252
253    fn parens(self) -> Self {
254        match self {
255            Self::Valid(expr) => Self::Valid(expr.parens()),
256            _ => Self::Error,
257        }
258    }
259}
260
261enum SpanLength {
262    Unknown,
263    Exact(usize),
264    Offset(isize, usize),
265}
266
267fn expect_inner<'a, F, T>(
268    mut parser: F,
269    make_err: fn(SourceSpan) -> ParseSingleError,
270    limit: SpanLength,
271) -> impl ModalParser<Span<'a>, Option<T>, Error>
272where
273    F: ModalParser<Span<'a>, T, Error>,
274{
275    move |input: &mut _| match parser.parse_next(input) {
276        Ok(out) => Ok(Some(out)),
277        Err(winnow::error::ErrMode::Backtrack(_)) | Err(winnow::error::ErrMode::Cut(_)) => {
278            let fragment_start = input.current_token_start();
279            let fragment_length = input.slice_len();
280            let span = match limit {
281                SpanLength::Unknown => (fragment_start, fragment_length).into(),
282                SpanLength::Exact(x) => (fragment_start, x.min(fragment_length)).into(),
283                SpanLength::Offset(offset, x) => {
284                    // e.g. fragment_start = 5, fragment_length = 2, offset = -1, x = 3.
285                    // Here, start = 4.
286                    let effective_start = fragment_start.saturating_add_signed(offset);
287                    // end = 6.
288                    let effective_end = effective_start + fragment_length;
289                    // len = min(3, 6 - 4) = 2.
290                    let len = (effective_end - effective_start).min(x);
291                    (effective_start, len).into()
292                }
293            };
294            let err = make_err(span);
295            input.state.report_error(err);
296            Ok(None)
297        }
298        Err(err) => Err(err),
299    }
300}
301
302fn expect<'a, F, T>(
303    parser: F,
304    make_err: fn(SourceSpan) -> ParseSingleError,
305) -> impl ModalParser<Span<'a>, Option<T>, Error>
306where
307    F: ModalParser<Span<'a>, T, Error>,
308{
309    expect_inner(parser, make_err, SpanLength::Unknown)
310}
311
312fn expect_n<'a, F, T>(
313    parser: F,
314    make_err: fn(SourceSpan) -> ParseSingleError,
315    limit: SpanLength,
316) -> impl ModalParser<Span<'a>, Option<T>, Error>
317where
318    F: ModalParser<Span<'a>, T, Error>,
319{
320    expect_inner(parser, make_err, limit)
321}
322
323fn expect_char<'a>(
324    c: char,
325    make_err: fn(SourceSpan) -> ParseSingleError,
326) -> impl ModalParser<Span<'a>, Option<char>, Error> {
327    expect_inner(ws(c), make_err, SpanLength::Exact(0))
328}
329
330fn silent_expect<'a, F, T>(mut parser: F) -> impl ModalParser<Span<'a>, Option<T>, Error>
331where
332    F: ModalParser<Span<'a>, T, Error>,
333{
334    move |input: &mut _| match parser.parse_next(input) {
335        Ok(out) => Ok(Some(out)),
336        Err(winnow::error::ErrMode::Backtrack(_)) | Err(winnow::error::ErrMode::Cut(_)) => Ok(None),
337        Err(err) => Err(err),
338    }
339}
340
341/// Succeeds when the next character is not an identifier-continuation
342/// character. The end of input also counts as a word boundary.
343///
344/// Used to enforce word boundaries on alphabetic operator keywords, so that
345/// e.g. `notall()` does not get tokenized as `not all()` while `not(test(x))`
346/// does parse as `not (test(x))`.
347fn word_boundary(input: &mut Span<'_>) -> PResult<()> {
348    peek(not(one_of(|c: char| c.is_alphanum() || c == '_'))).parse_next(input)
349}
350
351fn ws<'a, T, P: ModalParser<Span<'a>, T, Error>>(
352    mut inner: P,
353) -> impl ModalParser<Span<'a>, T, Error> {
354    move |input: &mut Span<'a>| {
355        let start = input.checkpoint();
356        () = repeat(
357            0..,
358            alt((
359                // Match individual space characters.
360                ' '.void(),
361                // Match CRLF and LF line endings. This allows filters to be specified as multiline TOML
362                // strings.
363                line_ending.void(),
364            )),
365        )
366        .parse_next(input)?;
367        match inner.parse_next(input) {
368            Ok(res) => Ok(res),
369            Err(winnow::error::ErrMode::Backtrack(err)) => {
370                input.reset(&start);
371                Err(winnow::error::ErrMode::Backtrack(err))
372            }
373            Err(winnow::error::ErrMode::Cut(err)) => {
374                input.reset(&start);
375                Err(winnow::error::ErrMode::Cut(err))
376            }
377            Err(err) => Err(err),
378        }
379    }
380}
381
382// This parse will never fail
383fn parse_matcher_text<'i>(input: &mut Span<'i>) -> PResult<Option<String>> {
384    trace("parse_matcher_text", |input: &mut Span<'i>| {
385        let res = match expect(
386            unicode_string::parse_string,
387            ParseSingleError::InvalidString,
388        )
389        .parse_next(input)
390        {
391            Ok(res) => res.flatten(),
392            Err(_) => unreachable!(),
393        };
394
395        if res.as_ref().map(|s| s.is_empty()).unwrap_or(false) {
396            let start = input.current_token_start();
397            input
398                .state
399                .report_error(ParseSingleError::InvalidString((start..0).into()));
400        }
401
402        Ok(res)
403    })
404    .parse_next(input)
405}
406
407fn parse_contains_matcher(input: &mut Span<'_>) -> PResult<Option<NameMatcher>> {
408    trace(
409        "parse_contains_matcher",
410        preceded('~', parse_matcher_text).map(|res: Option<String>| {
411            res.map(|value| NameMatcher::Contains {
412                value,
413                implicit: false,
414            })
415        }),
416    )
417    .parse_next(input)
418}
419
420fn parse_equal_matcher(input: &mut Span<'_>) -> PResult<Option<NameMatcher>> {
421    trace(
422        "parse_equal_matcher",
423        ws(
424            preceded('=', parse_matcher_text).map(|res: Option<String>| {
425                res.map(|value| NameMatcher::Equal {
426                    value,
427                    implicit: false,
428                })
429            }),
430        ),
431    )
432    .parse_next(input)
433}
434
435fn parse_regex_inner(input: &mut Span<'_>) -> PResult<String> {
436    trace("parse_regex_inner", |input: &mut _| {
437        enum Frag<'a> {
438            Literal(&'a str),
439            Escape(char),
440        }
441
442        let parse_escape = alt((r"\/".value('/'), '\\')).map(Frag::Escape);
443        let parse_literal = take_till(1.., ('\\', '/'))
444            .verify(|s: &str| !s.is_empty())
445            .map(|s: &str| Frag::Literal(s));
446        let parse_frag = alt((parse_escape, parse_literal));
447
448        let res = repeat(0.., parse_frag)
449            .fold(String::new, |mut string, frag| {
450                match frag {
451                    Frag::Escape(c) => string.push(c),
452                    Frag::Literal(s) => string.push_str(s),
453                }
454                string
455            })
456            .parse_next(input)?;
457
458        let _ = peek('/').parse_next(input)?;
459
460        Ok(res)
461    })
462    .parse_next(input)
463}
464
465// This should match parse_regex_inner above.
466pub(crate) struct DisplayParsedRegex<'a>(pub(crate) &'a regex::Regex);
467
468impl fmt::Display for DisplayParsedRegex<'_> {
469    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
470        let regex = self.0.as_str();
471        let mut escaped = false;
472        for c in regex.chars() {
473            if escaped {
474                escaped = false;
475                write!(f, "{c}")?;
476            } else if c == '\\' {
477                escaped = true;
478                write!(f, "{c}")?;
479            } else if c == '/' {
480                // '/' is the only additional escape.
481                write!(f, "\\/")?;
482            } else {
483                write!(f, "{c}")?;
484            }
485        }
486        Ok(())
487    }
488}
489
490fn parse_regex<'i>(input: &mut Span<'i>) -> PResult<Option<NameMatcher>> {
491    trace("parse_regex", |input: &mut Span<'i>| {
492        let start = input.checkpoint();
493        let res = match parse_regex_inner.parse_next(input) {
494            Ok(res) => res,
495            Err(_) => {
496                input.reset(&start);
497                match take_till::<_, _, Error>(0.., ')').parse_next(input) {
498                    Ok(_) => {
499                        let start = input.current_token_start();
500                        let err = ParseSingleError::ExpectedCloseRegex((start, 0).into());
501                        input.state.report_error(err);
502                        return Ok(None);
503                    }
504                    Err(_) => unreachable!(),
505                }
506            }
507        };
508        match regex::Regex::new(&res).map(NameMatcher::Regex) {
509            Ok(res) => Ok(Some(res)),
510            Err(_) => {
511                let end = input.checkpoint();
512
513                input.reset(&start);
514                let start = input.current_token_start();
515
516                input.reset(&end);
517                let end = input.current_token_start();
518
519                let err = ParseSingleError::invalid_regex(&res, start, end);
520                input.state.report_error(err);
521                Ok(None)
522            }
523        }
524    })
525    .parse_next(input)
526}
527
528fn parse_regex_matcher(input: &mut Span<'_>) -> PResult<Option<NameMatcher>> {
529    trace(
530        "parse_regex_matcher",
531        ws(delimited('/', parse_regex, silent_expect(ws('/')))),
532    )
533    .parse_next(input)
534}
535
536fn parse_glob_matcher(input: &mut Span<'_>) -> PResult<Option<NameMatcher>> {
537    trace(
538        "parse_glob_matcher",
539        ws(preceded('#', glob::parse_glob(false))),
540    )
541    .parse_next(input)
542}
543
544// This parse will never fail (because default_matcher won't)
545fn set_matcher<'a>(
546    default_matcher: DefaultMatcher,
547) -> impl ModalParser<Span<'a>, Option<NameMatcher>, Error> {
548    ws(alt((
549        parse_regex_matcher,
550        parse_glob_matcher,
551        parse_equal_matcher,
552        parse_contains_matcher,
553        default_matcher.into_parser(),
554    )))
555}
556
557fn recover_unexpected_comma<'i>(input: &mut Span<'i>) -> PResult<()> {
558    trace("recover_unexpected_comma", |input: &mut Span<'i>| {
559        let start = input.checkpoint();
560        match peek(ws(',')).parse_next(input) {
561            Ok(_) => {
562                let pos = input.current_token_start();
563                input
564                    .state
565                    .report_error(ParseSingleError::UnexpectedComma((pos..0).into()));
566                match take_till::<_, _, Error>(0.., ')').parse_next(input) {
567                    Ok(_) => Ok(()),
568                    Err(_) => unreachable!(),
569                }
570            }
571            Err(_) => {
572                input.reset(&start);
573                Ok(())
574            }
575        }
576    })
577    .parse_next(input)
578}
579
580fn nullary_set_def<'a>(
581    name: &'static str,
582    make_set: fn(SourceSpan) -> ParsedLeaf,
583) -> impl ModalParser<Span<'a>, Option<ParsedLeaf>, Error> {
584    move |i: &mut Span<'_>| {
585        let start = i.current_token_start();
586        let _ = literal(name).parse_next(i)?;
587        let _ = expect_char('(', ParseSingleError::ExpectedOpenParenthesis).parse_next(i)?;
588        let err_loc = i.current_token_start();
589        match take_till::<_, _, Error>(0.., ')').parse_next(i) {
590            Ok(res) => {
591                if !res.trim().is_empty() {
592                    let span = (err_loc, res.len()).into();
593                    let err = ParseSingleError::UnexpectedArgument(span);
594                    i.state.report_error(err);
595                }
596            }
597            Err(_) => unreachable!(),
598        };
599        let _ = expect_char(')', ParseSingleError::ExpectedCloseParenthesis).parse_next(i)?;
600        let end = i.current_token_start();
601        Ok(Some(make_set((start, end - start).into())))
602    }
603}
604
605#[derive(Copy, Clone, Debug)]
606enum DefaultMatcher {
607    // Equal is no longer used and glob is always favored.
608    Equal,
609    Contains,
610    Glob,
611}
612
613impl DefaultMatcher {
614    fn into_parser<'a>(self) -> impl ModalParser<Span<'a>, Option<NameMatcher>, Error> {
615        move |input: &mut _| match self {
616            Self::Equal => parse_matcher_text
617                .map(|res: Option<String>| res.map(NameMatcher::implicit_equal))
618                .parse_next(input),
619            Self::Contains => parse_matcher_text
620                .map(|res: Option<String>| res.map(NameMatcher::implicit_contains))
621                .parse_next(input),
622            Self::Glob => glob::parse_glob(true).parse_next(input),
623        }
624    }
625}
626
627fn unary_set_def<'a>(
628    name: &'static str,
629    default_matcher: DefaultMatcher,
630    make_set: fn(NameMatcher, SourceSpan) -> ParsedLeaf,
631) -> impl ModalParser<Span<'a>, Option<ParsedLeaf>, Error> {
632    move |i: &mut _| {
633        let _ = literal(name).parse_next(i)?;
634        let _ = expect_char('(', ParseSingleError::ExpectedOpenParenthesis).parse_next(i)?;
635        let start = i.current_token_start();
636        let res = set_matcher(default_matcher).parse_next(i)?;
637        let end = i.current_token_start();
638        recover_unexpected_comma.parse_next(i)?;
639        let _ = expect_char(')', ParseSingleError::ExpectedCloseParenthesis).parse_next(i)?;
640        Ok(res.map(|matcher| make_set(matcher, (start, end - start).into())))
641    }
642}
643
644fn platform_def(i: &mut Span<'_>) -> PResult<Option<ParsedLeaf>> {
645    let _ = "platform".parse_next(i)?;
646    let _ = expect_char('(', ParseSingleError::ExpectedOpenParenthesis).parse_next(i)?;
647    let start = i.current_token_start();
648    // Try parsing the argument as a string for better error messages.
649    let res = ws(parse_matcher_text).parse_next(i)?;
650    let end = i.current_token_start();
651    recover_unexpected_comma.parse_next(i)?;
652    let _ = expect_char(')', ParseSingleError::ExpectedCloseParenthesis).parse_next(i)?;
653
654    // The returned string will include leading and trailing whitespace.
655    let platform = match res.as_deref().map(|res| res.trim()) {
656        Some("host") => Some(BuildPlatform::Host),
657        Some("target") => Some(BuildPlatform::Target),
658        Some(_) => {
659            i.state
660                .report_error(ParseSingleError::InvalidPlatformArgument(
661                    (start, end - start).into(),
662                ));
663            None
664        }
665        None => {
666            // This was already reported above.
667            None
668        }
669    };
670    Ok(platform.map(|platform| ParsedLeaf::Platform(platform, (start, end - start).into())))
671}
672
673fn parse_set_def(input: &mut Span<'_>) -> PResult<Option<ParsedLeaf>> {
674    trace(
675        "parse_set_def",
676        ws(alt((
677            unary_set_def("package", DefaultMatcher::Glob, ParsedLeaf::Package),
678            unary_set_def("deps", DefaultMatcher::Glob, ParsedLeaf::Deps),
679            unary_set_def("rdeps", DefaultMatcher::Glob, ParsedLeaf::Rdeps),
680            unary_set_def("kind", DefaultMatcher::Equal, ParsedLeaf::Kind),
681            // binary_id must go above binary, otherwise we'll parse the opening predicate wrong.
682            unary_set_def("binary_id", DefaultMatcher::Glob, ParsedLeaf::BinaryId),
683            unary_set_def("binary", DefaultMatcher::Glob, ParsedLeaf::Binary),
684            unary_set_def("test", DefaultMatcher::Contains, ParsedLeaf::Test),
685            alt((
686                unary_set_def("group", DefaultMatcher::Glob, ParsedLeaf::Group),
687                platform_def,
688                nullary_set_def("default", ParsedLeaf::Default),
689                nullary_set_def("all", |_| ParsedLeaf::All),
690                nullary_set_def("none", |_| ParsedLeaf::None),
691            )),
692        ))),
693    )
694    .parse_next(input)
695}
696
697fn expect_expr<'a, P: ModalParser<Span<'a>, ExprResult, Error>>(
698    inner: P,
699) -> impl ModalParser<Span<'a>, ExprResult, Error> {
700    expect(inner, ParseSingleError::ExpectedExpr).map(|res| res.unwrap_or(ExprResult::Error))
701}
702
703fn parse_parentheses_expr(input: &mut Span<'_>) -> PResult<ExprResult> {
704    trace(
705        "parse_parentheses_expr",
706        delimited(
707            '(',
708            expect_expr(parse_expr),
709            expect_char(')', ParseSingleError::ExpectedCloseParenthesis),
710        )
711        .map(|expr| expr.parens()),
712    )
713    .parse_next(input)
714}
715
716fn parse_basic_expr(input: &mut Span<'_>) -> PResult<ExprResult> {
717    trace(
718        "parse_basic_expr",
719        ws(alt((
720            parse_set_def.map(|set| {
721                set.map(|set| ExprResult::Valid(ParsedExpr::Set(set)))
722                    .unwrap_or(ExprResult::Error)
723            }),
724            parse_expr_not,
725            parse_parentheses_expr,
726            // Recover from misuse of a unary or binary operator at prefix
727            // position.
728            //
729            // Emitting a dedicated diagnostic prevents a cascade of follow-up
730            // errors.
731            parse_misplaced_binary_op,
732            parse_misplaced_unary_op,
733        ))),
734    )
735    .parse_next(input)
736}
737
738fn parse_misplaced_binary_op(input: &mut Span<'_>) -> PResult<ExprResult> {
739    trace("parse_misplaced_binary_op", |input: &mut Span<'_>| {
740        let start = input.current_token_start();
741        let (op, suggest) = alt((
742            terminated("and", word_boundary).value(("and", "and")),
743            terminated("or", word_boundary).value(("or", "or")),
744            terminated("AND", word_boundary).value(("AND", "and")),
745            terminated("OR", word_boundary).value(("OR", "or")),
746            "&&".value(("&&", "&")),
747            "||".value(("||", "|")),
748        ))
749        .parse_next(input)?;
750        let err = ParseSingleError::ExprFoundBinaryOp {
751            op,
752            suggest,
753            span: (start, op.len()).into(),
754        };
755        input.state.report_error(err);
756        Ok(ExprResult::Error)
757    })
758    .parse_next(input)
759}
760
761fn parse_misplaced_unary_op(input: &mut Span<'_>) -> PResult<ExprResult> {
762    trace("parse_misplaced_unary_op", |input: &mut Span<'_>| {
763        let start = input.current_token_start();
764        let op: &'static str = terminated("NOT", word_boundary)
765            .value("NOT")
766            .parse_next(input)?;
767        let err = ParseSingleError::ExprFoundUnaryOp {
768            op,
769            suggest: "not",
770            span: (start, op.len()).into(),
771        };
772        input.state.report_error(err);
773        Ok(ExprResult::Error)
774    })
775    .parse_next(input)
776}
777
778#[derive(Clone, Copy, Debug, Eq, PartialEq)]
779#[cfg_attr(
780    any(test, feature = "internal-testing"),
781    derive(test_strategy::Arbitrary)
782)]
783pub enum NotOperator {
784    LiteralNot,
785    Exclamation,
786}
787
788impl fmt::Display for NotOperator {
789    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
790        match self {
791            NotOperator::LiteralNot => f.write_str("not"),
792            NotOperator::Exclamation => f.write_str("!"),
793        }
794    }
795}
796
797fn parse_expr_not(input: &mut Span<'_>) -> PResult<ExprResult> {
798    trace(
799        "parse_expr_not",
800        (
801            alt((
802                terminated("not", word_boundary).value(NotOperator::LiteralNot),
803                '!'.value(NotOperator::Exclamation),
804            )),
805            expect_expr(ws(parse_basic_expr)),
806        )
807            .map(|(op, expr)| expr.negate(op)),
808    )
809    .parse_next(input)
810}
811
812// ---
813
814#[derive(Clone, Copy, Debug, Eq, PartialEq)]
815#[cfg_attr(
816    any(test, feature = "internal-testing"),
817    derive(test_strategy::Arbitrary)
818)]
819pub enum OrOperator {
820    LiteralOr,
821    Pipe,
822    Plus,
823}
824
825impl fmt::Display for OrOperator {
826    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
827        match self {
828            OrOperator::LiteralOr => f.write_str("or"),
829            OrOperator::Pipe => f.write_str("|"),
830            OrOperator::Plus => f.write_str("+"),
831        }
832    }
833}
834
835fn parse_expr(input: &mut Span<'_>) -> PResult<ExprResult> {
836    trace("parse_expr", |input: &mut _| {
837        // "or" binds less tightly than "and", so parse and within or.
838        let expr = expect_expr(parse_and_or_difference_expr).parse_next(input)?;
839
840        let ops = repeat(
841            0..,
842            (parse_or_operator, expect_expr(parse_and_or_difference_expr)),
843        )
844        .fold(Vec::new, |mut ops, (op, expr)| {
845            ops.push((op, expr));
846            ops
847        })
848        .parse_next(input)?;
849
850        let expr = ops.into_iter().fold(expr, |expr_1, (op, expr_2)| {
851            if let Some(op) = op {
852                expr_1.combine(
853                    |expr_1, expr_2| ParsedExpr::union(op, expr_1, expr_2),
854                    expr_2,
855                )
856            } else {
857                ExprResult::Error
858            }
859        });
860
861        Ok(expr)
862    })
863    .parse_next(input)
864}
865
866fn parse_or_operator<'i>(input: &mut Span<'i>) -> PResult<Option<OrOperator>> {
867    trace(
868        "parse_or_operator",
869        ws(alt((
870            |input: &mut Span<'i>| {
871                let start = input.current_token_start();
872                // This is not a valid OR operator in this position, but catch it to provide a better
873                // experience.
874                let op = alt(("||", terminated("OR", word_boundary))).parse_next(input)?;
875                // || is not supported in filtersets: suggest using | instead.
876                let length = op.len();
877                let err = ParseSingleError::InvalidOrOperator((start, length).into());
878                input.state.report_error(err);
879                Ok(None)
880            },
881            terminated("or", word_boundary).value(Some(OrOperator::LiteralOr)),
882            '|'.value(Some(OrOperator::Pipe)),
883            '+'.value(Some(OrOperator::Plus)),
884        ))),
885    )
886    .parse_next(input)
887}
888
889// ---
890
891#[derive(Clone, Copy, Debug, Eq, PartialEq)]
892#[cfg_attr(
893    any(test, feature = "internal-testing"),
894    derive(test_strategy::Arbitrary)
895)]
896pub enum AndOperator {
897    LiteralAnd,
898    Ampersand,
899}
900
901impl fmt::Display for AndOperator {
902    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
903        match self {
904            AndOperator::LiteralAnd => f.write_str("and"),
905            AndOperator::Ampersand => f.write_str("&"),
906        }
907    }
908}
909
910#[derive(Clone, Copy, Debug, Eq, PartialEq)]
911#[cfg_attr(
912    any(test, feature = "internal-testing"),
913    derive(test_strategy::Arbitrary)
914)]
915pub enum DifferenceOperator {
916    Minus,
917}
918
919impl fmt::Display for DifferenceOperator {
920    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
921        match self {
922            DifferenceOperator::Minus => f.write_str("-"),
923        }
924    }
925}
926
927#[derive(Clone, Copy, Debug, Eq, PartialEq)]
928enum AndOrDifferenceOperator {
929    And(AndOperator),
930    Difference(DifferenceOperator),
931}
932
933fn parse_and_or_difference_expr(input: &mut Span<'_>) -> PResult<ExprResult> {
934    trace("parse_and_or_difference_expr", |input: &mut _| {
935        let expr = expect_expr(parse_basic_expr).parse_next(input)?;
936
937        let ops = repeat(
938            0..,
939            (
940                parse_and_or_difference_operator,
941                expect_expr(parse_basic_expr),
942            ),
943        )
944        .fold(Vec::new, |mut ops, (op, expr)| {
945            ops.push((op, expr));
946            ops
947        })
948        .parse_next(input)?;
949
950        let expr = ops.into_iter().fold(expr, |expr_1, (op, expr_2)| match op {
951            Some(AndOrDifferenceOperator::And(op)) => expr_1.combine(
952                |expr_1, expr_2| ParsedExpr::intersection(op, expr_1, expr_2),
953                expr_2,
954            ),
955            Some(AndOrDifferenceOperator::Difference(op)) => expr_1.combine(
956                |expr_1, expr_2| ParsedExpr::difference(op, expr_1, expr_2),
957                expr_2,
958            ),
959            None => ExprResult::Error,
960        });
961
962        Ok(expr)
963    })
964    .parse_next(input)
965}
966
967fn parse_and_or_difference_operator<'i>(
968    input: &mut Span<'i>,
969) -> PResult<Option<AndOrDifferenceOperator>> {
970    trace(
971        "parse_and_or_difference_operator",
972        ws(alt((
973            |input: &mut Span<'i>| {
974                let start = input.current_token_start();
975                let op = alt(("&&", terminated("AND", word_boundary))).parse_next(input)?;
976                // && is not supported in filtersets: suggest using & instead.
977                let length = op.len();
978                let err = ParseSingleError::InvalidAndOperator((start, length).into());
979                input.state.report_error(err);
980                Ok(None)
981            },
982            terminated("and", word_boundary)
983                .value(Some(AndOrDifferenceOperator::And(AndOperator::LiteralAnd))),
984            '&'.value(Some(AndOrDifferenceOperator::And(AndOperator::Ampersand))),
985            '-'.value(Some(AndOrDifferenceOperator::Difference(
986                DifferenceOperator::Minus,
987            ))),
988        ))),
989    )
990    .parse_next(input)
991}
992
993// ---
994
995pub(crate) fn parse(input: Span<'_>) -> Result<ExprResult, winnow::error::ErrMode<Error>> {
996    let (_, expr) = terminated(
997        parse_expr,
998        expect(ws(eof), ParseSingleError::ExpectedEndOfExpression),
999    )
1000    .parse_peek(input)?;
1001    Ok(expr)
1002}
1003
1004#[cfg(test)]
1005mod tests {
1006    use super::*;
1007
1008    #[track_caller]
1009    fn parse_regex(input: &str) -> NameMatcher {
1010        let mut errors = Vec::new();
1011        let span = new_span(input, &mut errors);
1012        parse_regex_matcher.parse_peek(span).unwrap().1.unwrap()
1013    }
1014
1015    #[test]
1016    fn test_parse_regex() {
1017        assert_eq!(
1018            NameMatcher::Regex(regex::Regex::new(r"some.*").unwrap()),
1019            parse_regex(r"/some.*/")
1020        );
1021
1022        assert_eq!(
1023            NameMatcher::Regex(regex::Regex::new(r"a/a").unwrap()),
1024            parse_regex(r"/a\/a/")
1025        );
1026
1027        assert_eq!(
1028            NameMatcher::Regex(regex::Regex::new(r"\w/a").unwrap()),
1029            parse_regex(r"/\w\/a/")
1030        );
1031
1032        assert_eq!(
1033            NameMatcher::Regex(regex::Regex::new(r"\w\\/a").unwrap()),
1034            parse_regex(r"/\w\\\/a/")
1035        );
1036
1037        assert_eq!(
1038            NameMatcher::Regex(regex::Regex::new(r"\p{Greek}\\/a").unwrap()),
1039            parse_regex(r"/\p{Greek}\\\/a/")
1040        );
1041    }
1042
1043    #[track_caller]
1044    fn parse_glob(input: &str) -> NameMatcher {
1045        let mut errors = Vec::new();
1046        let span = new_span(input, &mut errors);
1047        let matcher = parse_glob_matcher
1048            .parse_peek(span)
1049            .unwrap_or_else(|error| {
1050                panic!("for input {input}, parse_glob_matcher returned an error: {error}")
1051            })
1052            .1
1053            .unwrap_or_else(|| {
1054                panic!(
1055                    "for input {input}, parse_glob_matcher returned None \
1056                     (reported errors: {errors:?})"
1057                )
1058            });
1059        if !errors.is_empty() {
1060            panic!("for input {input}, parse_glob_matcher reported errors: {errors:?}");
1061        }
1062
1063        matcher
1064    }
1065
1066    fn make_glob_matcher(glob: &str, implicit: bool) -> NameMatcher {
1067        NameMatcher::Glob {
1068            glob: GenericGlob::new(glob.to_owned()).unwrap(),
1069            implicit,
1070        }
1071    }
1072
1073    #[test]
1074    fn test_parse_glob_matcher() {
1075        #[track_caller]
1076        fn assert_glob(input: &str, expected: &str) {
1077            assert_eq!(
1078                make_glob_matcher(expected, false),
1079                parse_glob(input),
1080                "expected matches actual for input {input:?}",
1081            );
1082        }
1083
1084        // Need the closing ) since that's used as the delimiter.
1085        assert_glob(r"#something)", "something");
1086        assert_glob(r"#something*)", "something*");
1087        assert_glob(r"#something?)", "something?");
1088        assert_glob(r"#something[abc])", "something[abc]");
1089        assert_glob(r"#something[!abc])", "something[!abc]");
1090        assert_glob(r"#something[a-c])", "something[a-c]");
1091        assert_glob(r"#foobar\b)", "foobar\u{08}");
1092        assert_glob(r"#foobar\\b)", "foobar\\b");
1093        assert_glob(r"#foobar\))", "foobar)");
1094    }
1095
1096    #[track_caller]
1097    fn parse_set(input: &str) -> ParsedLeaf {
1098        let mut errors = Vec::new();
1099        let span = new_span(input, &mut errors);
1100        parse_set_def.parse_peek(span).unwrap().1.unwrap()
1101    }
1102
1103    macro_rules! assert_parsed_leaf {
1104        ($input: expr, $name:ident, $matches:expr) => {
1105            assert!(matches!($input, ParsedLeaf::$name(x, _) if x == $matches));
1106        };
1107    }
1108
1109    #[test]
1110    fn test_parse_name_matcher() {
1111        // Basic matchers
1112        assert_parsed_leaf!(
1113            parse_set("test(~something)"),
1114            Test,
1115            NameMatcher::Contains {
1116                value: "something".to_string(),
1117                implicit: false,
1118            }
1119        );
1120
1121        assert_parsed_leaf!(
1122            parse_set("test(=something)"),
1123            Test,
1124            NameMatcher::Equal {
1125                value: "something".to_string(),
1126                implicit: false,
1127            }
1128        );
1129        assert_parsed_leaf!(
1130            parse_set("test(/some.*/)"),
1131            Test,
1132            NameMatcher::Regex(regex::Regex::new("some.*").unwrap())
1133        );
1134        assert_parsed_leaf!(
1135            parse_set("test(#something)"),
1136            Test,
1137            make_glob_matcher("something", false)
1138        );
1139        assert_parsed_leaf!(
1140            parse_set("test(#something*)"),
1141            Test,
1142            make_glob_matcher("something*", false)
1143        );
1144        assert_parsed_leaf!(
1145            parse_set(r"test(#something/[?])"),
1146            Test,
1147            make_glob_matcher("something/[?]", false)
1148        );
1149
1150        // Default matchers
1151        assert_parsed_leaf!(
1152            parse_set("test(something)"),
1153            Test,
1154            NameMatcher::Contains {
1155                value: "something".to_string(),
1156                implicit: true,
1157            }
1158        );
1159        assert_parsed_leaf!(
1160            parse_set("package(something)"),
1161            Package,
1162            make_glob_matcher("something", true)
1163        );
1164
1165        // Explicit contains matching
1166        assert_parsed_leaf!(
1167            parse_set("test(~something)"),
1168            Test,
1169            NameMatcher::Contains {
1170                value: "something".to_string(),
1171                implicit: false,
1172            }
1173        );
1174        assert_parsed_leaf!(
1175            parse_set("test(~~something)"),
1176            Test,
1177            NameMatcher::Contains {
1178                value: "~something".to_string(),
1179                implicit: false,
1180            }
1181        );
1182        assert_parsed_leaf!(
1183            parse_set("test(~=something)"),
1184            Test,
1185            NameMatcher::Contains {
1186                value: "=something".to_string(),
1187                implicit: false,
1188            }
1189        );
1190        assert_parsed_leaf!(
1191            parse_set("test(~/something/)"),
1192            Test,
1193            NameMatcher::Contains {
1194                value: "/something/".to_string(),
1195                implicit: false,
1196            }
1197        );
1198        assert_parsed_leaf!(
1199            parse_set("test(~#something)"),
1200            Test,
1201            NameMatcher::Contains {
1202                value: "#something".to_string(),
1203                implicit: false,
1204            }
1205        );
1206
1207        // Explicit equals matching.
1208        assert_parsed_leaf!(
1209            parse_set("test(=something)"),
1210            Test,
1211            NameMatcher::Equal {
1212                value: "something".to_string(),
1213                implicit: false,
1214            }
1215        );
1216        assert_parsed_leaf!(
1217            parse_set("test(=~something)"),
1218            Test,
1219            NameMatcher::Equal {
1220                value: "~something".to_string(),
1221                implicit: false,
1222            }
1223        );
1224        assert_parsed_leaf!(
1225            parse_set("test(==something)"),
1226            Test,
1227            NameMatcher::Equal {
1228                value: "=something".to_string(),
1229                implicit: false,
1230            }
1231        );
1232        assert_parsed_leaf!(
1233            parse_set("test(=/something/)"),
1234            Test,
1235            NameMatcher::Equal {
1236                value: "/something/".to_string(),
1237                implicit: false,
1238            }
1239        );
1240        assert_parsed_leaf!(
1241            parse_set("test(=#something)"),
1242            Test,
1243            NameMatcher::Equal {
1244                value: "#something".to_string(),
1245                implicit: false,
1246            }
1247        );
1248
1249        // Explicit glob matching.
1250        assert_parsed_leaf!(
1251            parse_set("test(#~something)"),
1252            Test,
1253            make_glob_matcher("~something", false)
1254        );
1255        assert_parsed_leaf!(
1256            parse_set("test(#=something)"),
1257            Test,
1258            make_glob_matcher("=something", false)
1259        );
1260        assert_parsed_leaf!(
1261            parse_set("test(#/something/)"),
1262            Test,
1263            make_glob_matcher("/something/", false)
1264        );
1265        assert_parsed_leaf!(
1266            parse_set("test(##something)"),
1267            Test,
1268            make_glob_matcher("#something", false)
1269        );
1270    }
1271
1272    #[test]
1273    fn test_parse_name_matcher_quote() {
1274        assert_parsed_leaf!(
1275            parse_set(r"test(some'thing)"),
1276            Test,
1277            NameMatcher::Contains {
1278                value: r"some'thing".to_string(),
1279                implicit: true,
1280            }
1281        );
1282        assert_parsed_leaf!(
1283            parse_set(r"test(some(thing\))"),
1284            Test,
1285            NameMatcher::Contains {
1286                value: r"some(thing)".to_string(),
1287                implicit: true,
1288            }
1289        );
1290        assert_parsed_leaf!(
1291            parse_set(r"test(some \u{55})"),
1292            Test,
1293            NameMatcher::Contains {
1294                value: r"some U".to_string(),
1295                implicit: true,
1296            }
1297        );
1298    }
1299
1300    #[test]
1301    fn test_parse_set_def() {
1302        assert_eq!(ParsedLeaf::All, parse_set("all()"));
1303        assert_eq!(ParsedLeaf::All, parse_set(" all ( ) "));
1304
1305        assert_eq!(ParsedLeaf::None, parse_set("none()"));
1306
1307        assert_parsed_leaf!(
1308            parse_set("package(=something)"),
1309            Package,
1310            NameMatcher::Equal {
1311                value: "something".to_string(),
1312                implicit: false,
1313            }
1314        );
1315        assert_parsed_leaf!(
1316            parse_set("deps(something)"),
1317            Deps,
1318            make_glob_matcher("something", true)
1319        );
1320        assert_parsed_leaf!(
1321            parse_set("rdeps(something)"),
1322            Rdeps,
1323            make_glob_matcher("something", true)
1324        );
1325        assert_parsed_leaf!(
1326            parse_set("test(something)"),
1327            Test,
1328            NameMatcher::Contains {
1329                value: "something".to_string(),
1330                implicit: true,
1331            }
1332        );
1333        assert_parsed_leaf!(parse_set("platform(host)"), Platform, BuildPlatform::Host);
1334        assert_parsed_leaf!(
1335            parse_set("platform(target)"),
1336            Platform,
1337            BuildPlatform::Target
1338        );
1339        assert_parsed_leaf!(
1340            parse_set("platform(    host    )"),
1341            Platform,
1342            BuildPlatform::Host
1343        );
1344    }
1345
1346    #[track_caller]
1347    fn parse(input: &str) -> ParsedExpr {
1348        match ParsedExpr::parse(input) {
1349            Ok(expr) => expr,
1350            Err(errors) => {
1351                for single_error in &errors {
1352                    let report = miette::Report::new(single_error.clone())
1353                        .with_source_code(input.to_owned());
1354                    eprintln!("{report:?}");
1355                }
1356                panic!("Not a valid expression!")
1357            }
1358        }
1359    }
1360
1361    #[test]
1362    fn test_parse_expr_set() {
1363        let expr = ParsedExpr::all();
1364        assert_eq!(expr, parse("all()"));
1365        assert_eq!(expr, parse("  all ( ) "));
1366        assert_eq!(format!("{expr}"), "all()");
1367    }
1368
1369    #[test]
1370    fn test_parse_expr_not() {
1371        let expr = ParsedExpr::all().not(NotOperator::LiteralNot);
1372        assert_eq_both_ways(&expr, "not all()");
1373        assert_eq!(expr, parse("not  all()"));
1374
1375        let expr = ParsedExpr::all().not(NotOperator::Exclamation);
1376        assert_eq_both_ways(&expr, "! all()");
1377        assert_eq!(expr, parse("!all()"));
1378
1379        let expr = ParsedExpr::all()
1380            .not(NotOperator::LiteralNot)
1381            .not(NotOperator::LiteralNot);
1382        assert_eq_both_ways(&expr, "not not all()");
1383
1384        // `not` followed by `(` without intervening whitespace must parse as
1385        // `not (...)`.
1386        let expr = ParsedExpr::all().parens().not(NotOperator::LiteralNot);
1387        assert_eq!(expr, parse("not(all())"));
1388        assert_eq_both_ways(&expr, "not (all())");
1389
1390        // `not` followed by `!` without intervening whitespace.
1391        let expr = ParsedExpr::all()
1392            .not(NotOperator::Exclamation)
1393            .not(NotOperator::LiteralNot);
1394        assert_eq!(expr, parse("not!all()"));
1395    }
1396
1397    #[test]
1398    fn test_parse_expr_intersection() {
1399        let expr = ParsedExpr::intersection(
1400            AndOperator::LiteralAnd,
1401            ParsedExpr::all(),
1402            ParsedExpr::none(),
1403        );
1404        assert_eq_both_ways(&expr, "all() and none()");
1405        assert_eq!(expr, parse("all()and none()"));
1406
1407        let expr = ParsedExpr::intersection(
1408            AndOperator::Ampersand,
1409            ParsedExpr::all(),
1410            ParsedExpr::none(),
1411        );
1412        assert_eq_both_ways(&expr, "all() & none()");
1413        assert_eq!(expr, parse("all()&none()"));
1414
1415        // Tight syntax: `and` followed by `(` without a space.
1416        let expr = ParsedExpr::intersection(
1417            AndOperator::LiteralAnd,
1418            ParsedExpr::all(),
1419            ParsedExpr::none().parens(),
1420        );
1421        assert_eq!(expr, parse("all()and(none())"));
1422        assert_eq!(expr, parse("all() and(none())"));
1423    }
1424
1425    #[test]
1426    fn test_parse_expr_union() {
1427        let expr = ParsedExpr::union(OrOperator::LiteralOr, ParsedExpr::all(), ParsedExpr::none());
1428        assert_eq_both_ways(&expr, "all() or none()");
1429        assert_eq!(expr, parse("all()or none()"));
1430
1431        let expr = ParsedExpr::union(OrOperator::Pipe, ParsedExpr::all(), ParsedExpr::none());
1432        assert_eq_both_ways(&expr, "all() | none()");
1433        assert_eq!(expr, parse("all()|none()"));
1434
1435        let expr = ParsedExpr::union(OrOperator::Plus, ParsedExpr::all(), ParsedExpr::none());
1436        assert_eq_both_ways(&expr, "all() + none()");
1437        assert_eq!(expr, parse("all()+none()"));
1438
1439        // Tight syntax: `or` followed by `(` without a space.
1440        let expr = ParsedExpr::union(
1441            OrOperator::LiteralOr,
1442            ParsedExpr::all(),
1443            ParsedExpr::none().parens(),
1444        );
1445        assert_eq!(expr, parse("all()or(none())"));
1446        assert_eq!(expr, parse("all() or(none())"));
1447    }
1448
1449    #[test]
1450    fn test_parse_expr_difference() {
1451        let expr = ParsedExpr::difference(
1452            DifferenceOperator::Minus,
1453            ParsedExpr::all(),
1454            ParsedExpr::none(),
1455        );
1456        assert_eq_both_ways(&expr, "all() - none()");
1457        assert_eq!(expr, parse("all()-none()"));
1458    }
1459
1460    #[test]
1461    fn test_parse_expr_precedence() {
1462        let expr = ParsedExpr::intersection(
1463            AndOperator::LiteralAnd,
1464            ParsedExpr::all().not(NotOperator::LiteralNot),
1465            ParsedExpr::none(),
1466        );
1467        assert_eq_both_ways(&expr, "not all() and none()");
1468
1469        let expr = ParsedExpr::intersection(
1470            AndOperator::LiteralAnd,
1471            ParsedExpr::all(),
1472            ParsedExpr::none().not(NotOperator::LiteralNot),
1473        );
1474        assert_eq_both_ways(&expr, "all() and not none()");
1475
1476        let expr = ParsedExpr::intersection(
1477            AndOperator::Ampersand,
1478            ParsedExpr::all(),
1479            ParsedExpr::none(),
1480        );
1481        let expr = ParsedExpr::union(OrOperator::Pipe, expr, ParsedExpr::all());
1482        assert_eq_both_ways(&expr, "all() & none() | all()");
1483
1484        let expr = ParsedExpr::intersection(
1485            AndOperator::Ampersand,
1486            ParsedExpr::none(),
1487            ParsedExpr::all(),
1488        );
1489        let expr = ParsedExpr::union(OrOperator::Pipe, ParsedExpr::all(), expr);
1490        assert_eq_both_ways(&expr, "all() | none() & all()");
1491
1492        let expr =
1493            ParsedExpr::union(OrOperator::Pipe, ParsedExpr::all(), ParsedExpr::none()).parens();
1494        let expr = ParsedExpr::intersection(AndOperator::Ampersand, expr, ParsedExpr::all());
1495        assert_eq_both_ways(&expr, "(all() | none()) & all()");
1496
1497        let expr = ParsedExpr::intersection(
1498            AndOperator::Ampersand,
1499            ParsedExpr::none(),
1500            ParsedExpr::all(),
1501        )
1502        .parens();
1503        let expr = ParsedExpr::union(OrOperator::Pipe, ParsedExpr::all(), expr);
1504        assert_eq_both_ways(&expr, "all() | (none() & all())");
1505
1506        let expr = ParsedExpr::difference(
1507            DifferenceOperator::Minus,
1508            ParsedExpr::all(),
1509            ParsedExpr::none(),
1510        );
1511        let expr = ParsedExpr::intersection(AndOperator::Ampersand, expr, ParsedExpr::all());
1512        assert_eq_both_ways(&expr, "all() - none() & all()");
1513
1514        let expr = ParsedExpr::intersection(
1515            AndOperator::Ampersand,
1516            ParsedExpr::all(),
1517            ParsedExpr::none(),
1518        );
1519        let expr = ParsedExpr::difference(DifferenceOperator::Minus, expr, ParsedExpr::all());
1520        assert_eq_both_ways(&expr, "all() & none() - all()");
1521
1522        let expr = ParsedExpr::intersection(
1523            AndOperator::Ampersand,
1524            ParsedExpr::none(),
1525            ParsedExpr::all(),
1526        )
1527        .parens()
1528        .not(NotOperator::LiteralNot);
1529        assert_eq_both_ways(&expr, "not (none() & all())");
1530    }
1531
1532    #[test]
1533    fn test_parse_comma() {
1534        // accept escaped comma
1535        let expr = ParsedExpr::Set(ParsedLeaf::Test(
1536            NameMatcher::Contains {
1537                value: "a,".to_string(),
1538                implicit: false,
1539            },
1540            (5, 4).into(),
1541        ));
1542        assert_eq_both_ways(&expr, r"test(~a\,)");
1543
1544        // string parsing is compatible with possible future syntax
1545        fn parse_future_syntax(
1546            input: &mut Span<'_>,
1547        ) -> PResult<(Option<NameMatcher>, Option<NameMatcher>)> {
1548            let _ = "something".parse_next(input)?;
1549            let _ = '('.parse_next(input)?;
1550            let n1 = set_matcher(DefaultMatcher::Contains).parse_next(input)?;
1551            let _ = ws(',').parse_next(input)?;
1552            let n2 = set_matcher(DefaultMatcher::Contains).parse_next(input)?;
1553            let _ = ')'.parse_next(input)?;
1554            Ok((n1, n2))
1555        }
1556
1557        let mut errors = Vec::new();
1558        let mut span = new_span("something(aa, bb)", &mut errors);
1559        if parse_future_syntax.parse_next(&mut span).is_err() {
1560            panic!("Failed to parse comma separated matchers");
1561        }
1562    }
1563
1564    #[track_caller]
1565    fn parse_err(input: &str) -> Vec<ParseSingleError> {
1566        let mut errors = Vec::new();
1567        let span = new_span(input, &mut errors);
1568        super::parse(span).unwrap();
1569        errors
1570    }
1571
1572    macro_rules! assert_error {
1573        ($error:ident, $name:ident, $start:literal, $end:literal) => {{
1574            let matches = matches!($error, ParseSingleError::$name(span) if span == ($start, $end).into());
1575            assert!(
1576                matches,
1577                "expected: {:?}, actual: error: {:?}",
1578                ParseSingleError::$name(($start, $end).into()),
1579                $error,
1580            );
1581        }};
1582    }
1583
1584    #[test]
1585    fn test_invalid_and_operator() {
1586        let src = "all() && none()";
1587        let mut errors = parse_err(src);
1588        assert_eq!(1, errors.len());
1589        let error = errors.remove(0);
1590        assert_error!(error, InvalidAndOperator, 6, 2);
1591
1592        let src = "all() AND none()";
1593        let mut errors = parse_err(src);
1594        assert_eq!(1, errors.len());
1595        let error = errors.remove(0);
1596        assert_error!(error, InvalidAndOperator, 6, 3);
1597
1598        // Tight syntax for the diagnostic variant must also surface the
1599        // dedicated error rather than a generic "unparsed input".
1600        let src = "all() AND(none())";
1601        let mut errors = parse_err(src);
1602        assert_eq!(1, errors.len());
1603        let error = errors.remove(0);
1604        assert_error!(error, InvalidAndOperator, 6, 3);
1605    }
1606
1607    #[test]
1608    fn test_invalid_or_operator() {
1609        let src = "all() || none()";
1610        let mut errors = parse_err(src);
1611        assert_eq!(1, errors.len());
1612        let error = errors.remove(0);
1613        assert_error!(error, InvalidOrOperator, 6, 2);
1614
1615        let src = "all() OR none()";
1616        let mut errors = parse_err(src);
1617        assert_eq!(1, errors.len());
1618        let error = errors.remove(0);
1619        assert_error!(error, InvalidOrOperator, 6, 2);
1620
1621        let src = "all() OR(none())";
1622        let mut errors = parse_err(src);
1623        assert_eq!(1, errors.len());
1624        let error = errors.remove(0);
1625        assert_error!(error, InvalidOrOperator, 6, 2);
1626    }
1627
1628    #[test]
1629    fn test_missing_close_parentheses() {
1630        let src = "all(";
1631        let mut errors = parse_err(src);
1632        assert_eq!(1, errors.len());
1633        let error = errors.remove(0);
1634        assert_error!(error, ExpectedCloseParenthesis, 4, 0);
1635    }
1636
1637    #[test]
1638    fn test_missing_open_parentheses() {
1639        let src = "all)";
1640        let mut errors = parse_err(src);
1641        assert_eq!(1, errors.len());
1642        let error = errors.remove(0);
1643        assert_error!(error, ExpectedOpenParenthesis, 3, 0);
1644    }
1645
1646    #[test]
1647    fn test_missing_parentheses() {
1648        let src = "all";
1649        let mut errors = parse_err(src);
1650        assert_eq!(2, errors.len());
1651        let error = errors.remove(0);
1652        assert_error!(error, ExpectedOpenParenthesis, 3, 0);
1653        let error = errors.remove(0);
1654        assert_error!(error, ExpectedCloseParenthesis, 3, 0);
1655    }
1656
1657    #[test]
1658    fn test_invalid_escapes() {
1659        let src = r"package(foobar\$\#\@baz)";
1660        let mut errors = parse_err(src);
1661        assert_eq!(3, errors.len());
1662
1663        // Ensure all three errors are reported.
1664        let error = errors.remove(0);
1665        assert_error!(error, InvalidEscapeCharacter, 14, 2);
1666
1667        let error = errors.remove(0);
1668        assert_error!(error, InvalidEscapeCharacter, 16, 2);
1669
1670        let error = errors.remove(0);
1671        assert_error!(error, InvalidEscapeCharacter, 18, 2);
1672    }
1673
1674    #[test]
1675    fn test_invalid_regex() {
1676        let src = "package(/)/)";
1677        let mut errors = parse_err(src);
1678        assert_eq!(1, errors.len());
1679        let error = errors.remove(0);
1680        assert!(
1681            matches!(error, ParseSingleError::InvalidRegex { span, .. } if span == (9, 1).into())
1682        );
1683
1684        // Ensure more detailed error messages if possible.
1685        let src = "package(/foo(ab/)";
1686        let mut errors = parse_err(src);
1687        assert_eq!(1, errors.len());
1688        let error = errors.remove(0);
1689        let (span, message) = match error {
1690            ParseSingleError::InvalidRegex { span, message } => (span, message),
1691            other => panic!("expected invalid regex with details, found {other}"),
1692        };
1693        assert_eq!(span, (12, 1).into(), "span matches");
1694        assert_eq!(message, "unclosed group");
1695    }
1696
1697    #[test]
1698    fn test_invalid_glob() {
1699        let src = "package(#)";
1700        let mut errors = parse_err(src);
1701        assert_eq!(1, errors.len());
1702        let error = errors.remove(0);
1703        assert_error!(error, InvalidString, 9, 0);
1704
1705        let src = "package(#foo[)";
1706        let mut errors = parse_err(src);
1707        assert_eq!(1, errors.len());
1708        let error = errors.remove(0);
1709        let (span, error) = match error {
1710            ParseSingleError::InvalidGlob { span, error } => (span, error),
1711            other => panic!("expected InvalidGlob with details, found {other}"),
1712        };
1713        assert_eq!(span, (9, 4).into(), "span matches");
1714        assert_eq!(error.to_string(), "unclosed character class; missing ']'");
1715    }
1716
1717    #[test]
1718    fn test_invalid_platform() {
1719        let src = "platform(foo)";
1720        let mut errors = parse_err(src);
1721        assert_eq!(1, errors.len());
1722        let error = errors.remove(0);
1723        assert_error!(error, InvalidPlatformArgument, 9, 3);
1724
1725        let src = "platform(   bar\\t)";
1726        let mut errors = parse_err(src);
1727        assert_eq!(1, errors.len());
1728        let error = errors.remove(0);
1729        assert_error!(error, InvalidPlatformArgument, 9, 8);
1730    }
1731
1732    #[test]
1733    fn test_missing_close_regex() {
1734        let src = "package(/aaa)";
1735        let mut errors = parse_err(src);
1736        assert_eq!(1, errors.len());
1737        let error = errors.remove(0);
1738        assert_error!(error, ExpectedCloseRegex, 12, 0);
1739    }
1740
1741    #[test]
1742    fn test_unexpected_argument() {
1743        let src = "all(aaa)";
1744        let mut errors = parse_err(src);
1745        assert_eq!(1, errors.len());
1746        let error = errors.remove(0);
1747        assert_error!(error, UnexpectedArgument, 4, 3);
1748    }
1749
1750    #[test]
1751    fn test_expected_expr() {
1752        let src = "all() + ";
1753        let mut errors = parse_err(src);
1754        assert_eq!(1, errors.len());
1755        let error = errors.remove(0);
1756        assert_error!(error, ExpectedExpr, 7, 1);
1757    }
1758
1759    #[test]
1760    fn test_misplaced_binary_op() {
1761        // `and(a, b)`.
1762        let mut errors = parse_err("and(a, b)");
1763        assert_eq!(2, errors.len(), "errors: {errors:?}");
1764        match errors.remove(0) {
1765            ParseSingleError::ExprFoundBinaryOp { op, suggest, span } => {
1766                assert_eq!(op, "and");
1767                assert_eq!(suggest, "and");
1768                assert_eq!(span, (0, 3).into());
1769            }
1770            other => panic!("expected ExprFoundBinaryOp, got: {other:?}"),
1771        }
1772        let error = errors.remove(0);
1773        assert_error!(error, ExpectedEndOfExpression, 3, 6);
1774
1775        // `or(a, b)`.
1776        let mut errors = parse_err("or(a, b)");
1777        assert_eq!(2, errors.len(), "errors: {errors:?}");
1778        match errors.remove(0) {
1779            ParseSingleError::ExprFoundBinaryOp { op, suggest, span } => {
1780                assert_eq!(op, "or");
1781                assert_eq!(suggest, "or");
1782                assert_eq!(span, (0, 2).into());
1783            }
1784            other => panic!("expected ExprFoundBinaryOp, got: {other:?}"),
1785        }
1786
1787        // Uppercase variants are caught and the suggestion uses the
1788        // canonical lowercase form (the uppercase form is not a recognized
1789        // operator either).
1790        let errors = parse_err("AND(test(foo))");
1791        match &errors[0] {
1792            ParseSingleError::ExprFoundBinaryOp { op, suggest, span } => {
1793                assert_eq!(*op, "AND");
1794                assert_eq!(*suggest, "and");
1795                assert_eq!(*span, (0, 3).into());
1796            }
1797            other => panic!("expected ExprFoundBinaryOp, got: {other:?}"),
1798        }
1799
1800        let errors = parse_err("OR(test(foo))");
1801        match &errors[0] {
1802            ParseSingleError::ExprFoundBinaryOp { op, suggest, span } => {
1803                assert_eq!(*op, "OR");
1804                assert_eq!(*suggest, "or");
1805                assert_eq!(*span, (0, 2).into());
1806            }
1807            other => panic!("expected ExprFoundBinaryOp, got: {other:?}"),
1808        }
1809
1810        // A misplaced binary operator on the right-hand side of another
1811        // operator should also produce the dedicated error.
1812        let errors = parse_err("all() and or all()");
1813        let positioned = errors
1814            .iter()
1815            .find(|e| matches!(e, ParseSingleError::ExprFoundBinaryOp { op: "or", .. }));
1816        assert!(
1817            positioned.is_some(),
1818            "expected ExprFoundBinaryOp for `or` in RHS position; got: {errors:?}"
1819        );
1820
1821        // Inside parentheses too.
1822        let errors = parse_err("(and all())");
1823        let positioned = errors
1824            .iter()
1825            .find(|e| matches!(e, ParseSingleError::ExprFoundBinaryOp { op: "and", .. }));
1826        assert!(
1827            positioned.is_some(),
1828            "expected ExprFoundBinaryOp inside parens; got: {errors:?}"
1829        );
1830
1831        // The sigil forms `&&` and `||` are also caught as misplaced binary ops
1832        // when they appear at the start of an expression. The suggestion is the
1833        // matching single-character sigil.
1834        let mut errors = parse_err("&&(a, b)");
1835        assert_eq!(2, errors.len(), "errors: {errors:?}");
1836        match errors.remove(0) {
1837            ParseSingleError::ExprFoundBinaryOp { op, suggest, span } => {
1838                assert_eq!(op, "&&");
1839                assert_eq!(suggest, "&");
1840                assert_eq!(span, (0, 2).into());
1841            }
1842            other => panic!("expected ExprFoundBinaryOp, got: {other:?}"),
1843        }
1844        let error = errors.remove(0);
1845        assert_error!(error, ExpectedEndOfExpression, 2, 6);
1846
1847        let mut errors = parse_err("||(a, b)");
1848        assert_eq!(2, errors.len(), "errors: {errors:?}");
1849        match errors.remove(0) {
1850            ParseSingleError::ExprFoundBinaryOp { op, suggest, span } => {
1851                assert_eq!(op, "||");
1852                assert_eq!(suggest, "|");
1853                assert_eq!(span, (0, 2).into());
1854            }
1855            other => panic!("expected ExprFoundBinaryOp, got: {other:?}"),
1856        }
1857        let error = errors.remove(0);
1858        assert_error!(error, ExpectedEndOfExpression, 2, 6);
1859    }
1860
1861    #[test]
1862    fn test_misplaced_unary_op() {
1863        let mut errors = parse_err("NOT(all())");
1864        assert_eq!(errors.len(), 2, "errors: {errors:?}");
1865        match errors.remove(0) {
1866            ParseSingleError::ExprFoundUnaryOp { op, suggest, span } => {
1867                assert_eq!(op, "NOT");
1868                assert_eq!(suggest, "not");
1869                assert_eq!(span, (0, 3).into());
1870            }
1871            other => panic!("expected ExprFoundUnaryOp, got: {other:?}"),
1872        }
1873        let error = errors.remove(0);
1874        assert_error!(error, ExpectedEndOfExpression, 3, 7);
1875
1876        // Also fires inside parentheses and on the right-hand side of a
1877        // binary operator.
1878        let errors = parse_err("all() and NOT all()");
1879        let positioned = errors
1880            .iter()
1881            .find(|e| matches!(e, ParseSingleError::ExprFoundUnaryOp { op: "NOT", .. }));
1882        assert!(
1883            positioned.is_some(),
1884            "expected ExprFoundUnaryOp for `NOT` in RHS position; got: {errors:?}"
1885        );
1886    }
1887
1888    #[test]
1889    fn test_keyword_word_boundary() {
1890        // `notall()` must not parse as `not all()`. No keyword matches at
1891        // position 0, so the parser reports `ExpectedExpr` and then
1892        // `ExpectedEndOfExpression`, both spanning the entire input.
1893        let mut errors = parse_err("notall()");
1894        assert_eq!(2, errors.len(), "errors: {errors:?}");
1895        let error = errors.remove(0);
1896        assert_error!(error, ExpectedExpr, 0, 8);
1897        let error = errors.remove(0);
1898        assert_error!(error, ExpectedEndOfExpression, 0, 8);
1899
1900        // Same shape for `nottest(foo)` (12 chars).
1901        let mut errors = parse_err("nottest(foo)");
1902        assert_eq!(2, errors.len(), "errors: {errors:?}");
1903        let error = errors.remove(0);
1904        assert_error!(error, ExpectedExpr, 0, 12);
1905        let error = errors.remove(0);
1906        assert_error!(error, ExpectedEndOfExpression, 0, 12);
1907
1908        // `all() andall()`: the `all()` LHS parses, but no operator matches
1909        // (because `andall` fails the word-boundary check on `and`), so the
1910        // remaining ` andall()` is reported as `ExpectedEndOfExpression`.
1911        let mut errors = parse_err("all() andall()");
1912        assert_eq!(1, errors.len(), "errors: {errors:?}");
1913        let error = errors.remove(0);
1914        assert_error!(error, ExpectedEndOfExpression, 5, 9);
1915
1916        let mut errors = parse_err("all() ornone()");
1917        assert_eq!(1, errors.len(), "errors: {errors:?}");
1918        let error = errors.remove(0);
1919        assert_error!(error, ExpectedEndOfExpression, 5, 9);
1920    }
1921
1922    #[test]
1923    fn test_expected_eof() {
1924        let src = "all() blabla";
1925        let mut errors = parse_err(src);
1926        assert_eq!(1, errors.len());
1927        let error = errors.remove(0);
1928        assert_error!(error, ExpectedEndOfExpression, 5, 7);
1929    }
1930
1931    #[test]
1932    fn test_missing_argument() {
1933        let src = "test()";
1934        let mut errors = parse_err(src);
1935        assert_eq!(1, errors.len());
1936        let error = errors.remove(0);
1937        assert_error!(error, InvalidString, 5, 0);
1938    }
1939
1940    #[test]
1941    fn test_unexpected_comma() {
1942        let src = "test(aa, )";
1943        let mut errors = parse_err(src);
1944        assert_eq!(1, errors.len());
1945        let error = errors.remove(0);
1946        assert_error!(error, UnexpectedComma, 7, 0);
1947    }
1948
1949    #[test]
1950    fn test_complex_error() {
1951        let src = "all) + package(/not) - deps(expr none)";
1952        let mut errors = parse_err(src);
1953        assert_eq!(2, errors.len(), "{errors:?}");
1954        let error = errors.remove(0);
1955        assert_error!(error, ExpectedOpenParenthesis, 3, 0);
1956        let error = errors.remove(0);
1957        assert_error!(error, ExpectedCloseRegex, 19, 0);
1958    }
1959
1960    #[test_strategy::proptest]
1961    fn proptest_expr_roundtrip(#[strategy(ParsedExpr::strategy())] expr: ParsedExpr<()>) {
1962        let expr_string = expr.to_string();
1963        eprintln!("expr string: {expr_string}");
1964        let expr_2 = parse(&expr_string).drop_source_span();
1965
1966        assert_eq!(expr, expr_2, "exprs must roundtrip");
1967    }
1968
1969    #[track_caller]
1970    fn assert_eq_both_ways(expr: &ParsedExpr, string: &str) {
1971        assert_eq!(expr, &parse(string));
1972        assert_eq!(format!("{expr}"), string);
1973    }
1974}