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
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        // Space is allowed around infix operators and function arguments
1077        assert_eq!(
1078            parse_normalized(
1079                "   description(  arg1 ) ~    file(  arg1 ,   arg2 )  ~ visible_heads(  )  ",
1080            ),
1081            parse_normalized("(description(arg1) ~ file(arg1, arg2)) ~ visible_heads()"),
1082        );
1083        // Space is allowed around keyword arguments
1084        assert_eq!(
1085            parse_normalized("remote_bookmarks( remote  =   foo  )"),
1086            parse_normalized("remote_bookmarks(remote=foo)"),
1087        );
1088
1089        // Trailing comma isn't allowed for empty argument
1090        assert!(parse_into_kind("bookmarks(,)").is_err());
1091        // Trailing comma is allowed for the last argument
1092        assert_eq!(
1093            parse_normalized("bookmarks(a,)"),
1094            parse_normalized("bookmarks(a)")
1095        );
1096        assert_eq!(
1097            parse_normalized("bookmarks(a ,  )"),
1098            parse_normalized("bookmarks(a)")
1099        );
1100        assert!(parse_into_kind("bookmarks(,a)").is_err());
1101        assert!(parse_into_kind("bookmarks(a,,)").is_err());
1102        assert!(parse_into_kind("bookmarks(a  , , )").is_err());
1103        assert_eq!(
1104            parse_normalized("file(a,b,)"),
1105            parse_normalized("file(a, b)")
1106        );
1107        assert!(parse_into_kind("file(a,,b)").is_err());
1108        assert_eq!(
1109            parse_normalized("remote_bookmarks(a,remote=b  , )"),
1110            parse_normalized("remote_bookmarks(a, remote=b)"),
1111        );
1112        assert!(parse_into_kind("remote_bookmarks(a,,remote=b)").is_err());
1113    }
1114
1115    #[test]
1116    fn test_parse_revset_with_modifier() {
1117        // all: is a program modifier, but all:: isn't
1118        assert_eq!(
1119            parse_into_kind("all:"),
1120            Err(RevsetParseErrorKind::SyntaxError)
1121        );
1122        assert_matches!(
1123            parse_into_kind("all:foo"),
1124            Ok(ExpressionKind::Modifier(modifier)) if modifier.name == "all"
1125        );
1126        assert_matches!(
1127            parse_into_kind("all::"),
1128            Ok(ExpressionKind::Unary(UnaryOp::DagRangePost, _))
1129        );
1130        assert_matches!(
1131            parse_into_kind("all::foo"),
1132            Ok(ExpressionKind::Binary(BinaryOp::DagRange, _, _))
1133        );
1134
1135        // all::: could be parsed as all:(::), but rejected for simplicity
1136        assert_eq!(
1137            parse_into_kind("all:::"),
1138            Err(RevsetParseErrorKind::SyntaxError)
1139        );
1140        assert_eq!(
1141            parse_into_kind("all:::foo"),
1142            Err(RevsetParseErrorKind::SyntaxError)
1143        );
1144
1145        assert_eq!(parse_normalized("all:(foo)"), parse_normalized("all:foo"));
1146        assert_eq!(
1147            parse_normalized("all:all::foo"),
1148            parse_normalized("all:(all::foo)"),
1149        );
1150        assert_eq!(
1151            parse_normalized("all:all | foo"),
1152            parse_normalized("all:(all | foo)"),
1153        );
1154
1155        assert_eq!(
1156            parse_normalized("all: ::foo"),
1157            parse_normalized("all:(::foo)"),
1158        );
1159        assert_eq!(parse_normalized(" all: foo"), parse_normalized("all:foo"));
1160        assert_eq!(
1161            parse_into_kind("(all:foo)"),
1162            Ok(ExpressionKind::StringPattern {
1163                kind: "all",
1164                value: "foo".to_owned()
1165            })
1166        );
1167        assert_matches!(
1168            parse_into_kind("all :foo"),
1169            Err(RevsetParseErrorKind::SyntaxError)
1170        );
1171        assert_eq!(
1172            parse_normalized("all:all:all"),
1173            parse_normalized("all:(all:all)"),
1174        );
1175    }
1176
1177    #[test]
1178    fn test_parse_whitespace() {
1179        let ascii_whitespaces: String = ('\x00'..='\x7f')
1180            .filter(char::is_ascii_whitespace)
1181            .collect();
1182        assert_eq!(
1183            parse_normalized(&format!("{ascii_whitespaces}all()")),
1184            parse_normalized("all()"),
1185        );
1186    }
1187
1188    #[test]
1189    fn test_parse_identifier() {
1190        // Integer is a symbol
1191        assert_eq!(parse_into_kind("0"), Ok(ExpressionKind::Identifier("0")));
1192        // Tag/bookmark name separated by /
1193        assert_eq!(
1194            parse_into_kind("foo_bar/baz"),
1195            Ok(ExpressionKind::Identifier("foo_bar/baz"))
1196        );
1197
1198        // Internal '.', '-', and '+' are allowed
1199        assert_eq!(
1200            parse_into_kind("foo.bar-v1+7"),
1201            Ok(ExpressionKind::Identifier("foo.bar-v1+7"))
1202        );
1203        assert_eq!(
1204            parse_normalized("foo.bar-v1+7-"),
1205            parse_normalized("(foo.bar-v1+7)-")
1206        );
1207        // '.' is not allowed at the beginning or end
1208        assert_eq!(
1209            parse_into_kind(".foo"),
1210            Err(RevsetParseErrorKind::SyntaxError)
1211        );
1212        assert_eq!(
1213            parse_into_kind("foo."),
1214            Err(RevsetParseErrorKind::SyntaxError)
1215        );
1216        // Multiple '.', '-', '+' are not allowed
1217        assert_eq!(
1218            parse_into_kind("foo.+bar"),
1219            Err(RevsetParseErrorKind::SyntaxError)
1220        );
1221        assert_eq!(
1222            parse_into_kind("foo--bar"),
1223            Err(RevsetParseErrorKind::SyntaxError)
1224        );
1225        assert_eq!(
1226            parse_into_kind("foo+-bar"),
1227            Err(RevsetParseErrorKind::SyntaxError)
1228        );
1229
1230        // Parse a parenthesized symbol
1231        assert_eq!(parse_normalized("(foo)"), parse_normalized("foo"));
1232
1233        // Non-ASCII tag/bookmark name
1234        assert_eq!(
1235            parse_into_kind("柔術+jj"),
1236            Ok(ExpressionKind::Identifier("柔術+jj"))
1237        );
1238    }
1239
1240    #[test]
1241    fn test_parse_string_literal() {
1242        // "\<char>" escapes
1243        assert_eq!(
1244            parse_into_kind(r#" "\t\r\n\"\\\0\e" "#),
1245            Ok(ExpressionKind::String("\t\r\n\"\\\0\u{1b}".to_owned()))
1246        );
1247
1248        // Invalid "\<char>" escape
1249        assert_eq!(
1250            parse_into_kind(r#" "\y" "#),
1251            Err(RevsetParseErrorKind::SyntaxError)
1252        );
1253
1254        // Single-quoted raw string
1255        assert_eq!(
1256            parse_into_kind(r#" '' "#),
1257            Ok(ExpressionKind::String("".to_owned()))
1258        );
1259        assert_eq!(
1260            parse_into_kind(r#" 'a\n' "#),
1261            Ok(ExpressionKind::String(r"a\n".to_owned()))
1262        );
1263        assert_eq!(
1264            parse_into_kind(r#" '\' "#),
1265            Ok(ExpressionKind::String(r"\".to_owned()))
1266        );
1267        assert_eq!(
1268            parse_into_kind(r#" '"' "#),
1269            Ok(ExpressionKind::String(r#"""#.to_owned()))
1270        );
1271
1272        // Hex bytes
1273        assert_eq!(
1274            parse_into_kind(r#""\x61\x65\x69\x6f\x75""#),
1275            Ok(ExpressionKind::String("aeiou".to_owned()))
1276        );
1277        assert_eq!(
1278            parse_into_kind(r#""\xe0\xe8\xec\xf0\xf9""#),
1279            Ok(ExpressionKind::String("àèìðù".to_owned()))
1280        );
1281        assert_eq!(
1282            parse_into_kind(r#""\x""#),
1283            Err(RevsetParseErrorKind::SyntaxError)
1284        );
1285        assert_eq!(
1286            parse_into_kind(r#""\xf""#),
1287            Err(RevsetParseErrorKind::SyntaxError)
1288        );
1289        assert_eq!(
1290            parse_into_kind(r#""\xgg""#),
1291            Err(RevsetParseErrorKind::SyntaxError)
1292        );
1293    }
1294
1295    #[test]
1296    fn test_parse_string_pattern() {
1297        assert_eq!(
1298            parse_into_kind(r#"(substring:"foo")"#),
1299            Ok(ExpressionKind::StringPattern {
1300                kind: "substring",
1301                value: "foo".to_owned()
1302            })
1303        );
1304        assert_eq!(
1305            parse_into_kind(r#"("exact:foo")"#),
1306            Ok(ExpressionKind::String("exact:foo".to_owned()))
1307        );
1308        assert_eq!(
1309            parse_normalized(r#"(exact:"foo" )"#),
1310            parse_normalized(r#"(exact:"foo")"#),
1311        );
1312        assert_eq!(
1313            parse_into_kind(r#"(exact:'\')"#),
1314            Ok(ExpressionKind::StringPattern {
1315                kind: "exact",
1316                value: r"\".to_owned()
1317            })
1318        );
1319        assert_matches!(
1320            parse_into_kind(r#"(exact:("foo" ))"#),
1321            Err(RevsetParseErrorKind::NotInfixOperator { .. })
1322        );
1323    }
1324
1325    #[test]
1326    fn test_parse_symbol_explicitly() {
1327        assert_matches!(parse_symbol("").as_deref(), Err(_));
1328        // empty string could be a valid ref name, but it would be super
1329        // confusing if identifier was empty.
1330        assert_matches!(parse_symbol("''").as_deref(), Err(_));
1331
1332        assert_matches!(parse_symbol("foo.bar").as_deref(), Ok("foo.bar"));
1333        assert_matches!(parse_symbol("foo@bar").as_deref(), Err(_));
1334        assert_matches!(parse_symbol("foo bar").as_deref(), Err(_));
1335
1336        assert_matches!(parse_symbol("'foo bar'").as_deref(), Ok("foo bar"));
1337        assert_matches!(parse_symbol(r#""foo\tbar""#).as_deref(), Ok("foo\tbar"));
1338
1339        // leading/trailing whitespace is NOT ignored.
1340        assert_matches!(parse_symbol(" foo").as_deref(), Err(_));
1341        assert_matches!(parse_symbol("foo ").as_deref(), Err(_));
1342
1343        // (foo) could be parsed as a symbol "foo", but is rejected because user
1344        // might expect a literal "(foo)".
1345        assert_matches!(parse_symbol("(foo)").as_deref(), Err(_));
1346    }
1347
1348    #[test]
1349    fn parse_at_workspace_and_remote_symbol() {
1350        // Parse "@" (the current working copy)
1351        assert_eq!(parse_into_kind("@"), Ok(ExpressionKind::AtCurrentWorkspace));
1352        assert_eq!(
1353            parse_into_kind("main@"),
1354            Ok(ExpressionKind::AtWorkspace("main".to_owned()))
1355        );
1356        assert_eq!(
1357            parse_into_kind("main@origin"),
1358            Ok(ExpressionKind::RemoteSymbol(RemoteRefSymbolBuf {
1359                name: "main".into(),
1360                remote: "origin".into()
1361            }))
1362        );
1363
1364        // Quoted component in @ expression
1365        assert_eq!(
1366            parse_into_kind(r#""foo bar"@"#),
1367            Ok(ExpressionKind::AtWorkspace("foo bar".to_owned()))
1368        );
1369        assert_eq!(
1370            parse_into_kind(r#""foo bar"@origin"#),
1371            Ok(ExpressionKind::RemoteSymbol(RemoteRefSymbolBuf {
1372                name: "foo bar".into(),
1373                remote: "origin".into()
1374            }))
1375        );
1376        assert_eq!(
1377            parse_into_kind(r#"main@"foo bar""#),
1378            Ok(ExpressionKind::RemoteSymbol(RemoteRefSymbolBuf {
1379                name: "main".into(),
1380                remote: "foo bar".into()
1381            }))
1382        );
1383        assert_eq!(
1384            parse_into_kind(r#"'foo bar'@'bar baz'"#),
1385            Ok(ExpressionKind::RemoteSymbol(RemoteRefSymbolBuf {
1386                name: "foo bar".into(),
1387                remote: "bar baz".into()
1388            }))
1389        );
1390
1391        // Quoted "@" is not interpreted as a working copy or remote symbol
1392        assert_eq!(
1393            parse_into_kind(r#""@""#),
1394            Ok(ExpressionKind::String("@".to_owned()))
1395        );
1396        assert_eq!(
1397            parse_into_kind(r#""main@""#),
1398            Ok(ExpressionKind::String("main@".to_owned()))
1399        );
1400        assert_eq!(
1401            parse_into_kind(r#""main@origin""#),
1402            Ok(ExpressionKind::String("main@origin".to_owned()))
1403        );
1404
1405        // Non-ASCII name
1406        assert_eq!(
1407            parse_into_kind("柔術@"),
1408            Ok(ExpressionKind::AtWorkspace("柔術".to_owned()))
1409        );
1410        assert_eq!(
1411            parse_into_kind("柔@術"),
1412            Ok(ExpressionKind::RemoteSymbol(RemoteRefSymbolBuf {
1413                name: "柔".into(),
1414                remote: "術".into()
1415            }))
1416        );
1417    }
1418
1419    #[test]
1420    fn test_parse_revset_alias_symbol_decl() {
1421        let mut aliases_map = RevsetAliasesMap::new();
1422        // Working copy or remote symbol cannot be used as an alias name.
1423        assert!(aliases_map.insert("@", "none()").is_err());
1424        assert!(aliases_map.insert("a@", "none()").is_err());
1425        assert!(aliases_map.insert("a@b", "none()").is_err());
1426        // Non-ASCII character isn't allowed in alias symbol. This rule can be
1427        // relaxed if needed.
1428        assert!(aliases_map.insert("柔術", "none()").is_err());
1429    }
1430
1431    #[test]
1432    fn test_parse_revset_alias_func_decl() {
1433        let mut aliases_map = RevsetAliasesMap::new();
1434        assert!(aliases_map.insert("5func()", r#""is function 0""#).is_err());
1435        aliases_map.insert("func()", r#""is function 0""#).unwrap();
1436        aliases_map
1437            .insert("func(a, b)", r#""is function 2""#)
1438            .unwrap();
1439        aliases_map.insert("func(a)", r#""is function a""#).unwrap();
1440        aliases_map.insert("func(b)", r#""is function b""#).unwrap();
1441
1442        let (id, params, defn) = aliases_map.get_function("func", 0).unwrap();
1443        assert_eq!(id, AliasId::Function("func", &[]));
1444        assert!(params.is_empty());
1445        assert_eq!(defn, r#""is function 0""#);
1446
1447        let (id, params, defn) = aliases_map.get_function("func", 1).unwrap();
1448        assert_eq!(id, AliasId::Function("func", &["b".to_owned()]));
1449        assert_eq!(params, ["b"]);
1450        assert_eq!(defn, r#""is function b""#);
1451
1452        let (id, params, defn) = aliases_map.get_function("func", 2).unwrap();
1453        assert_eq!(
1454            id,
1455            AliasId::Function("func", &["a".to_owned(), "b".to_owned()])
1456        );
1457        assert_eq!(params, ["a", "b"]);
1458        assert_eq!(defn, r#""is function 2""#);
1459
1460        assert!(aliases_map.get_function("func", 3).is_none());
1461    }
1462
1463    #[test]
1464    fn test_parse_revset_alias_formal_parameter() {
1465        let mut aliases_map = RevsetAliasesMap::new();
1466        // Working copy or remote symbol cannot be used as an parameter name.
1467        assert!(aliases_map.insert("f(@)", "none()").is_err());
1468        assert!(aliases_map.insert("f(a@)", "none()").is_err());
1469        assert!(aliases_map.insert("f(a@b)", "none()").is_err());
1470        // Trailing comma isn't allowed for empty parameter
1471        assert!(aliases_map.insert("f(,)", "none()").is_err());
1472        // Trailing comma is allowed for the last parameter
1473        assert!(aliases_map.insert("g(a,)", "none()").is_ok());
1474        assert!(aliases_map.insert("h(a ,  )", "none()").is_ok());
1475        assert!(aliases_map.insert("i(,a)", "none()").is_err());
1476        assert!(aliases_map.insert("j(a,,)", "none()").is_err());
1477        assert!(aliases_map.insert("k(a  , , )", "none()").is_err());
1478        assert!(aliases_map.insert("l(a,b,)", "none()").is_ok());
1479        assert!(aliases_map.insert("m(a,,b)", "none()").is_err());
1480    }
1481
1482    #[test]
1483    fn test_parse_revset_compat_operator() {
1484        assert_eq!(
1485            parse_into_kind(":foo"),
1486            Err(RevsetParseErrorKind::NotPrefixOperator {
1487                op: ":".to_owned(),
1488                similar_op: "::".to_owned(),
1489                description: "ancestors".to_owned(),
1490            })
1491        );
1492        assert_eq!(
1493            parse_into_kind("foo^"),
1494            Err(RevsetParseErrorKind::NotPostfixOperator {
1495                op: "^".to_owned(),
1496                similar_op: "-".to_owned(),
1497                description: "parents".to_owned(),
1498            })
1499        );
1500        assert_eq!(
1501            parse_into_kind("foo + bar"),
1502            Err(RevsetParseErrorKind::NotInfixOperator {
1503                op: "+".to_owned(),
1504                similar_op: "|".to_owned(),
1505                description: "union".to_owned(),
1506            })
1507        );
1508        assert_eq!(
1509            parse_into_kind("foo - bar"),
1510            Err(RevsetParseErrorKind::NotInfixOperator {
1511                op: "-".to_owned(),
1512                similar_op: "~".to_owned(),
1513                description: "difference".to_owned(),
1514            })
1515        );
1516    }
1517
1518    #[test]
1519    fn test_parse_revset_operator_combinations() {
1520        // Parse repeated "parents" operator
1521        assert_eq!(parse_normalized("foo---"), parse_normalized("((foo-)-)-"));
1522        // Parse repeated "children" operator
1523        assert_eq!(parse_normalized("foo+++"), parse_normalized("((foo+)+)+"));
1524        // Set operator associativity/precedence
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"), parse_normalized("x~(~y)"));
1528        assert_eq!(parse_normalized("x~~~y"), parse_normalized("x~(~(~y))"));
1529        assert_eq!(parse_normalized("~x::y"), parse_normalized("~(x::y)"));
1530        assert_eq!(parse_normalized("x|y|z"), parse_normalized("(x|y)|z"));
1531        assert_eq!(parse_normalized("x&y|z"), parse_normalized("(x&y)|z"));
1532        assert_eq!(parse_normalized("x|y&z"), parse_normalized("x|(y&z)"));
1533        assert_eq!(parse_normalized("x|y~z"), parse_normalized("x|(y~z)"));
1534        assert_eq!(parse_normalized("::&.."), parse_normalized("(::)&(..)"));
1535        // Parse repeated "ancestors"/"descendants"/"dag range"/"range" operators
1536        assert_eq!(
1537            parse_into_kind("::foo::"),
1538            Err(RevsetParseErrorKind::SyntaxError)
1539        );
1540        assert_eq!(
1541            parse_into_kind(":::foo"),
1542            Err(RevsetParseErrorKind::SyntaxError)
1543        );
1544        assert_eq!(
1545            parse_into_kind("::::foo"),
1546            Err(RevsetParseErrorKind::SyntaxError)
1547        );
1548        assert_eq!(
1549            parse_into_kind("foo:::"),
1550            Err(RevsetParseErrorKind::SyntaxError)
1551        );
1552        assert_eq!(
1553            parse_into_kind("foo::::"),
1554            Err(RevsetParseErrorKind::SyntaxError)
1555        );
1556        assert_eq!(
1557            parse_into_kind("foo:::bar"),
1558            Err(RevsetParseErrorKind::SyntaxError)
1559        );
1560        assert_eq!(
1561            parse_into_kind("foo::::bar"),
1562            Err(RevsetParseErrorKind::SyntaxError)
1563        );
1564        assert_eq!(
1565            parse_into_kind("::foo::bar"),
1566            Err(RevsetParseErrorKind::SyntaxError)
1567        );
1568        assert_eq!(
1569            parse_into_kind("foo::bar::"),
1570            Err(RevsetParseErrorKind::SyntaxError)
1571        );
1572        assert_eq!(
1573            parse_into_kind("::::"),
1574            Err(RevsetParseErrorKind::SyntaxError)
1575        );
1576        assert_eq!(
1577            parse_into_kind("....foo"),
1578            Err(RevsetParseErrorKind::SyntaxError)
1579        );
1580        assert_eq!(
1581            parse_into_kind("foo...."),
1582            Err(RevsetParseErrorKind::SyntaxError)
1583        );
1584        assert_eq!(
1585            parse_into_kind("foo.....bar"),
1586            Err(RevsetParseErrorKind::SyntaxError)
1587        );
1588        assert_eq!(
1589            parse_into_kind("..foo..bar"),
1590            Err(RevsetParseErrorKind::SyntaxError)
1591        );
1592        assert_eq!(
1593            parse_into_kind("foo..bar.."),
1594            Err(RevsetParseErrorKind::SyntaxError)
1595        );
1596        assert_eq!(
1597            parse_into_kind("...."),
1598            Err(RevsetParseErrorKind::SyntaxError)
1599        );
1600        assert_eq!(
1601            parse_into_kind("::.."),
1602            Err(RevsetParseErrorKind::SyntaxError)
1603        );
1604        // Parse combinations of "parents"/"children" operators and the range operators.
1605        // The former bind more strongly.
1606        assert_eq!(parse_normalized("foo-+"), parse_normalized("(foo-)+"));
1607        assert_eq!(parse_normalized("foo-::"), parse_normalized("(foo-)::"));
1608        assert_eq!(parse_normalized("::foo+"), parse_normalized("::(foo+)"));
1609        assert_eq!(
1610            parse_into_kind("::-"),
1611            Err(RevsetParseErrorKind::SyntaxError)
1612        );
1613        assert_eq!(
1614            parse_into_kind("..+"),
1615            Err(RevsetParseErrorKind::SyntaxError)
1616        );
1617    }
1618
1619    #[test]
1620    fn test_parse_revset_function() {
1621        assert_matches!(
1622            parse_into_kind("parents(foo)"),
1623            Ok(ExpressionKind::FunctionCall(_))
1624        );
1625        assert_eq!(
1626            parse_normalized("parents((foo))"),
1627            parse_normalized("parents(foo)"),
1628        );
1629        assert_eq!(
1630            parse_into_kind("parents(foo"),
1631            Err(RevsetParseErrorKind::SyntaxError)
1632        );
1633    }
1634
1635    #[test]
1636    fn test_expand_symbol_alias() {
1637        assert_eq!(
1638            with_aliases([("AB", "a&b")]).parse_normalized("AB|c"),
1639            parse_normalized("(a&b)|c")
1640        );
1641        assert_eq!(
1642            with_aliases([("AB", "a|b")]).parse_normalized("AB::heads(AB)"),
1643            parse_normalized("(a|b)::heads(a|b)")
1644        );
1645
1646        // Not string substitution 'a&b|c', but tree substitution.
1647        assert_eq!(
1648            with_aliases([("BC", "b|c")]).parse_normalized("a&BC"),
1649            parse_normalized("a&(b|c)")
1650        );
1651
1652        // String literal should not be substituted with alias.
1653        assert_eq!(
1654            with_aliases([("A", "a")]).parse_normalized(r#"A|"A"|'A'"#),
1655            parse_normalized("a|'A'|'A'")
1656        );
1657
1658        // Part of string pattern cannot be substituted.
1659        assert_eq!(
1660            with_aliases([("A", "a")]).parse_normalized("author(exact:A)"),
1661            parse_normalized("author(exact:A)")
1662        );
1663
1664        // Part of @ symbol cannot be substituted.
1665        assert_eq!(
1666            with_aliases([("A", "a")]).parse_normalized("A@"),
1667            parse_normalized("A@")
1668        );
1669        assert_eq!(
1670            with_aliases([("A", "a")]).parse_normalized("A@b"),
1671            parse_normalized("A@b")
1672        );
1673        assert_eq!(
1674            with_aliases([("B", "b")]).parse_normalized("a@B"),
1675            parse_normalized("a@B")
1676        );
1677
1678        // Modifier cannot be substituted.
1679        assert_eq!(
1680            with_aliases([("all", "ALL")]).parse_normalized("all:all"),
1681            parse_normalized("all:ALL")
1682        );
1683
1684        // Top-level alias can be substituted to modifier expression.
1685        assert_eq!(
1686            with_aliases([("A", "all:a")]).parse_normalized("A"),
1687            parse_normalized("all:a")
1688        );
1689
1690        // Multi-level substitution.
1691        assert_eq!(
1692            with_aliases([("A", "BC"), ("BC", "b|C"), ("C", "c")]).parse_normalized("A"),
1693            parse_normalized("b|c")
1694        );
1695
1696        // Infinite recursion, where the top-level error isn't of RecursiveAlias kind.
1697        assert_eq!(
1698            *with_aliases([("A", "A")]).parse("A").unwrap_err().kind,
1699            RevsetParseErrorKind::InAliasExpansion("A".to_owned())
1700        );
1701        assert_eq!(
1702            *with_aliases([("A", "B"), ("B", "b|C"), ("C", "c|B")])
1703                .parse("A")
1704                .unwrap_err()
1705                .kind,
1706            RevsetParseErrorKind::InAliasExpansion("A".to_owned())
1707        );
1708
1709        // Error in alias definition.
1710        assert_eq!(
1711            *with_aliases([("A", "a(")]).parse("A").unwrap_err().kind,
1712            RevsetParseErrorKind::InAliasExpansion("A".to_owned())
1713        );
1714    }
1715
1716    #[test]
1717    fn test_expand_function_alias() {
1718        assert_eq!(
1719            with_aliases([("F(  )", "a")]).parse_normalized("F()"),
1720            parse_normalized("a")
1721        );
1722        assert_eq!(
1723            with_aliases([("F( x  )", "x")]).parse_normalized("F(a)"),
1724            parse_normalized("a")
1725        );
1726        assert_eq!(
1727            with_aliases([("F( x,  y )", "x|y")]).parse_normalized("F(a, b)"),
1728            parse_normalized("a|b")
1729        );
1730
1731        // Not recursion because functions are overloaded by arity.
1732        assert_eq!(
1733            with_aliases([("F(x)", "F(x,b)"), ("F(x,y)", "x|y")]).parse_normalized("F(a)"),
1734            parse_normalized("a|b")
1735        );
1736
1737        // Arguments should be resolved in the current scope.
1738        assert_eq!(
1739            with_aliases([("F(x,y)", "x|y")]).parse_normalized("F(a::y,b::x)"),
1740            parse_normalized("(a::y)|(b::x)")
1741        );
1742        // F(a) -> G(a)&y -> (x|a)&y
1743        assert_eq!(
1744            with_aliases([("F(x)", "G(x)&y"), ("G(y)", "x|y")]).parse_normalized("F(a)"),
1745            parse_normalized("(x|a)&y")
1746        );
1747        // F(G(a)) -> F(x|a) -> G(x|a)&y -> (x|(x|a))&y
1748        assert_eq!(
1749            with_aliases([("F(x)", "G(x)&y"), ("G(y)", "x|y")]).parse_normalized("F(G(a))"),
1750            parse_normalized("(x|(x|a))&y")
1751        );
1752
1753        // Function parameter should precede the symbol alias.
1754        assert_eq!(
1755            with_aliases([("F(X)", "X"), ("X", "x")]).parse_normalized("F(a)|X"),
1756            parse_normalized("a|x")
1757        );
1758
1759        // Function parameter shouldn't be expanded in symbol alias.
1760        assert_eq!(
1761            with_aliases([("F(x)", "x|A"), ("A", "x")]).parse_normalized("F(a)"),
1762            parse_normalized("a|x")
1763        );
1764
1765        // String literal should not be substituted with function parameter.
1766        assert_eq!(
1767            with_aliases([("F(x)", r#"x|"x""#)]).parse_normalized("F(a)"),
1768            parse_normalized("a|'x'")
1769        );
1770
1771        // Modifier expression body as parameter.
1772        assert_eq!(
1773            with_aliases([("F(x)", "all:x")]).parse_normalized("F(a|b)"),
1774            parse_normalized("all:(a|b)")
1775        );
1776
1777        // Function and symbol aliases reside in separate namespaces.
1778        assert_eq!(
1779            with_aliases([("A()", "A"), ("A", "a")]).parse_normalized("A()"),
1780            parse_normalized("a")
1781        );
1782
1783        // Invalid number of arguments.
1784        assert_eq!(
1785            *with_aliases([("F()", "x")]).parse("F(a)").unwrap_err().kind,
1786            RevsetParseErrorKind::InvalidFunctionArguments {
1787                name: "F".to_owned(),
1788                message: "Expected 0 arguments".to_owned()
1789            }
1790        );
1791        assert_eq!(
1792            *with_aliases([("F(x)", "x")]).parse("F()").unwrap_err().kind,
1793            RevsetParseErrorKind::InvalidFunctionArguments {
1794                name: "F".to_owned(),
1795                message: "Expected 1 arguments".to_owned()
1796            }
1797        );
1798        assert_eq!(
1799            *with_aliases([("F(x,y)", "x|y")])
1800                .parse("F(a,b,c)")
1801                .unwrap_err()
1802                .kind,
1803            RevsetParseErrorKind::InvalidFunctionArguments {
1804                name: "F".to_owned(),
1805                message: "Expected 2 arguments".to_owned()
1806            }
1807        );
1808        assert_eq!(
1809            *with_aliases([("F(x)", "x"), ("F(x,y)", "x|y")])
1810                .parse("F()")
1811                .unwrap_err()
1812                .kind,
1813            RevsetParseErrorKind::InvalidFunctionArguments {
1814                name: "F".to_owned(),
1815                message: "Expected 1 to 2 arguments".to_owned()
1816            }
1817        );
1818        assert_eq!(
1819            *with_aliases([("F()", "x"), ("F(x,y)", "x|y")])
1820                .parse("F(a)")
1821                .unwrap_err()
1822                .kind,
1823            RevsetParseErrorKind::InvalidFunctionArguments {
1824                name: "F".to_owned(),
1825                message: "Expected 0, 2 arguments".to_owned()
1826            }
1827        );
1828
1829        // Keyword argument isn't supported for now.
1830        assert_eq!(
1831            *with_aliases([("F(x)", "x")])
1832                .parse("F(x=y)")
1833                .unwrap_err()
1834                .kind,
1835            RevsetParseErrorKind::InvalidFunctionArguments {
1836                name: "F".to_owned(),
1837                message: "Unexpected keyword arguments".to_owned()
1838            }
1839        );
1840
1841        // Infinite recursion, where the top-level error isn't of RecursiveAlias kind.
1842        assert_eq!(
1843            *with_aliases([("F(x)", "G(x)"), ("G(x)", "H(x)"), ("H(x)", "F(x)")])
1844                .parse("F(a)")
1845                .unwrap_err()
1846                .kind,
1847            RevsetParseErrorKind::InAliasExpansion("F(x)".to_owned())
1848        );
1849        assert_eq!(
1850            *with_aliases([("F(x)", "F(x,b)"), ("F(x,y)", "F(x|y)")])
1851                .parse("F(a)")
1852                .unwrap_err()
1853                .kind,
1854            RevsetParseErrorKind::InAliasExpansion("F(x)".to_owned())
1855        );
1856    }
1857
1858    #[test]
1859    fn test_expand_with_locals() {
1860        // Local variable should precede the symbol alias.
1861        assert_eq!(
1862            with_aliases([("A", "symbol")])
1863                .set_local("A", "local")
1864                .parse_normalized("A"),
1865            parse_normalized("local")
1866        );
1867
1868        // Local variable shouldn't be expanded within aliases.
1869        assert_eq!(
1870            with_aliases([("B", "A"), ("F(x)", "x&A")])
1871                .set_local("A", "a")
1872                .parse_normalized("A|B|F(A)"),
1873            parse_normalized("a|A|(a&A)")
1874        );
1875    }
1876}