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