jj_lib/
revset_parser.rs

1// Copyright 2021-2024 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
15#![expect(missing_docs)]
16
17use std::collections::HashSet;
18use std::error;
19use std::mem;
20use std::str::FromStr;
21use std::sync::LazyLock;
22
23use itertools::Itertools as _;
24use pest::Parser as _;
25use pest::iterators::Pair;
26use pest::pratt_parser::Assoc;
27use pest::pratt_parser::Op;
28use pest::pratt_parser::PrattParser;
29use pest_derive::Parser;
30use thiserror::Error;
31
32use crate::dsl_util;
33use crate::dsl_util::AliasDeclaration;
34use crate::dsl_util::AliasDeclarationParser;
35use crate::dsl_util::AliasDefinitionParser;
36use crate::dsl_util::AliasExpandError;
37use crate::dsl_util::AliasExpandableExpression;
38use crate::dsl_util::AliasId;
39use crate::dsl_util::AliasesMap;
40use crate::dsl_util::Diagnostics;
41use crate::dsl_util::ExpressionFolder;
42use crate::dsl_util::FoldableExpression;
43use crate::dsl_util::FunctionCallParser;
44use crate::dsl_util::InvalidArguments;
45use crate::dsl_util::StringLiteralParser;
46use crate::dsl_util::collect_similar;
47use crate::ref_name::RefNameBuf;
48use crate::ref_name::RemoteNameBuf;
49use crate::ref_name::RemoteRefSymbolBuf;
50
51#[derive(Parser)]
52#[grammar = "revset.pest"]
53struct RevsetParser;
54
55const STRING_LITERAL_PARSER: StringLiteralParser<Rule> = StringLiteralParser {
56    content_rule: Rule::string_content,
57    escape_rule: Rule::string_escape,
58};
59const FUNCTION_CALL_PARSER: FunctionCallParser<Rule> = FunctionCallParser {
60    function_name_rule: Rule::function_name,
61    function_arguments_rule: Rule::function_arguments,
62    keyword_argument_rule: Rule::keyword_argument,
63    argument_name_rule: Rule::strict_identifier,
64    argument_value_rule: Rule::expression,
65};
66
67impl Rule {
68    /// Whether this is a placeholder rule for compatibility with the other
69    /// systems.
70    fn is_compat(&self) -> bool {
71        matches!(
72            self,
73            Self::compat_parents_op
74                | Self::compat_dag_range_op
75                | Self::compat_dag_range_pre_op
76                | Self::compat_dag_range_post_op
77                | Self::compat_add_op
78                | Self::compat_sub_op
79        )
80    }
81
82    fn to_symbol(self) -> Option<&'static str> {
83        match self {
84            Self::EOI => None,
85            Self::whitespace => None,
86            Self::identifier_part => None,
87            Self::identifier => None,
88            Self::strict_identifier_part => None,
89            Self::strict_identifier => None,
90            Self::symbol => None,
91            Self::string_escape => None,
92            Self::string_content_char => None,
93            Self::string_content => None,
94            Self::string_literal => None,
95            Self::raw_string_content => None,
96            Self::raw_string_literal => None,
97            Self::at_op => Some("@"),
98            Self::pattern_kind_op => Some(":"),
99            Self::parents_op => Some("-"),
100            Self::children_op => Some("+"),
101            Self::compat_parents_op => Some("^"),
102            Self::dag_range_op
103            | Self::dag_range_pre_op
104            | Self::dag_range_post_op
105            | Self::dag_range_all_op => Some("::"),
106            Self::compat_dag_range_op
107            | Self::compat_dag_range_pre_op
108            | Self::compat_dag_range_post_op => Some(":"),
109            Self::range_op => Some(".."),
110            Self::range_pre_op | Self::range_post_op | Self::range_all_op => Some(".."),
111            Self::range_ops => None,
112            Self::range_pre_ops => None,
113            Self::range_post_ops => None,
114            Self::range_all_ops => None,
115            Self::negate_op => Some("~"),
116            Self::union_op => Some("|"),
117            Self::intersection_op => Some("&"),
118            Self::difference_op => Some("~"),
119            Self::compat_add_op => Some("+"),
120            Self::compat_sub_op => Some("-"),
121            Self::infix_op => None,
122            Self::function => None,
123            Self::function_name => None,
124            Self::keyword_argument => None,
125            Self::argument => None,
126            Self::function_arguments => None,
127            Self::formal_parameters => None,
128            Self::string_pattern => None,
129            Self::primary => None,
130            Self::neighbors_expression => None,
131            Self::range_expression => None,
132            Self::expression => None,
133            Self::program_modifier => None,
134            Self::program => None,
135            Self::symbol_name => None,
136            Self::function_alias_declaration => None,
137            Self::alias_declaration => None,
138        }
139    }
140}
141
142/// Manages diagnostic messages emitted during revset parsing and function-call
143/// resolution.
144pub type RevsetDiagnostics = Diagnostics<RevsetParseError>;
145
146#[derive(Debug, Error)]
147#[error("{pest_error}")]
148pub struct RevsetParseError {
149    kind: Box<RevsetParseErrorKind>,
150    pest_error: Box<pest::error::Error<Rule>>,
151    source: Option<Box<dyn error::Error + Send + Sync>>,
152}
153
154#[derive(Debug, Error, PartialEq, Eq)]
155pub enum RevsetParseErrorKind {
156    #[error("Syntax error")]
157    SyntaxError,
158    #[error("`{op}` is not a prefix operator")]
159    NotPrefixOperator {
160        op: String,
161        similar_op: String,
162        description: String,
163    },
164    #[error("`{op}` is not a postfix operator")]
165    NotPostfixOperator {
166        op: String,
167        similar_op: String,
168        description: String,
169    },
170    #[error("`{op}` is not an infix operator")]
171    NotInfixOperator {
172        op: String,
173        similar_op: String,
174        description: String,
175    },
176    #[error("Modifier `{0}` doesn't exist")]
177    NoSuchModifier(String),
178    #[error("Function `{name}` doesn't exist")]
179    NoSuchFunction {
180        name: String,
181        candidates: Vec<String>,
182    },
183    #[error("Function `{name}`: {message}")]
184    InvalidFunctionArguments { name: String, message: String },
185    #[error("Cannot resolve file pattern without workspace")]
186    FsPathWithoutWorkspace,
187    #[error("Cannot resolve `@` without workspace")]
188    WorkingCopyWithoutWorkspace,
189    #[error("Redefinition of function parameter")]
190    RedefinedFunctionParameter,
191    #[error("{0}")]
192    Expression(String),
193    #[error("In alias `{0}`")]
194    InAliasExpansion(String),
195    #[error("In function parameter `{0}`")]
196    InParameterExpansion(String),
197    #[error("Alias `{0}` expanded recursively")]
198    RecursiveAlias(String),
199}
200
201impl RevsetParseError {
202    pub(super) fn with_span(kind: RevsetParseErrorKind, span: pest::Span<'_>) -> Self {
203        let message = kind.to_string();
204        let pest_error = Box::new(pest::error::Error::new_from_span(
205            pest::error::ErrorVariant::CustomError { message },
206            span,
207        ));
208        Self {
209            kind: Box::new(kind),
210            pest_error,
211            source: None,
212        }
213    }
214
215    pub(super) fn with_source(
216        mut self,
217        source: impl Into<Box<dyn error::Error + Send + Sync>>,
218    ) -> Self {
219        self.source = Some(source.into());
220        self
221    }
222
223    /// Some other expression error.
224    pub fn expression(message: impl Into<String>, span: pest::Span<'_>) -> Self {
225        Self::with_span(RevsetParseErrorKind::Expression(message.into()), span)
226    }
227
228    /// If this is a `NoSuchFunction` error, expands the candidates list with
229    /// the given `other_functions`.
230    pub(super) fn extend_function_candidates<I>(mut self, other_functions: I) -> Self
231    where
232        I: IntoIterator,
233        I::Item: AsRef<str>,
234    {
235        if let RevsetParseErrorKind::NoSuchFunction { name, candidates } = self.kind.as_mut() {
236            let other_candidates = collect_similar(name, other_functions);
237            *candidates = itertools::merge(mem::take(candidates), other_candidates)
238                .dedup()
239                .collect();
240        }
241        self
242    }
243
244    pub fn kind(&self) -> &RevsetParseErrorKind {
245        &self.kind
246    }
247
248    /// Original parsing error which typically occurred in an alias expression.
249    pub fn origin(&self) -> Option<&Self> {
250        self.source.as_ref().and_then(|e| e.downcast_ref())
251    }
252}
253
254impl AliasExpandError for RevsetParseError {
255    fn invalid_arguments(err: InvalidArguments<'_>) -> Self {
256        err.into()
257    }
258
259    fn recursive_expansion(id: AliasId<'_>, span: pest::Span<'_>) -> Self {
260        Self::with_span(RevsetParseErrorKind::RecursiveAlias(id.to_string()), span)
261    }
262
263    fn within_alias_expansion(self, id: AliasId<'_>, span: pest::Span<'_>) -> Self {
264        let kind = match id {
265            AliasId::Symbol(_) | AliasId::Function(..) => {
266                RevsetParseErrorKind::InAliasExpansion(id.to_string())
267            }
268            AliasId::Parameter(_) => RevsetParseErrorKind::InParameterExpansion(id.to_string()),
269        };
270        Self::with_span(kind, span).with_source(self)
271    }
272}
273
274impl From<pest::error::Error<Rule>> for RevsetParseError {
275    fn from(err: pest::error::Error<Rule>) -> Self {
276        Self {
277            kind: Box::new(RevsetParseErrorKind::SyntaxError),
278            pest_error: Box::new(rename_rules_in_pest_error(err)),
279            source: None,
280        }
281    }
282}
283
284impl From<InvalidArguments<'_>> for RevsetParseError {
285    fn from(err: InvalidArguments<'_>) -> Self {
286        let kind = RevsetParseErrorKind::InvalidFunctionArguments {
287            name: err.name.to_owned(),
288            message: err.message,
289        };
290        Self::with_span(kind, err.span)
291    }
292}
293
294fn rename_rules_in_pest_error(mut err: pest::error::Error<Rule>) -> pest::error::Error<Rule> {
295    let pest::error::ErrorVariant::ParsingError {
296        positives,
297        negatives,
298    } = &mut err.variant
299    else {
300        return err;
301    };
302
303    // Remove duplicated symbols. Compat symbols are also removed from the
304    // (positive) suggestion.
305    let mut known_syms = HashSet::new();
306    positives.retain(|rule| {
307        !rule.is_compat() && rule.to_symbol().is_none_or(|sym| known_syms.insert(sym))
308    });
309    let mut known_syms = HashSet::new();
310    negatives.retain(|rule| rule.to_symbol().is_none_or(|sym| known_syms.insert(sym)));
311    err.renamed_rules(|rule| {
312        rule.to_symbol()
313            .map(|sym| format!("`{sym}`"))
314            .unwrap_or_else(|| format!("<{rule:?}>"))
315    })
316}
317
318#[derive(Clone, Debug, Eq, PartialEq)]
319pub enum ExpressionKind<'i> {
320    /// Unquoted symbol.
321    Identifier(&'i str),
322    /// Quoted symbol or string.
323    String(String),
324    /// `<kind>:<value>`
325    StringPattern {
326        kind: &'i str,
327        value: String,
328    },
329    /// `<name>@<remote>`
330    RemoteSymbol(RemoteRefSymbolBuf),
331    /// `<name>@`
332    AtWorkspace(String),
333    /// `@`
334    AtCurrentWorkspace,
335    /// `::`
336    DagRangeAll,
337    /// `..`
338    RangeAll,
339    Unary(UnaryOp, Box<ExpressionNode<'i>>),
340    Binary(BinaryOp, Box<ExpressionNode<'i>>, Box<ExpressionNode<'i>>),
341    /// `x | y | ..`
342    UnionAll(Vec<ExpressionNode<'i>>),
343    FunctionCall(Box<FunctionCallNode<'i>>),
344    /// `name: body`
345    Modifier(Box<ModifierNode<'i>>),
346    /// Identity node to preserve the span in the source text.
347    AliasExpanded(AliasId<'i>, Box<ExpressionNode<'i>>),
348}
349
350impl<'i> FoldableExpression<'i> for ExpressionKind<'i> {
351    fn fold<F>(self, folder: &mut F, span: pest::Span<'i>) -> Result<Self, F::Error>
352    where
353        F: ExpressionFolder<'i, Self> + ?Sized,
354    {
355        match self {
356            Self::Identifier(name) => folder.fold_identifier(name, span),
357            Self::String(_)
358            | Self::StringPattern { .. }
359            | Self::RemoteSymbol(_)
360            | ExpressionKind::AtWorkspace(_)
361            | Self::AtCurrentWorkspace
362            | Self::DagRangeAll
363            | Self::RangeAll => Ok(self),
364            Self::Unary(op, arg) => {
365                let arg = Box::new(folder.fold_expression(*arg)?);
366                Ok(Self::Unary(op, arg))
367            }
368            Self::Binary(op, lhs, rhs) => {
369                let lhs = Box::new(folder.fold_expression(*lhs)?);
370                let rhs = Box::new(folder.fold_expression(*rhs)?);
371                Ok(Self::Binary(op, lhs, rhs))
372            }
373            Self::UnionAll(nodes) => {
374                let nodes = dsl_util::fold_expression_nodes(folder, nodes)?;
375                Ok(Self::UnionAll(nodes))
376            }
377            Self::FunctionCall(function) => folder.fold_function_call(function, span),
378            Self::Modifier(modifier) => {
379                let modifier = Box::new(ModifierNode {
380                    name: modifier.name,
381                    name_span: modifier.name_span,
382                    body: folder.fold_expression(modifier.body)?,
383                });
384                Ok(Self::Modifier(modifier))
385            }
386            Self::AliasExpanded(id, subst) => {
387                let subst = Box::new(folder.fold_expression(*subst)?);
388                Ok(Self::AliasExpanded(id, subst))
389            }
390        }
391    }
392}
393
394impl<'i> AliasExpandableExpression<'i> for ExpressionKind<'i> {
395    fn identifier(name: &'i str) -> Self {
396        Self::Identifier(name)
397    }
398
399    fn function_call(function: Box<FunctionCallNode<'i>>) -> Self {
400        Self::FunctionCall(function)
401    }
402
403    fn alias_expanded(id: AliasId<'i>, subst: Box<ExpressionNode<'i>>) -> Self {
404        Self::AliasExpanded(id, subst)
405    }
406}
407
408#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
409pub enum UnaryOp {
410    /// `~x`
411    Negate,
412    /// `::x`
413    DagRangePre,
414    /// `x::`
415    DagRangePost,
416    /// `..x`
417    RangePre,
418    /// `x..`
419    RangePost,
420    /// `x-`
421    Parents,
422    /// `x+`
423    Children,
424}
425
426#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
427pub enum BinaryOp {
428    /// `&`
429    Intersection,
430    /// `~`
431    Difference,
432    /// `::`
433    DagRange,
434    /// `..`
435    Range,
436}
437
438pub type ExpressionNode<'i> = dsl_util::ExpressionNode<'i, ExpressionKind<'i>>;
439pub type FunctionCallNode<'i> = dsl_util::FunctionCallNode<'i, ExpressionKind<'i>>;
440
441/// Expression with modifier `name: body`.
442#[derive(Clone, Debug, Eq, PartialEq)]
443pub struct ModifierNode<'i> {
444    /// Modifier name.
445    pub name: &'i str,
446    /// Span of the modifier name.
447    pub name_span: pest::Span<'i>,
448    /// Expression body.
449    pub body: ExpressionNode<'i>,
450}
451
452fn union_nodes<'i>(lhs: ExpressionNode<'i>, rhs: ExpressionNode<'i>) -> ExpressionNode<'i> {
453    let span = lhs.span.start_pos().span(&rhs.span.end_pos());
454    let expr = match lhs.kind {
455        // Flatten "x | y | z" to save recursion stack. Machine-generated query
456        // might have long chain of unions.
457        ExpressionKind::UnionAll(mut nodes) => {
458            nodes.push(rhs);
459            ExpressionKind::UnionAll(nodes)
460        }
461        _ => ExpressionKind::UnionAll(vec![lhs, rhs]),
462    };
463    ExpressionNode::new(expr, span)
464}
465
466/// Parses text into expression tree. No name resolution is made at this stage.
467pub fn parse_program(revset_str: &str) -> Result<ExpressionNode<'_>, RevsetParseError> {
468    let mut pairs = RevsetParser::parse(Rule::program, revset_str)?;
469    let first = pairs.next().unwrap();
470    match first.as_rule() {
471        Rule::expression => parse_expression_node(first),
472        Rule::program_modifier => {
473            let [lhs, op] = first.into_inner().collect_array().unwrap();
474            let rhs = pairs.next().unwrap();
475            assert_eq!(lhs.as_rule(), Rule::strict_identifier);
476            assert_eq!(op.as_rule(), Rule::pattern_kind_op);
477            assert_eq!(rhs.as_rule(), Rule::expression);
478            let span = lhs.as_span().start_pos().span(&rhs.as_span().end_pos());
479            let modifier = Box::new(ModifierNode {
480                name: lhs.as_str(),
481                name_span: lhs.as_span(),
482                body: parse_expression_node(rhs)?,
483            });
484            let expr = ExpressionKind::Modifier(modifier);
485            Ok(ExpressionNode::new(expr, span))
486        }
487        r => panic!("unexpected revset parse rule: {r:?}"),
488    }
489}
490
491fn parse_expression_node(pair: Pair<Rule>) -> Result<ExpressionNode, RevsetParseError> {
492    fn not_prefix_op(
493        op: &Pair<Rule>,
494        similar_op: impl Into<String>,
495        description: impl Into<String>,
496    ) -> RevsetParseError {
497        RevsetParseError::with_span(
498            RevsetParseErrorKind::NotPrefixOperator {
499                op: op.as_str().to_owned(),
500                similar_op: similar_op.into(),
501                description: description.into(),
502            },
503            op.as_span(),
504        )
505    }
506
507    fn not_postfix_op(
508        op: &Pair<Rule>,
509        similar_op: impl Into<String>,
510        description: impl Into<String>,
511    ) -> RevsetParseError {
512        RevsetParseError::with_span(
513            RevsetParseErrorKind::NotPostfixOperator {
514                op: op.as_str().to_owned(),
515                similar_op: similar_op.into(),
516                description: description.into(),
517            },
518            op.as_span(),
519        )
520    }
521
522    fn not_infix_op(
523        op: &Pair<Rule>,
524        similar_op: impl Into<String>,
525        description: impl Into<String>,
526    ) -> RevsetParseError {
527        RevsetParseError::with_span(
528            RevsetParseErrorKind::NotInfixOperator {
529                op: op.as_str().to_owned(),
530                similar_op: similar_op.into(),
531                description: description.into(),
532            },
533            op.as_span(),
534        )
535    }
536
537    static PRATT: LazyLock<PrattParser<Rule>> = LazyLock::new(|| {
538        PrattParser::new()
539            .op(Op::infix(Rule::union_op, Assoc::Left)
540                | Op::infix(Rule::compat_add_op, Assoc::Left))
541            .op(Op::infix(Rule::intersection_op, Assoc::Left)
542                | Op::infix(Rule::difference_op, Assoc::Left)
543                | Op::infix(Rule::compat_sub_op, Assoc::Left))
544            .op(Op::prefix(Rule::negate_op))
545            // Ranges can't be nested without parentheses. Associativity doesn't matter.
546            .op(Op::infix(Rule::dag_range_op, Assoc::Left)
547                | Op::infix(Rule::compat_dag_range_op, Assoc::Left)
548                | Op::infix(Rule::range_op, Assoc::Left))
549            .op(Op::prefix(Rule::dag_range_pre_op)
550                | Op::prefix(Rule::compat_dag_range_pre_op)
551                | Op::prefix(Rule::range_pre_op))
552            .op(Op::postfix(Rule::dag_range_post_op)
553                | Op::postfix(Rule::compat_dag_range_post_op)
554                | Op::postfix(Rule::range_post_op))
555            // Neighbors
556            .op(Op::postfix(Rule::parents_op)
557                | Op::postfix(Rule::children_op)
558                | Op::postfix(Rule::compat_parents_op))
559    });
560    PRATT
561        .map_primary(|primary| {
562            let expr = match primary.as_rule() {
563                Rule::primary => return parse_primary_node(primary),
564                Rule::dag_range_all_op => ExpressionKind::DagRangeAll,
565                Rule::range_all_op => ExpressionKind::RangeAll,
566                r => panic!("unexpected primary rule {r:?}"),
567            };
568            Ok(ExpressionNode::new(expr, primary.as_span()))
569        })
570        .map_prefix(|op, rhs| {
571            let op_kind = match op.as_rule() {
572                Rule::negate_op => UnaryOp::Negate,
573                Rule::dag_range_pre_op => UnaryOp::DagRangePre,
574                Rule::compat_dag_range_pre_op => Err(not_prefix_op(&op, "::", "ancestors"))?,
575                Rule::range_pre_op => UnaryOp::RangePre,
576                r => panic!("unexpected prefix operator rule {r:?}"),
577            };
578            let rhs = Box::new(rhs?);
579            let span = op.as_span().start_pos().span(&rhs.span.end_pos());
580            let expr = ExpressionKind::Unary(op_kind, rhs);
581            Ok(ExpressionNode::new(expr, span))
582        })
583        .map_postfix(|lhs, op| {
584            let op_kind = match op.as_rule() {
585                Rule::dag_range_post_op => UnaryOp::DagRangePost,
586                Rule::compat_dag_range_post_op => Err(not_postfix_op(&op, "::", "descendants"))?,
587                Rule::range_post_op => UnaryOp::RangePost,
588                Rule::parents_op => UnaryOp::Parents,
589                Rule::children_op => UnaryOp::Children,
590                Rule::compat_parents_op => Err(not_postfix_op(&op, "-", "parents"))?,
591                r => panic!("unexpected postfix operator rule {r:?}"),
592            };
593            let lhs = Box::new(lhs?);
594            let span = lhs.span.start_pos().span(&op.as_span().end_pos());
595            let expr = ExpressionKind::Unary(op_kind, lhs);
596            Ok(ExpressionNode::new(expr, span))
597        })
598        .map_infix(|lhs, op, rhs| {
599            let op_kind = match op.as_rule() {
600                Rule::union_op => return Ok(union_nodes(lhs?, rhs?)),
601                Rule::compat_add_op => Err(not_infix_op(&op, "|", "union"))?,
602                Rule::intersection_op => BinaryOp::Intersection,
603                Rule::difference_op => BinaryOp::Difference,
604                Rule::compat_sub_op => Err(not_infix_op(&op, "~", "difference"))?,
605                Rule::dag_range_op => BinaryOp::DagRange,
606                Rule::compat_dag_range_op => Err(not_infix_op(&op, "::", "DAG range"))?,
607                Rule::range_op => BinaryOp::Range,
608                r => panic!("unexpected infix operator rule {r:?}"),
609            };
610            let lhs = Box::new(lhs?);
611            let rhs = Box::new(rhs?);
612            let span = lhs.span.start_pos().span(&rhs.span.end_pos());
613            let expr = ExpressionKind::Binary(op_kind, lhs, rhs);
614            Ok(ExpressionNode::new(expr, span))
615        })
616        .parse(pair.into_inner())
617}
618
619fn parse_primary_node(pair: Pair<Rule>) -> Result<ExpressionNode, RevsetParseError> {
620    let span = pair.as_span();
621    let mut pairs = pair.into_inner();
622    let first = pairs.next().unwrap();
623    let expr = match first.as_rule() {
624        // Ignore inner span to preserve parenthesized expression as such.
625        Rule::expression => parse_expression_node(first)?.kind,
626        Rule::function => {
627            let function = Box::new(FUNCTION_CALL_PARSER.parse(
628                first,
629                |pair| Ok(pair.as_str()),
630                |pair| parse_expression_node(pair),
631            )?);
632            ExpressionKind::FunctionCall(function)
633        }
634        Rule::string_pattern => {
635            let [lhs, op, rhs] = first.into_inner().collect_array().unwrap();
636            assert_eq!(lhs.as_rule(), Rule::strict_identifier);
637            assert_eq!(op.as_rule(), Rule::pattern_kind_op);
638            let kind = lhs.as_str();
639            let value = parse_as_string_literal(rhs);
640            ExpressionKind::StringPattern { kind, value }
641        }
642        // Identifier without "@" may be substituted by aliases. Primary expression including "@"
643        // is considered an indecomposable unit, and no alias substitution would be made.
644        Rule::identifier if pairs.peek().is_none() => ExpressionKind::Identifier(first.as_str()),
645        Rule::identifier | Rule::string_literal | Rule::raw_string_literal => {
646            let name = parse_as_string_literal(first);
647            match pairs.next() {
648                None => ExpressionKind::String(name),
649                Some(op) => {
650                    assert_eq!(op.as_rule(), Rule::at_op);
651                    match pairs.next() {
652                        // postfix "<name>@"
653                        None => ExpressionKind::AtWorkspace(name),
654                        // infix "<name>@<remote>"
655                        Some(second) => {
656                            let name: RefNameBuf = name.into();
657                            let remote: RemoteNameBuf = parse_as_string_literal(second).into();
658                            ExpressionKind::RemoteSymbol(RemoteRefSymbolBuf { name, remote })
659                        }
660                    }
661                }
662            }
663        }
664        // nullary "@"
665        Rule::at_op => ExpressionKind::AtCurrentWorkspace,
666        r => panic!("unexpected revset parse rule: {r:?}"),
667    };
668    Ok(ExpressionNode::new(expr, span))
669}
670
671/// Parses part of compound symbol to string.
672fn parse_as_string_literal(pair: Pair<Rule>) -> String {
673    match pair.as_rule() {
674        Rule::identifier => pair.as_str().to_owned(),
675        Rule::string_literal => STRING_LITERAL_PARSER.parse(pair.into_inner()),
676        Rule::raw_string_literal => {
677            let [content] = pair.into_inner().collect_array().unwrap();
678            assert_eq!(content.as_rule(), Rule::raw_string_content);
679            content.as_str().to_owned()
680        }
681        _ => {
682            panic!("unexpected string literal rule: {:?}", pair.as_str());
683        }
684    }
685}
686
687/// Checks if the text is a valid identifier
688pub fn is_identifier(text: &str) -> bool {
689    match RevsetParser::parse(Rule::identifier, text) {
690        Ok(mut pairs) => pairs.next().unwrap().as_span().end() == text.len(),
691        Err(_) => false,
692    }
693}
694
695/// Parses the text as a revset symbol, rejects empty string.
696pub fn parse_symbol(text: &str) -> Result<String, RevsetParseError> {
697    let mut pairs = RevsetParser::parse(Rule::symbol_name, text)?;
698    let first = pairs.next().unwrap();
699    let span = first.as_span();
700    let name = parse_as_string_literal(first);
701    if name.is_empty() {
702        Err(RevsetParseError::expression(
703            "Expected non-empty string",
704            span,
705        ))
706    } else {
707        Ok(name)
708    }
709}
710
711pub type RevsetAliasesMap = AliasesMap<RevsetAliasParser, String>;
712
713#[derive(Clone, Debug, Default)]
714pub struct RevsetAliasParser;
715
716impl AliasDeclarationParser for RevsetAliasParser {
717    type Error = RevsetParseError;
718
719    fn parse_declaration(&self, source: &str) -> Result<AliasDeclaration, Self::Error> {
720        let mut pairs = RevsetParser::parse(Rule::alias_declaration, source)?;
721        let first = pairs.next().unwrap();
722        match first.as_rule() {
723            Rule::strict_identifier => Ok(AliasDeclaration::Symbol(first.as_str().to_owned())),
724            Rule::function_alias_declaration => {
725                let [name_pair, params_pair] = first.into_inner().collect_array().unwrap();
726                assert_eq!(name_pair.as_rule(), Rule::function_name);
727                assert_eq!(params_pair.as_rule(), Rule::formal_parameters);
728                let name = name_pair.as_str().to_owned();
729                let params_span = params_pair.as_span();
730                let params = params_pair
731                    .into_inner()
732                    .map(|pair| match pair.as_rule() {
733                        Rule::strict_identifier => pair.as_str().to_owned(),
734                        r => panic!("unexpected formal parameter rule {r:?}"),
735                    })
736                    .collect_vec();
737                if params.iter().all_unique() {
738                    Ok(AliasDeclaration::Function(name, params))
739                } else {
740                    Err(RevsetParseError::with_span(
741                        RevsetParseErrorKind::RedefinedFunctionParameter,
742                        params_span,
743                    ))
744                }
745            }
746            r => panic!("unexpected alias declaration rule {r:?}"),
747        }
748    }
749}
750
751impl AliasDefinitionParser for RevsetAliasParser {
752    type Output<'i> = ExpressionKind<'i>;
753    type Error = RevsetParseError;
754
755    fn parse_definition<'i>(&self, source: &'i str) -> Result<ExpressionNode<'i>, Self::Error> {
756        parse_program(source)
757    }
758}
759
760pub(super) fn expect_string_pattern<'a>(
761    type_name: &str,
762    node: &'a ExpressionNode<'_>,
763) -> Result<(&'a str, Option<&'a str>), RevsetParseError> {
764    catch_aliases_no_diagnostics(node, |node| match &node.kind {
765        ExpressionKind::Identifier(name) => Ok((*name, None)),
766        ExpressionKind::String(name) => Ok((name, None)),
767        ExpressionKind::StringPattern { kind, value } => Ok((value, Some(*kind))),
768        _ => Err(RevsetParseError::expression(
769            format!("Expected {type_name}"),
770            node.span,
771        )),
772    })
773}
774
775pub fn expect_literal<T: FromStr>(
776    type_name: &str,
777    node: &ExpressionNode,
778) -> Result<T, RevsetParseError> {
779    catch_aliases_no_diagnostics(node, |node| {
780        let value = expect_string_literal(type_name, node)?;
781        value
782            .parse()
783            .map_err(|_| RevsetParseError::expression(format!("Expected {type_name}"), node.span))
784    })
785}
786
787pub(super) fn expect_string_literal<'a>(
788    type_name: &str,
789    node: &'a ExpressionNode<'_>,
790) -> Result<&'a str, RevsetParseError> {
791    catch_aliases_no_diagnostics(node, |node| match &node.kind {
792        ExpressionKind::Identifier(name) => Ok(*name),
793        ExpressionKind::String(name) => Ok(name),
794        _ => Err(RevsetParseError::expression(
795            format!("Expected {type_name}"),
796            node.span,
797        )),
798    })
799}
800
801/// Applies the given function to the innermost `node` by unwrapping alias
802/// expansion nodes. Appends alias expansion stack to error and diagnostics.
803pub(super) fn catch_aliases<'a, 'i, T>(
804    diagnostics: &mut RevsetDiagnostics,
805    node: &'a ExpressionNode<'i>,
806    f: impl FnOnce(&mut RevsetDiagnostics, &'a ExpressionNode<'i>) -> Result<T, RevsetParseError>,
807) -> Result<T, RevsetParseError> {
808    let (node, stack) = skip_aliases(node);
809    if stack.is_empty() {
810        f(diagnostics, node)
811    } else {
812        let mut inner_diagnostics = RevsetDiagnostics::new();
813        let result = f(&mut inner_diagnostics, node);
814        diagnostics.extend_with(inner_diagnostics, |diag| attach_aliases_err(diag, &stack));
815        result.map_err(|err| attach_aliases_err(err, &stack))
816    }
817}
818
819fn catch_aliases_no_diagnostics<'a, 'i, T>(
820    node: &'a ExpressionNode<'i>,
821    f: impl FnOnce(&'a ExpressionNode<'i>) -> Result<T, RevsetParseError>,
822) -> Result<T, RevsetParseError> {
823    let (node, stack) = skip_aliases(node);
824    f(node).map_err(|err| attach_aliases_err(err, &stack))
825}
826
827fn skip_aliases<'a, 'i>(
828    mut node: &'a ExpressionNode<'i>,
829) -> (&'a ExpressionNode<'i>, Vec<(AliasId<'i>, pest::Span<'i>)>) {
830    let mut stack = Vec::new();
831    while let ExpressionKind::AliasExpanded(id, subst) = &node.kind {
832        stack.push((*id, node.span));
833        node = subst;
834    }
835    (node, stack)
836}
837
838fn attach_aliases_err(
839    err: RevsetParseError,
840    stack: &[(AliasId<'_>, pest::Span<'_>)],
841) -> RevsetParseError {
842    stack
843        .iter()
844        .rfold(err, |err, &(id, span)| err.within_alias_expansion(id, span))
845}
846
847#[cfg(test)]
848mod tests {
849    use std::collections::HashMap;
850
851    use assert_matches::assert_matches;
852
853    use super::*;
854    use crate::dsl_util::KeywordArgument;
855
856    #[derive(Debug)]
857    struct WithRevsetAliasesMap<'i> {
858        aliases_map: RevsetAliasesMap,
859        locals: HashMap<&'i str, ExpressionNode<'i>>,
860    }
861
862    impl<'i> WithRevsetAliasesMap<'i> {
863        fn set_local(mut self, name: &'i str, value: &'i str) -> Self {
864            self.locals.insert(name, parse_program(value).unwrap());
865            self
866        }
867
868        fn parse(&'i self, text: &'i str) -> Result<ExpressionNode<'i>, RevsetParseError> {
869            let node = parse_program(text)?;
870            dsl_util::expand_aliases_with_locals(node, &self.aliases_map, &self.locals)
871        }
872
873        fn parse_normalized(&'i self, text: &'i str) -> ExpressionNode<'i> {
874            normalize_tree(self.parse(text).unwrap())
875        }
876    }
877
878    fn with_aliases<'i>(
879        aliases: impl IntoIterator<Item = (impl AsRef<str>, impl Into<String>)>,
880    ) -> WithRevsetAliasesMap<'i> {
881        let mut aliases_map = RevsetAliasesMap::new();
882        for (decl, defn) in aliases {
883            aliases_map.insert(decl, defn).unwrap();
884        }
885        WithRevsetAliasesMap {
886            aliases_map,
887            locals: HashMap::new(),
888        }
889    }
890
891    fn parse_into_kind(text: &str) -> Result<ExpressionKind<'_>, RevsetParseErrorKind> {
892        parse_program(text)
893            .map(|node| node.kind)
894            .map_err(|err| *err.kind)
895    }
896
897    fn parse_normalized(text: &str) -> ExpressionNode<'_> {
898        normalize_tree(parse_program(text).unwrap())
899    }
900
901    /// Drops auxiliary data from parsed tree so it can be compared with other.
902    fn normalize_tree(node: ExpressionNode) -> ExpressionNode {
903        fn empty_span() -> pest::Span<'static> {
904            pest::Span::new("", 0, 0).unwrap()
905        }
906
907        fn normalize_list(nodes: Vec<ExpressionNode>) -> Vec<ExpressionNode> {
908            nodes.into_iter().map(normalize_tree).collect()
909        }
910
911        fn normalize_function_call(function: FunctionCallNode) -> FunctionCallNode {
912            FunctionCallNode {
913                name: function.name,
914                name_span: empty_span(),
915                args: normalize_list(function.args),
916                keyword_args: function
917                    .keyword_args
918                    .into_iter()
919                    .map(|arg| KeywordArgument {
920                        name: arg.name,
921                        name_span: empty_span(),
922                        value: normalize_tree(arg.value),
923                    })
924                    .collect(),
925                args_span: empty_span(),
926            }
927        }
928
929        let normalized_kind = match node.kind {
930            ExpressionKind::Identifier(_)
931            | ExpressionKind::String(_)
932            | ExpressionKind::StringPattern { .. }
933            | ExpressionKind::RemoteSymbol(_)
934            | ExpressionKind::AtWorkspace(_)
935            | ExpressionKind::AtCurrentWorkspace
936            | ExpressionKind::DagRangeAll
937            | ExpressionKind::RangeAll => node.kind,
938            ExpressionKind::Unary(op, arg) => {
939                let arg = Box::new(normalize_tree(*arg));
940                ExpressionKind::Unary(op, arg)
941            }
942            ExpressionKind::Binary(op, lhs, rhs) => {
943                let lhs = Box::new(normalize_tree(*lhs));
944                let rhs = Box::new(normalize_tree(*rhs));
945                ExpressionKind::Binary(op, lhs, rhs)
946            }
947            ExpressionKind::UnionAll(nodes) => {
948                let nodes = normalize_list(nodes);
949                ExpressionKind::UnionAll(nodes)
950            }
951            ExpressionKind::FunctionCall(function) => {
952                let function = Box::new(normalize_function_call(*function));
953                ExpressionKind::FunctionCall(function)
954            }
955            ExpressionKind::Modifier(modifier) => {
956                let modifier = Box::new(ModifierNode {
957                    name: modifier.name,
958                    name_span: empty_span(),
959                    body: normalize_tree(modifier.body),
960                });
961                ExpressionKind::Modifier(modifier)
962            }
963            ExpressionKind::AliasExpanded(_, subst) => normalize_tree(*subst).kind,
964        };
965        ExpressionNode {
966            kind: normalized_kind,
967            span: empty_span(),
968        }
969    }
970
971    #[test]
972    fn test_parse_tree_eq() {
973        assert_eq!(
974            parse_normalized(r#" foo( x ) | ~bar:"baz" "#),
975            parse_normalized(r#"(foo(x))|(~(bar:"baz"))"#)
976        );
977        assert_ne!(parse_normalized(r#" foo "#), parse_normalized(r#" "foo" "#));
978    }
979
980    #[test]
981    fn test_parse_revset() {
982        // Parse a quoted symbol
983        assert_eq!(
984            parse_into_kind("\"foo\""),
985            Ok(ExpressionKind::String("foo".to_owned()))
986        );
987        assert_eq!(
988            parse_into_kind("'foo'"),
989            Ok(ExpressionKind::String("foo".to_owned()))
990        );
991        // Parse the "parents" operator
992        assert_matches!(
993            parse_into_kind("foo-"),
994            Ok(ExpressionKind::Unary(UnaryOp::Parents, _))
995        );
996        // Parse the "children" operator
997        assert_matches!(
998            parse_into_kind("foo+"),
999            Ok(ExpressionKind::Unary(UnaryOp::Children, _))
1000        );
1001        // Parse the "ancestors" operator
1002        assert_matches!(
1003            parse_into_kind("::foo"),
1004            Ok(ExpressionKind::Unary(UnaryOp::DagRangePre, _))
1005        );
1006        // Parse the "descendants" operator
1007        assert_matches!(
1008            parse_into_kind("foo::"),
1009            Ok(ExpressionKind::Unary(UnaryOp::DagRangePost, _))
1010        );
1011        // Parse the "dag range" operator
1012        assert_matches!(
1013            parse_into_kind("foo::bar"),
1014            Ok(ExpressionKind::Binary(BinaryOp::DagRange, _, _))
1015        );
1016        // Parse the nullary "dag range" operator
1017        assert_matches!(parse_into_kind("::"), Ok(ExpressionKind::DagRangeAll));
1018        // Parse the "range" prefix operator
1019        assert_matches!(
1020            parse_into_kind("..foo"),
1021            Ok(ExpressionKind::Unary(UnaryOp::RangePre, _))
1022        );
1023        assert_matches!(
1024            parse_into_kind("foo.."),
1025            Ok(ExpressionKind::Unary(UnaryOp::RangePost, _))
1026        );
1027        assert_matches!(
1028            parse_into_kind("foo..bar"),
1029            Ok(ExpressionKind::Binary(BinaryOp::Range, _, _))
1030        );
1031        // Parse the nullary "range" operator
1032        assert_matches!(parse_into_kind(".."), Ok(ExpressionKind::RangeAll));
1033        // Parse the "negate" operator
1034        assert_matches!(
1035            parse_into_kind("~ foo"),
1036            Ok(ExpressionKind::Unary(UnaryOp::Negate, _))
1037        );
1038        assert_eq!(
1039            parse_normalized("~ ~~ foo"),
1040            parse_normalized("~(~(~(foo)))"),
1041        );
1042        // Parse the "intersection" operator
1043        assert_matches!(
1044            parse_into_kind("foo & bar"),
1045            Ok(ExpressionKind::Binary(BinaryOp::Intersection, _, _))
1046        );
1047        // Parse the "union" operator
1048        assert_matches!(
1049            parse_into_kind("foo | bar"),
1050            Ok(ExpressionKind::UnionAll(nodes)) if nodes.len() == 2
1051        );
1052        assert_matches!(
1053            parse_into_kind("foo | bar | baz"),
1054            Ok(ExpressionKind::UnionAll(nodes)) if nodes.len() == 3
1055        );
1056        // Parse the "difference" operator
1057        assert_matches!(
1058            parse_into_kind("foo ~ bar"),
1059            Ok(ExpressionKind::Binary(BinaryOp::Difference, _, _))
1060        );
1061        // Parentheses are allowed before suffix operators
1062        assert_eq!(parse_normalized("(foo)-"), parse_normalized("foo-"));
1063        // Space is allowed around expressions
1064        assert_eq!(parse_normalized(" ::foo "), parse_normalized("::foo"));
1065        assert_eq!(parse_normalized("( ::foo )"), parse_normalized("::foo"));
1066        // Space is not allowed around prefix operators
1067        assert_eq!(
1068            parse_into_kind(" :: foo "),
1069            Err(RevsetParseErrorKind::SyntaxError)
1070        );
1071        // Incomplete parse
1072        assert_eq!(
1073            parse_into_kind("foo | -"),
1074            Err(RevsetParseErrorKind::SyntaxError)
1075        );
1076
1077        // Expression span
1078        assert_eq!(parse_program(" ~ x ").unwrap().span.as_str(), "~ x");
1079        assert_eq!(parse_program(" x+ ").unwrap().span.as_str(), "x+");
1080        assert_eq!(parse_program(" x |y ").unwrap().span.as_str(), "x |y");
1081        assert_eq!(parse_program(" (x) ").unwrap().span.as_str(), "(x)");
1082        assert_eq!(parse_program("~( x|y) ").unwrap().span.as_str(), "~( x|y)");
1083        assert_eq!(parse_program(" ( x )- ").unwrap().span.as_str(), "( x )-");
1084    }
1085
1086    #[test]
1087    fn test_parse_revset_with_modifier() {
1088        // all: is a program modifier, but all:: isn't
1089        assert_eq!(
1090            parse_into_kind("all:"),
1091            Err(RevsetParseErrorKind::SyntaxError)
1092        );
1093        assert_matches!(
1094            parse_into_kind("all:foo"),
1095            Ok(ExpressionKind::Modifier(modifier)) if modifier.name == "all"
1096        );
1097        assert_matches!(
1098            parse_into_kind("all::"),
1099            Ok(ExpressionKind::Unary(UnaryOp::DagRangePost, _))
1100        );
1101        assert_matches!(
1102            parse_into_kind("all::foo"),
1103            Ok(ExpressionKind::Binary(BinaryOp::DagRange, _, _))
1104        );
1105
1106        // all::: could be parsed as all:(::), but rejected for simplicity
1107        assert_eq!(
1108            parse_into_kind("all:::"),
1109            Err(RevsetParseErrorKind::SyntaxError)
1110        );
1111        assert_eq!(
1112            parse_into_kind("all:::foo"),
1113            Err(RevsetParseErrorKind::SyntaxError)
1114        );
1115
1116        assert_eq!(parse_normalized("all:(foo)"), parse_normalized("all:foo"));
1117        assert_eq!(
1118            parse_normalized("all:all::foo"),
1119            parse_normalized("all:(all::foo)"),
1120        );
1121        assert_eq!(
1122            parse_normalized("all:all | foo"),
1123            parse_normalized("all:(all | foo)"),
1124        );
1125
1126        assert_eq!(
1127            parse_normalized("all: ::foo"),
1128            parse_normalized("all:(::foo)"),
1129        );
1130        assert_eq!(parse_normalized(" all: foo"), parse_normalized("all:foo"));
1131        assert_eq!(
1132            parse_into_kind("(all:foo)"),
1133            Ok(ExpressionKind::StringPattern {
1134                kind: "all",
1135                value: "foo".to_owned()
1136            })
1137        );
1138        assert_matches!(
1139            parse_into_kind("all :foo"),
1140            Err(RevsetParseErrorKind::SyntaxError)
1141        );
1142        assert_eq!(
1143            parse_normalized("all:all:all"),
1144            parse_normalized("all:(all:all)"),
1145        );
1146    }
1147
1148    #[test]
1149    fn test_parse_whitespace() {
1150        let ascii_whitespaces: String = ('\x00'..='\x7f')
1151            .filter(char::is_ascii_whitespace)
1152            .collect();
1153        assert_eq!(
1154            parse_normalized(&format!("{ascii_whitespaces}all()")),
1155            parse_normalized("all()"),
1156        );
1157    }
1158
1159    #[test]
1160    fn test_parse_identifier() {
1161        // Integer is a symbol
1162        assert_eq!(parse_into_kind("0"), Ok(ExpressionKind::Identifier("0")));
1163        // Tag/bookmark name separated by /
1164        assert_eq!(
1165            parse_into_kind("foo_bar/baz"),
1166            Ok(ExpressionKind::Identifier("foo_bar/baz"))
1167        );
1168
1169        // Internal '.', '-', and '+' are allowed
1170        assert_eq!(
1171            parse_into_kind("foo.bar-v1+7"),
1172            Ok(ExpressionKind::Identifier("foo.bar-v1+7"))
1173        );
1174        assert_eq!(
1175            parse_normalized("foo.bar-v1+7-"),
1176            parse_normalized("(foo.bar-v1+7)-")
1177        );
1178        // '.' is not allowed at the beginning or end
1179        assert_eq!(
1180            parse_into_kind(".foo"),
1181            Err(RevsetParseErrorKind::SyntaxError)
1182        );
1183        assert_eq!(
1184            parse_into_kind("foo."),
1185            Err(RevsetParseErrorKind::SyntaxError)
1186        );
1187        // Multiple '.', '-', '+' are not allowed
1188        assert_eq!(
1189            parse_into_kind("foo.+bar"),
1190            Err(RevsetParseErrorKind::SyntaxError)
1191        );
1192        assert_eq!(
1193            parse_into_kind("foo--bar"),
1194            Err(RevsetParseErrorKind::SyntaxError)
1195        );
1196        assert_eq!(
1197            parse_into_kind("foo+-bar"),
1198            Err(RevsetParseErrorKind::SyntaxError)
1199        );
1200
1201        // Parse a parenthesized symbol
1202        assert_eq!(parse_normalized("(foo)"), parse_normalized("foo"));
1203
1204        // Non-ASCII tag/bookmark name
1205        assert_eq!(
1206            parse_into_kind("柔術+jj"),
1207            Ok(ExpressionKind::Identifier("柔術+jj"))
1208        );
1209    }
1210
1211    #[test]
1212    fn test_parse_string_literal() {
1213        // "\<char>" escapes
1214        assert_eq!(
1215            parse_into_kind(r#" "\t\r\n\"\\\0\e" "#),
1216            Ok(ExpressionKind::String("\t\r\n\"\\\0\u{1b}".to_owned()))
1217        );
1218
1219        // Invalid "\<char>" escape
1220        assert_eq!(
1221            parse_into_kind(r#" "\y" "#),
1222            Err(RevsetParseErrorKind::SyntaxError)
1223        );
1224
1225        // Single-quoted raw string
1226        assert_eq!(
1227            parse_into_kind(r#" '' "#),
1228            Ok(ExpressionKind::String("".to_owned()))
1229        );
1230        assert_eq!(
1231            parse_into_kind(r#" 'a\n' "#),
1232            Ok(ExpressionKind::String(r"a\n".to_owned()))
1233        );
1234        assert_eq!(
1235            parse_into_kind(r#" '\' "#),
1236            Ok(ExpressionKind::String(r"\".to_owned()))
1237        );
1238        assert_eq!(
1239            parse_into_kind(r#" '"' "#),
1240            Ok(ExpressionKind::String(r#"""#.to_owned()))
1241        );
1242
1243        // Hex bytes
1244        assert_eq!(
1245            parse_into_kind(r#""\x61\x65\x69\x6f\x75""#),
1246            Ok(ExpressionKind::String("aeiou".to_owned()))
1247        );
1248        assert_eq!(
1249            parse_into_kind(r#""\xe0\xe8\xec\xf0\xf9""#),
1250            Ok(ExpressionKind::String("àèìðù".to_owned()))
1251        );
1252        assert_eq!(
1253            parse_into_kind(r#""\x""#),
1254            Err(RevsetParseErrorKind::SyntaxError)
1255        );
1256        assert_eq!(
1257            parse_into_kind(r#""\xf""#),
1258            Err(RevsetParseErrorKind::SyntaxError)
1259        );
1260        assert_eq!(
1261            parse_into_kind(r#""\xgg""#),
1262            Err(RevsetParseErrorKind::SyntaxError)
1263        );
1264    }
1265
1266    #[test]
1267    fn test_parse_string_pattern() {
1268        assert_eq!(
1269            parse_into_kind(r#"(substring:"foo")"#),
1270            Ok(ExpressionKind::StringPattern {
1271                kind: "substring",
1272                value: "foo".to_owned()
1273            })
1274        );
1275        assert_eq!(
1276            parse_into_kind(r#"("exact:foo")"#),
1277            Ok(ExpressionKind::String("exact:foo".to_owned()))
1278        );
1279        assert_eq!(
1280            parse_normalized(r#"(exact:"foo" )"#),
1281            parse_normalized(r#"(exact:"foo")"#),
1282        );
1283        assert_eq!(
1284            parse_into_kind(r#"(exact:'\')"#),
1285            Ok(ExpressionKind::StringPattern {
1286                kind: "exact",
1287                value: r"\".to_owned()
1288            })
1289        );
1290        assert_matches!(
1291            parse_into_kind(r#"(exact:("foo" ))"#),
1292            Err(RevsetParseErrorKind::NotInfixOperator { .. })
1293        );
1294    }
1295
1296    #[test]
1297    fn test_parse_symbol_explicitly() {
1298        assert_matches!(parse_symbol("").as_deref(), Err(_));
1299        // empty string could be a valid ref name, but it would be super
1300        // confusing if identifier was empty.
1301        assert_matches!(parse_symbol("''").as_deref(), Err(_));
1302
1303        assert_matches!(parse_symbol("foo.bar").as_deref(), Ok("foo.bar"));
1304        assert_matches!(parse_symbol("foo@bar").as_deref(), Err(_));
1305        assert_matches!(parse_symbol("foo bar").as_deref(), Err(_));
1306
1307        assert_matches!(parse_symbol("'foo bar'").as_deref(), Ok("foo bar"));
1308        assert_matches!(parse_symbol(r#""foo\tbar""#).as_deref(), Ok("foo\tbar"));
1309
1310        // leading/trailing whitespace is NOT ignored.
1311        assert_matches!(parse_symbol(" foo").as_deref(), Err(_));
1312        assert_matches!(parse_symbol("foo ").as_deref(), Err(_));
1313
1314        // (foo) could be parsed as a symbol "foo", but is rejected because user
1315        // might expect a literal "(foo)".
1316        assert_matches!(parse_symbol("(foo)").as_deref(), Err(_));
1317    }
1318
1319    #[test]
1320    fn parse_at_workspace_and_remote_symbol() {
1321        // Parse "@" (the current working copy)
1322        assert_eq!(parse_into_kind("@"), Ok(ExpressionKind::AtCurrentWorkspace));
1323        assert_eq!(
1324            parse_into_kind("main@"),
1325            Ok(ExpressionKind::AtWorkspace("main".to_owned()))
1326        );
1327        assert_eq!(
1328            parse_into_kind("main@origin"),
1329            Ok(ExpressionKind::RemoteSymbol(RemoteRefSymbolBuf {
1330                name: "main".into(),
1331                remote: "origin".into()
1332            }))
1333        );
1334
1335        // Quoted component in @ expression
1336        assert_eq!(
1337            parse_into_kind(r#""foo bar"@"#),
1338            Ok(ExpressionKind::AtWorkspace("foo bar".to_owned()))
1339        );
1340        assert_eq!(
1341            parse_into_kind(r#""foo bar"@origin"#),
1342            Ok(ExpressionKind::RemoteSymbol(RemoteRefSymbolBuf {
1343                name: "foo bar".into(),
1344                remote: "origin".into()
1345            }))
1346        );
1347        assert_eq!(
1348            parse_into_kind(r#"main@"foo bar""#),
1349            Ok(ExpressionKind::RemoteSymbol(RemoteRefSymbolBuf {
1350                name: "main".into(),
1351                remote: "foo bar".into()
1352            }))
1353        );
1354        assert_eq!(
1355            parse_into_kind(r#"'foo bar'@'bar baz'"#),
1356            Ok(ExpressionKind::RemoteSymbol(RemoteRefSymbolBuf {
1357                name: "foo bar".into(),
1358                remote: "bar baz".into()
1359            }))
1360        );
1361
1362        // Quoted "@" is not interpreted as a working copy or remote symbol
1363        assert_eq!(
1364            parse_into_kind(r#""@""#),
1365            Ok(ExpressionKind::String("@".to_owned()))
1366        );
1367        assert_eq!(
1368            parse_into_kind(r#""main@""#),
1369            Ok(ExpressionKind::String("main@".to_owned()))
1370        );
1371        assert_eq!(
1372            parse_into_kind(r#""main@origin""#),
1373            Ok(ExpressionKind::String("main@origin".to_owned()))
1374        );
1375
1376        // Non-ASCII name
1377        assert_eq!(
1378            parse_into_kind("柔術@"),
1379            Ok(ExpressionKind::AtWorkspace("柔術".to_owned()))
1380        );
1381        assert_eq!(
1382            parse_into_kind("柔@術"),
1383            Ok(ExpressionKind::RemoteSymbol(RemoteRefSymbolBuf {
1384                name: "柔".into(),
1385                remote: "術".into()
1386            }))
1387        );
1388    }
1389
1390    #[test]
1391    fn test_parse_function_call() {
1392        fn unwrap_function_call(node: ExpressionNode<'_>) -> Box<FunctionCallNode<'_>> {
1393            match node.kind {
1394                ExpressionKind::FunctionCall(function) => function,
1395                _ => panic!("unexpected expression: {node:?}"),
1396            }
1397        }
1398
1399        // Space is allowed around infix operators and function arguments
1400        assert_eq!(
1401            parse_normalized(
1402                "   description(  arg1 ) ~    file(  arg1 ,   arg2 )  ~ visible_heads(  )  ",
1403            ),
1404            parse_normalized("(description(arg1) ~ file(arg1, arg2)) ~ visible_heads()"),
1405        );
1406        // Space is allowed around keyword arguments
1407        assert_eq!(
1408            parse_normalized("remote_bookmarks( remote  =   foo  )"),
1409            parse_normalized("remote_bookmarks(remote=foo)"),
1410        );
1411
1412        // Trailing comma isn't allowed for empty argument
1413        assert!(parse_into_kind("bookmarks(,)").is_err());
1414        // Trailing comma is allowed for the last argument
1415        assert_eq!(
1416            parse_normalized("bookmarks(a,)"),
1417            parse_normalized("bookmarks(a)")
1418        );
1419        assert_eq!(
1420            parse_normalized("bookmarks(a ,  )"),
1421            parse_normalized("bookmarks(a)")
1422        );
1423        assert!(parse_into_kind("bookmarks(,a)").is_err());
1424        assert!(parse_into_kind("bookmarks(a,,)").is_err());
1425        assert!(parse_into_kind("bookmarks(a  , , )").is_err());
1426        assert_eq!(
1427            parse_normalized("file(a,b,)"),
1428            parse_normalized("file(a, b)")
1429        );
1430        assert!(parse_into_kind("file(a,,b)").is_err());
1431        assert_eq!(
1432            parse_normalized("remote_bookmarks(a,remote=b  , )"),
1433            parse_normalized("remote_bookmarks(a, remote=b)"),
1434        );
1435        assert!(parse_into_kind("remote_bookmarks(a,,remote=b)").is_err());
1436
1437        // Expression span
1438        let function =
1439            unwrap_function_call(parse_program("foo( a, (b) , ~(c), d = (e) )").unwrap());
1440        assert_eq!(function.name_span.as_str(), "foo");
1441        assert_eq!(function.args_span.as_str(), "a, (b) , ~(c), d = (e)");
1442        assert_eq!(function.args[0].span.as_str(), "a");
1443        assert_eq!(function.args[1].span.as_str(), "(b)");
1444        assert_eq!(function.args[2].span.as_str(), "~(c)");
1445        assert_eq!(function.keyword_args[0].name_span.as_str(), "d");
1446        assert_eq!(function.keyword_args[0].value.span.as_str(), "(e)");
1447    }
1448
1449    #[test]
1450    fn test_parse_revset_alias_symbol_decl() {
1451        let mut aliases_map = RevsetAliasesMap::new();
1452        // Working copy or remote symbol cannot be used as an alias name.
1453        assert!(aliases_map.insert("@", "none()").is_err());
1454        assert!(aliases_map.insert("a@", "none()").is_err());
1455        assert!(aliases_map.insert("a@b", "none()").is_err());
1456        // Non-ASCII character isn't allowed in alias symbol. This rule can be
1457        // relaxed if needed.
1458        assert!(aliases_map.insert("柔術", "none()").is_err());
1459    }
1460
1461    #[test]
1462    fn test_parse_revset_alias_func_decl() {
1463        let mut aliases_map = RevsetAliasesMap::new();
1464        assert!(aliases_map.insert("5func()", r#""is function 0""#).is_err());
1465        aliases_map.insert("func()", r#""is function 0""#).unwrap();
1466        aliases_map
1467            .insert("func(a, b)", r#""is function 2""#)
1468            .unwrap();
1469        aliases_map.insert("func(a)", r#""is function a""#).unwrap();
1470        aliases_map.insert("func(b)", r#""is function b""#).unwrap();
1471
1472        let (id, params, defn) = aliases_map.get_function("func", 0).unwrap();
1473        assert_eq!(id, AliasId::Function("func", &[]));
1474        assert!(params.is_empty());
1475        assert_eq!(defn, r#""is function 0""#);
1476
1477        let (id, params, defn) = aliases_map.get_function("func", 1).unwrap();
1478        assert_eq!(id, AliasId::Function("func", &["b".to_owned()]));
1479        assert_eq!(params, ["b"]);
1480        assert_eq!(defn, r#""is function b""#);
1481
1482        let (id, params, defn) = aliases_map.get_function("func", 2).unwrap();
1483        assert_eq!(
1484            id,
1485            AliasId::Function("func", &["a".to_owned(), "b".to_owned()])
1486        );
1487        assert_eq!(params, ["a", "b"]);
1488        assert_eq!(defn, r#""is function 2""#);
1489
1490        assert!(aliases_map.get_function("func", 3).is_none());
1491    }
1492
1493    #[test]
1494    fn test_parse_revset_alias_formal_parameter() {
1495        let mut aliases_map = RevsetAliasesMap::new();
1496        // Working copy or remote symbol cannot be used as an parameter name.
1497        assert!(aliases_map.insert("f(@)", "none()").is_err());
1498        assert!(aliases_map.insert("f(a@)", "none()").is_err());
1499        assert!(aliases_map.insert("f(a@b)", "none()").is_err());
1500        // Trailing comma isn't allowed for empty parameter
1501        assert!(aliases_map.insert("f(,)", "none()").is_err());
1502        // Trailing comma is allowed for the last parameter
1503        assert!(aliases_map.insert("g(a,)", "none()").is_ok());
1504        assert!(aliases_map.insert("h(a ,  )", "none()").is_ok());
1505        assert!(aliases_map.insert("i(,a)", "none()").is_err());
1506        assert!(aliases_map.insert("j(a,,)", "none()").is_err());
1507        assert!(aliases_map.insert("k(a  , , )", "none()").is_err());
1508        assert!(aliases_map.insert("l(a,b,)", "none()").is_ok());
1509        assert!(aliases_map.insert("m(a,,b)", "none()").is_err());
1510    }
1511
1512    #[test]
1513    fn test_parse_revset_compat_operator() {
1514        assert_eq!(
1515            parse_into_kind(":foo"),
1516            Err(RevsetParseErrorKind::NotPrefixOperator {
1517                op: ":".to_owned(),
1518                similar_op: "::".to_owned(),
1519                description: "ancestors".to_owned(),
1520            })
1521        );
1522        assert_eq!(
1523            parse_into_kind("foo^"),
1524            Err(RevsetParseErrorKind::NotPostfixOperator {
1525                op: "^".to_owned(),
1526                similar_op: "-".to_owned(),
1527                description: "parents".to_owned(),
1528            })
1529        );
1530        assert_eq!(
1531            parse_into_kind("foo + bar"),
1532            Err(RevsetParseErrorKind::NotInfixOperator {
1533                op: "+".to_owned(),
1534                similar_op: "|".to_owned(),
1535                description: "union".to_owned(),
1536            })
1537        );
1538        assert_eq!(
1539            parse_into_kind("foo - bar"),
1540            Err(RevsetParseErrorKind::NotInfixOperator {
1541                op: "-".to_owned(),
1542                similar_op: "~".to_owned(),
1543                description: "difference".to_owned(),
1544            })
1545        );
1546    }
1547
1548    #[test]
1549    fn test_parse_revset_operator_combinations() {
1550        // Parse repeated "parents" operator
1551        assert_eq!(parse_normalized("foo---"), parse_normalized("((foo-)-)-"));
1552        // Parse repeated "children" operator
1553        assert_eq!(parse_normalized("foo+++"), parse_normalized("((foo+)+)+"));
1554        // Set operator associativity/precedence
1555        assert_eq!(parse_normalized("~x|y"), parse_normalized("(~x)|y"));
1556        assert_eq!(parse_normalized("x&~y"), parse_normalized("x&(~y)"));
1557        assert_eq!(parse_normalized("x~~y"), parse_normalized("x~(~y)"));
1558        assert_eq!(parse_normalized("x~~~y"), parse_normalized("x~(~(~y))"));
1559        assert_eq!(parse_normalized("~x::y"), parse_normalized("~(x::y)"));
1560        assert_eq!(parse_normalized("x|y|z"), parse_normalized("(x|y)|z"));
1561        assert_eq!(parse_normalized("x&y|z"), parse_normalized("(x&y)|z"));
1562        assert_eq!(parse_normalized("x|y&z"), parse_normalized("x|(y&z)"));
1563        assert_eq!(parse_normalized("x|y~z"), parse_normalized("x|(y~z)"));
1564        assert_eq!(parse_normalized("::&.."), parse_normalized("(::)&(..)"));
1565        // Parse repeated "ancestors"/"descendants"/"dag range"/"range" operators
1566        assert_eq!(
1567            parse_into_kind("::foo::"),
1568            Err(RevsetParseErrorKind::SyntaxError)
1569        );
1570        assert_eq!(
1571            parse_into_kind(":::foo"),
1572            Err(RevsetParseErrorKind::SyntaxError)
1573        );
1574        assert_eq!(
1575            parse_into_kind("::::foo"),
1576            Err(RevsetParseErrorKind::SyntaxError)
1577        );
1578        assert_eq!(
1579            parse_into_kind("foo:::"),
1580            Err(RevsetParseErrorKind::SyntaxError)
1581        );
1582        assert_eq!(
1583            parse_into_kind("foo::::"),
1584            Err(RevsetParseErrorKind::SyntaxError)
1585        );
1586        assert_eq!(
1587            parse_into_kind("foo:::bar"),
1588            Err(RevsetParseErrorKind::SyntaxError)
1589        );
1590        assert_eq!(
1591            parse_into_kind("foo::::bar"),
1592            Err(RevsetParseErrorKind::SyntaxError)
1593        );
1594        assert_eq!(
1595            parse_into_kind("::foo::bar"),
1596            Err(RevsetParseErrorKind::SyntaxError)
1597        );
1598        assert_eq!(
1599            parse_into_kind("foo::bar::"),
1600            Err(RevsetParseErrorKind::SyntaxError)
1601        );
1602        assert_eq!(
1603            parse_into_kind("::::"),
1604            Err(RevsetParseErrorKind::SyntaxError)
1605        );
1606        assert_eq!(
1607            parse_into_kind("....foo"),
1608            Err(RevsetParseErrorKind::SyntaxError)
1609        );
1610        assert_eq!(
1611            parse_into_kind("foo...."),
1612            Err(RevsetParseErrorKind::SyntaxError)
1613        );
1614        assert_eq!(
1615            parse_into_kind("foo.....bar"),
1616            Err(RevsetParseErrorKind::SyntaxError)
1617        );
1618        assert_eq!(
1619            parse_into_kind("..foo..bar"),
1620            Err(RevsetParseErrorKind::SyntaxError)
1621        );
1622        assert_eq!(
1623            parse_into_kind("foo..bar.."),
1624            Err(RevsetParseErrorKind::SyntaxError)
1625        );
1626        assert_eq!(
1627            parse_into_kind("...."),
1628            Err(RevsetParseErrorKind::SyntaxError)
1629        );
1630        assert_eq!(
1631            parse_into_kind("::.."),
1632            Err(RevsetParseErrorKind::SyntaxError)
1633        );
1634        // Parse combinations of "parents"/"children" operators and the range operators.
1635        // The former bind more strongly.
1636        assert_eq!(parse_normalized("foo-+"), parse_normalized("(foo-)+"));
1637        assert_eq!(parse_normalized("foo-::"), parse_normalized("(foo-)::"));
1638        assert_eq!(parse_normalized("::foo+"), parse_normalized("::(foo+)"));
1639        assert_eq!(
1640            parse_into_kind("::-"),
1641            Err(RevsetParseErrorKind::SyntaxError)
1642        );
1643        assert_eq!(
1644            parse_into_kind("..+"),
1645            Err(RevsetParseErrorKind::SyntaxError)
1646        );
1647    }
1648
1649    #[test]
1650    fn test_parse_revset_function() {
1651        assert_matches!(
1652            parse_into_kind("parents(foo)"),
1653            Ok(ExpressionKind::FunctionCall(_))
1654        );
1655        assert_eq!(
1656            parse_normalized("parents((foo))"),
1657            parse_normalized("parents(foo)"),
1658        );
1659        assert_eq!(
1660            parse_into_kind("parents(foo"),
1661            Err(RevsetParseErrorKind::SyntaxError)
1662        );
1663    }
1664
1665    #[test]
1666    fn test_expand_symbol_alias() {
1667        assert_eq!(
1668            with_aliases([("AB", "a&b")]).parse_normalized("AB|c"),
1669            parse_normalized("(a&b)|c")
1670        );
1671        assert_eq!(
1672            with_aliases([("AB", "a|b")]).parse_normalized("AB::heads(AB)"),
1673            parse_normalized("(a|b)::heads(a|b)")
1674        );
1675
1676        // Not string substitution 'a&b|c', but tree substitution.
1677        assert_eq!(
1678            with_aliases([("BC", "b|c")]).parse_normalized("a&BC"),
1679            parse_normalized("a&(b|c)")
1680        );
1681
1682        // String literal should not be substituted with alias.
1683        assert_eq!(
1684            with_aliases([("A", "a")]).parse_normalized(r#"A|"A"|'A'"#),
1685            parse_normalized("a|'A'|'A'")
1686        );
1687
1688        // Part of string pattern cannot be substituted.
1689        assert_eq!(
1690            with_aliases([("A", "a")]).parse_normalized("author(exact:A)"),
1691            parse_normalized("author(exact:A)")
1692        );
1693
1694        // Part of @ symbol cannot be substituted.
1695        assert_eq!(
1696            with_aliases([("A", "a")]).parse_normalized("A@"),
1697            parse_normalized("A@")
1698        );
1699        assert_eq!(
1700            with_aliases([("A", "a")]).parse_normalized("A@b"),
1701            parse_normalized("A@b")
1702        );
1703        assert_eq!(
1704            with_aliases([("B", "b")]).parse_normalized("a@B"),
1705            parse_normalized("a@B")
1706        );
1707
1708        // Modifier cannot be substituted.
1709        assert_eq!(
1710            with_aliases([("all", "ALL")]).parse_normalized("all:all"),
1711            parse_normalized("all:ALL")
1712        );
1713
1714        // Top-level alias can be substituted to modifier expression.
1715        assert_eq!(
1716            with_aliases([("A", "all:a")]).parse_normalized("A"),
1717            parse_normalized("all:a")
1718        );
1719
1720        // Multi-level substitution.
1721        assert_eq!(
1722            with_aliases([("A", "BC"), ("BC", "b|C"), ("C", "c")]).parse_normalized("A"),
1723            parse_normalized("b|c")
1724        );
1725
1726        // Infinite recursion, where the top-level error isn't of RecursiveAlias kind.
1727        assert_eq!(
1728            *with_aliases([("A", "A")]).parse("A").unwrap_err().kind,
1729            RevsetParseErrorKind::InAliasExpansion("A".to_owned())
1730        );
1731        assert_eq!(
1732            *with_aliases([("A", "B"), ("B", "b|C"), ("C", "c|B")])
1733                .parse("A")
1734                .unwrap_err()
1735                .kind,
1736            RevsetParseErrorKind::InAliasExpansion("A".to_owned())
1737        );
1738
1739        // Error in alias definition.
1740        assert_eq!(
1741            *with_aliases([("A", "a(")]).parse("A").unwrap_err().kind,
1742            RevsetParseErrorKind::InAliasExpansion("A".to_owned())
1743        );
1744    }
1745
1746    #[test]
1747    fn test_expand_function_alias() {
1748        assert_eq!(
1749            with_aliases([("F(  )", "a")]).parse_normalized("F()"),
1750            parse_normalized("a")
1751        );
1752        assert_eq!(
1753            with_aliases([("F( x  )", "x")]).parse_normalized("F(a)"),
1754            parse_normalized("a")
1755        );
1756        assert_eq!(
1757            with_aliases([("F( x,  y )", "x|y")]).parse_normalized("F(a, b)"),
1758            parse_normalized("a|b")
1759        );
1760
1761        // Not recursion because functions are overloaded by arity.
1762        assert_eq!(
1763            with_aliases([("F(x)", "F(x,b)"), ("F(x,y)", "x|y")]).parse_normalized("F(a)"),
1764            parse_normalized("a|b")
1765        );
1766
1767        // Arguments should be resolved in the current scope.
1768        assert_eq!(
1769            with_aliases([("F(x,y)", "x|y")]).parse_normalized("F(a::y,b::x)"),
1770            parse_normalized("(a::y)|(b::x)")
1771        );
1772        // F(a) -> G(a)&y -> (x|a)&y
1773        assert_eq!(
1774            with_aliases([("F(x)", "G(x)&y"), ("G(y)", "x|y")]).parse_normalized("F(a)"),
1775            parse_normalized("(x|a)&y")
1776        );
1777        // F(G(a)) -> F(x|a) -> G(x|a)&y -> (x|(x|a))&y
1778        assert_eq!(
1779            with_aliases([("F(x)", "G(x)&y"), ("G(y)", "x|y")]).parse_normalized("F(G(a))"),
1780            parse_normalized("(x|(x|a))&y")
1781        );
1782
1783        // Function parameter should precede the symbol alias.
1784        assert_eq!(
1785            with_aliases([("F(X)", "X"), ("X", "x")]).parse_normalized("F(a)|X"),
1786            parse_normalized("a|x")
1787        );
1788
1789        // Function parameter shouldn't be expanded in symbol alias.
1790        assert_eq!(
1791            with_aliases([("F(x)", "x|A"), ("A", "x")]).parse_normalized("F(a)"),
1792            parse_normalized("a|x")
1793        );
1794
1795        // String literal should not be substituted with function parameter.
1796        assert_eq!(
1797            with_aliases([("F(x)", r#"x|"x""#)]).parse_normalized("F(a)"),
1798            parse_normalized("a|'x'")
1799        );
1800
1801        // Modifier expression body as parameter.
1802        assert_eq!(
1803            with_aliases([("F(x)", "all:x")]).parse_normalized("F(a|b)"),
1804            parse_normalized("all:(a|b)")
1805        );
1806
1807        // Function and symbol aliases reside in separate namespaces.
1808        assert_eq!(
1809            with_aliases([("A()", "A"), ("A", "a")]).parse_normalized("A()"),
1810            parse_normalized("a")
1811        );
1812
1813        // Invalid number of arguments.
1814        assert_eq!(
1815            *with_aliases([("F()", "x")]).parse("F(a)").unwrap_err().kind,
1816            RevsetParseErrorKind::InvalidFunctionArguments {
1817                name: "F".to_owned(),
1818                message: "Expected 0 arguments".to_owned()
1819            }
1820        );
1821        assert_eq!(
1822            *with_aliases([("F(x)", "x")]).parse("F()").unwrap_err().kind,
1823            RevsetParseErrorKind::InvalidFunctionArguments {
1824                name: "F".to_owned(),
1825                message: "Expected 1 arguments".to_owned()
1826            }
1827        );
1828        assert_eq!(
1829            *with_aliases([("F(x,y)", "x|y")])
1830                .parse("F(a,b,c)")
1831                .unwrap_err()
1832                .kind,
1833            RevsetParseErrorKind::InvalidFunctionArguments {
1834                name: "F".to_owned(),
1835                message: "Expected 2 arguments".to_owned()
1836            }
1837        );
1838        assert_eq!(
1839            *with_aliases([("F(x)", "x"), ("F(x,y)", "x|y")])
1840                .parse("F()")
1841                .unwrap_err()
1842                .kind,
1843            RevsetParseErrorKind::InvalidFunctionArguments {
1844                name: "F".to_owned(),
1845                message: "Expected 1 to 2 arguments".to_owned()
1846            }
1847        );
1848        assert_eq!(
1849            *with_aliases([("F()", "x"), ("F(x,y)", "x|y")])
1850                .parse("F(a)")
1851                .unwrap_err()
1852                .kind,
1853            RevsetParseErrorKind::InvalidFunctionArguments {
1854                name: "F".to_owned(),
1855                message: "Expected 0, 2 arguments".to_owned()
1856            }
1857        );
1858
1859        // Keyword argument isn't supported for now.
1860        assert_eq!(
1861            *with_aliases([("F(x)", "x")])
1862                .parse("F(x=y)")
1863                .unwrap_err()
1864                .kind,
1865            RevsetParseErrorKind::InvalidFunctionArguments {
1866                name: "F".to_owned(),
1867                message: "Unexpected keyword arguments".to_owned()
1868            }
1869        );
1870
1871        // Infinite recursion, where the top-level error isn't of RecursiveAlias kind.
1872        assert_eq!(
1873            *with_aliases([("F(x)", "G(x)"), ("G(x)", "H(x)"), ("H(x)", "F(x)")])
1874                .parse("F(a)")
1875                .unwrap_err()
1876                .kind,
1877            RevsetParseErrorKind::InAliasExpansion("F(x)".to_owned())
1878        );
1879        assert_eq!(
1880            *with_aliases([("F(x)", "F(x,b)"), ("F(x,y)", "F(x|y)")])
1881                .parse("F(a)")
1882                .unwrap_err()
1883                .kind,
1884            RevsetParseErrorKind::InAliasExpansion("F(x)".to_owned())
1885        );
1886    }
1887
1888    #[test]
1889    fn test_expand_with_locals() {
1890        // Local variable should precede the symbol alias.
1891        assert_eq!(
1892            with_aliases([("A", "symbol")])
1893                .set_local("A", "local")
1894                .parse_normalized("A"),
1895            parse_normalized("local")
1896        );
1897
1898        // Local variable shouldn't be expanded within aliases.
1899        assert_eq!(
1900            with_aliases([("B", "A"), ("F(x)", "x&A")])
1901                .set_local("A", "a")
1902                .parse_normalized("A|B|F(A)"),
1903            parse_normalized("a|A|(a&A)")
1904        );
1905    }
1906}