jujutsu_lib/
revset.rs

1// Copyright 2021 The Jujutsu Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::borrow::Borrow;
16use std::cmp::{Ordering, Reverse};
17use std::collections::{HashMap, HashSet};
18use std::iter::Peekable;
19use std::ops::Range;
20use std::path::Path;
21use std::rc::Rc;
22use std::sync::Arc;
23use std::{error, fmt};
24
25use itertools::Itertools;
26use once_cell::sync::Lazy;
27use pest::iterators::{Pair, Pairs};
28use pest::pratt_parser::{Assoc, Op, PrattParser};
29use pest::Parser;
30use pest_derive::Parser;
31use thiserror::Error;
32
33use crate::backend::{BackendError, BackendResult, CommitId, ObjectId};
34use crate::commit::Commit;
35use crate::hex_util::to_forward_hex;
36use crate::index::{HexPrefix, IndexEntry, PrefixResolution};
37use crate::matchers::{EverythingMatcher, Matcher, PrefixMatcher};
38use crate::op_store::WorkspaceId;
39use crate::repo::Repo;
40use crate::repo_path::{FsPathParseError, RepoPath};
41use crate::revset_graph_iterator::RevsetGraphIterator;
42use crate::rewrite;
43use crate::store::Store;
44
45#[derive(Debug, Error)]
46pub enum RevsetError {
47    #[error("Revision \"{0}\" doesn't exist")]
48    NoSuchRevision(String),
49    #[error("Commit or change id prefix \"{0}\" is ambiguous")]
50    AmbiguousIdPrefix(String),
51    #[error("Unexpected error from store: {0}")]
52    StoreError(#[source] BackendError),
53}
54
55fn resolve_git_ref(repo: &dyn Repo, symbol: &str) -> Option<Vec<CommitId>> {
56    let view = repo.view();
57    for git_ref_prefix in &["", "refs/", "refs/heads/", "refs/tags/", "refs/remotes/"] {
58        if let Some(ref_target) = view.git_refs().get(&(git_ref_prefix.to_string() + symbol)) {
59            return Some(ref_target.adds());
60        }
61    }
62    None
63}
64
65fn resolve_branch(repo: &dyn Repo, symbol: &str) -> Option<Vec<CommitId>> {
66    if let Some(branch_target) = repo.view().branches().get(symbol) {
67        return Some(
68            branch_target
69                .local_target
70                .as_ref()
71                .map(|target| target.adds())
72                .unwrap_or_default(),
73        );
74    }
75    if let Some((name, remote_name)) = symbol.split_once('@') {
76        if let Some(branch_target) = repo.view().branches().get(name) {
77            if let Some(target) = branch_target.remote_targets.get(remote_name) {
78                return Some(target.adds());
79            }
80        }
81    }
82    None
83}
84
85fn resolve_full_commit_id(
86    repo: &dyn Repo,
87    symbol: &str,
88) -> Result<Option<Vec<CommitId>>, RevsetError> {
89    if let Ok(binary_commit_id) = hex::decode(symbol) {
90        if repo.store().commit_id_length() != binary_commit_id.len() {
91            return Ok(None);
92        }
93        let commit_id = CommitId::new(binary_commit_id);
94        match repo.store().get_commit(&commit_id) {
95            // Only recognize a commit if we have indexed it
96            Ok(_) if repo.index().entry_by_id(&commit_id).is_some() => Ok(Some(vec![commit_id])),
97            Ok(_) | Err(BackendError::ObjectNotFound { .. }) => Ok(None),
98            Err(err) => Err(RevsetError::StoreError(err)),
99        }
100    } else {
101        Ok(None)
102    }
103}
104
105fn resolve_short_commit_id(
106    repo: &dyn Repo,
107    symbol: &str,
108) -> Result<Option<Vec<CommitId>>, RevsetError> {
109    if let Some(prefix) = HexPrefix::new(symbol) {
110        match repo.index().resolve_prefix(&prefix) {
111            PrefixResolution::NoMatch => Ok(None),
112            PrefixResolution::AmbiguousMatch => {
113                Err(RevsetError::AmbiguousIdPrefix(symbol.to_owned()))
114            }
115            PrefixResolution::SingleMatch(commit_id) => Ok(Some(vec![commit_id])),
116        }
117    } else {
118        Ok(None)
119    }
120}
121
122fn resolve_change_id(repo: &dyn Repo, symbol: &str) -> Result<Option<Vec<CommitId>>, RevsetError> {
123    if let Some(prefix) = to_forward_hex(symbol).as_deref().and_then(HexPrefix::new) {
124        match repo.resolve_change_id_prefix(&prefix) {
125            PrefixResolution::NoMatch => Ok(None),
126            PrefixResolution::AmbiguousMatch => {
127                Err(RevsetError::AmbiguousIdPrefix(symbol.to_owned()))
128            }
129            PrefixResolution::SingleMatch(entries) => {
130                Ok(Some(entries.iter().map(|e| e.commit_id()).collect()))
131            }
132        }
133    } else {
134        Ok(None)
135    }
136}
137
138pub fn resolve_symbol(
139    repo: &dyn Repo,
140    symbol: &str,
141    workspace_id: Option<&WorkspaceId>,
142) -> Result<Vec<CommitId>, RevsetError> {
143    if symbol.ends_with('@') {
144        let target_workspace = if symbol == "@" {
145            if let Some(workspace_id) = workspace_id {
146                workspace_id.clone()
147            } else {
148                return Err(RevsetError::NoSuchRevision(symbol.to_owned()));
149            }
150        } else {
151            WorkspaceId::new(symbol.strip_suffix('@').unwrap().to_string())
152        };
153        if let Some(commit_id) = repo.view().get_wc_commit_id(&target_workspace) {
154            Ok(vec![commit_id.clone()])
155        } else {
156            Err(RevsetError::NoSuchRevision(symbol.to_owned()))
157        }
158    } else if symbol == "root" {
159        Ok(vec![repo.store().root_commit_id().clone()])
160    } else {
161        // Try to resolve as a tag
162        if let Some(target) = repo.view().tags().get(symbol) {
163            return Ok(target.adds());
164        }
165
166        // Try to resolve as a branch
167        if let Some(ids) = resolve_branch(repo, symbol) {
168            return Ok(ids);
169        }
170
171        // Try to resolve as a git ref
172        if let Some(ids) = resolve_git_ref(repo, symbol) {
173            return Ok(ids);
174        }
175
176        // Try to resolve as a full commit id. We assume a full commit id is unambiguous
177        // even if it's shorter than change id.
178        if let Some(ids) = resolve_full_commit_id(repo, symbol)? {
179            return Ok(ids);
180        }
181
182        // Try to resolve as a commit id.
183        if let Some(ids) = resolve_short_commit_id(repo, symbol)? {
184            return Ok(ids);
185        }
186
187        // Try to resolve as a change id.
188        if let Some(ids) = resolve_change_id(repo, symbol)? {
189            return Ok(ids);
190        }
191
192        Err(RevsetError::NoSuchRevision(symbol.to_owned()))
193    }
194}
195
196#[derive(Parser)]
197#[grammar = "revset.pest"]
198pub struct RevsetParser;
199
200#[derive(Debug)]
201pub struct RevsetParseError {
202    kind: RevsetParseErrorKind,
203    pest_error: Option<Box<pest::error::Error<Rule>>>,
204    origin: Option<Box<RevsetParseError>>,
205}
206
207#[derive(Debug, Error, PartialEq, Eq)]
208pub enum RevsetParseErrorKind {
209    #[error("Syntax error")]
210    SyntaxError,
211    #[error("'{op}' is not a postfix operator (Did you mean '{similar_op}' for {description}?)")]
212    NotPostfixOperator {
213        op: String,
214        similar_op: String,
215        description: String,
216    },
217    #[error("'{op}' is not an infix operator (Did you mean '{similar_op}' for {description}?)")]
218    NotInfixOperator {
219        op: String,
220        similar_op: String,
221        description: String,
222    },
223    #[error("Revset function \"{0}\" doesn't exist")]
224    NoSuchFunction(String),
225    #[error("Invalid arguments to revset function \"{name}\": {message}")]
226    InvalidFunctionArguments { name: String, message: String },
227    #[error("Invalid file pattern: {0}")]
228    FsPathParseError(#[source] FsPathParseError),
229    #[error("Cannot resolve file pattern without workspace")]
230    FsPathWithoutWorkspace,
231    #[error("Redefinition of function parameter")]
232    RedefinedFunctionParameter,
233    #[error(r#"Alias "{0}" cannot be expanded"#)]
234    BadAliasExpansion(String),
235    #[error(r#"Alias "{0}" expanded recursively"#)]
236    RecursiveAlias(String),
237}
238
239impl RevsetParseError {
240    fn new(kind: RevsetParseErrorKind) -> Self {
241        RevsetParseError {
242            kind,
243            pest_error: None,
244            origin: None,
245        }
246    }
247
248    fn with_span(kind: RevsetParseErrorKind, span: pest::Span<'_>) -> Self {
249        let err = pest::error::Error::new_from_span(
250            pest::error::ErrorVariant::CustomError {
251                message: kind.to_string(),
252            },
253            span,
254        );
255        RevsetParseError {
256            kind,
257            pest_error: Some(Box::new(err)),
258            origin: None,
259        }
260    }
261
262    fn with_span_and_origin(
263        kind: RevsetParseErrorKind,
264        span: pest::Span<'_>,
265        origin: Self,
266    ) -> Self {
267        let err = pest::error::Error::new_from_span(
268            pest::error::ErrorVariant::CustomError {
269                message: kind.to_string(),
270            },
271            span,
272        );
273        RevsetParseError {
274            kind,
275            pest_error: Some(Box::new(err)),
276            origin: Some(Box::new(origin)),
277        }
278    }
279
280    pub fn kind(&self) -> &RevsetParseErrorKind {
281        &self.kind
282    }
283
284    /// Original parsing error which typically occurred in an alias expression.
285    pub fn origin(&self) -> Option<&Self> {
286        self.origin.as_deref()
287    }
288}
289
290impl From<pest::error::Error<Rule>> for RevsetParseError {
291    fn from(err: pest::error::Error<Rule>) -> Self {
292        RevsetParseError {
293            kind: RevsetParseErrorKind::SyntaxError,
294            pest_error: Some(Box::new(err)),
295            origin: None,
296        }
297    }
298}
299
300impl fmt::Display for RevsetParseError {
301    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
302        if let Some(err) = &self.pest_error {
303            err.fmt(f)
304        } else {
305            self.kind.fmt(f)
306        }
307    }
308}
309
310impl error::Error for RevsetParseError {
311    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
312        if let Some(e) = self.origin() {
313            return Some(e as &dyn error::Error);
314        }
315        match &self.kind {
316            // SyntaxError is a wrapper for pest::error::Error.
317            RevsetParseErrorKind::SyntaxError => {
318                self.pest_error.as_ref().map(|e| e as &dyn error::Error)
319            }
320            // Otherwise the kind represents this error.
321            e => e.source(),
322        }
323    }
324}
325
326// assumes index has less than u32::MAX entries.
327const GENERATION_RANGE_FULL: Range<u32> = 0..u32::MAX;
328const GENERATION_RANGE_EMPTY: Range<u32> = 0..0;
329
330#[derive(Clone, Debug, Eq, PartialEq)]
331pub enum RevsetFilterPredicate {
332    /// Commits with number of parents in the range.
333    ParentCount(Range<u32>),
334    /// Commits with description containing the needle.
335    Description(String),
336    /// Commits with author's name or email containing the needle.
337    Author(String),
338    /// Commits with committer's name or email containing the needle.
339    Committer(String),
340    /// Commits modifying the paths specified by the pattern.
341    File(Option<Vec<RepoPath>>), // TODO: embed matcher expression?
342}
343
344#[derive(Debug, PartialEq, Eq, Clone)]
345pub enum RevsetExpression {
346    None,
347    All,
348    Commits(Vec<CommitId>),
349    Symbol(String),
350    Children(Rc<RevsetExpression>),
351    Ancestors {
352        heads: Rc<RevsetExpression>,
353        generation: Range<u32>,
354    },
355    // Commits that are ancestors of "heads" but not ancestors of "roots"
356    Range {
357        roots: Rc<RevsetExpression>,
358        heads: Rc<RevsetExpression>,
359        generation: Range<u32>,
360    },
361    // Commits that are descendants of "roots" and ancestors of "heads"
362    DagRange {
363        roots: Rc<RevsetExpression>,
364        heads: Rc<RevsetExpression>,
365    },
366    Heads(Rc<RevsetExpression>),
367    Roots(Rc<RevsetExpression>),
368    VisibleHeads,
369    PublicHeads,
370    Branches(String),
371    RemoteBranches {
372        branch_needle: String,
373        remote_needle: String,
374    },
375    Tags,
376    GitRefs,
377    GitHead,
378    Filter(RevsetFilterPredicate),
379    /// Marker for subtree that should be intersected as filter.
380    AsFilter(Rc<RevsetExpression>),
381    Present(Rc<RevsetExpression>),
382    NotIn(Rc<RevsetExpression>),
383    Union(Rc<RevsetExpression>, Rc<RevsetExpression>),
384    Intersection(Rc<RevsetExpression>, Rc<RevsetExpression>),
385    Difference(Rc<RevsetExpression>, Rc<RevsetExpression>),
386}
387
388impl RevsetExpression {
389    pub fn none() -> Rc<RevsetExpression> {
390        Rc::new(RevsetExpression::None)
391    }
392
393    pub fn all() -> Rc<RevsetExpression> {
394        Rc::new(RevsetExpression::All)
395    }
396
397    pub fn symbol(value: String) -> Rc<RevsetExpression> {
398        Rc::new(RevsetExpression::Symbol(value))
399    }
400
401    pub fn commit(commit_id: CommitId) -> Rc<RevsetExpression> {
402        RevsetExpression::commits(vec![commit_id])
403    }
404
405    pub fn commits(commit_ids: Vec<CommitId>) -> Rc<RevsetExpression> {
406        Rc::new(RevsetExpression::Commits(commit_ids))
407    }
408
409    pub fn visible_heads() -> Rc<RevsetExpression> {
410        Rc::new(RevsetExpression::VisibleHeads)
411    }
412
413    pub fn public_heads() -> Rc<RevsetExpression> {
414        Rc::new(RevsetExpression::PublicHeads)
415    }
416
417    pub fn branches(needle: String) -> Rc<RevsetExpression> {
418        Rc::new(RevsetExpression::Branches(needle))
419    }
420
421    pub fn remote_branches(branch_needle: String, remote_needle: String) -> Rc<RevsetExpression> {
422        Rc::new(RevsetExpression::RemoteBranches {
423            branch_needle,
424            remote_needle,
425        })
426    }
427
428    pub fn tags() -> Rc<RevsetExpression> {
429        Rc::new(RevsetExpression::Tags)
430    }
431
432    pub fn git_refs() -> Rc<RevsetExpression> {
433        Rc::new(RevsetExpression::GitRefs)
434    }
435
436    pub fn git_head() -> Rc<RevsetExpression> {
437        Rc::new(RevsetExpression::GitHead)
438    }
439
440    pub fn filter(predicate: RevsetFilterPredicate) -> Rc<RevsetExpression> {
441        Rc::new(RevsetExpression::Filter(predicate))
442    }
443
444    /// Commits in `self` that don't have descendants in `self`.
445    pub fn heads(self: &Rc<RevsetExpression>) -> Rc<RevsetExpression> {
446        Rc::new(RevsetExpression::Heads(self.clone()))
447    }
448
449    /// Commits in `self` that don't have ancestors in `self`.
450    pub fn roots(self: &Rc<RevsetExpression>) -> Rc<RevsetExpression> {
451        Rc::new(RevsetExpression::Roots(self.clone()))
452    }
453
454    /// Parents of `self`.
455    pub fn parents(self: &Rc<RevsetExpression>) -> Rc<RevsetExpression> {
456        Rc::new(RevsetExpression::Ancestors {
457            heads: self.clone(),
458            generation: 1..2,
459        })
460    }
461
462    /// Ancestors of `self`, including `self`.
463    pub fn ancestors(self: &Rc<RevsetExpression>) -> Rc<RevsetExpression> {
464        Rc::new(RevsetExpression::Ancestors {
465            heads: self.clone(),
466            generation: GENERATION_RANGE_FULL,
467        })
468    }
469
470    /// Children of `self`.
471    pub fn children(self: &Rc<RevsetExpression>) -> Rc<RevsetExpression> {
472        Rc::new(RevsetExpression::Children(self.clone()))
473    }
474
475    /// Descendants of `self`, including `self`.
476    pub fn descendants(self: &Rc<RevsetExpression>) -> Rc<RevsetExpression> {
477        self.dag_range_to(&RevsetExpression::visible_heads())
478    }
479
480    /// Commits that are descendants of `self` and ancestors of `heads`, both
481    /// inclusive.
482    pub fn dag_range_to(
483        self: &Rc<RevsetExpression>,
484        heads: &Rc<RevsetExpression>,
485    ) -> Rc<RevsetExpression> {
486        Rc::new(RevsetExpression::DagRange {
487            roots: self.clone(),
488            heads: heads.clone(),
489        })
490    }
491
492    /// Connects any ancestors and descendants in the set by adding the commits
493    /// between them.
494    pub fn connected(self: &Rc<RevsetExpression>) -> Rc<RevsetExpression> {
495        self.dag_range_to(self)
496    }
497
498    /// Commits reachable from `heads` but not from `self`.
499    pub fn range(
500        self: &Rc<RevsetExpression>,
501        heads: &Rc<RevsetExpression>,
502    ) -> Rc<RevsetExpression> {
503        Rc::new(RevsetExpression::Range {
504            roots: self.clone(),
505            heads: heads.clone(),
506            generation: GENERATION_RANGE_FULL,
507        })
508    }
509
510    /// Commits that are not in `self`, i.e. the complement of `self`.
511    pub fn negated(self: &Rc<RevsetExpression>) -> Rc<RevsetExpression> {
512        Rc::new(RevsetExpression::NotIn(self.clone()))
513    }
514
515    /// Commits that are in `self` or in `other` (or both).
516    pub fn union(
517        self: &Rc<RevsetExpression>,
518        other: &Rc<RevsetExpression>,
519    ) -> Rc<RevsetExpression> {
520        Rc::new(RevsetExpression::Union(self.clone(), other.clone()))
521    }
522
523    /// Commits that are in `self` and in `other`.
524    pub fn intersection(
525        self: &Rc<RevsetExpression>,
526        other: &Rc<RevsetExpression>,
527    ) -> Rc<RevsetExpression> {
528        Rc::new(RevsetExpression::Intersection(self.clone(), other.clone()))
529    }
530
531    /// Commits that are in `self` but not in `other`.
532    pub fn minus(
533        self: &Rc<RevsetExpression>,
534        other: &Rc<RevsetExpression>,
535    ) -> Rc<RevsetExpression> {
536        Rc::new(RevsetExpression::Difference(self.clone(), other.clone()))
537    }
538
539    pub fn evaluate<'repo>(
540        &self,
541        repo: &'repo dyn Repo,
542        workspace_ctx: Option<&RevsetWorkspaceContext>,
543    ) -> Result<Box<dyn Revset<'repo> + 'repo>, RevsetError> {
544        evaluate_expression(repo, self, workspace_ctx)
545    }
546}
547
548#[derive(Clone, Debug, Default)]
549pub struct RevsetAliasesMap {
550    symbol_aliases: HashMap<String, String>,
551    function_aliases: HashMap<String, (Vec<String>, String)>,
552}
553
554impl RevsetAliasesMap {
555    pub fn new() -> Self {
556        Self::default()
557    }
558
559    /// Adds new substitution rule `decl = defn`.
560    ///
561    /// Returns error if `decl` is invalid. The `defn` part isn't checked. A bad
562    /// `defn` will be reported when the alias is substituted.
563    pub fn insert(
564        &mut self,
565        decl: impl AsRef<str>,
566        defn: impl Into<String>,
567    ) -> Result<(), RevsetParseError> {
568        match RevsetAliasDeclaration::parse(decl.as_ref())? {
569            RevsetAliasDeclaration::Symbol(name) => {
570                self.symbol_aliases.insert(name, defn.into());
571            }
572            RevsetAliasDeclaration::Function(name, params) => {
573                self.function_aliases.insert(name, (params, defn.into()));
574            }
575        }
576        Ok(())
577    }
578
579    fn get_symbol<'a>(&'a self, name: &str) -> Option<(RevsetAliasId<'a>, &'a str)> {
580        self.symbol_aliases
581            .get_key_value(name)
582            .map(|(name, defn)| (RevsetAliasId::Symbol(name), defn.as_ref()))
583    }
584
585    fn get_function<'a>(
586        &'a self,
587        name: &str,
588    ) -> Option<(RevsetAliasId<'a>, &'a [String], &'a str)> {
589        self.function_aliases
590            .get_key_value(name)
591            .map(|(name, (params, defn))| {
592                (
593                    RevsetAliasId::Function(name),
594                    params.as_ref(),
595                    defn.as_ref(),
596                )
597            })
598    }
599}
600
601/// Parsed declaration part of alias rule.
602#[derive(Clone, Debug)]
603enum RevsetAliasDeclaration {
604    Symbol(String),
605    Function(String, Vec<String>),
606}
607
608impl RevsetAliasDeclaration {
609    fn parse(source: &str) -> Result<Self, RevsetParseError> {
610        let mut pairs = RevsetParser::parse(Rule::alias_declaration, source)?;
611        let first = pairs.next().unwrap();
612        match first.as_rule() {
613            Rule::identifier => Ok(RevsetAliasDeclaration::Symbol(first.as_str().to_owned())),
614            Rule::function_name => {
615                let name = first.as_str().to_owned();
616                let params_pair = pairs.next().unwrap();
617                let params_span = params_pair.as_span();
618                let params = params_pair
619                    .into_inner()
620                    .map(|pair| match pair.as_rule() {
621                        Rule::identifier => pair.as_str().to_owned(),
622                        r => panic!("unexpected formal parameter rule {r:?}"),
623                    })
624                    .collect_vec();
625                if params.iter().all_unique() {
626                    Ok(RevsetAliasDeclaration::Function(name, params))
627                } else {
628                    Err(RevsetParseError::with_span(
629                        RevsetParseErrorKind::RedefinedFunctionParameter,
630                        params_span,
631                    ))
632                }
633            }
634            r => panic!("unexpected alias declaration rule {r:?}"),
635        }
636    }
637}
638
639/// Borrowed reference to identify alias expression.
640#[derive(Clone, Copy, Debug, Eq, PartialEq)]
641enum RevsetAliasId<'a> {
642    Symbol(&'a str),
643    Function(&'a str),
644}
645
646impl fmt::Display for RevsetAliasId<'_> {
647    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
648        match self {
649            RevsetAliasId::Symbol(name) => write!(f, "{name}"),
650            RevsetAliasId::Function(name) => write!(f, "{name}()"),
651        }
652    }
653}
654
655#[derive(Clone, Copy, Debug)]
656struct ParseState<'a> {
657    aliases_map: &'a RevsetAliasesMap,
658    aliases_expanding: &'a [RevsetAliasId<'a>],
659    locals: &'a HashMap<&'a str, Rc<RevsetExpression>>,
660    workspace_ctx: Option<&'a RevsetWorkspaceContext<'a>>,
661}
662
663impl ParseState<'_> {
664    fn with_alias_expanding<T>(
665        self,
666        id: RevsetAliasId<'_>,
667        locals: &HashMap<&str, Rc<RevsetExpression>>,
668        span: pest::Span<'_>,
669        f: impl FnOnce(ParseState<'_>) -> Result<T, RevsetParseError>,
670    ) -> Result<T, RevsetParseError> {
671        // The stack should be short, so let's simply do linear search and duplicate.
672        if self.aliases_expanding.contains(&id) {
673            return Err(RevsetParseError::with_span(
674                RevsetParseErrorKind::RecursiveAlias(id.to_string()),
675                span,
676            ));
677        }
678        let mut aliases_expanding = self.aliases_expanding.to_vec();
679        aliases_expanding.push(id);
680        let expanding_state = ParseState {
681            aliases_map: self.aliases_map,
682            aliases_expanding: &aliases_expanding,
683            locals,
684            workspace_ctx: self.workspace_ctx,
685        };
686        f(expanding_state).map_err(|e| {
687            RevsetParseError::with_span_and_origin(
688                RevsetParseErrorKind::BadAliasExpansion(id.to_string()),
689                span,
690                e,
691            )
692        })
693    }
694}
695
696fn parse_program(
697    revset_str: &str,
698    state: ParseState,
699) -> Result<Rc<RevsetExpression>, RevsetParseError> {
700    let mut pairs = RevsetParser::parse(Rule::program, revset_str)?;
701    let first = pairs.next().unwrap();
702    parse_expression_rule(first.into_inner(), state)
703}
704
705fn parse_expression_rule(
706    pairs: Pairs<Rule>,
707    state: ParseState,
708) -> Result<Rc<RevsetExpression>, RevsetParseError> {
709    fn not_postfix_op(
710        op: &Pair<Rule>,
711        similar_op: impl Into<String>,
712        description: impl Into<String>,
713    ) -> RevsetParseError {
714        RevsetParseError::with_span(
715            RevsetParseErrorKind::NotPostfixOperator {
716                op: op.as_str().to_owned(),
717                similar_op: similar_op.into(),
718                description: description.into(),
719            },
720            op.as_span(),
721        )
722    }
723
724    fn not_infix_op(
725        op: &Pair<Rule>,
726        similar_op: impl Into<String>,
727        description: impl Into<String>,
728    ) -> RevsetParseError {
729        RevsetParseError::with_span(
730            RevsetParseErrorKind::NotInfixOperator {
731                op: op.as_str().to_owned(),
732                similar_op: similar_op.into(),
733                description: description.into(),
734            },
735            op.as_span(),
736        )
737    }
738
739    static PRATT: Lazy<PrattParser<Rule>> = Lazy::new(|| {
740        PrattParser::new()
741            .op(Op::infix(Rule::union_op, Assoc::Left)
742                | Op::infix(Rule::compat_add_op, Assoc::Left))
743            .op(Op::infix(Rule::intersection_op, Assoc::Left)
744                | Op::infix(Rule::difference_op, Assoc::Left)
745                | Op::infix(Rule::compat_sub_op, Assoc::Left))
746            .op(Op::prefix(Rule::negate_op))
747            // Ranges can't be nested without parentheses. Associativity doesn't matter.
748            .op(Op::infix(Rule::dag_range_op, Assoc::Left) | Op::infix(Rule::range_op, Assoc::Left))
749            .op(Op::prefix(Rule::dag_range_pre_op) | Op::prefix(Rule::range_pre_op))
750            .op(Op::postfix(Rule::dag_range_post_op) | Op::postfix(Rule::range_post_op))
751            // Neighbors
752            .op(Op::postfix(Rule::parents_op)
753                | Op::postfix(Rule::children_op)
754                | Op::postfix(Rule::compat_parents_op))
755    });
756    PRATT
757        .map_primary(|primary| parse_primary_rule(primary, state))
758        .map_prefix(|op, rhs| match op.as_rule() {
759            Rule::negate_op => Ok(rhs?.negated()),
760            Rule::dag_range_pre_op | Rule::range_pre_op => Ok(rhs?.ancestors()),
761            r => panic!("unexpected prefix operator rule {r:?}"),
762        })
763        .map_postfix(|lhs, op| match op.as_rule() {
764            Rule::dag_range_post_op => Ok(lhs?.descendants()),
765            Rule::range_post_op => Ok(lhs?.range(&RevsetExpression::visible_heads())),
766            Rule::parents_op => Ok(lhs?.parents()),
767            Rule::children_op => Ok(lhs?.children()),
768            Rule::compat_parents_op => Err(not_postfix_op(&op, "-", "parents")),
769            r => panic!("unexpected postfix operator rule {r:?}"),
770        })
771        .map_infix(|lhs, op, rhs| match op.as_rule() {
772            Rule::union_op => Ok(lhs?.union(&rhs?)),
773            Rule::compat_add_op => Err(not_infix_op(&op, "|", "union")),
774            Rule::intersection_op => Ok(lhs?.intersection(&rhs?)),
775            Rule::difference_op => Ok(lhs?.minus(&rhs?)),
776            Rule::compat_sub_op => Err(not_infix_op(&op, "~", "difference")),
777            Rule::dag_range_op => Ok(lhs?.dag_range_to(&rhs?)),
778            Rule::range_op => Ok(lhs?.range(&rhs?)),
779            r => panic!("unexpected infix operator rule {r:?}"),
780        })
781        .parse(pairs)
782}
783
784fn parse_primary_rule(
785    pair: Pair<Rule>,
786    state: ParseState,
787) -> Result<Rc<RevsetExpression>, RevsetParseError> {
788    let span = pair.as_span();
789    let mut pairs = pair.into_inner();
790    let first = pairs.next().unwrap();
791    match first.as_rule() {
792        Rule::expression => parse_expression_rule(first.into_inner(), state),
793        Rule::function_name => {
794            let arguments_pair = pairs.next().unwrap();
795            parse_function_expression(first, arguments_pair, state, span)
796        }
797        Rule::symbol => parse_symbol_rule(first.into_inner(), state),
798        _ => {
799            panic!("unexpected revset parse rule: {:?}", first.as_str());
800        }
801    }
802}
803
804fn parse_symbol_rule(
805    mut pairs: Pairs<Rule>,
806    state: ParseState,
807) -> Result<Rc<RevsetExpression>, RevsetParseError> {
808    let first = pairs.next().unwrap();
809    match first.as_rule() {
810        Rule::identifier => {
811            let name = first.as_str();
812            if let Some(expr) = state.locals.get(name) {
813                Ok(expr.clone())
814            } else if let Some((id, defn)) = state.aliases_map.get_symbol(name) {
815                let locals = HashMap::new(); // Don't spill out the current scope
816                state.with_alias_expanding(id, &locals, first.as_span(), |state| {
817                    parse_program(defn, state)
818                })
819            } else {
820                Ok(RevsetExpression::symbol(name.to_owned()))
821            }
822        }
823        Rule::literal_string => {
824            return Ok(RevsetExpression::symbol(
825                first
826                    .as_str()
827                    .strip_prefix('"')
828                    .unwrap()
829                    .strip_suffix('"')
830                    .unwrap()
831                    .to_owned(),
832            ));
833        }
834        _ => {
835            panic!("unexpected symbol parse rule: {:?}", first.as_str());
836        }
837    }
838}
839
840fn parse_function_expression(
841    name_pair: Pair<Rule>,
842    arguments_pair: Pair<Rule>,
843    state: ParseState,
844    primary_span: pest::Span<'_>,
845) -> Result<Rc<RevsetExpression>, RevsetParseError> {
846    let name = name_pair.as_str();
847    if let Some((id, params, defn)) = state.aliases_map.get_function(name) {
848        // Resolve arguments in the current scope, and pass them in to the alias
849        // expansion scope.
850        let (required, optional) =
851            expect_named_arguments_vec(name, &[], arguments_pair, params.len(), params.len())?;
852        assert!(optional.is_empty());
853        let args: Vec<_> = required
854            .into_iter()
855            .map(|arg| parse_expression_rule(arg.into_inner(), state))
856            .try_collect()?;
857        let locals = params.iter().map(|s| s.as_str()).zip(args).collect();
858        state.with_alias_expanding(id, &locals, primary_span, |state| {
859            parse_program(defn, state)
860        })
861    } else {
862        parse_builtin_function(name_pair, arguments_pair, state)
863    }
864}
865
866fn parse_builtin_function(
867    name_pair: Pair<Rule>,
868    arguments_pair: Pair<Rule>,
869    state: ParseState,
870) -> Result<Rc<RevsetExpression>, RevsetParseError> {
871    let name = name_pair.as_str();
872    match name {
873        "parents" => {
874            let arg = expect_one_argument(name, arguments_pair)?;
875            let expression = parse_expression_rule(arg.into_inner(), state)?;
876            Ok(expression.parents())
877        }
878        "children" => {
879            let arg = expect_one_argument(name, arguments_pair)?;
880            let expression = parse_expression_rule(arg.into_inner(), state)?;
881            Ok(expression.children())
882        }
883        "ancestors" => {
884            let arg = expect_one_argument(name, arguments_pair)?;
885            let expression = parse_expression_rule(arg.into_inner(), state)?;
886            Ok(expression.ancestors())
887        }
888        "descendants" => {
889            let arg = expect_one_argument(name, arguments_pair)?;
890            let expression = parse_expression_rule(arg.into_inner(), state)?;
891            Ok(expression.descendants())
892        }
893        "connected" => {
894            let arg = expect_one_argument(name, arguments_pair)?;
895            let candidates = parse_expression_rule(arg.into_inner(), state)?;
896            Ok(candidates.connected())
897        }
898        "none" => {
899            expect_no_arguments(name, arguments_pair)?;
900            Ok(RevsetExpression::none())
901        }
902        "all" => {
903            expect_no_arguments(name, arguments_pair)?;
904            Ok(RevsetExpression::all())
905        }
906        "heads" => {
907            let ([], [opt_arg]) = expect_arguments(name, arguments_pair)?;
908            if let Some(arg) = opt_arg {
909                let candidates = parse_expression_rule(arg.into_inner(), state)?;
910                Ok(candidates.heads())
911            } else {
912                Ok(RevsetExpression::visible_heads())
913            }
914        }
915        "roots" => {
916            let arg = expect_one_argument(name, arguments_pair)?;
917            let candidates = parse_expression_rule(arg.into_inner(), state)?;
918            Ok(candidates.roots())
919        }
920        "public_heads" => {
921            expect_no_arguments(name, arguments_pair)?;
922            Ok(RevsetExpression::public_heads())
923        }
924        "branches" => {
925            let ([], [opt_arg]) = expect_arguments(name, arguments_pair)?;
926            let needle = if let Some(arg) = opt_arg {
927                parse_function_argument_to_string(name, arg, state)?
928            } else {
929                "".to_owned()
930            };
931            Ok(RevsetExpression::branches(needle))
932        }
933        "remote_branches" => {
934            let ([], [branch_opt_arg, remote_opt_arg]) =
935                expect_named_arguments(name, &["", "remote"], arguments_pair)?;
936            let branch_needle = if let Some(branch_arg) = branch_opt_arg {
937                parse_function_argument_to_string(name, branch_arg, state)?
938            } else {
939                "".to_owned()
940            };
941            let remote_needle = if let Some(remote_arg) = remote_opt_arg {
942                parse_function_argument_to_string(name, remote_arg, state)?
943            } else {
944                "".to_owned()
945            };
946            Ok(RevsetExpression::remote_branches(
947                branch_needle,
948                remote_needle,
949            ))
950        }
951        "tags" => {
952            expect_no_arguments(name, arguments_pair)?;
953            Ok(RevsetExpression::tags())
954        }
955        "git_refs" => {
956            expect_no_arguments(name, arguments_pair)?;
957            Ok(RevsetExpression::git_refs())
958        }
959        "git_head" => {
960            expect_no_arguments(name, arguments_pair)?;
961            Ok(RevsetExpression::git_head())
962        }
963        "merges" => {
964            expect_no_arguments(name, arguments_pair)?;
965            Ok(RevsetExpression::filter(
966                RevsetFilterPredicate::ParentCount(2..u32::MAX),
967            ))
968        }
969        "description" => {
970            let arg = expect_one_argument(name, arguments_pair)?;
971            let needle = parse_function_argument_to_string(name, arg, state)?;
972            Ok(RevsetExpression::filter(
973                RevsetFilterPredicate::Description(needle),
974            ))
975        }
976        "author" => {
977            let arg = expect_one_argument(name, arguments_pair)?;
978            let needle = parse_function_argument_to_string(name, arg, state)?;
979            Ok(RevsetExpression::filter(RevsetFilterPredicate::Author(
980                needle,
981            )))
982        }
983        "committer" => {
984            let arg = expect_one_argument(name, arguments_pair)?;
985            let needle = parse_function_argument_to_string(name, arg, state)?;
986            Ok(RevsetExpression::filter(RevsetFilterPredicate::Committer(
987                needle,
988            )))
989        }
990        "empty" => {
991            expect_no_arguments(name, arguments_pair)?;
992            Ok(RevsetExpression::filter(RevsetFilterPredicate::File(None)).negated())
993        }
994        "file" => {
995            if let Some(ctx) = state.workspace_ctx {
996                let arguments_span = arguments_pair.as_span();
997                let paths: Vec<_> = arguments_pair
998                    .into_inner()
999                    .map(|arg| -> Result<_, RevsetParseError> {
1000                        let span = arg.as_span();
1001                        let needle = parse_function_argument_to_string(name, arg, state)?;
1002                        let path = RepoPath::parse_fs_path(ctx.cwd, ctx.workspace_root, &needle)
1003                            .map_err(|e| {
1004                                RevsetParseError::with_span(
1005                                    RevsetParseErrorKind::FsPathParseError(e),
1006                                    span,
1007                                )
1008                            })?;
1009                        Ok(path)
1010                    })
1011                    .try_collect()?;
1012                if paths.is_empty() {
1013                    Err(RevsetParseError::with_span(
1014                        RevsetParseErrorKind::InvalidFunctionArguments {
1015                            name: name.to_owned(),
1016                            message: "Expected at least 1 argument".to_string(),
1017                        },
1018                        arguments_span,
1019                    ))
1020                } else {
1021                    Ok(RevsetExpression::filter(RevsetFilterPredicate::File(Some(
1022                        paths,
1023                    ))))
1024                }
1025            } else {
1026                Err(RevsetParseError::new(
1027                    RevsetParseErrorKind::FsPathWithoutWorkspace,
1028                ))
1029            }
1030        }
1031        "present" => {
1032            let arg = expect_one_argument(name, arguments_pair)?;
1033            let expression = parse_expression_rule(arg.into_inner(), state)?;
1034            Ok(Rc::new(RevsetExpression::Present(expression)))
1035        }
1036        _ => Err(RevsetParseError::with_span(
1037            RevsetParseErrorKind::NoSuchFunction(name.to_owned()),
1038            name_pair.as_span(),
1039        )),
1040    }
1041}
1042
1043type OptionalArg<'i> = Option<Pair<'i, Rule>>;
1044
1045fn expect_no_arguments(
1046    function_name: &str,
1047    arguments_pair: Pair<Rule>,
1048) -> Result<(), RevsetParseError> {
1049    let ([], []) = expect_arguments(function_name, arguments_pair)?;
1050    Ok(())
1051}
1052
1053fn expect_one_argument<'i>(
1054    function_name: &str,
1055    arguments_pair: Pair<'i, Rule>,
1056) -> Result<Pair<'i, Rule>, RevsetParseError> {
1057    let ([arg], []) = expect_arguments(function_name, arguments_pair)?;
1058    Ok(arg)
1059}
1060
1061fn expect_arguments<'i, const N: usize, const M: usize>(
1062    function_name: &str,
1063    arguments_pair: Pair<'i, Rule>,
1064) -> Result<([Pair<'i, Rule>; N], [OptionalArg<'i>; M]), RevsetParseError> {
1065    expect_named_arguments(function_name, &[], arguments_pair)
1066}
1067
1068/// Extracts N required arguments and M optional arguments.
1069///
1070/// `argument_names` is a list of argument names. Unnamed positional arguments
1071/// should be padded with `""`.
1072fn expect_named_arguments<'i, const N: usize, const M: usize>(
1073    function_name: &str,
1074    argument_names: &[&str],
1075    arguments_pair: Pair<'i, Rule>,
1076) -> Result<([Pair<'i, Rule>; N], [OptionalArg<'i>; M]), RevsetParseError> {
1077    let (required, optional) =
1078        expect_named_arguments_vec(function_name, argument_names, arguments_pair, N, N + M)?;
1079    Ok((required.try_into().unwrap(), optional.try_into().unwrap()))
1080}
1081
1082fn expect_named_arguments_vec<'i>(
1083    function_name: &str,
1084    argument_names: &[&str],
1085    arguments_pair: Pair<'i, Rule>,
1086    min_arg_count: usize,
1087    max_arg_count: usize,
1088) -> Result<(Vec<Pair<'i, Rule>>, Vec<OptionalArg<'i>>), RevsetParseError> {
1089    assert!(argument_names.len() <= max_arg_count);
1090    let arguments_span = arguments_pair.as_span();
1091    let make_error = |message, span| {
1092        RevsetParseError::with_span(
1093            RevsetParseErrorKind::InvalidFunctionArguments {
1094                name: function_name.to_owned(),
1095                message,
1096            },
1097            span,
1098        )
1099    };
1100    let make_count_error = || {
1101        let message = if min_arg_count == max_arg_count {
1102            format!("Expected {min_arg_count} arguments")
1103        } else {
1104            format!("Expected {min_arg_count} to {max_arg_count} arguments")
1105        };
1106        make_error(message, arguments_span)
1107    };
1108
1109    let mut pos_iter = Some(0..max_arg_count);
1110    let mut extracted_pairs = vec![None; max_arg_count];
1111    for pair in arguments_pair.into_inner() {
1112        let span = pair.as_span();
1113        match pair.as_rule() {
1114            Rule::expression => {
1115                let pos = pos_iter
1116                    .as_mut()
1117                    .ok_or_else(|| {
1118                        make_error(
1119                            "Positional argument follows keyword argument".to_owned(),
1120                            span,
1121                        )
1122                    })?
1123                    .next()
1124                    .ok_or_else(make_count_error)?;
1125                assert!(extracted_pairs[pos].is_none());
1126                extracted_pairs[pos] = Some(pair);
1127            }
1128            Rule::keyword_argument => {
1129                pos_iter = None; // No more positional arguments
1130                let mut pairs = pair.into_inner();
1131                let name = pairs.next().unwrap();
1132                let expr = pairs.next().unwrap();
1133                assert_eq!(name.as_rule(), Rule::identifier);
1134                assert_eq!(expr.as_rule(), Rule::expression);
1135                let pos = argument_names
1136                    .iter()
1137                    .position(|&n| n == name.as_str())
1138                    .ok_or_else(|| {
1139                        make_error(
1140                            format!(r#"Unexpected keyword argument "{}""#, name.as_str()),
1141                            span,
1142                        )
1143                    })?;
1144                if extracted_pairs[pos].is_some() {
1145                    return Err(make_error(
1146                        format!(r#"Got multiple values for keyword "{}""#, name.as_str()),
1147                        span,
1148                    ));
1149                }
1150                extracted_pairs[pos] = Some(expr);
1151            }
1152            r => panic!("unexpected argument rule {r:?}"),
1153        }
1154    }
1155
1156    assert_eq!(extracted_pairs.len(), max_arg_count);
1157    let optional = extracted_pairs.split_off(min_arg_count);
1158    let required = extracted_pairs.into_iter().flatten().collect_vec();
1159    if required.len() != min_arg_count {
1160        return Err(make_count_error());
1161    }
1162    Ok((required, optional))
1163}
1164
1165fn parse_function_argument_to_string(
1166    name: &str,
1167    pair: Pair<Rule>,
1168    state: ParseState,
1169) -> Result<String, RevsetParseError> {
1170    let span = pair.as_span();
1171    let expression = parse_expression_rule(pair.into_inner(), state)?;
1172    match expression.as_ref() {
1173        RevsetExpression::Symbol(symbol) => Ok(symbol.clone()),
1174        _ => Err(RevsetParseError::with_span(
1175            RevsetParseErrorKind::InvalidFunctionArguments {
1176                name: name.to_string(),
1177                message: "Expected function argument of type string".to_owned(),
1178            },
1179            span,
1180        )),
1181    }
1182}
1183
1184pub fn parse(
1185    revset_str: &str,
1186    aliases_map: &RevsetAliasesMap,
1187    workspace_ctx: Option<&RevsetWorkspaceContext>,
1188) -> Result<Rc<RevsetExpression>, RevsetParseError> {
1189    let state = ParseState {
1190        aliases_map,
1191        aliases_expanding: &[],
1192        locals: &HashMap::new(),
1193        workspace_ctx,
1194    };
1195    parse_program(revset_str, state)
1196}
1197
1198/// Walks `expression` tree and applies `f` recursively from leaf nodes.
1199///
1200/// If `f` returns `None`, the original expression node is reused. If no nodes
1201/// rewritten, returns `None`. `std::iter::successors()` could be used if
1202/// the transformation needs to be applied repeatedly until converged.
1203fn transform_expression_bottom_up(
1204    expression: &Rc<RevsetExpression>,
1205    mut f: impl FnMut(&Rc<RevsetExpression>) -> Option<Rc<RevsetExpression>>,
1206) -> Option<Rc<RevsetExpression>> {
1207    fn transform_child_rec(
1208        expression: &Rc<RevsetExpression>,
1209        f: &mut impl FnMut(&Rc<RevsetExpression>) -> Option<Rc<RevsetExpression>>,
1210    ) -> Option<Rc<RevsetExpression>> {
1211        match expression.as_ref() {
1212            RevsetExpression::None => None,
1213            RevsetExpression::All => None,
1214            RevsetExpression::Commits(_) => None,
1215            RevsetExpression::Symbol(_) => None,
1216            RevsetExpression::Children(roots) => {
1217                transform_rec(roots, f).map(RevsetExpression::Children)
1218            }
1219            RevsetExpression::Ancestors { heads, generation } => {
1220                transform_rec(heads, f).map(|heads| RevsetExpression::Ancestors {
1221                    heads,
1222                    generation: generation.clone(),
1223                })
1224            }
1225            RevsetExpression::Range {
1226                roots,
1227                heads,
1228                generation,
1229            } => transform_rec_pair((roots, heads), f).map(|(roots, heads)| {
1230                RevsetExpression::Range {
1231                    roots,
1232                    heads,
1233                    generation: generation.clone(),
1234                }
1235            }),
1236            RevsetExpression::DagRange { roots, heads } => transform_rec_pair((roots, heads), f)
1237                .map(|(roots, heads)| RevsetExpression::DagRange { roots, heads }),
1238            RevsetExpression::VisibleHeads => None,
1239            RevsetExpression::Heads(candidates) => {
1240                transform_rec(candidates, f).map(RevsetExpression::Heads)
1241            }
1242            RevsetExpression::Roots(candidates) => {
1243                transform_rec(candidates, f).map(RevsetExpression::Roots)
1244            }
1245            RevsetExpression::PublicHeads => None,
1246            RevsetExpression::Branches(_) => None,
1247            RevsetExpression::RemoteBranches { .. } => None,
1248            RevsetExpression::Tags => None,
1249            RevsetExpression::GitRefs => None,
1250            RevsetExpression::GitHead => None,
1251            RevsetExpression::Filter(_) => None,
1252            RevsetExpression::AsFilter(candidates) => {
1253                transform_rec(candidates, f).map(RevsetExpression::AsFilter)
1254            }
1255            RevsetExpression::Present(candidates) => {
1256                transform_rec(candidates, f).map(RevsetExpression::Present)
1257            }
1258            RevsetExpression::NotIn(complement) => {
1259                transform_rec(complement, f).map(RevsetExpression::NotIn)
1260            }
1261            RevsetExpression::Union(expression1, expression2) => {
1262                transform_rec_pair((expression1, expression2), f).map(
1263                    |(expression1, expression2)| RevsetExpression::Union(expression1, expression2),
1264                )
1265            }
1266            RevsetExpression::Intersection(expression1, expression2) => {
1267                transform_rec_pair((expression1, expression2), f).map(
1268                    |(expression1, expression2)| {
1269                        RevsetExpression::Intersection(expression1, expression2)
1270                    },
1271                )
1272            }
1273            RevsetExpression::Difference(expression1, expression2) => {
1274                transform_rec_pair((expression1, expression2), f).map(
1275                    |(expression1, expression2)| {
1276                        RevsetExpression::Difference(expression1, expression2)
1277                    },
1278                )
1279            }
1280        }
1281        .map(Rc::new)
1282    }
1283
1284    fn transform_rec_pair(
1285        (expression1, expression2): (&Rc<RevsetExpression>, &Rc<RevsetExpression>),
1286        f: &mut impl FnMut(&Rc<RevsetExpression>) -> Option<Rc<RevsetExpression>>,
1287    ) -> Option<(Rc<RevsetExpression>, Rc<RevsetExpression>)> {
1288        match (transform_rec(expression1, f), transform_rec(expression2, f)) {
1289            (Some(new_expression1), Some(new_expression2)) => {
1290                Some((new_expression1, new_expression2))
1291            }
1292            (Some(new_expression1), None) => Some((new_expression1, expression2.clone())),
1293            (None, Some(new_expression2)) => Some((expression1.clone(), new_expression2)),
1294            (None, None) => None,
1295        }
1296    }
1297
1298    fn transform_rec(
1299        expression: &Rc<RevsetExpression>,
1300        f: &mut impl FnMut(&Rc<RevsetExpression>) -> Option<Rc<RevsetExpression>>,
1301    ) -> Option<Rc<RevsetExpression>> {
1302        if let Some(new_expression) = transform_child_rec(expression, f) {
1303            // must propagate new expression tree
1304            Some(f(&new_expression).unwrap_or(new_expression))
1305        } else {
1306            f(expression)
1307        }
1308    }
1309
1310    transform_rec(expression, &mut f)
1311}
1312
1313/// Transforms filter expressions, by applying the following rules.
1314///
1315/// a. Moves as many sets to left of filter intersection as possible, to
1316///    minimize the filter inputs.
1317/// b. TODO: Rewrites set operations to and/or/not of predicates, to
1318///    help further optimization (e.g. combine `file(_)` matchers.)
1319/// c. Wraps union of filter and set (e.g. `author(_) | heads()`), to
1320///    ensure inner filter wouldn't need to evaluate all the input sets.
1321fn internalize_filter(expression: &Rc<RevsetExpression>) -> Option<Rc<RevsetExpression>> {
1322    fn is_filter(expression: &RevsetExpression) -> bool {
1323        matches!(
1324            expression,
1325            RevsetExpression::Filter(_) | RevsetExpression::AsFilter(_)
1326        )
1327    }
1328
1329    fn is_filter_tree(expression: &RevsetExpression) -> bool {
1330        is_filter(expression) || as_filter_intersection(expression).is_some()
1331    }
1332
1333    // Extracts 'c & f' from intersect_down()-ed node.
1334    fn as_filter_intersection(
1335        expression: &RevsetExpression,
1336    ) -> Option<(&Rc<RevsetExpression>, &Rc<RevsetExpression>)> {
1337        if let RevsetExpression::Intersection(expression1, expression2) = expression {
1338            is_filter(expression2).then(|| (expression1, expression2))
1339        } else {
1340            None
1341        }
1342    }
1343
1344    // Since both sides must have already been intersect_down()-ed, we don't need to
1345    // apply the whole bottom-up pass to new intersection node. Instead, just push
1346    // new 'c & (d & g)' down-left to '(c & d) & g' while either side is
1347    // an intersection of filter node.
1348    fn intersect_down(
1349        expression1: &Rc<RevsetExpression>,
1350        expression2: &Rc<RevsetExpression>,
1351    ) -> Option<Rc<RevsetExpression>> {
1352        let recurse = |e1, e2| intersect_down(e1, e2).unwrap_or_else(|| e1.intersection(e2));
1353        match (expression1.as_ref(), expression2.as_ref()) {
1354            // Don't reorder 'f1 & f2'
1355            (_, e2) if is_filter(e2) => None,
1356            // f1 & e2 -> e2 & f1
1357            (e1, _) if is_filter(e1) => Some(expression2.intersection(expression1)),
1358            (e1, e2) => match (as_filter_intersection(e1), as_filter_intersection(e2)) {
1359                // e1 & (c2 & f2) -> (e1 & c2) & f2
1360                // (c1 & f1) & (c2 & f2) -> ((c1 & f1) & c2) & f2 -> ((c1 & c2) & f1) & f2
1361                (_, Some((c2, f2))) => Some(recurse(expression1, c2).intersection(f2)),
1362                // (c1 & f1) & e2 -> (c1 & e2) & f1
1363                // ((c1 & f1) & g1) & e2 -> ((c1 & f1) & e2) & g1 -> ((c1 & e2) & f1) & g1
1364                (Some((c1, f1)), _) => Some(recurse(c1, expression2).intersection(f1)),
1365                (None, None) => None,
1366            },
1367        }
1368    }
1369
1370    // Bottom-up pass pulls up-right filter node from leaf '(c & f) & e' ->
1371    // '(c & e) & f', so that an intersection of filter node can be found as
1372    // a direct child of another intersection node. However, the rewritten
1373    // intersection node 'c & e' can also be a rewrite target if 'e' contains
1374    // a filter node. That's why intersect_down() is also recursive.
1375    transform_expression_bottom_up(expression, |expression| match expression.as_ref() {
1376        RevsetExpression::Present(e) => {
1377            is_filter_tree(e).then(|| Rc::new(RevsetExpression::AsFilter(expression.clone())))
1378        }
1379        RevsetExpression::NotIn(e) => {
1380            is_filter_tree(e).then(|| Rc::new(RevsetExpression::AsFilter(expression.clone())))
1381        }
1382        RevsetExpression::Union(e1, e2) => (is_filter_tree(e1) || is_filter_tree(e2))
1383            .then(|| Rc::new(RevsetExpression::AsFilter(expression.clone()))),
1384        RevsetExpression::Intersection(expression1, expression2) => {
1385            intersect_down(expression1, expression2)
1386        }
1387        // Difference(e1, e2) should have been unfolded to Intersection(e1, NotIn(e2)).
1388        _ => None,
1389    })
1390}
1391
1392/// Eliminates redundant nodes like `x & all()`, `~~x`.
1393///
1394/// This does not rewrite 'x & none()' to 'none()' because 'x' may be an invalid
1395/// symbol.
1396fn fold_redundant_expression(expression: &Rc<RevsetExpression>) -> Option<Rc<RevsetExpression>> {
1397    transform_expression_bottom_up(expression, |expression| match expression.as_ref() {
1398        RevsetExpression::NotIn(outer) => match outer.as_ref() {
1399            RevsetExpression::NotIn(inner) => Some(inner.clone()),
1400            _ => None,
1401        },
1402        RevsetExpression::Intersection(expression1, expression2) => {
1403            match (expression1.as_ref(), expression2.as_ref()) {
1404                (_, RevsetExpression::All) => Some(expression1.clone()),
1405                (RevsetExpression::All, _) => Some(expression2.clone()),
1406                _ => None,
1407            }
1408        }
1409        _ => None,
1410    })
1411}
1412
1413/// Transforms negative intersection to difference. Redundant intersections like
1414/// `all() & e` should have been removed.
1415fn fold_difference(expression: &Rc<RevsetExpression>) -> Option<Rc<RevsetExpression>> {
1416    fn to_difference(
1417        expression: &Rc<RevsetExpression>,
1418        complement: &Rc<RevsetExpression>,
1419    ) -> Rc<RevsetExpression> {
1420        match (expression.as_ref(), complement.as_ref()) {
1421            // :heads & ~(:roots) -> roots..heads
1422            (
1423                RevsetExpression::Ancestors { heads, generation },
1424                RevsetExpression::Ancestors {
1425                    heads: roots,
1426                    generation: GENERATION_RANGE_FULL,
1427                },
1428            ) => Rc::new(RevsetExpression::Range {
1429                roots: roots.clone(),
1430                heads: heads.clone(),
1431                generation: generation.clone(),
1432            }),
1433            _ => expression.minus(complement),
1434        }
1435    }
1436
1437    transform_expression_bottom_up(expression, |expression| match expression.as_ref() {
1438        RevsetExpression::Intersection(expression1, expression2) => {
1439            match (expression1.as_ref(), expression2.as_ref()) {
1440                (_, RevsetExpression::NotIn(complement)) => {
1441                    Some(to_difference(expression1, complement))
1442                }
1443                (RevsetExpression::NotIn(complement), _) => {
1444                    Some(to_difference(expression2, complement))
1445                }
1446                _ => None,
1447            }
1448        }
1449        _ => None,
1450    })
1451}
1452
1453/// Transforms binary difference to more primitive negative intersection.
1454///
1455/// For example, `all() ~ e` will become `all() & ~e`, which can be simplified
1456/// further by `fold_redundant_expression()`.
1457fn unfold_difference(expression: &Rc<RevsetExpression>) -> Option<Rc<RevsetExpression>> {
1458    transform_expression_bottom_up(expression, |expression| match expression.as_ref() {
1459        // roots..heads -> :heads & ~(:roots)
1460        RevsetExpression::Range {
1461            roots,
1462            heads,
1463            generation,
1464        } => {
1465            let heads_ancestors = Rc::new(RevsetExpression::Ancestors {
1466                heads: heads.clone(),
1467                generation: generation.clone(),
1468            });
1469            Some(heads_ancestors.intersection(&roots.ancestors().negated()))
1470        }
1471        RevsetExpression::Difference(expression1, expression2) => {
1472            Some(expression1.intersection(&expression2.negated()))
1473        }
1474        _ => None,
1475    })
1476}
1477
1478/// Transforms nested `ancestors()`/`parents()` like `h---`.
1479fn fold_ancestors(expression: &Rc<RevsetExpression>) -> Option<Rc<RevsetExpression>> {
1480    transform_expression_bottom_up(expression, |expression| match expression.as_ref() {
1481        RevsetExpression::Ancestors {
1482            heads,
1483            generation: generation1,
1484        } => {
1485            match heads.as_ref() {
1486                // (h-)- -> ancestors(ancestors(h, 1), 1) -> ancestors(h, 2)
1487                // :(h-) -> ancestors(ancestors(h, 1), ..) -> ancestors(h, 1..)
1488                // (:h)- -> ancestors(ancestors(h, ..), 1) -> ancestors(h, 1..)
1489                RevsetExpression::Ancestors {
1490                    heads,
1491                    generation: generation2,
1492                } => {
1493                    // For any (g1, g2) in (generation1, generation2), g1 + g2.
1494                    let generation = if generation1.is_empty() || generation2.is_empty() {
1495                        GENERATION_RANGE_EMPTY
1496                    } else {
1497                        let start = u32::saturating_add(generation1.start, generation2.start);
1498                        let end = u32::saturating_add(generation1.end, generation2.end - 1);
1499                        start..end
1500                    };
1501                    Some(Rc::new(RevsetExpression::Ancestors {
1502                        heads: heads.clone(),
1503                        generation,
1504                    }))
1505                }
1506                _ => None,
1507            }
1508        }
1509        // Range should have been unfolded to intersection of Ancestors.
1510        _ => None,
1511    })
1512}
1513
1514/// Rewrites the given `expression` tree to reduce evaluation cost. Returns new
1515/// tree.
1516pub fn optimize(expression: Rc<RevsetExpression>) -> Rc<RevsetExpression> {
1517    let expression = unfold_difference(&expression).unwrap_or(expression);
1518    let expression = fold_redundant_expression(&expression).unwrap_or(expression);
1519    let expression = fold_ancestors(&expression).unwrap_or(expression);
1520    let expression = internalize_filter(&expression).unwrap_or(expression);
1521    fold_difference(&expression).unwrap_or(expression)
1522}
1523
1524pub trait Revset<'repo>: ToPredicateFn<'repo> {
1525    // All revsets currently iterate in order of descending index position
1526    fn iter<'revset>(&'revset self) -> RevsetIterator<'revset, 'repo>;
1527
1528    fn is_empty(&self) -> bool {
1529        self.iter().next().is_none()
1530    }
1531}
1532
1533// This trait is implementation detail, which can be hidden in private module.
1534pub trait ToPredicateFn<'repo> {
1535    /// Creates function that tests if the given entry is included in the set.
1536    ///
1537    /// The predicate function is evaluated in order of `RevsetIterator`.
1538    fn to_predicate_fn(&self) -> Box<dyn FnMut(&IndexEntry<'repo>) -> bool + '_>;
1539}
1540
1541impl<'repo, T> ToPredicateFn<'repo> for Box<T>
1542where
1543    T: ToPredicateFn<'repo> + ?Sized,
1544{
1545    fn to_predicate_fn(&self) -> Box<dyn FnMut(&IndexEntry<'repo>) -> bool + '_> {
1546        <T as ToPredicateFn<'repo>>::to_predicate_fn(self)
1547    }
1548}
1549
1550pub struct RevsetIterator<'revset, 'repo: 'revset> {
1551    inner: Box<dyn Iterator<Item = IndexEntry<'repo>> + 'revset>,
1552}
1553
1554impl<'revset, 'repo> RevsetIterator<'revset, 'repo> {
1555    fn new(inner: Box<dyn Iterator<Item = IndexEntry<'repo>> + 'revset>) -> Self {
1556        Self { inner }
1557    }
1558
1559    pub fn commit_ids(self) -> RevsetCommitIdIterator<'revset, 'repo> {
1560        RevsetCommitIdIterator(self.inner)
1561    }
1562
1563    pub fn commits(self, store: &Arc<Store>) -> RevsetCommitIterator<'revset, 'repo> {
1564        RevsetCommitIterator {
1565            iter: self.inner,
1566            store: store.clone(),
1567        }
1568    }
1569
1570    pub fn reversed(self) -> ReverseRevsetIterator<'repo> {
1571        ReverseRevsetIterator {
1572            entries: self.into_iter().collect_vec(),
1573        }
1574    }
1575
1576    pub fn graph(self) -> RevsetGraphIterator<'revset, 'repo> {
1577        RevsetGraphIterator::new(self)
1578    }
1579
1580    fn into_predicate_fn(self) -> Box<dyn FnMut(&IndexEntry<'repo>) -> bool + 'revset> {
1581        let mut iter = self.fuse().peekable();
1582        Box::new(move |entry| {
1583            while iter.next_if(|e| e.position() > entry.position()).is_some() {
1584                continue;
1585            }
1586            iter.next_if(|e| e.position() == entry.position()).is_some()
1587        })
1588    }
1589}
1590
1591impl<'repo> Iterator for RevsetIterator<'_, 'repo> {
1592    type Item = IndexEntry<'repo>;
1593
1594    fn next(&mut self) -> Option<Self::Item> {
1595        self.inner.next()
1596    }
1597}
1598
1599pub struct RevsetCommitIdIterator<'revset, 'repo: 'revset>(
1600    Box<dyn Iterator<Item = IndexEntry<'repo>> + 'revset>,
1601);
1602
1603impl Iterator for RevsetCommitIdIterator<'_, '_> {
1604    type Item = CommitId;
1605
1606    fn next(&mut self) -> Option<Self::Item> {
1607        self.0.next().map(|index_entry| index_entry.commit_id())
1608    }
1609}
1610
1611pub struct RevsetCommitIterator<'revset, 'repo: 'revset> {
1612    store: Arc<Store>,
1613    iter: Box<dyn Iterator<Item = IndexEntry<'repo>> + 'revset>,
1614}
1615
1616impl Iterator for RevsetCommitIterator<'_, '_> {
1617    type Item = BackendResult<Commit>;
1618
1619    fn next(&mut self) -> Option<Self::Item> {
1620        self.iter
1621            .next()
1622            .map(|index_entry| self.store.get_commit(&index_entry.commit_id()))
1623    }
1624}
1625
1626pub struct ReverseRevsetIterator<'repo> {
1627    entries: Vec<IndexEntry<'repo>>,
1628}
1629
1630impl<'repo> Iterator for ReverseRevsetIterator<'repo> {
1631    type Item = IndexEntry<'repo>;
1632
1633    fn next(&mut self) -> Option<Self::Item> {
1634        self.entries.pop()
1635    }
1636}
1637
1638struct EagerRevset<'repo> {
1639    index_entries: Vec<IndexEntry<'repo>>,
1640}
1641
1642impl EagerRevset<'static> {
1643    pub const fn empty() -> Self {
1644        EagerRevset {
1645            index_entries: Vec::new(),
1646        }
1647    }
1648}
1649
1650impl<'repo> Revset<'repo> for EagerRevset<'repo> {
1651    fn iter<'revset>(&'revset self) -> RevsetIterator<'revset, 'repo> {
1652        RevsetIterator::new(Box::new(self.index_entries.iter().cloned()))
1653    }
1654}
1655
1656impl<'repo> ToPredicateFn<'repo> for EagerRevset<'repo> {
1657    fn to_predicate_fn(&self) -> Box<dyn FnMut(&IndexEntry<'repo>) -> bool + '_> {
1658        self.iter().into_predicate_fn()
1659    }
1660}
1661
1662struct RevWalkRevset<'repo, T>
1663where
1664    // RevWalkRevset<'repo> appears to be needed to assert 'repo outlives 'a
1665    // in to_predicate_fn<'a>(&'a self) -> Box<dyn 'a>.
1666    T: Iterator<Item = IndexEntry<'repo>>,
1667{
1668    walk: T,
1669}
1670
1671impl<'repo, T> Revset<'repo> for RevWalkRevset<'repo, T>
1672where
1673    T: Iterator<Item = IndexEntry<'repo>> + Clone,
1674{
1675    fn iter<'revset>(&'revset self) -> RevsetIterator<'revset, 'repo> {
1676        RevsetIterator::new(Box::new(self.walk.clone()))
1677    }
1678}
1679
1680impl<'repo, T> ToPredicateFn<'repo> for RevWalkRevset<'repo, T>
1681where
1682    T: Iterator<Item = IndexEntry<'repo>> + Clone,
1683{
1684    fn to_predicate_fn(&self) -> Box<dyn FnMut(&IndexEntry<'repo>) -> bool + '_> {
1685        self.iter().into_predicate_fn()
1686    }
1687}
1688
1689struct ChildrenRevset<'revset, 'repo: 'revset> {
1690    // The revisions we want to find children for
1691    root_set: Box<dyn Revset<'repo> + 'revset>,
1692    // Consider only candidates from this set
1693    candidate_set: Box<dyn Revset<'repo> + 'revset>,
1694}
1695
1696impl<'repo> Revset<'repo> for ChildrenRevset<'_, 'repo> {
1697    fn iter<'revset>(&'revset self) -> RevsetIterator<'revset, 'repo> {
1698        let roots: HashSet<_> = self
1699            .root_set
1700            .iter()
1701            .map(|parent| parent.position())
1702            .collect();
1703
1704        RevsetIterator::new(Box::new(self.candidate_set.iter().filter(
1705            move |candidate| {
1706                candidate
1707                    .parent_positions()
1708                    .iter()
1709                    .any(|parent_pos| roots.contains(parent_pos))
1710            },
1711        )))
1712    }
1713}
1714
1715impl<'repo> ToPredicateFn<'repo> for ChildrenRevset<'_, 'repo> {
1716    fn to_predicate_fn(&self) -> Box<dyn FnMut(&IndexEntry<'repo>) -> bool + '_> {
1717        // TODO: can be optimized if candidate_set contains all heads
1718        self.iter().into_predicate_fn()
1719    }
1720}
1721
1722struct FilterRevset<'revset, 'repo: 'revset, P> {
1723    candidates: Box<dyn Revset<'repo> + 'revset>,
1724    predicate: P,
1725}
1726
1727impl<'repo, P> Revset<'repo> for FilterRevset<'_, 'repo, P>
1728where
1729    P: ToPredicateFn<'repo>,
1730{
1731    fn iter<'revset>(&'revset self) -> RevsetIterator<'revset, 'repo> {
1732        let p = self.predicate.to_predicate_fn();
1733        RevsetIterator::new(Box::new(self.candidates.iter().filter(p)))
1734    }
1735}
1736
1737impl<'repo, P> ToPredicateFn<'repo> for FilterRevset<'_, 'repo, P>
1738where
1739    P: ToPredicateFn<'repo>,
1740{
1741    fn to_predicate_fn(&self) -> Box<dyn FnMut(&IndexEntry<'repo>) -> bool + '_> {
1742        // TODO: optimize 'p1' out if candidates = All
1743        let mut p1 = self.candidates.to_predicate_fn();
1744        let mut p2 = self.predicate.to_predicate_fn();
1745        Box::new(move |entry| p1(entry) && p2(entry))
1746    }
1747}
1748
1749struct UnionRevset<'revset, 'repo: 'revset> {
1750    set1: Box<dyn Revset<'repo> + 'revset>,
1751    set2: Box<dyn Revset<'repo> + 'revset>,
1752}
1753
1754impl<'repo> Revset<'repo> for UnionRevset<'_, 'repo> {
1755    fn iter<'revset>(&'revset self) -> RevsetIterator<'revset, 'repo> {
1756        RevsetIterator::new(Box::new(UnionRevsetIterator {
1757            iter1: self.set1.iter().peekable(),
1758            iter2: self.set2.iter().peekable(),
1759        }))
1760    }
1761}
1762
1763impl<'repo> ToPredicateFn<'repo> for UnionRevset<'_, 'repo> {
1764    fn to_predicate_fn(&self) -> Box<dyn FnMut(&IndexEntry<'repo>) -> bool + '_> {
1765        let mut p1 = self.set1.to_predicate_fn();
1766        let mut p2 = self.set2.to_predicate_fn();
1767        Box::new(move |entry| p1(entry) || p2(entry))
1768    }
1769}
1770
1771struct UnionRevsetIterator<'revset, 'repo> {
1772    iter1: Peekable<RevsetIterator<'revset, 'repo>>,
1773    iter2: Peekable<RevsetIterator<'revset, 'repo>>,
1774}
1775
1776impl<'revset, 'repo> Iterator for UnionRevsetIterator<'revset, 'repo> {
1777    type Item = IndexEntry<'repo>;
1778
1779    fn next(&mut self) -> Option<Self::Item> {
1780        match (self.iter1.peek(), self.iter2.peek()) {
1781            (None, _) => self.iter2.next(),
1782            (_, None) => self.iter1.next(),
1783            (Some(entry1), Some(entry2)) => match entry1.position().cmp(&entry2.position()) {
1784                Ordering::Less => self.iter2.next(),
1785                Ordering::Equal => {
1786                    self.iter1.next();
1787                    self.iter2.next()
1788                }
1789                Ordering::Greater => self.iter1.next(),
1790            },
1791        }
1792    }
1793}
1794
1795struct IntersectionRevset<'revset, 'repo: 'revset> {
1796    set1: Box<dyn Revset<'repo> + 'revset>,
1797    set2: Box<dyn Revset<'repo> + 'revset>,
1798}
1799
1800impl<'repo> Revset<'repo> for IntersectionRevset<'_, 'repo> {
1801    fn iter<'revset>(&'revset self) -> RevsetIterator<'revset, 'repo> {
1802        RevsetIterator::new(Box::new(IntersectionRevsetIterator {
1803            iter1: self.set1.iter().peekable(),
1804            iter2: self.set2.iter().peekable(),
1805        }))
1806    }
1807}
1808
1809impl<'repo> ToPredicateFn<'repo> for IntersectionRevset<'_, 'repo> {
1810    fn to_predicate_fn(&self) -> Box<dyn FnMut(&IndexEntry<'repo>) -> bool + '_> {
1811        let mut p1 = self.set1.to_predicate_fn();
1812        let mut p2 = self.set2.to_predicate_fn();
1813        Box::new(move |entry| p1(entry) && p2(entry))
1814    }
1815}
1816
1817struct IntersectionRevsetIterator<'revset, 'repo> {
1818    iter1: Peekable<RevsetIterator<'revset, 'repo>>,
1819    iter2: Peekable<RevsetIterator<'revset, 'repo>>,
1820}
1821
1822impl<'revset, 'repo> Iterator for IntersectionRevsetIterator<'revset, 'repo> {
1823    type Item = IndexEntry<'repo>;
1824
1825    fn next(&mut self) -> Option<Self::Item> {
1826        loop {
1827            match (self.iter1.peek(), self.iter2.peek()) {
1828                (None, _) => {
1829                    return None;
1830                }
1831                (_, None) => {
1832                    return None;
1833                }
1834                (Some(entry1), Some(entry2)) => match entry1.position().cmp(&entry2.position()) {
1835                    Ordering::Less => {
1836                        self.iter2.next();
1837                    }
1838                    Ordering::Equal => {
1839                        self.iter1.next();
1840                        return self.iter2.next();
1841                    }
1842                    Ordering::Greater => {
1843                        self.iter1.next();
1844                    }
1845                },
1846            }
1847        }
1848    }
1849}
1850
1851struct DifferenceRevset<'revset, 'repo: 'revset> {
1852    // The minuend (what to subtract from)
1853    set1: Box<dyn Revset<'repo> + 'revset>,
1854    // The subtrahend (what to subtract)
1855    set2: Box<dyn Revset<'repo> + 'revset>,
1856}
1857
1858impl<'repo> Revset<'repo> for DifferenceRevset<'_, 'repo> {
1859    fn iter<'revset>(&'revset self) -> RevsetIterator<'revset, 'repo> {
1860        RevsetIterator::new(Box::new(DifferenceRevsetIterator {
1861            iter1: self.set1.iter().peekable(),
1862            iter2: self.set2.iter().peekable(),
1863        }))
1864    }
1865}
1866
1867impl<'repo> ToPredicateFn<'repo> for DifferenceRevset<'_, 'repo> {
1868    fn to_predicate_fn(&self) -> Box<dyn FnMut(&IndexEntry<'repo>) -> bool + '_> {
1869        // TODO: optimize 'p1' out for unary negate?
1870        let mut p1 = self.set1.to_predicate_fn();
1871        let mut p2 = self.set2.to_predicate_fn();
1872        Box::new(move |entry| p1(entry) && !p2(entry))
1873    }
1874}
1875
1876struct DifferenceRevsetIterator<'revset, 'repo> {
1877    iter1: Peekable<RevsetIterator<'revset, 'repo>>,
1878    iter2: Peekable<RevsetIterator<'revset, 'repo>>,
1879}
1880
1881impl<'revset, 'repo> Iterator for DifferenceRevsetIterator<'revset, 'repo> {
1882    type Item = IndexEntry<'repo>;
1883
1884    fn next(&mut self) -> Option<Self::Item> {
1885        loop {
1886            match (self.iter1.peek(), self.iter2.peek()) {
1887                (None, _) => {
1888                    return None;
1889                }
1890                (_, None) => {
1891                    return self.iter1.next();
1892                }
1893                (Some(entry1), Some(entry2)) => match entry1.position().cmp(&entry2.position()) {
1894                    Ordering::Less => {
1895                        self.iter2.next();
1896                    }
1897                    Ordering::Equal => {
1898                        self.iter2.next();
1899                        self.iter1.next();
1900                    }
1901                    Ordering::Greater => {
1902                        return self.iter1.next();
1903                    }
1904                },
1905            }
1906        }
1907    }
1908}
1909
1910/// Workspace information needed to evaluate revset expression.
1911#[derive(Clone, Debug)]
1912pub struct RevsetWorkspaceContext<'a> {
1913    pub cwd: &'a Path,
1914    pub workspace_id: &'a WorkspaceId,
1915    pub workspace_root: &'a Path,
1916}
1917
1918pub fn evaluate_expression<'repo>(
1919    repo: &'repo dyn Repo,
1920    expression: &RevsetExpression,
1921    workspace_ctx: Option<&RevsetWorkspaceContext>,
1922) -> Result<Box<dyn Revset<'repo> + 'repo>, RevsetError> {
1923    match expression {
1924        RevsetExpression::None => Ok(Box::new(EagerRevset::empty())),
1925        RevsetExpression::All => {
1926            // Since `all()` does not include hidden commits, some of the logical
1927            // transformation rules may subtly change the evaluated set. For example,
1928            // `all() & x` is not `x` if `x` is hidden. This wouldn't matter in practice,
1929            // but if it does, the heads set could be extended to include the commits
1930            // (and `remote_branches()`) specified in the revset expression. Alternatively,
1931            // some optimization rules could be removed, but that means `author(_) & x`
1932            // would have to test `:heads() & x`.
1933            evaluate_expression(
1934                repo,
1935                &RevsetExpression::visible_heads().ancestors(),
1936                workspace_ctx,
1937            )
1938        }
1939        RevsetExpression::Commits(commit_ids) => Ok(revset_for_commit_ids(repo, commit_ids)),
1940        RevsetExpression::Symbol(symbol) => {
1941            let commit_ids = resolve_symbol(repo, symbol, workspace_ctx.map(|c| c.workspace_id))?;
1942            evaluate_expression(repo, &RevsetExpression::Commits(commit_ids), workspace_ctx)
1943        }
1944        RevsetExpression::Children(roots) => {
1945            let root_set = roots.evaluate(repo, workspace_ctx)?;
1946            let candidates_expression = roots.descendants();
1947            let candidate_set = candidates_expression.evaluate(repo, workspace_ctx)?;
1948            Ok(Box::new(ChildrenRevset {
1949                root_set,
1950                candidate_set,
1951            }))
1952        }
1953        RevsetExpression::Ancestors { heads, generation } => {
1954            let range_expression = RevsetExpression::Range {
1955                roots: RevsetExpression::none(),
1956                heads: heads.clone(),
1957                generation: generation.clone(),
1958            };
1959            range_expression.evaluate(repo, workspace_ctx)
1960        }
1961        RevsetExpression::Range {
1962            roots,
1963            heads,
1964            generation,
1965        } => {
1966            let root_set = roots.evaluate(repo, workspace_ctx)?;
1967            let root_ids = root_set.iter().commit_ids().collect_vec();
1968            let head_set = heads.evaluate(repo, workspace_ctx)?;
1969            let head_ids = head_set.iter().commit_ids().collect_vec();
1970            let walk = repo.index().walk_revs(&head_ids, &root_ids);
1971            if generation == &GENERATION_RANGE_FULL {
1972                Ok(Box::new(RevWalkRevset { walk }))
1973            } else {
1974                let walk = walk.filter_by_generation(generation.clone());
1975                Ok(Box::new(RevWalkRevset { walk }))
1976            }
1977        }
1978        RevsetExpression::DagRange { roots, heads } => {
1979            let root_set = roots.evaluate(repo, workspace_ctx)?;
1980            let candidate_set = heads.ancestors().evaluate(repo, workspace_ctx)?;
1981            let mut reachable: HashSet<_> = root_set.iter().map(|entry| entry.position()).collect();
1982            let mut result = vec![];
1983            let candidates = candidate_set.iter().collect_vec();
1984            for candidate in candidates.into_iter().rev() {
1985                if reachable.contains(&candidate.position())
1986                    || candidate
1987                        .parent_positions()
1988                        .iter()
1989                        .any(|parent_pos| reachable.contains(parent_pos))
1990                {
1991                    reachable.insert(candidate.position());
1992                    result.push(candidate);
1993                }
1994            }
1995            result.reverse();
1996            Ok(Box::new(EagerRevset {
1997                index_entries: result,
1998            }))
1999        }
2000        RevsetExpression::VisibleHeads => Ok(revset_for_commit_ids(
2001            repo,
2002            &repo.view().heads().iter().cloned().collect_vec(),
2003        )),
2004        RevsetExpression::Heads(candidates) => {
2005            let candidate_set = candidates.evaluate(repo, workspace_ctx)?;
2006            let candidate_ids = candidate_set.iter().commit_ids().collect_vec();
2007            Ok(revset_for_commit_ids(
2008                repo,
2009                &repo.index().heads(&mut candidate_ids.iter()),
2010            ))
2011        }
2012        RevsetExpression::Roots(candidates) => {
2013            let connected_set = candidates.connected().evaluate(repo, workspace_ctx)?;
2014            let filled: HashSet<_> = connected_set.iter().map(|entry| entry.position()).collect();
2015            let mut index_entries = vec![];
2016            let candidate_set = candidates.evaluate(repo, workspace_ctx)?;
2017            for candidate in candidate_set.iter() {
2018                if !candidate
2019                    .parent_positions()
2020                    .iter()
2021                    .any(|parent| filled.contains(parent))
2022                {
2023                    index_entries.push(candidate);
2024                }
2025            }
2026            Ok(Box::new(EagerRevset { index_entries }))
2027        }
2028        RevsetExpression::PublicHeads => Ok(revset_for_commit_ids(
2029            repo,
2030            &repo.view().public_heads().iter().cloned().collect_vec(),
2031        )),
2032        RevsetExpression::Branches(needle) => {
2033            let mut commit_ids = vec![];
2034            for (branch_name, branch_target) in repo.view().branches() {
2035                if !branch_name.contains(needle) {
2036                    continue;
2037                }
2038                if let Some(local_target) = &branch_target.local_target {
2039                    commit_ids.extend(local_target.adds());
2040                }
2041            }
2042            Ok(revset_for_commit_ids(repo, &commit_ids))
2043        }
2044        RevsetExpression::RemoteBranches {
2045            branch_needle,
2046            remote_needle,
2047        } => {
2048            let mut commit_ids = vec![];
2049            for (branch_name, branch_target) in repo.view().branches() {
2050                if !branch_name.contains(branch_needle) {
2051                    continue;
2052                }
2053                for (remote_name, remote_target) in branch_target.remote_targets.iter() {
2054                    if remote_name.contains(remote_needle) {
2055                        commit_ids.extend(remote_target.adds());
2056                    }
2057                }
2058            }
2059            Ok(revset_for_commit_ids(repo, &commit_ids))
2060        }
2061        RevsetExpression::Tags => {
2062            let mut commit_ids = vec![];
2063            for ref_target in repo.view().tags().values() {
2064                commit_ids.extend(ref_target.adds());
2065            }
2066            Ok(revset_for_commit_ids(repo, &commit_ids))
2067        }
2068        RevsetExpression::GitRefs => {
2069            let mut commit_ids = vec![];
2070            for ref_target in repo.view().git_refs().values() {
2071                commit_ids.extend(ref_target.adds());
2072            }
2073            Ok(revset_for_commit_ids(repo, &commit_ids))
2074        }
2075        RevsetExpression::GitHead => {
2076            let mut commit_ids = vec![];
2077            if let Some(ref_target) = repo.view().git_head() {
2078                commit_ids.extend(ref_target.adds());
2079            }
2080            Ok(revset_for_commit_ids(repo, &commit_ids))
2081        }
2082        RevsetExpression::Filter(predicate) => Ok(Box::new(FilterRevset {
2083            candidates: RevsetExpression::All.evaluate(repo, workspace_ctx)?,
2084            predicate: build_predicate_fn(repo, predicate),
2085        })),
2086        RevsetExpression::AsFilter(candidates) => candidates.evaluate(repo, workspace_ctx),
2087        RevsetExpression::Present(candidates) => match candidates.evaluate(repo, workspace_ctx) {
2088            Ok(set) => Ok(set),
2089            Err(RevsetError::NoSuchRevision(_)) => Ok(Box::new(EagerRevset::empty())),
2090            r @ Err(RevsetError::AmbiguousIdPrefix(_) | RevsetError::StoreError(_)) => r,
2091        },
2092        RevsetExpression::NotIn(complement) => {
2093            let set1 = RevsetExpression::All.evaluate(repo, workspace_ctx)?;
2094            let set2 = complement.evaluate(repo, workspace_ctx)?;
2095            Ok(Box::new(DifferenceRevset { set1, set2 }))
2096        }
2097        RevsetExpression::Union(expression1, expression2) => {
2098            let set1 = expression1.evaluate(repo, workspace_ctx)?;
2099            let set2 = expression2.evaluate(repo, workspace_ctx)?;
2100            Ok(Box::new(UnionRevset { set1, set2 }))
2101        }
2102        RevsetExpression::Intersection(expression1, expression2) => {
2103            match expression2.as_ref() {
2104                RevsetExpression::Filter(predicate) => Ok(Box::new(FilterRevset {
2105                    candidates: expression1.evaluate(repo, workspace_ctx)?,
2106                    predicate: build_predicate_fn(repo, predicate),
2107                })),
2108                RevsetExpression::AsFilter(expression2) => Ok(Box::new(FilterRevset {
2109                    candidates: expression1.evaluate(repo, workspace_ctx)?,
2110                    predicate: expression2.evaluate(repo, workspace_ctx)?,
2111                })),
2112                _ => {
2113                    // TODO: 'set2' can be turned into a predicate, and use FilterRevset
2114                    // if a predicate function can terminate the 'set1' iterator early.
2115                    let set1 = expression1.evaluate(repo, workspace_ctx)?;
2116                    let set2 = expression2.evaluate(repo, workspace_ctx)?;
2117                    Ok(Box::new(IntersectionRevset { set1, set2 }))
2118                }
2119            }
2120        }
2121        RevsetExpression::Difference(expression1, expression2) => {
2122            let set1 = expression1.evaluate(repo, workspace_ctx)?;
2123            let set2 = expression2.evaluate(repo, workspace_ctx)?;
2124            Ok(Box::new(DifferenceRevset { set1, set2 }))
2125        }
2126    }
2127}
2128
2129fn revset_for_commit_ids<'revset, 'repo: 'revset>(
2130    repo: &'repo dyn Repo,
2131    commit_ids: &[CommitId],
2132) -> Box<dyn Revset<'repo> + 'revset> {
2133    let index = repo.index();
2134    let mut index_entries = vec![];
2135    for id in commit_ids {
2136        index_entries.push(index.entry_by_id(id).unwrap());
2137    }
2138    index_entries.sort_by_key(|b| Reverse(b.position()));
2139    index_entries.dedup();
2140    Box::new(EagerRevset { index_entries })
2141}
2142
2143pub fn revset_for_commits<'revset, 'repo: 'revset>(
2144    repo: &'repo dyn Repo,
2145    commits: &[&Commit],
2146) -> Box<dyn Revset<'repo> + 'revset> {
2147    let index = repo.index();
2148    let mut index_entries = commits
2149        .iter()
2150        .map(|commit| index.entry_by_id(commit.id()).unwrap())
2151        .collect_vec();
2152    index_entries.sort_by_key(|b| Reverse(b.position()));
2153    Box::new(EagerRevset { index_entries })
2154}
2155
2156type PurePredicateFn<'repo> = Box<dyn Fn(&IndexEntry<'repo>) -> bool + 'repo>;
2157
2158impl<'repo> ToPredicateFn<'repo> for PurePredicateFn<'repo> {
2159    fn to_predicate_fn(&self) -> Box<dyn FnMut(&IndexEntry<'repo>) -> bool + '_> {
2160        Box::new(self)
2161    }
2162}
2163
2164fn build_predicate_fn<'repo>(
2165    repo: &'repo dyn Repo,
2166    predicate: &RevsetFilterPredicate,
2167) -> PurePredicateFn<'repo> {
2168    match predicate {
2169        RevsetFilterPredicate::ParentCount(parent_count_range) => {
2170            let parent_count_range = parent_count_range.clone();
2171            Box::new(move |entry| parent_count_range.contains(&entry.num_parents()))
2172        }
2173        RevsetFilterPredicate::Description(needle) => {
2174            let needle = needle.clone();
2175            Box::new(move |entry| {
2176                repo.store()
2177                    .get_commit(&entry.commit_id())
2178                    .unwrap()
2179                    .description()
2180                    .contains(needle.as_str())
2181            })
2182        }
2183        RevsetFilterPredicate::Author(needle) => {
2184            let needle = needle.clone();
2185            // TODO: Make these functions that take a needle to search for accept some
2186            // syntax for specifying whether it's a regex and whether it's
2187            // case-sensitive.
2188            Box::new(move |entry| {
2189                let commit = repo.store().get_commit(&entry.commit_id()).unwrap();
2190                commit.author().name.contains(needle.as_str())
2191                    || commit.author().email.contains(needle.as_str())
2192            })
2193        }
2194        RevsetFilterPredicate::Committer(needle) => {
2195            let needle = needle.clone();
2196            Box::new(move |entry| {
2197                let commit = repo.store().get_commit(&entry.commit_id()).unwrap();
2198                commit.committer().name.contains(needle.as_str())
2199                    || commit.committer().email.contains(needle.as_str())
2200            })
2201        }
2202        RevsetFilterPredicate::File(paths) => {
2203            // TODO: Add support for globs and other formats
2204            let matcher: Box<dyn Matcher> = if let Some(paths) = paths {
2205                Box::new(PrefixMatcher::new(paths))
2206            } else {
2207                Box::new(EverythingMatcher)
2208            };
2209            Box::new(move |entry| has_diff_from_parent(repo, entry, matcher.as_ref()))
2210        }
2211    }
2212}
2213
2214pub fn filter_by_diff<'revset, 'repo: 'revset>(
2215    repo: &'repo dyn Repo,
2216    matcher: impl Borrow<dyn Matcher + 'repo> + 'repo,
2217    candidates: Box<dyn Revset<'repo> + 'revset>,
2218) -> Box<dyn Revset<'repo> + 'revset> {
2219    Box::new(FilterRevset::<PurePredicateFn> {
2220        candidates,
2221        predicate: Box::new(move |entry| has_diff_from_parent(repo, entry, matcher.borrow())),
2222    })
2223}
2224
2225fn has_diff_from_parent(repo: &dyn Repo, entry: &IndexEntry<'_>, matcher: &dyn Matcher) -> bool {
2226    let commit = repo.store().get_commit(&entry.commit_id()).unwrap();
2227    let parents = commit.parents();
2228    let from_tree = rewrite::merge_commit_trees(repo, &parents);
2229    let to_tree = commit.tree();
2230    from_tree.diff(&to_tree, matcher).next().is_some()
2231}
2232
2233#[cfg(test)]
2234mod tests {
2235    use super::*;
2236    use crate::backend::ChangeId;
2237    use crate::index::{Index, MutableIndex};
2238
2239    /// Generator of unique 16-byte ChangeId excluding root id
2240    fn change_id_generator() -> impl FnMut() -> ChangeId {
2241        let mut iter = (1_u128..).map(|n| ChangeId::new(n.to_le_bytes().into()));
2242        move || iter.next().unwrap()
2243    }
2244
2245    fn parse(revset_str: &str) -> Result<Rc<RevsetExpression>, RevsetParseErrorKind> {
2246        parse_with_aliases(revset_str, [] as [(&str, &str); 0])
2247    }
2248
2249    fn parse_with_aliases(
2250        revset_str: &str,
2251        aliases: impl IntoIterator<Item = (impl AsRef<str>, impl Into<String>)>,
2252    ) -> Result<Rc<RevsetExpression>, RevsetParseErrorKind> {
2253        let mut aliases_map = RevsetAliasesMap::new();
2254        for (decl, defn) in aliases {
2255            aliases_map.insert(decl, defn).unwrap();
2256        }
2257        // Set up pseudo context to resolve file(path)
2258        let workspace_ctx = RevsetWorkspaceContext {
2259            cwd: Path::new("/"),
2260            workspace_id: &WorkspaceId::default(),
2261            workspace_root: Path::new("/"),
2262        };
2263        // Map error to comparable object
2264        super::parse(revset_str, &aliases_map, Some(&workspace_ctx)).map_err(|e| e.kind)
2265    }
2266
2267    #[test]
2268    fn test_revset_expression_building() {
2269        let wc_symbol = RevsetExpression::symbol("@".to_string());
2270        let foo_symbol = RevsetExpression::symbol("foo".to_string());
2271        assert_eq!(
2272            wc_symbol,
2273            Rc::new(RevsetExpression::Symbol("@".to_string()))
2274        );
2275        assert_eq!(
2276            wc_symbol.heads(),
2277            Rc::new(RevsetExpression::Heads(wc_symbol.clone()))
2278        );
2279        assert_eq!(
2280            wc_symbol.roots(),
2281            Rc::new(RevsetExpression::Roots(wc_symbol.clone()))
2282        );
2283        assert_eq!(
2284            wc_symbol.parents(),
2285            Rc::new(RevsetExpression::Ancestors {
2286                heads: wc_symbol.clone(),
2287                generation: 1..2,
2288            })
2289        );
2290        assert_eq!(
2291            wc_symbol.ancestors(),
2292            Rc::new(RevsetExpression::Ancestors {
2293                heads: wc_symbol.clone(),
2294                generation: GENERATION_RANGE_FULL,
2295            })
2296        );
2297        assert_eq!(
2298            foo_symbol.children(),
2299            Rc::new(RevsetExpression::Children(foo_symbol.clone()))
2300        );
2301        assert_eq!(
2302            foo_symbol.descendants(),
2303            Rc::new(RevsetExpression::DagRange {
2304                roots: foo_symbol.clone(),
2305                heads: RevsetExpression::visible_heads(),
2306            })
2307        );
2308        assert_eq!(
2309            foo_symbol.dag_range_to(&wc_symbol),
2310            Rc::new(RevsetExpression::DagRange {
2311                roots: foo_symbol.clone(),
2312                heads: wc_symbol.clone(),
2313            })
2314        );
2315        assert_eq!(
2316            foo_symbol.connected(),
2317            Rc::new(RevsetExpression::DagRange {
2318                roots: foo_symbol.clone(),
2319                heads: foo_symbol.clone(),
2320            })
2321        );
2322        assert_eq!(
2323            foo_symbol.range(&wc_symbol),
2324            Rc::new(RevsetExpression::Range {
2325                roots: foo_symbol.clone(),
2326                heads: wc_symbol.clone(),
2327                generation: GENERATION_RANGE_FULL,
2328            })
2329        );
2330        assert_eq!(
2331            foo_symbol.negated(),
2332            Rc::new(RevsetExpression::NotIn(foo_symbol.clone()))
2333        );
2334        assert_eq!(
2335            foo_symbol.union(&wc_symbol),
2336            Rc::new(RevsetExpression::Union(
2337                foo_symbol.clone(),
2338                wc_symbol.clone()
2339            ))
2340        );
2341        assert_eq!(
2342            foo_symbol.intersection(&wc_symbol),
2343            Rc::new(RevsetExpression::Intersection(
2344                foo_symbol.clone(),
2345                wc_symbol.clone()
2346            ))
2347        );
2348        assert_eq!(
2349            foo_symbol.minus(&wc_symbol),
2350            Rc::new(RevsetExpression::Difference(foo_symbol, wc_symbol.clone()))
2351        );
2352    }
2353
2354    #[test]
2355    fn test_parse_revset() {
2356        let wc_symbol = RevsetExpression::symbol("@".to_string());
2357        let foo_symbol = RevsetExpression::symbol("foo".to_string());
2358        let bar_symbol = RevsetExpression::symbol("bar".to_string());
2359        // Parse a single symbol (specifically the "checkout" symbol)
2360        assert_eq!(parse("@"), Ok(wc_symbol.clone()));
2361        // Parse a single symbol
2362        assert_eq!(parse("foo"), Ok(foo_symbol.clone()));
2363        // Internal '.', '-', and '+' are allowed
2364        assert_eq!(
2365            parse("foo.bar-v1+7"),
2366            Ok(RevsetExpression::symbol("foo.bar-v1+7".to_string()))
2367        );
2368        assert_eq!(
2369            parse("foo.bar-v1+7-"),
2370            Ok(RevsetExpression::symbol("foo.bar-v1+7".to_string()).parents())
2371        );
2372        // Default arguments for *branches() are all ""
2373        assert_eq!(parse("branches()"), parse(r#"branches("")"#));
2374        assert_eq!(parse("remote_branches()"), parse(r#"remote_branches("")"#));
2375        assert_eq!(
2376            parse("remote_branches()"),
2377            parse(r#"remote_branches("", "")"#)
2378        );
2379        // '.' is not allowed at the beginning or end
2380        assert_eq!(parse(".foo"), Err(RevsetParseErrorKind::SyntaxError));
2381        assert_eq!(parse("foo."), Err(RevsetParseErrorKind::SyntaxError));
2382        // Multiple '.', '-', '+' are not allowed
2383        assert_eq!(parse("foo.+bar"), Err(RevsetParseErrorKind::SyntaxError));
2384        assert_eq!(parse("foo--bar"), Err(RevsetParseErrorKind::SyntaxError));
2385        assert_eq!(parse("foo+-bar"), Err(RevsetParseErrorKind::SyntaxError));
2386        // Parse a parenthesized symbol
2387        assert_eq!(parse("(foo)"), Ok(foo_symbol.clone()));
2388        // Parse a quoted symbol
2389        assert_eq!(parse("\"foo\""), Ok(foo_symbol.clone()));
2390        // Parse the "parents" operator
2391        assert_eq!(parse("@-"), Ok(wc_symbol.parents()));
2392        // Parse the "children" operator
2393        assert_eq!(parse("@+"), Ok(wc_symbol.children()));
2394        // Parse the "ancestors" operator
2395        assert_eq!(parse(":@"), Ok(wc_symbol.ancestors()));
2396        // Parse the "descendants" operator
2397        assert_eq!(parse("@:"), Ok(wc_symbol.descendants()));
2398        // Parse the "dag range" operator
2399        assert_eq!(parse("foo:bar"), Ok(foo_symbol.dag_range_to(&bar_symbol)));
2400        // Parse the "range" prefix operator
2401        assert_eq!(parse("..@"), Ok(wc_symbol.ancestors()));
2402        assert_eq!(
2403            parse("@.."),
2404            Ok(wc_symbol.range(&RevsetExpression::visible_heads()))
2405        );
2406        assert_eq!(parse("foo..bar"), Ok(foo_symbol.range(&bar_symbol)));
2407        // Parse the "negate" operator
2408        assert_eq!(parse("~ foo"), Ok(foo_symbol.negated()));
2409        assert_eq!(
2410            parse("~ ~~ foo"),
2411            Ok(foo_symbol.negated().negated().negated())
2412        );
2413        // Parse the "intersection" operator
2414        assert_eq!(parse("foo & bar"), Ok(foo_symbol.intersection(&bar_symbol)));
2415        // Parse the "union" operator
2416        assert_eq!(parse("foo | bar"), Ok(foo_symbol.union(&bar_symbol)));
2417        // Parse the "difference" operator
2418        assert_eq!(parse("foo ~ bar"), Ok(foo_symbol.minus(&bar_symbol)));
2419        // Parentheses are allowed before suffix operators
2420        assert_eq!(parse("(@)-"), Ok(wc_symbol.parents()));
2421        // Space is allowed around expressions
2422        assert_eq!(parse(" :@ "), Ok(wc_symbol.ancestors()));
2423        assert_eq!(parse("( :@ )"), Ok(wc_symbol.ancestors()));
2424        // Space is not allowed around prefix operators
2425        assert_eq!(parse(" : @ "), Err(RevsetParseErrorKind::SyntaxError));
2426        // Incomplete parse
2427        assert_eq!(parse("foo | -"), Err(RevsetParseErrorKind::SyntaxError));
2428        // Space is allowed around infix operators and function arguments
2429        assert_eq!(
2430            parse("   description(  arg1 ) ~    file(  arg1 ,   arg2 )  ~ heads(  )  "),
2431            Ok(
2432                RevsetExpression::filter(RevsetFilterPredicate::Description("arg1".to_string()))
2433                    .minus(&RevsetExpression::filter(RevsetFilterPredicate::File(
2434                        Some(vec![
2435                            RepoPath::from_internal_string("arg1"),
2436                            RepoPath::from_internal_string("arg2"),
2437                        ])
2438                    )))
2439                    .minus(&RevsetExpression::visible_heads())
2440            )
2441        );
2442        // Space is allowed around keyword arguments
2443        assert_eq!(
2444            parse("remote_branches( remote  =   foo  )").unwrap(),
2445            parse("remote_branches(remote=foo)").unwrap(),
2446        );
2447
2448        // Trailing comma isn't allowed for empty argument
2449        assert!(parse("branches(,)").is_err());
2450        // Trailing comma is allowed for the last argument
2451        assert!(parse("branches(a,)").is_ok());
2452        assert!(parse("branches(a ,  )").is_ok());
2453        assert!(parse("branches(,a)").is_err());
2454        assert!(parse("branches(a,,)").is_err());
2455        assert!(parse("branches(a  , , )").is_err());
2456        assert!(parse("file(a,b,)").is_ok());
2457        assert!(parse("file(a,,b)").is_err());
2458        assert!(parse("remote_branches(a,remote=b  , )").is_ok());
2459        assert!(parse("remote_branches(a,,remote=b)").is_err());
2460    }
2461
2462    #[test]
2463    fn test_parse_revset_alias_formal_parameter() {
2464        let mut aliases_map = RevsetAliasesMap::new();
2465        // Trailing comma isn't allowed for empty parameter
2466        assert!(aliases_map.insert("f(,)", "none()").is_err());
2467        // Trailing comma is allowed for the last parameter
2468        assert!(aliases_map.insert("g(a,)", "none()").is_ok());
2469        assert!(aliases_map.insert("h(a ,  )", "none()").is_ok());
2470        assert!(aliases_map.insert("i(,a)", "none()").is_err());
2471        assert!(aliases_map.insert("j(a,,)", "none()").is_err());
2472        assert!(aliases_map.insert("k(a  , , )", "none()").is_err());
2473        assert!(aliases_map.insert("l(a,b,)", "none()").is_ok());
2474        assert!(aliases_map.insert("m(a,,b)", "none()").is_err());
2475    }
2476
2477    #[test]
2478    fn test_parse_revset_compat_operator() {
2479        assert_eq!(
2480            parse("foo^"),
2481            Err(RevsetParseErrorKind::NotPostfixOperator {
2482                op: "^".to_owned(),
2483                similar_op: "-".to_owned(),
2484                description: "parents".to_owned(),
2485            })
2486        );
2487        assert_eq!(
2488            parse("foo + bar"),
2489            Err(RevsetParseErrorKind::NotInfixOperator {
2490                op: "+".to_owned(),
2491                similar_op: "|".to_owned(),
2492                description: "union".to_owned(),
2493            })
2494        );
2495        assert_eq!(
2496            parse("foo - bar"),
2497            Err(RevsetParseErrorKind::NotInfixOperator {
2498                op: "-".to_owned(),
2499                similar_op: "~".to_owned(),
2500                description: "difference".to_owned(),
2501            })
2502        );
2503    }
2504
2505    #[test]
2506    fn test_parse_revset_operator_combinations() {
2507        let foo_symbol = RevsetExpression::symbol("foo".to_string());
2508        // Parse repeated "parents" operator
2509        assert_eq!(
2510            parse("foo---"),
2511            Ok(foo_symbol.parents().parents().parents())
2512        );
2513        // Parse repeated "children" operator
2514        assert_eq!(
2515            parse("foo+++"),
2516            Ok(foo_symbol.children().children().children())
2517        );
2518        // Set operator associativity/precedence
2519        assert_eq!(parse("~x|y").unwrap(), parse("(~x)|y").unwrap());
2520        assert_eq!(parse("x&~y").unwrap(), parse("x&(~y)").unwrap());
2521        assert_eq!(parse("x~~y").unwrap(), parse("x~(~y)").unwrap());
2522        assert_eq!(parse("x~~~y").unwrap(), parse("x~(~(~y))").unwrap());
2523        assert_eq!(parse("~x:y").unwrap(), parse("~(x:y)").unwrap());
2524        assert_eq!(parse("x|y|z").unwrap(), parse("(x|y)|z").unwrap());
2525        assert_eq!(parse("x&y|z").unwrap(), parse("(x&y)|z").unwrap());
2526        assert_eq!(parse("x|y&z").unwrap(), parse("x|(y&z)").unwrap());
2527        assert_eq!(parse("x|y~z").unwrap(), parse("x|(y~z)").unwrap());
2528        // Parse repeated "ancestors"/"descendants"/"dag range"/"range" operators
2529        assert_eq!(parse(":foo:"), Err(RevsetParseErrorKind::SyntaxError));
2530        assert_eq!(parse("::foo"), Err(RevsetParseErrorKind::SyntaxError));
2531        assert_eq!(parse("foo::"), Err(RevsetParseErrorKind::SyntaxError));
2532        assert_eq!(parse("foo::bar"), Err(RevsetParseErrorKind::SyntaxError));
2533        assert_eq!(parse(":foo:bar"), Err(RevsetParseErrorKind::SyntaxError));
2534        assert_eq!(parse("foo:bar:"), Err(RevsetParseErrorKind::SyntaxError));
2535        assert_eq!(parse("....foo"), Err(RevsetParseErrorKind::SyntaxError));
2536        assert_eq!(parse("foo...."), Err(RevsetParseErrorKind::SyntaxError));
2537        assert_eq!(parse("foo.....bar"), Err(RevsetParseErrorKind::SyntaxError));
2538        assert_eq!(parse("..foo..bar"), Err(RevsetParseErrorKind::SyntaxError));
2539        assert_eq!(parse("foo..bar.."), Err(RevsetParseErrorKind::SyntaxError));
2540        // Parse combinations of "parents"/"children" operators and the range operators.
2541        // The former bind more strongly.
2542        assert_eq!(parse("foo-+"), Ok(foo_symbol.parents().children()));
2543        assert_eq!(parse("foo-:"), Ok(foo_symbol.parents().descendants()));
2544        assert_eq!(parse(":foo+"), Ok(foo_symbol.children().ancestors()));
2545    }
2546
2547    #[test]
2548    fn test_parse_revset_function() {
2549        let wc_symbol = RevsetExpression::symbol("@".to_string());
2550        assert_eq!(parse("parents(@)"), Ok(wc_symbol.parents()));
2551        assert_eq!(parse("parents((@))"), Ok(wc_symbol.parents()));
2552        assert_eq!(parse("parents(\"@\")"), Ok(wc_symbol.parents()));
2553        assert_eq!(
2554            parse("ancestors(parents(@))"),
2555            Ok(wc_symbol.parents().ancestors())
2556        );
2557        assert_eq!(parse("parents(@"), Err(RevsetParseErrorKind::SyntaxError));
2558        assert_eq!(
2559            parse("parents(@,@)"),
2560            Err(RevsetParseErrorKind::InvalidFunctionArguments {
2561                name: "parents".to_string(),
2562                message: "Expected 1 arguments".to_string()
2563            })
2564        );
2565        assert_eq!(
2566            parse(r#"description("")"#),
2567            Ok(RevsetExpression::filter(
2568                RevsetFilterPredicate::Description("".to_string())
2569            ))
2570        );
2571        assert_eq!(
2572            parse("description(foo)"),
2573            Ok(RevsetExpression::filter(
2574                RevsetFilterPredicate::Description("foo".to_string())
2575            ))
2576        );
2577        assert_eq!(
2578            parse("description(heads())"),
2579            Err(RevsetParseErrorKind::InvalidFunctionArguments {
2580                name: "description".to_string(),
2581                message: "Expected function argument of type string".to_string()
2582            })
2583        );
2584        assert_eq!(
2585            parse("description((foo))"),
2586            Ok(RevsetExpression::filter(
2587                RevsetFilterPredicate::Description("foo".to_string())
2588            ))
2589        );
2590        assert_eq!(
2591            parse("description(\"(foo)\")"),
2592            Ok(RevsetExpression::filter(
2593                RevsetFilterPredicate::Description("(foo)".to_string())
2594            ))
2595        );
2596        assert_eq!(
2597            parse("empty()"),
2598            Ok(RevsetExpression::filter(RevsetFilterPredicate::File(None)).negated())
2599        );
2600        assert!(parse("empty(foo)").is_err());
2601        assert!(parse("file()").is_err());
2602        assert_eq!(
2603            parse("file(foo)"),
2604            Ok(RevsetExpression::filter(RevsetFilterPredicate::File(Some(
2605                vec![RepoPath::from_internal_string("foo")]
2606            ))))
2607        );
2608        assert_eq!(
2609            parse("file(foo, bar, baz)"),
2610            Ok(RevsetExpression::filter(RevsetFilterPredicate::File(Some(
2611                vec![
2612                    RepoPath::from_internal_string("foo"),
2613                    RepoPath::from_internal_string("bar"),
2614                    RepoPath::from_internal_string("baz"),
2615                ]
2616            ))))
2617        );
2618    }
2619
2620    #[test]
2621    fn test_parse_revset_keyword_arguments() {
2622        assert_eq!(
2623            parse("remote_branches(remote=foo)").unwrap(),
2624            parse(r#"remote_branches("", foo)"#).unwrap(),
2625        );
2626        assert_eq!(
2627            parse("remote_branches(foo, remote=bar)").unwrap(),
2628            parse(r#"remote_branches(foo, bar)"#).unwrap(),
2629        );
2630        insta::assert_debug_snapshot!(
2631            parse(r#"remote_branches(remote=foo, bar)"#).unwrap_err(),
2632            @r###"
2633        InvalidFunctionArguments {
2634            name: "remote_branches",
2635            message: "Positional argument follows keyword argument",
2636        }
2637        "###);
2638        insta::assert_debug_snapshot!(
2639            parse(r#"remote_branches("", foo, remote=bar)"#).unwrap_err(),
2640            @r###"
2641        InvalidFunctionArguments {
2642            name: "remote_branches",
2643            message: "Got multiple values for keyword \"remote\"",
2644        }
2645        "###);
2646        insta::assert_debug_snapshot!(
2647            parse(r#"remote_branches(remote=bar, remote=bar)"#).unwrap_err(),
2648            @r###"
2649        InvalidFunctionArguments {
2650            name: "remote_branches",
2651            message: "Got multiple values for keyword \"remote\"",
2652        }
2653        "###);
2654        insta::assert_debug_snapshot!(
2655            parse(r#"remote_branches(unknown=bar)"#).unwrap_err(),
2656            @r###"
2657        InvalidFunctionArguments {
2658            name: "remote_branches",
2659            message: "Unexpected keyword argument \"unknown\"",
2660        }
2661        "###);
2662    }
2663
2664    #[test]
2665    fn test_expand_symbol_alias() {
2666        assert_eq!(
2667            parse_with_aliases("AB|c", [("AB", "a|b")]).unwrap(),
2668            parse("(a|b)|c").unwrap()
2669        );
2670        assert_eq!(
2671            parse_with_aliases("AB:heads(AB)", [("AB", "a|b")]).unwrap(),
2672            parse("(a|b):heads(a|b)").unwrap()
2673        );
2674
2675        // Not string substitution 'a&b|c', but tree substitution.
2676        assert_eq!(
2677            parse_with_aliases("a&BC", [("BC", "b|c")]).unwrap(),
2678            parse("a&(b|c)").unwrap()
2679        );
2680
2681        // String literal should not be substituted with alias.
2682        assert_eq!(
2683            parse_with_aliases(r#"A|"A""#, [("A", "a")]).unwrap(),
2684            parse("a|A").unwrap()
2685        );
2686
2687        // Alias can be substituted to string literal.
2688        assert_eq!(
2689            parse_with_aliases("author(A)", [("A", "a")]).unwrap(),
2690            parse("author(a)").unwrap()
2691        );
2692
2693        // Multi-level substitution.
2694        assert_eq!(
2695            parse_with_aliases("A", [("A", "BC"), ("BC", "b|C"), ("C", "c")]).unwrap(),
2696            parse("b|c").unwrap()
2697        );
2698
2699        // Infinite recursion, where the top-level error isn't of RecursiveAlias kind.
2700        assert_eq!(
2701            parse_with_aliases("A", [("A", "A")]),
2702            Err(RevsetParseErrorKind::BadAliasExpansion("A".to_owned()))
2703        );
2704        assert_eq!(
2705            parse_with_aliases("A", [("A", "B"), ("B", "b|C"), ("C", "c|B")]),
2706            Err(RevsetParseErrorKind::BadAliasExpansion("A".to_owned()))
2707        );
2708
2709        // Error in alias definition.
2710        assert_eq!(
2711            parse_with_aliases("A", [("A", "a(")]),
2712            Err(RevsetParseErrorKind::BadAliasExpansion("A".to_owned()))
2713        );
2714    }
2715
2716    #[test]
2717    fn test_expand_function_alias() {
2718        assert_eq!(
2719            parse_with_aliases("F()", [("F(  )", "a")]).unwrap(),
2720            parse("a").unwrap()
2721        );
2722        assert_eq!(
2723            parse_with_aliases("F(a)", [("F( x  )", "x")]).unwrap(),
2724            parse("a").unwrap()
2725        );
2726        assert_eq!(
2727            parse_with_aliases("F(a, b)", [("F( x,  y )", "x|y")]).unwrap(),
2728            parse("a|b").unwrap()
2729        );
2730
2731        // Arguments should be resolved in the current scope.
2732        assert_eq!(
2733            parse_with_aliases("F(a:y,b:x)", [("F(x,y)", "x|y")]).unwrap(),
2734            parse("(a:y)|(b:x)").unwrap()
2735        );
2736        // F(a) -> G(a)&y -> (x|a)&y
2737        assert_eq!(
2738            parse_with_aliases("F(a)", [("F(x)", "G(x)&y"), ("G(y)", "x|y")]).unwrap(),
2739            parse("(x|a)&y").unwrap()
2740        );
2741        // F(G(a)) -> F(x|a) -> G(x|a)&y -> (x|(x|a))&y
2742        assert_eq!(
2743            parse_with_aliases("F(G(a))", [("F(x)", "G(x)&y"), ("G(y)", "x|y")]).unwrap(),
2744            parse("(x|(x|a))&y").unwrap()
2745        );
2746
2747        // Function parameter should precede the symbol alias.
2748        assert_eq!(
2749            parse_with_aliases("F(a)|X", [("F(X)", "X"), ("X", "x")]).unwrap(),
2750            parse("a|x").unwrap()
2751        );
2752
2753        // Function parameter shouldn't be expanded in symbol alias.
2754        assert_eq!(
2755            parse_with_aliases("F(a)", [("F(x)", "x|A"), ("A", "x")]).unwrap(),
2756            parse("a|x").unwrap()
2757        );
2758
2759        // String literal should not be substituted with function parameter.
2760        assert_eq!(
2761            parse_with_aliases("F(a)", [("F(x)", r#"x|"x""#)]).unwrap(),
2762            parse("a|x").unwrap()
2763        );
2764
2765        // Pass string literal as parameter.
2766        assert_eq!(
2767            parse_with_aliases("F(a)", [("F(x)", "author(x)|committer(x)")]).unwrap(),
2768            parse("author(a)|committer(a)").unwrap()
2769        );
2770
2771        // Function and symbol aliases reside in separate namespaces.
2772        assert_eq!(
2773            parse_with_aliases("A()", [("A()", "A"), ("A", "a")]).unwrap(),
2774            parse("a").unwrap()
2775        );
2776
2777        // Invalid number of arguments.
2778        assert_eq!(
2779            parse_with_aliases("F(a)", [("F()", "x")]),
2780            Err(RevsetParseErrorKind::InvalidFunctionArguments {
2781                name: "F".to_owned(),
2782                message: "Expected 0 arguments".to_owned()
2783            })
2784        );
2785        assert_eq!(
2786            parse_with_aliases("F()", [("F(x)", "x")]),
2787            Err(RevsetParseErrorKind::InvalidFunctionArguments {
2788                name: "F".to_owned(),
2789                message: "Expected 1 arguments".to_owned()
2790            })
2791        );
2792        assert_eq!(
2793            parse_with_aliases("F(a,b,c)", [("F(x,y)", "x|y")]),
2794            Err(RevsetParseErrorKind::InvalidFunctionArguments {
2795                name: "F".to_owned(),
2796                message: "Expected 2 arguments".to_owned()
2797            })
2798        );
2799
2800        // Keyword argument isn't supported for now.
2801        assert_eq!(
2802            parse_with_aliases("F(x=y)", [("F(x)", "x")]),
2803            Err(RevsetParseErrorKind::InvalidFunctionArguments {
2804                name: "F".to_owned(),
2805                message: r#"Unexpected keyword argument "x""#.to_owned()
2806            })
2807        );
2808
2809        // Infinite recursion, where the top-level error isn't of RecursiveAlias kind.
2810        assert_eq!(
2811            parse_with_aliases(
2812                "F(a)",
2813                [("F(x)", "G(x)"), ("G(x)", "H(x)"), ("H(x)", "F(x)")]
2814            ),
2815            Err(RevsetParseErrorKind::BadAliasExpansion("F()".to_owned()))
2816        );
2817    }
2818
2819    #[test]
2820    fn test_optimize_subtree() {
2821        // Check that transform_expression_bottom_up() never rewrites enum variant
2822        // (e.g. Range -> DagRange) nor reorders arguments unintentionally.
2823
2824        assert_eq!(
2825            optimize(parse("parents(branches() & all())").unwrap()),
2826            RevsetExpression::branches("".to_owned()).parents()
2827        );
2828        assert_eq!(
2829            optimize(parse("children(branches() & all())").unwrap()),
2830            RevsetExpression::branches("".to_owned()).children()
2831        );
2832        assert_eq!(
2833            optimize(parse("ancestors(branches() & all())").unwrap()),
2834            RevsetExpression::branches("".to_owned()).ancestors()
2835        );
2836        assert_eq!(
2837            optimize(parse("descendants(branches() & all())").unwrap()),
2838            RevsetExpression::branches("".to_owned()).descendants()
2839        );
2840
2841        assert_eq!(
2842            optimize(parse("(branches() & all())..(all() & tags())").unwrap()),
2843            RevsetExpression::branches("".to_owned()).range(&RevsetExpression::tags())
2844        );
2845        assert_eq!(
2846            optimize(parse("(branches() & all()):(all() & tags())").unwrap()),
2847            RevsetExpression::branches("".to_owned()).dag_range_to(&RevsetExpression::tags())
2848        );
2849
2850        assert_eq!(
2851            optimize(parse("heads(branches() & all())").unwrap()),
2852            RevsetExpression::branches("".to_owned()).heads()
2853        );
2854        assert_eq!(
2855            optimize(parse("roots(branches() & all())").unwrap()),
2856            RevsetExpression::branches("".to_owned()).roots()
2857        );
2858
2859        assert_eq!(
2860            optimize(parse("present(author(foo) ~ bar)").unwrap()),
2861            Rc::new(RevsetExpression::AsFilter(Rc::new(
2862                RevsetExpression::Present(
2863                    RevsetExpression::filter(RevsetFilterPredicate::Author("foo".to_owned()))
2864                        .minus(&RevsetExpression::symbol("bar".to_owned()))
2865                )
2866            )))
2867        );
2868        assert_eq!(
2869            optimize(parse("present(branches() & all())").unwrap()),
2870            Rc::new(RevsetExpression::Present(RevsetExpression::branches(
2871                "".to_owned()
2872            )))
2873        );
2874
2875        assert_eq!(
2876            optimize(parse("~branches() & all()").unwrap()),
2877            RevsetExpression::branches("".to_owned()).negated()
2878        );
2879        assert_eq!(
2880            optimize(parse("(branches() & all()) | (all() & tags())").unwrap()),
2881            RevsetExpression::branches("".to_owned()).union(&RevsetExpression::tags())
2882        );
2883        assert_eq!(
2884            optimize(parse("(branches() & all()) & (all() & tags())").unwrap()),
2885            RevsetExpression::branches("".to_owned()).intersection(&RevsetExpression::tags())
2886        );
2887        assert_eq!(
2888            optimize(parse("(branches() & all()) ~ (all() & tags())").unwrap()),
2889            RevsetExpression::branches("".to_owned()).minus(&RevsetExpression::tags())
2890        );
2891    }
2892
2893    #[test]
2894    fn test_optimize_unchanged_subtree() {
2895        fn unwrap_union(
2896            expression: &RevsetExpression,
2897        ) -> (&Rc<RevsetExpression>, &Rc<RevsetExpression>) {
2898            match expression {
2899                RevsetExpression::Union(left, right) => (left, right),
2900                _ => panic!("unexpected expression: {expression:?}"),
2901            }
2902        }
2903
2904        // transform_expression_bottom_up() should not recreate tree unnecessarily.
2905        let parsed = parse("foo-").unwrap();
2906        let optimized = optimize(parsed.clone());
2907        assert!(Rc::ptr_eq(&parsed, &optimized));
2908
2909        let parsed = parse("branches() | tags()").unwrap();
2910        let optimized = optimize(parsed.clone());
2911        assert!(Rc::ptr_eq(&parsed, &optimized));
2912
2913        let parsed = parse("branches() & tags()").unwrap();
2914        let optimized = optimize(parsed.clone());
2915        assert!(Rc::ptr_eq(&parsed, &optimized));
2916
2917        // Only left subtree should be rewritten.
2918        let parsed = parse("(branches() & all()) | tags()").unwrap();
2919        let optimized = optimize(parsed.clone());
2920        assert_eq!(
2921            unwrap_union(&optimized).0.as_ref(),
2922            &RevsetExpression::Branches("".to_owned())
2923        );
2924        assert!(Rc::ptr_eq(
2925            unwrap_union(&parsed).1,
2926            unwrap_union(&optimized).1
2927        ));
2928
2929        // Only right subtree should be rewritten.
2930        let parsed = parse("branches() | (all() & tags())").unwrap();
2931        let optimized = optimize(parsed.clone());
2932        assert!(Rc::ptr_eq(
2933            unwrap_union(&parsed).0,
2934            unwrap_union(&optimized).0
2935        ));
2936        assert_eq!(unwrap_union(&optimized).1.as_ref(), &RevsetExpression::Tags);
2937    }
2938
2939    #[test]
2940    fn test_optimize_difference() {
2941        insta::assert_debug_snapshot!(optimize(parse("foo & ~bar").unwrap()), @r###"
2942        Difference(
2943            Symbol(
2944                "foo",
2945            ),
2946            Symbol(
2947                "bar",
2948            ),
2949        )
2950        "###);
2951        insta::assert_debug_snapshot!(optimize(parse("~foo & bar").unwrap()), @r###"
2952        Difference(
2953            Symbol(
2954                "bar",
2955            ),
2956            Symbol(
2957                "foo",
2958            ),
2959        )
2960        "###);
2961        insta::assert_debug_snapshot!(optimize(parse("~foo & bar & ~baz").unwrap()), @r###"
2962        Difference(
2963            Difference(
2964                Symbol(
2965                    "bar",
2966                ),
2967                Symbol(
2968                    "foo",
2969                ),
2970            ),
2971            Symbol(
2972                "baz",
2973            ),
2974        )
2975        "###);
2976        insta::assert_debug_snapshot!(optimize(parse("(all() & ~foo) & bar").unwrap()), @r###"
2977        Difference(
2978            Symbol(
2979                "bar",
2980            ),
2981            Symbol(
2982                "foo",
2983            ),
2984        )
2985        "###);
2986
2987        // Binary difference operation should go through the same optimization passes.
2988        insta::assert_debug_snapshot!(optimize(parse("all() ~ foo").unwrap()), @r###"
2989        NotIn(
2990            Symbol(
2991                "foo",
2992            ),
2993        )
2994        "###);
2995        insta::assert_debug_snapshot!(optimize(parse("foo ~ bar").unwrap()), @r###"
2996        Difference(
2997            Symbol(
2998                "foo",
2999            ),
3000            Symbol(
3001                "bar",
3002            ),
3003        )
3004        "###);
3005        insta::assert_debug_snapshot!(optimize(parse("(all() ~ foo) & bar").unwrap()), @r###"
3006        Difference(
3007            Symbol(
3008                "bar",
3009            ),
3010            Symbol(
3011                "foo",
3012            ),
3013        )
3014        "###);
3015
3016        // Range expression.
3017        insta::assert_debug_snapshot!(optimize(parse(":foo & ~:bar").unwrap()), @r###"
3018        Range {
3019            roots: Symbol(
3020                "bar",
3021            ),
3022            heads: Symbol(
3023                "foo",
3024            ),
3025            generation: 0..4294967295,
3026        }
3027        "###);
3028        insta::assert_debug_snapshot!(optimize(parse("~:foo & :bar").unwrap()), @r###"
3029        Range {
3030            roots: Symbol(
3031                "foo",
3032            ),
3033            heads: Symbol(
3034                "bar",
3035            ),
3036            generation: 0..4294967295,
3037        }
3038        "###);
3039        insta::assert_debug_snapshot!(optimize(parse("foo..").unwrap()), @r###"
3040        Range {
3041            roots: Symbol(
3042                "foo",
3043            ),
3044            heads: VisibleHeads,
3045            generation: 0..4294967295,
3046        }
3047        "###);
3048        insta::assert_debug_snapshot!(optimize(parse("foo..bar").unwrap()), @r###"
3049        Range {
3050            roots: Symbol(
3051                "foo",
3052            ),
3053            heads: Symbol(
3054                "bar",
3055            ),
3056            generation: 0..4294967295,
3057        }
3058        "###);
3059
3060        // Double/triple negates.
3061        insta::assert_debug_snapshot!(optimize(parse("foo & ~~bar").unwrap()), @r###"
3062        Intersection(
3063            Symbol(
3064                "foo",
3065            ),
3066            Symbol(
3067                "bar",
3068            ),
3069        )
3070        "###);
3071        insta::assert_debug_snapshot!(optimize(parse("foo & ~~~bar").unwrap()), @r###"
3072        Difference(
3073            Symbol(
3074                "foo",
3075            ),
3076            Symbol(
3077                "bar",
3078            ),
3079        )
3080        "###);
3081        insta::assert_debug_snapshot!(optimize(parse("~(all() & ~foo) & bar").unwrap()), @r###"
3082        Intersection(
3083            Symbol(
3084                "foo",
3085            ),
3086            Symbol(
3087                "bar",
3088            ),
3089        )
3090        "###);
3091
3092        // Should be better than '(all() & ~foo) & (all() & ~bar)'.
3093        insta::assert_debug_snapshot!(optimize(parse("~foo & ~bar").unwrap()), @r###"
3094        Difference(
3095            NotIn(
3096                Symbol(
3097                    "foo",
3098                ),
3099            ),
3100            Symbol(
3101                "bar",
3102            ),
3103        )
3104        "###);
3105    }
3106
3107    #[test]
3108    fn test_optimize_filter_difference() {
3109        // '~empty()' -> '~~file(*)' -> 'file(*)'
3110        insta::assert_debug_snapshot!(optimize(parse("~empty()").unwrap()), @r###"
3111        Filter(
3112            File(
3113                None,
3114            ),
3115        )
3116        "###);
3117
3118        // '& baz' can be moved into the filter node, and form a difference node.
3119        insta::assert_debug_snapshot!(
3120            optimize(parse("(author(foo) & ~bar) & baz").unwrap()), @r###"
3121        Intersection(
3122            Difference(
3123                Symbol(
3124                    "baz",
3125                ),
3126                Symbol(
3127                    "bar",
3128                ),
3129            ),
3130            Filter(
3131                Author(
3132                    "foo",
3133                ),
3134            ),
3135        )
3136        "###)
3137    }
3138
3139    #[test]
3140    fn test_optimize_filter_intersection() {
3141        insta::assert_debug_snapshot!(optimize(parse("author(foo)").unwrap()), @r###"
3142        Filter(
3143            Author(
3144                "foo",
3145            ),
3146        )
3147        "###);
3148
3149        insta::assert_debug_snapshot!(optimize(parse("foo & description(bar)").unwrap()), @r###"
3150        Intersection(
3151            Symbol(
3152                "foo",
3153            ),
3154            Filter(
3155                Description(
3156                    "bar",
3157                ),
3158            ),
3159        )
3160        "###);
3161        insta::assert_debug_snapshot!(optimize(parse("author(foo) & bar").unwrap()), @r###"
3162        Intersection(
3163            Symbol(
3164                "bar",
3165            ),
3166            Filter(
3167                Author(
3168                    "foo",
3169                ),
3170            ),
3171        )
3172        "###);
3173        insta::assert_debug_snapshot!(
3174            optimize(parse("author(foo) & committer(bar)").unwrap()), @r###"
3175        Intersection(
3176            Filter(
3177                Author(
3178                    "foo",
3179                ),
3180            ),
3181            Filter(
3182                Committer(
3183                    "bar",
3184                ),
3185            ),
3186        )
3187        "###);
3188
3189        insta::assert_debug_snapshot!(
3190            optimize(parse("foo & description(bar) & author(baz)").unwrap()), @r###"
3191        Intersection(
3192            Intersection(
3193                Symbol(
3194                    "foo",
3195                ),
3196                Filter(
3197                    Description(
3198                        "bar",
3199                    ),
3200                ),
3201            ),
3202            Filter(
3203                Author(
3204                    "baz",
3205                ),
3206            ),
3207        )
3208        "###);
3209        insta::assert_debug_snapshot!(
3210            optimize(parse("committer(foo) & bar & author(baz)").unwrap()), @r###"
3211        Intersection(
3212            Intersection(
3213                Symbol(
3214                    "bar",
3215                ),
3216                Filter(
3217                    Committer(
3218                        "foo",
3219                    ),
3220                ),
3221            ),
3222            Filter(
3223                Author(
3224                    "baz",
3225                ),
3226            ),
3227        )
3228        "###);
3229        insta::assert_debug_snapshot!(
3230            optimize(parse("committer(foo) & file(bar) & baz").unwrap()), @r###"
3231        Intersection(
3232            Intersection(
3233                Symbol(
3234                    "baz",
3235                ),
3236                Filter(
3237                    Committer(
3238                        "foo",
3239                    ),
3240                ),
3241            ),
3242            Filter(
3243                File(
3244                    Some(
3245                        [
3246                            "bar",
3247                        ],
3248                    ),
3249                ),
3250            ),
3251        )
3252        "###);
3253        insta::assert_debug_snapshot!(
3254            optimize(parse("committer(foo) & file(bar) & author(baz)").unwrap()), @r###"
3255        Intersection(
3256            Intersection(
3257                Filter(
3258                    Committer(
3259                        "foo",
3260                    ),
3261                ),
3262                Filter(
3263                    File(
3264                        Some(
3265                            [
3266                                "bar",
3267                            ],
3268                        ),
3269                    ),
3270                ),
3271            ),
3272            Filter(
3273                Author(
3274                    "baz",
3275                ),
3276            ),
3277        )
3278        "###);
3279        insta::assert_debug_snapshot!(optimize(parse("foo & file(bar) & baz").unwrap()), @r###"
3280        Intersection(
3281            Intersection(
3282                Symbol(
3283                    "foo",
3284                ),
3285                Symbol(
3286                    "baz",
3287                ),
3288            ),
3289            Filter(
3290                File(
3291                    Some(
3292                        [
3293                            "bar",
3294                        ],
3295                    ),
3296                ),
3297            ),
3298        )
3299        "###);
3300
3301        insta::assert_debug_snapshot!(
3302            optimize(parse("foo & description(bar) & author(baz) & qux").unwrap()), @r###"
3303        Intersection(
3304            Intersection(
3305                Intersection(
3306                    Symbol(
3307                        "foo",
3308                    ),
3309                    Symbol(
3310                        "qux",
3311                    ),
3312                ),
3313                Filter(
3314                    Description(
3315                        "bar",
3316                    ),
3317                ),
3318            ),
3319            Filter(
3320                Author(
3321                    "baz",
3322                ),
3323            ),
3324        )
3325        "###);
3326        insta::assert_debug_snapshot!(
3327            optimize(parse("foo & description(bar) & parents(author(baz)) & qux").unwrap()), @r###"
3328        Intersection(
3329            Intersection(
3330                Intersection(
3331                    Symbol(
3332                        "foo",
3333                    ),
3334                    Ancestors {
3335                        heads: Filter(
3336                            Author(
3337                                "baz",
3338                            ),
3339                        ),
3340                        generation: 1..2,
3341                    },
3342                ),
3343                Symbol(
3344                    "qux",
3345                ),
3346            ),
3347            Filter(
3348                Description(
3349                    "bar",
3350                ),
3351            ),
3352        )
3353        "###);
3354        insta::assert_debug_snapshot!(
3355            optimize(parse("foo & description(bar) & parents(author(baz) & qux)").unwrap()), @r###"
3356        Intersection(
3357            Intersection(
3358                Symbol(
3359                    "foo",
3360                ),
3361                Ancestors {
3362                    heads: Intersection(
3363                        Symbol(
3364                            "qux",
3365                        ),
3366                        Filter(
3367                            Author(
3368                                "baz",
3369                            ),
3370                        ),
3371                    ),
3372                    generation: 1..2,
3373                },
3374            ),
3375            Filter(
3376                Description(
3377                    "bar",
3378                ),
3379            ),
3380        )
3381        "###);
3382
3383        // Symbols have to be pushed down to the innermost filter node.
3384        insta::assert_debug_snapshot!(
3385            optimize(parse("(a & author(A)) & (b & author(B)) & (c & author(C))").unwrap()), @r###"
3386        Intersection(
3387            Intersection(
3388                Intersection(
3389                    Intersection(
3390                        Intersection(
3391                            Symbol(
3392                                "a",
3393                            ),
3394                            Symbol(
3395                                "b",
3396                            ),
3397                        ),
3398                        Symbol(
3399                            "c",
3400                        ),
3401                    ),
3402                    Filter(
3403                        Author(
3404                            "A",
3405                        ),
3406                    ),
3407                ),
3408                Filter(
3409                    Author(
3410                        "B",
3411                    ),
3412                ),
3413            ),
3414            Filter(
3415                Author(
3416                    "C",
3417                ),
3418            ),
3419        )
3420        "###);
3421        insta::assert_debug_snapshot!(
3422            optimize(parse("(a & author(A)) & ((b & author(B)) & (c & author(C))) & d").unwrap()),
3423            @r###"
3424        Intersection(
3425            Intersection(
3426                Intersection(
3427                    Intersection(
3428                        Intersection(
3429                            Symbol(
3430                                "a",
3431                            ),
3432                            Intersection(
3433                                Symbol(
3434                                    "b",
3435                                ),
3436                                Symbol(
3437                                    "c",
3438                                ),
3439                            ),
3440                        ),
3441                        Symbol(
3442                            "d",
3443                        ),
3444                    ),
3445                    Filter(
3446                        Author(
3447                            "A",
3448                        ),
3449                    ),
3450                ),
3451                Filter(
3452                    Author(
3453                        "B",
3454                    ),
3455                ),
3456            ),
3457            Filter(
3458                Author(
3459                    "C",
3460                ),
3461            ),
3462        )
3463        "###);
3464
3465        // 'all()' moves in to 'filter()' first, so 'A & filter()' can be found.
3466        insta::assert_debug_snapshot!(
3467            optimize(parse("foo & (all() & description(bar)) & (author(baz) & all())").unwrap()),
3468            @r###"
3469        Intersection(
3470            Intersection(
3471                Symbol(
3472                    "foo",
3473                ),
3474                Filter(
3475                    Description(
3476                        "bar",
3477                    ),
3478                ),
3479            ),
3480            Filter(
3481                Author(
3482                    "baz",
3483                ),
3484            ),
3485        )
3486        "###);
3487    }
3488
3489    #[test]
3490    fn test_optimize_filter_subtree() {
3491        insta::assert_debug_snapshot!(
3492            optimize(parse("(author(foo) | bar) & baz").unwrap()), @r###"
3493        Intersection(
3494            Symbol(
3495                "baz",
3496            ),
3497            AsFilter(
3498                Union(
3499                    Filter(
3500                        Author(
3501                            "foo",
3502                        ),
3503                    ),
3504                    Symbol(
3505                        "bar",
3506                    ),
3507                ),
3508            ),
3509        )
3510        "###);
3511
3512        insta::assert_debug_snapshot!(
3513            optimize(parse("(foo | committer(bar)) & description(baz) & qux").unwrap()), @r###"
3514        Intersection(
3515            Intersection(
3516                Symbol(
3517                    "qux",
3518                ),
3519                AsFilter(
3520                    Union(
3521                        Symbol(
3522                            "foo",
3523                        ),
3524                        Filter(
3525                            Committer(
3526                                "bar",
3527                            ),
3528                        ),
3529                    ),
3530                ),
3531            ),
3532            Filter(
3533                Description(
3534                    "baz",
3535                ),
3536            ),
3537        )
3538        "###);
3539
3540        insta::assert_debug_snapshot!(
3541            optimize(parse("(~present(author(foo) & bar) | baz) & qux").unwrap()), @r###"
3542        Intersection(
3543            Symbol(
3544                "qux",
3545            ),
3546            AsFilter(
3547                Union(
3548                    AsFilter(
3549                        NotIn(
3550                            AsFilter(
3551                                Present(
3552                                    Intersection(
3553                                        Symbol(
3554                                            "bar",
3555                                        ),
3556                                        Filter(
3557                                            Author(
3558                                                "foo",
3559                                            ),
3560                                        ),
3561                                    ),
3562                                ),
3563                            ),
3564                        ),
3565                    ),
3566                    Symbol(
3567                        "baz",
3568                    ),
3569                ),
3570            ),
3571        )
3572        "###);
3573
3574        // Symbols have to be pushed down to the innermost filter node.
3575        insta::assert_debug_snapshot!(
3576            optimize(parse(
3577                "(a & (author(A) | 0)) & (b & (author(B) | 1)) & (c & (author(C) | 2))").unwrap()),
3578            @r###"
3579        Intersection(
3580            Intersection(
3581                Intersection(
3582                    Intersection(
3583                        Intersection(
3584                            Symbol(
3585                                "a",
3586                            ),
3587                            Symbol(
3588                                "b",
3589                            ),
3590                        ),
3591                        Symbol(
3592                            "c",
3593                        ),
3594                    ),
3595                    AsFilter(
3596                        Union(
3597                            Filter(
3598                                Author(
3599                                    "A",
3600                                ),
3601                            ),
3602                            Symbol(
3603                                "0",
3604                            ),
3605                        ),
3606                    ),
3607                ),
3608                AsFilter(
3609                    Union(
3610                        Filter(
3611                            Author(
3612                                "B",
3613                            ),
3614                        ),
3615                        Symbol(
3616                            "1",
3617                        ),
3618                    ),
3619                ),
3620            ),
3621            AsFilter(
3622                Union(
3623                    Filter(
3624                        Author(
3625                            "C",
3626                        ),
3627                    ),
3628                    Symbol(
3629                        "2",
3630                    ),
3631                ),
3632            ),
3633        )
3634        "###);
3635    }
3636
3637    #[test]
3638    fn test_optimize_ancestors() {
3639        // Typical scenario: fold nested parents()
3640        insta::assert_debug_snapshot!(optimize(parse("foo--").unwrap()), @r###"
3641        Ancestors {
3642            heads: Symbol(
3643                "foo",
3644            ),
3645            generation: 2..3,
3646        }
3647        "###);
3648        insta::assert_debug_snapshot!(optimize(parse(":(foo---)").unwrap()), @r###"
3649        Ancestors {
3650            heads: Symbol(
3651                "foo",
3652            ),
3653            generation: 3..4294967295,
3654        }
3655        "###);
3656        insta::assert_debug_snapshot!(optimize(parse("(:foo)---").unwrap()), @r###"
3657        Ancestors {
3658            heads: Symbol(
3659                "foo",
3660            ),
3661            generation: 3..4294967295,
3662        }
3663        "###);
3664
3665        // 'foo-+' is not 'foo'.
3666        insta::assert_debug_snapshot!(optimize(parse("foo---+").unwrap()), @r###"
3667        Children(
3668            Ancestors {
3669                heads: Symbol(
3670                    "foo",
3671                ),
3672                generation: 3..4,
3673            },
3674        )
3675        "###);
3676
3677        // For 'roots..heads', heads can be folded.
3678        insta::assert_debug_snapshot!(optimize(parse("foo..(bar--)").unwrap()), @r###"
3679        Range {
3680            roots: Symbol(
3681                "foo",
3682            ),
3683            heads: Symbol(
3684                "bar",
3685            ),
3686            generation: 2..4294967295,
3687        }
3688        "###);
3689        // roots can also be folded, but range expression cannot be reconstructed.
3690        // No idea if this is better than the original range expression.
3691        insta::assert_debug_snapshot!(optimize(parse("(foo--)..(bar---)").unwrap()), @r###"
3692        Difference(
3693            Ancestors {
3694                heads: Symbol(
3695                    "bar",
3696                ),
3697                generation: 3..4294967295,
3698            },
3699            Ancestors {
3700                heads: Symbol(
3701                    "foo",
3702                ),
3703                generation: 2..4294967295,
3704            },
3705        )
3706        "###);
3707
3708        // If inner range is bounded by roots, it cannot be merged.
3709        // e.g. '..(foo..foo)' is equivalent to '..none()', not to '..foo'
3710        insta::assert_debug_snapshot!(optimize(parse("(foo..bar)--").unwrap()), @r###"
3711        Ancestors {
3712            heads: Range {
3713                roots: Symbol(
3714                    "foo",
3715                ),
3716                heads: Symbol(
3717                    "bar",
3718                ),
3719                generation: 0..4294967295,
3720            },
3721            generation: 2..3,
3722        }
3723        "###);
3724        insta::assert_debug_snapshot!(optimize(parse("foo..(bar..baz)").unwrap()), @r###"
3725        Range {
3726            roots: Symbol(
3727                "foo",
3728            ),
3729            heads: Range {
3730                roots: Symbol(
3731                    "bar",
3732                ),
3733                heads: Symbol(
3734                    "baz",
3735                ),
3736                generation: 0..4294967295,
3737            },
3738            generation: 0..4294967295,
3739        }
3740        "###);
3741
3742        // Ancestors of empty generation range should be empty.
3743        // TODO: rewrite these tests if we added syntax for arbitrary generation
3744        // ancestors
3745        let empty_generation_ancestors = |heads| {
3746            Rc::new(RevsetExpression::Ancestors {
3747                heads,
3748                generation: GENERATION_RANGE_EMPTY,
3749            })
3750        };
3751        insta::assert_debug_snapshot!(
3752            optimize(empty_generation_ancestors(
3753                RevsetExpression::symbol("foo".to_owned()).ancestors()
3754            )),
3755            @r###"
3756        Ancestors {
3757            heads: Symbol(
3758                "foo",
3759            ),
3760            generation: 0..0,
3761        }
3762        "###
3763        );
3764        insta::assert_debug_snapshot!(
3765            optimize(
3766                empty_generation_ancestors(RevsetExpression::symbol("foo".to_owned())).ancestors()
3767            ),
3768            @r###"
3769        Ancestors {
3770            heads: Symbol(
3771                "foo",
3772            ),
3773            generation: 0..0,
3774        }
3775        "###
3776        );
3777    }
3778
3779    #[test]
3780    fn test_revset_combinator() {
3781        let mut new_change_id = change_id_generator();
3782        let mut index = MutableIndex::full(3, 16);
3783        let id_0 = CommitId::from_hex("000000");
3784        let id_1 = CommitId::from_hex("111111");
3785        let id_2 = CommitId::from_hex("222222");
3786        let id_3 = CommitId::from_hex("333333");
3787        let id_4 = CommitId::from_hex("444444");
3788        index.add_commit_data(id_0.clone(), new_change_id(), &[]);
3789        index.add_commit_data(id_1.clone(), new_change_id(), &[id_0.clone()]);
3790        index.add_commit_data(id_2.clone(), new_change_id(), &[id_1.clone()]);
3791        index.add_commit_data(id_3.clone(), new_change_id(), &[id_2.clone()]);
3792        index.add_commit_data(id_4.clone(), new_change_id(), &[id_3.clone()]);
3793
3794        let get_entry = |id: &CommitId| index.entry_by_id(id).unwrap();
3795        let make_entries = |ids: &[&CommitId]| ids.iter().map(|id| get_entry(id)).collect_vec();
3796        let make_set = |ids: &[&CommitId]| -> Box<dyn Revset<'_> + '_> {
3797            let index_entries = make_entries(ids);
3798            Box::new(EagerRevset { index_entries })
3799        };
3800
3801        let set = make_set(&[&id_4, &id_3, &id_2, &id_0]);
3802        let mut p = set.to_predicate_fn();
3803        assert!(p(&get_entry(&id_4)));
3804        assert!(p(&get_entry(&id_3)));
3805        assert!(p(&get_entry(&id_2)));
3806        assert!(!p(&get_entry(&id_1)));
3807        assert!(p(&get_entry(&id_0)));
3808        // Uninteresting entries can be skipped
3809        let mut p = set.to_predicate_fn();
3810        assert!(p(&get_entry(&id_3)));
3811        assert!(!p(&get_entry(&id_1)));
3812        assert!(p(&get_entry(&id_0)));
3813
3814        let set = FilterRevset::<PurePredicateFn> {
3815            candidates: make_set(&[&id_4, &id_2, &id_0]),
3816            predicate: Box::new(|entry| entry.commit_id() != id_4),
3817        };
3818        assert_eq!(set.iter().collect_vec(), make_entries(&[&id_2, &id_0]));
3819        let mut p = set.to_predicate_fn();
3820        assert!(!p(&get_entry(&id_4)));
3821        assert!(!p(&get_entry(&id_3)));
3822        assert!(p(&get_entry(&id_2)));
3823        assert!(!p(&get_entry(&id_1)));
3824        assert!(p(&get_entry(&id_0)));
3825
3826        // Intersection by FilterRevset
3827        let set = FilterRevset {
3828            candidates: make_set(&[&id_4, &id_2, &id_0]),
3829            predicate: make_set(&[&id_3, &id_2, &id_1]),
3830        };
3831        assert_eq!(set.iter().collect_vec(), make_entries(&[&id_2]));
3832        let mut p = set.to_predicate_fn();
3833        assert!(!p(&get_entry(&id_4)));
3834        assert!(!p(&get_entry(&id_3)));
3835        assert!(p(&get_entry(&id_2)));
3836        assert!(!p(&get_entry(&id_1)));
3837        assert!(!p(&get_entry(&id_0)));
3838
3839        let set = UnionRevset {
3840            set1: make_set(&[&id_4, &id_2]),
3841            set2: make_set(&[&id_3, &id_2, &id_1]),
3842        };
3843        assert_eq!(
3844            set.iter().collect_vec(),
3845            make_entries(&[&id_4, &id_3, &id_2, &id_1])
3846        );
3847        let mut p = set.to_predicate_fn();
3848        assert!(p(&get_entry(&id_4)));
3849        assert!(p(&get_entry(&id_3)));
3850        assert!(p(&get_entry(&id_2)));
3851        assert!(p(&get_entry(&id_1)));
3852        assert!(!p(&get_entry(&id_0)));
3853
3854        let set = IntersectionRevset {
3855            set1: make_set(&[&id_4, &id_2, &id_0]),
3856            set2: make_set(&[&id_3, &id_2, &id_1]),
3857        };
3858        assert_eq!(set.iter().collect_vec(), make_entries(&[&id_2]));
3859        let mut p = set.to_predicate_fn();
3860        assert!(!p(&get_entry(&id_4)));
3861        assert!(!p(&get_entry(&id_3)));
3862        assert!(p(&get_entry(&id_2)));
3863        assert!(!p(&get_entry(&id_1)));
3864        assert!(!p(&get_entry(&id_0)));
3865
3866        let set = DifferenceRevset {
3867            set1: make_set(&[&id_4, &id_2, &id_0]),
3868            set2: make_set(&[&id_3, &id_2, &id_1]),
3869        };
3870        assert_eq!(set.iter().collect_vec(), make_entries(&[&id_4, &id_0]));
3871        let mut p = set.to_predicate_fn();
3872        assert!(p(&get_entry(&id_4)));
3873        assert!(!p(&get_entry(&id_3)));
3874        assert!(!p(&get_entry(&id_2)));
3875        assert!(!p(&get_entry(&id_1)));
3876        assert!(p(&get_entry(&id_0)));
3877    }
3878}